-
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
83da84c
commit 1053248
Showing
6 changed files
with
420 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,338 @@ | ||
package component | ||
|
||
import ( | ||
"strconv" | ||
|
||
"gioui.org/layout" | ||
"gioui.org/unit" | ||
"gioui.org/widget" | ||
"gioui.org/widget/material" | ||
"github.com/google/uuid" | ||
|
||
"github.com/chapar-rest/chapar/internal/domain" | ||
"github.com/chapar-rest/chapar/ui/chapartheme" | ||
"github.com/chapar-rest/chapar/ui/keys" | ||
"github.com/chapar-rest/chapar/ui/widgets" | ||
) | ||
|
||
type Variables struct { | ||
theme *chapartheme.Theme | ||
|
||
Items []*Variable | ||
addButton *widgets.IconButton | ||
list *widget.List | ||
|
||
onSelectFile func(id string) | ||
onChanged func(values []domain.Variable) | ||
|
||
preview string | ||
} | ||
|
||
type Variable struct { | ||
Identifier string | ||
TargetEnvVariable string // The environment variable to set | ||
From domain.VariableFrom // Source: "body", "header", "cookie" | ||
SourceKey string // For "header" or "cookie", specify the key name | ||
OnStatusCode int // Trigger on a specific status code | ||
JsonPath string // JSONPath for extracting value (for "body") | ||
Enable bool // Enable or disable this variable | ||
|
||
targetEnvEditor *widget.Editor | ||
fromDropDown *widgets.DropDown | ||
sourceKeyEditor *widget.Editor | ||
onStatusCodeEditor *widgets.NumericEditor | ||
jsonPathCodeEditor *widget.Editor | ||
|
||
enableBool *widget.Bool | ||
deleteButton widget.Clickable | ||
} | ||
|
||
func NewVariables(theme *chapartheme.Theme, items ...*Variable) *Variables { | ||
f := &Variables{ | ||
theme: theme, | ||
addButton: &widgets.IconButton{ | ||
Icon: widgets.PlusIcon, | ||
Size: unit.Dp(20), | ||
Clickable: &widget.Clickable{}, | ||
}, | ||
list: &widget.List{ | ||
List: layout.List{ | ||
Axis: layout.Vertical, | ||
}, | ||
}, | ||
} | ||
|
||
for _, item := range items { | ||
f.addItem(item) | ||
} | ||
|
||
f.addButton.OnClick = func() { | ||
f.addItem(NewVariable()) | ||
if f.onChanged != nil { | ||
f.onChanged(f.GetValues()) | ||
} | ||
} | ||
|
||
return f | ||
} | ||
|
||
func (f *Variables) SetOnChanged(fn func(values []domain.Variable)) { | ||
f.onChanged = fn | ||
} | ||
|
||
func (f *Variables) GetValues() []domain.Variable { | ||
values := make([]domain.Variable, 0, len(f.Items)) | ||
for _, item := range f.Items { | ||
values = append(values, domain.Variable{ | ||
ID: item.Identifier, | ||
TargetEnvVariable: item.TargetEnvVariable, | ||
From: item.From, | ||
SourceKey: item.SourceKey, | ||
OnStatusCode: item.OnStatusCode, | ||
JsonPath: item.JsonPath, | ||
Enable: item.Enable, | ||
}) | ||
} | ||
return values | ||
} | ||
|
||
func (f *Variables) SetValues(values []domain.Variable) { | ||
f.Items = make([]*Variable, 0, len(values)) | ||
for _, item := range values { | ||
f.addItem(&Variable{ | ||
Identifier: item.ID, | ||
TargetEnvVariable: item.TargetEnvVariable, | ||
From: item.From, | ||
SourceKey: item.SourceKey, | ||
OnStatusCode: item.OnStatusCode, | ||
JsonPath: item.JsonPath, | ||
Enable: item.Enable, | ||
}) | ||
} | ||
} | ||
|
||
func (f *Variables) addItem(item *Variable) { | ||
item.fromDropDown = widgets.NewDropDownWithoutBorder( | ||
f.theme, | ||
widgets.NewDropDownOption("Body").WithIdentifier("body").WithValue("body"), | ||
widgets.NewDropDownOption("Header").WithIdentifier("header").WithValue("header"), | ||
widgets.NewDropDownOption("Cookie").WithIdentifier("cookie").WithValue("cookie"), | ||
) | ||
item.fromDropDown.SetSelectedByValue(item.From.String()) | ||
item.fromDropDown.MinWidth = unit.Dp(60) | ||
item.fromDropDown.MaxWidth = unit.Dp(80) | ||
|
||
item.targetEnvEditor = &widget.Editor{SingleLine: true} | ||
item.targetEnvEditor.SetText(item.TargetEnvVariable) | ||
|
||
item.sourceKeyEditor = &widget.Editor{SingleLine: true} | ||
item.sourceKeyEditor.SetText(item.SourceKey) | ||
|
||
item.onStatusCodeEditor = &widgets.NumericEditor{Editor: widget.Editor{SingleLine: true}} | ||
item.onStatusCodeEditor.Editor.SetText(strconv.Itoa(item.OnStatusCode)) | ||
|
||
item.enableBool = new(widget.Bool) | ||
item.enableBool.Value = item.Enable | ||
|
||
item.fromDropDown.SetOnChanged(func(selected string) { | ||
item.From = domain.VariableFrom(selected) | ||
f.triggerChanged() | ||
}) | ||
|
||
item.jsonPathCodeEditor = &widget.Editor{SingleLine: true} | ||
item.jsonPathCodeEditor.SetText(item.JsonPath) | ||
|
||
f.Items = append(f.Items, item) | ||
} | ||
|
||
func NewVariable() *Variable { | ||
return &Variable{ | ||
Identifier: uuid.NewString(), | ||
TargetEnvVariable: "", | ||
From: domain.VariableFromBody, | ||
SourceKey: "", | ||
OnStatusCode: 0, | ||
JsonPath: "", | ||
Enable: true, | ||
} | ||
} | ||
|
||
func (f *Variables) itemLayout(gtx layout.Context, theme *chapartheme.Theme, item *Variable) layout.Dimensions { | ||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, f.itemLayouts(gtx, theme, item)...) | ||
} | ||
|
||
func (f *Variables) triggerChanged() { | ||
if f.onChanged != nil { | ||
f.onChanged(f.GetValues()) | ||
} | ||
} | ||
|
||
func (f *Variables) itemLayouts(gtx layout.Context, theme *chapartheme.Theme, item *Variable) []layout.FlexChild { | ||
keys.OnEditorChange(gtx, item.targetEnvEditor, func() { | ||
item.TargetEnvVariable = item.targetEnvEditor.Text() | ||
f.triggerChanged() | ||
}) | ||
|
||
keys.OnEditorChange(gtx, item.sourceKeyEditor, func() { | ||
item.SourceKey = item.sourceKeyEditor.Text() | ||
f.triggerChanged() | ||
}) | ||
|
||
keys.OnEditorChange(gtx, &item.onStatusCodeEditor.Editor, func() { | ||
item.OnStatusCode = item.onStatusCodeEditor.Value() | ||
f.triggerChanged() | ||
}) | ||
|
||
keys.OnEditorChange(gtx, item.jsonPathCodeEditor, func() { | ||
item.JsonPath = item.jsonPathCodeEditor.Text() | ||
f.triggerChanged() | ||
}) | ||
|
||
if item.enableBool.Update(gtx) { | ||
item.Enable = item.enableBool.Value | ||
f.triggerChanged() | ||
} | ||
|
||
items := []layout.FlexChild{ | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
ch := widgets.CheckBox(theme.Material(), item.enableBool, "") | ||
ch.IconColor = theme.CheckBoxColor | ||
return ch.Layout(gtx) | ||
}), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return layout.Inset{Left: unit.Dp(1), Right: unit.Dp(4)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(100)) | ||
editor := material.Editor(theme.Material(), item.sourceKeyEditor, "Target") | ||
editor.SelectionColor = theme.TextSelectionColor | ||
return editor.Layout(gtx) | ||
}) | ||
}), | ||
widgets.DrawLineFlex(theme.TableBorderColor, unit.Dp(35), unit.Dp(1)), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return item.fromDropDown.Layout(gtx, theme) | ||
}), | ||
widgets.DrawLineFlex(theme.TableBorderColor, unit.Dp(35), unit.Dp(1)), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return layout.Inset{Left: unit.Dp(4), Right: unit.Dp(4)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(40)) | ||
return item.onStatusCodeEditor.Layout(gtx, theme) | ||
}) | ||
}), | ||
widgets.DrawLineFlex(theme.TableBorderColor, unit.Dp(35), unit.Dp(1)), | ||
} | ||
|
||
itemType := item.fromDropDown.GetSelected().Identifier | ||
if itemType == string(domain.VariableFromBody) { | ||
items = append(items, layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { | ||
return layout.Inset{Left: unit.Dp(4), Right: unit.Dp(4)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
editor := material.Editor(theme.Material(), item.jsonPathCodeEditor, "e.g. $.data[0].name") | ||
editor.SelectionColor = theme.TextSelectionColor | ||
return editor.Layout(gtx) | ||
}) | ||
})) | ||
} else { | ||
items = append(items, layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { | ||
return layout.Inset{Left: unit.Dp(4), Right: unit.Dp(4)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
editor := material.Editor(theme.Material(), item.sourceKeyEditor, "Source Key") | ||
editor.SelectionColor = theme.TextSelectionColor | ||
return editor.Layout(gtx) | ||
}) | ||
})) | ||
} | ||
|
||
items = append(items, layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
ib := widgets.IconButton{ | ||
Icon: widgets.DeleteIcon, | ||
Size: unit.Dp(18), | ||
Color: theme.TextColor, | ||
Clickable: &item.deleteButton, | ||
} | ||
return ib.Layout(gtx, theme) | ||
})) | ||
|
||
return items | ||
} | ||
|
||
func (f *Variables) layout(gtx layout.Context, theme *chapartheme.Theme) layout.Dimensions { | ||
border := widget.Border{ | ||
Color: theme.TableBorderColor, | ||
CornerRadius: unit.Dp(4), | ||
Width: unit.Dp(1), | ||
} | ||
|
||
return border.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
if len(f.Items) == 0 { | ||
return layout.UniformInset(unit.Dp(10)).Layout(gtx, material.Label(theme.Material(), unit.Sp(14), "No items").Layout) | ||
} | ||
|
||
return material.List(theme.Material(), f.list).Layout(gtx, len(f.Items), func(gtx layout.Context, i int) layout.Dimensions { | ||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return f.itemLayout(gtx, theme, f.Items[i]) | ||
}), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
// only if it's not the last item | ||
if i == len(f.Items)-1 { | ||
return layout.Dimensions{} | ||
} | ||
return widgets.DrawLine(gtx, theme.TableBorderColor, unit.Dp(1), unit.Dp(gtx.Constraints.Max.X)) | ||
}), | ||
) | ||
}) | ||
}) | ||
} | ||
|
||
func (f *Variables) Layout(gtx layout.Context, title, hint string, theme *chapartheme.Theme) layout.Dimensions { | ||
for i, field := range f.Items { | ||
if field.deleteButton.Clicked(gtx) { | ||
f.Items = append(f.Items[:i], f.Items[i+1:]...) | ||
f.triggerChanged() | ||
} | ||
} | ||
|
||
inset := layout.Inset{Top: unit.Dp(15), Right: unit.Dp(10)} | ||
prevInset := layout.Inset{Top: unit.Dp(8), Bottom: unit.Dp(4)} | ||
return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle, Spacing: layout.SpaceBetween}.Layout(gtx, | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return material.Label(theme.Material(), theme.TextSize, title).Layout(gtx) | ||
}), | ||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { | ||
return layout.Inset{ | ||
Left: unit.Dp(10), | ||
Right: unit.Dp(10), | ||
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
return material.Label(theme.Material(), unit.Sp(10), hint).Layout(gtx) | ||
}) | ||
}), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return layout.Inset{ | ||
Top: 0, | ||
Bottom: unit.Dp(10), | ||
Left: 0, | ||
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
f.addButton.BackgroundColor = theme.Palette.Bg | ||
f.addButton.Color = theme.TextColor | ||
return f.addButton.Layout(gtx, theme) | ||
}) | ||
}), | ||
) | ||
}), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return f.layout(gtx, theme) | ||
}), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return prevInset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
return material.Label(theme.Material(), theme.TextSize, "Preview:").Layout(gtx) | ||
}) | ||
}), | ||
layout.Rigid(func(gtx layout.Context) layout.Dimensions { | ||
return prevInset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { | ||
return material.Label(theme.Material(), theme.TextSize, f.preview).Layout(gtx) | ||
}) | ||
}), | ||
) | ||
}) | ||
} |
Oops, something went wrong.