WIP essentials 16

This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 14:39:12 -04:00
1 parent c851668eb8
commit 719a059113
23 files changed
+1733 -1

No files matched your search

+260 -1
View File
@@ -1,3 +1,262 @@
package main
var essentialsPages = []page{}
var essentialsPages = []page{
{
ID: "481", Name: "SplitButton", ZhName: "拆分按钮 主操作 下拉", Category: "Essentials",
Demo: splitButtonDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.SplitButton(c, "Save", mujica.SplitButtonOptions{}, func(m *mujica.PopupMenu) {
if m.Item("Save as…", mujica.PopupMenuItemOptions{}) {
// ask for a file name
}
})
if r.Changed() {
// save
}
}`,
Keys: "Tab reaches the main half, then the menu half. Enter or Space runs the action; on the menu half, Enter, Space or Down opens the menu.",
},
{
ID: "482", Name: "CopyButton", ZhName: "复制按钮 剪贴板", Category: "Essentials",
Demo: copyButtonDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.CopyButton(c, "https://example.com/invite/42", mujica.CopyButtonOptions{Label: "Copy link"})
}`,
Keys: "Tab focuses; Enter or Space copies. The copied state is announced.",
},
{
ID: "483", Name: "HoldToConfirm", ZhName: "按住确认 危险操作", Category: "Essentials",
Demo: holdToConfirmDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.HoldToConfirm(c, "Delete project", mujica.HoldToConfirmOptions{}).Changed() {
// delete the project
}
}`,
Keys: "Focus it and hold Space for the full duration; a click, Enter or a short Space does nothing.",
},
{
ID: "484", Name: "BulkActionBar", ZhName: "批量操作条 多选", Category: "Essentials",
Demo: bulkActionBarDemo,
Code: `selected := 3
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.BulkActionBar(c, selected, mujica.BulkActionBarOptions{}, func() {
mujica.Button(c, "Archive", mujica.ButtonOptions{Variant: mujica.Secondary})
})
if r.Changed() {
selected = 0
}
}`,
Keys: "Tab moves through the bar's actions; Escape inside it clears the selection.",
},
{
ID: "485", Name: "ScrubInput", ZhName: "拖动数值 拖动标签", Category: "Essentials",
Demo: scrubInputDemo,
Code: `opacity := 0.6
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.ScrubInput(c, "Opacity", &opacity, mujica.ScrubInputOptions{Max: 1, Step: 0.1})
}`,
Keys: "The label is pointer-only; in the field, Up and Down step and typing sets the value.",
},
{
ID: "486", Name: "CurrencyInput", ZhName: "货币输入 金额 千分位", Category: "Essentials",
Demo: currencyInputDemo,
Code: `cents := int64(123450)
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.CurrencyInput(c, &cents, mujica.CurrencyInputOptions{Symbol: "$", Label: "Price"})
}`,
Keys: "Tab focuses the field, which shows plain digits while focused and groups thousands when left.",
},
{
ID: "487", Name: "MaskedInput", ZhName: "掩码输入 电话 卡号", Category: "Essentials",
Demo: maskedInputDemo,
Code: `phone := ""
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.MaskedInput(c, &phone, mujica.MaskedInputOptions{Mask: "(999) 999-9999", Label: "Phone"})
}`,
Keys: "Arrows, Home and End move the caret; Backspace and Delete remove; paste keeps the characters that fit; Enter submits.",
},
{
ID: "488", Name: "MentionInput", ZhName: "提及输入 @ 候选", Category: "Essentials",
Demo: mentionInputDemo,
Code: `text, mentions := "", []mujica.Mention(nil)
people := []mujica.MentionCandidate{{ID: "u1", Name: "Alice"}, {ID: "u2", Name: "Bob"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.MentionInput(c, &text, &mentions, mujica.MentionInputOptions{
Label: "Comment",
Candidates: func(q string) []mujica.MentionCandidate {
var out []mujica.MentionCandidate
for _, p := range people {
if strings.HasPrefix(strings.ToLower(p.Name), strings.ToLower(q)) {
out = append(out, p)
}
}
return out
},
})
}`,
Keys: "After @, Up and Down pick a candidate, Enter or Tab inserts it, Escape closes the list. Arrows step over a mention; Backspace removes it whole.",
},
{
ID: "489", Name: "ChoiceChips", ZhName: "筛选芯片 单选 多选", Category: "Essentials",
Demo: choiceChipsDemo,
Code: `var status []string
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.ChoiceChips(c, &status, []mujica.ToggleItem[string]{
{Value: "open", Label: "Open"}, {Value: "closed", Label: "Closed"},
}, mujica.ChoiceChipsOptions{Multiple: true, Label: "Status"})
}`,
Keys: "The chips are one Tab stop; Left and Right move, skipping disabled chips; Space or Enter toggles.",
},
{
ID: "490", Name: "Knob", ZhName: "旋钮 调节", Category: "Essentials",
Demo: knobDemo,
Code: `gain := 35.0
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.Knob(c, &gain, mujica.KnobOptions{Label: "Gain", Ticks: 11})
}`,
Keys: "Arrows step the value; Home and End go to the ends; drag up or down with the pointer.",
},
{
ID: "491", Name: "VerticalSlider", ZhName: "纵向滑块", Category: "Essentials",
Demo: verticalSliderDemo,
Code: `level := 40.0
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.VerticalSlider(c, &level, mujica.VerticalSliderOptions{Step: 10, Label: "Level"})
}`,
Keys: "Up and Right raise the value, Down and Left lower it, Home and End go to the ends.",
},
{
ID: "492", Name: "ColorPalette", ZhName: "预设色板 颜色", Category: "Essentials",
Demo: colorPaletteDemo,
Code: `color := ui.Hex("#7D2034")
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.ColorPalette(c, &color, []mujica.ColorSwatch{
{Color: ui.Hex("#7D2034"), Name: "Wine"},
{Color: ui.Hex("#AD9164"), Name: "Old gold"},
{Color: ui.Hex("#355E80"), Name: "Slate blue"},
}, mujica.ColorPaletteOptions{Custom: true, Label: "Tag color"})
}`,
Keys: "The palette is one Tab stop; Left and Right move by one swatch, Up and Down by a row; Enter or Space chooses.",
},
{
ID: "493", Name: "SignaturePad", ZhName: "签名板 手写", Category: "Essentials",
Demo: signaturePadDemo,
Code: `var strokes [][]mujica.SignaturePoint
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.SignaturePad(c, &strokes, mujica.SignaturePadOptions{}).Changed() {
_ = mujica.SignaturePath(strokes) // SVG path data to store
}
}`,
Keys: "Tab focuses the pad; Delete or Backspace clears it. Drawing needs the pointer.",
},
{
ID: "494", Name: "Banner", ZhName: "横幅 页面提示", Category: "Essentials",
Demo: bannerDemo,
Code: `open := true
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.Banner(c, &open, "Maintenance tonight at 22:00.", mujica.BannerOptions{
Severity: mujica.SeverityWarning,
Action: "Details",
})
if r.Action {
// show the details
}
}`,
Keys: "Tab reaches the action and the dismiss button; Enter or Space presses them.",
},
{
ID: "495", Name: "UndoToast", ZhName: "撤销提示 倒计时", Category: "Essentials",
Demo: undoToastDemo,
Code: `open := true
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.UndoToast(c, &open, "Note deleted", mujica.UndoToastOptions{
OnUndo: func() {
// restore the note
},
})
}`,
Keys: "It never takes the focus. Tab reaches Undo and the close button after the page; Escape inside closes it. Hover or focus pauses the countdown.",
},
{
ID: "496", Name: "Masonry", ZhName: "瀑布流 卡片", Category: "Essentials",
Demo: masonryDemo,
Code: `titles := []string{"Sea", "Forest", "Desert", "Glacier"}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.Masonry(c, len(titles), mujica.MasonryOptions{
Key: func(i int) string { return titles[i] },
}, func(i int) {
ui.Text(c, titles[i])
}).Size(380, 280)
}`,
Keys: "Scroll with the wheel or the scrollbar; only cards in view are built.",
},
{
ID: "497", Name: "ResizablePanelGroup", ZhName: "可调面板 分隔条", Category: "Essentials",
Demo: resizablePanelGroupDemo,
Code: `var sizes []float32
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.ResizablePanelGroup(c, &sizes, []mujica.ResizablePanel{
{Label: "Files", Collapsible: true, Build: func() { ui.Text(c, "Files") }},
{Label: "Editor", Build: func() { ui.Text(c, "Editor") }},
}, mujica.ResizablePanelGroupOptions{})
if r.Committed {
// save sizes
}
}`,
Keys: "Tab focuses a divider; arrows move it, Home and End go to the limits, Enter collapses or restores a collapsible neighbor.",
},
{
ID: "498", Name: "DockPanel", ZhName: "停靠面板 标签", Category: "Essentials",
Demo: dockPanelDemo,
Code: `var layout mujica.DockLayout
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.DockPanel(c, &layout, []mujica.DockItem{
{ID: "files", Title: "Files", Build: func() { ui.Text(c, "Files") }},
{ID: "terminal", Title: "Terminal", Build: func() { ui.Text(c, "$") }},
}, mujica.DockPanelOptions{})
if r.Changed() {
// save layout
}
}`,
Keys: "Each zone's tabs are one Tab stop; Left and Right switch tabs. Shift+Alt+Left, Right or Down docks the focused tab to that edge; Shift+Alt+Up returns it to the center.",
},
{
ID: "499", Name: "QRCode", ZhName: "二维码", Category: "Essentials",
Demo: qrCodeDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.QRCode(c, "https://example.com/mujica", mujica.QRCodeOptions{Level: mujica.QRQuartile})
}`,
Keys: "Not focusable; assistive technology reads its name.",
},
{
ID: "500", Name: "ActivityFeed", ZhName: "活动流 动态", Category: "Essentials",
Demo: activityFeedDemo,
Code: `events := []mujica.Activity{
{ID: "1", Actor: "Lin", Action: "commented on", Object: "Quarterly report", Time: time.Now()},
}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.ActivityFeed(c, events, mujica.ActivityFeedOptions{}).Size(380, 280)
}`,
Keys: "Scroll with the wheel or the scrollbar; only rows in view are built.",
},
}
+267
View File
@@ -0,0 +1,267 @@
package main
import (
"fmt"
"time"
"github.com/egoist/mygo/ui"
mujica "mujicaui"
)
func splitButtonDemo(c *ui.Context, log func(string)) {
type state struct{ variant, disabled bool }
s := ui.Local(c.Root(), "481", func() state { return state{} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Parameters", func() {
ui.Row(c).Gap(16).Children(func() {
ui.Checkbox(c, &s.variant, "Secondary")
ui.Checkbox(c, &s.disabled, "Disabled")
})
})
feedbackSection(c, "Live", func() {
v := mujica.Primary
if s.variant {
v = mujica.Secondary
}
ui.Row(c).Children(func() {
r := mujica.SplitButton(c, "Save", mujica.SplitButtonOptions{Variant: v, Disabled: s.disabled}, func(m *mujica.PopupMenu) {
for _, it := range []string{"Save as…", "Save a copy", "Save all"} {
if m.Item(it, mujica.PopupMenuItemOptions{}) {
log("split button: menu " + it)
}
}
})
if r.Changed() {
log("split button: main action")
}
})
})
})
}
func copyButtonDemo(c *ui.Context, log func(string)) {
type state struct {
text string
disabled bool
}
s := ui.Local(c.Root(), "482", func() state { return state{text: "https://example.com/invite/42"} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Parameters", func() {
ui.TextInput(c, &s.text).Width(320)
ui.Checkbox(c, &s.disabled, "Disabled")
})
feedbackSection(c, "Live", func() {
ui.Row(c).Gap(12).Children(func() {
if mujica.CopyButton(c, s.text, mujica.CopyButtonOptions{Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("copy button: copied %d characters", len([]rune(s.text))))
}
if mujica.CopyButton(c, s.text, mujica.CopyButtonOptions{Label: "Copy link", Disabled: s.disabled}).Changed() {
log("copy button: copied with label")
}
})
})
})
}
func holdToConfirmDemo(c *ui.Context, log func(string)) {
type state struct {
seconds float64
disabled bool
deleted int
}
s := ui.Local(c.Root(), "483", func() state { return state{seconds: 1.5} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Parameters", func() {
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
ui.Text(c, fmt.Sprintf("Hold %.1f s", s.seconds))
ui.Slider(c, &s.seconds, 0.5, 4).Width(200)
})
ui.Checkbox(c, &s.disabled, "Disabled")
})
feedbackSection(c, "Live (turn on reduced motion to see the countdown text)", func() {
ui.Row(c).Children(func() {
d := time.Duration(s.seconds * float64(time.Second))
if mujica.HoldToConfirm(c, "Delete project", mujica.HoldToConfirmOptions{Duration: d, Disabled: s.disabled}).Changed() {
s.deleted++
log("hold to confirm: confirmed")
}
})
ui.Text(c, fmt.Sprintf("Confirmed %d times", s.deleted))
})
})
}
func bulkActionBarDemo(c *ui.Context, log func(string)) {
type state struct {
picked [6]bool
disabled bool
}
s := ui.Local(c.Root(), "484", func() state { return state{picked: [6]bool{true, false, true}} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Rows", func() {
for i := range s.picked {
if ui.Checkbox(c, &s.picked[i], fmt.Sprintf("Invoice %d", 1001+i)).Changed() {
log(fmt.Sprintf("bulk bar: row %d %v", i, s.picked[i]))
}
}
ui.Checkbox(c, &s.disabled, "Disable the bar")
})
n := 0
for _, p := range s.picked {
if p {
n++
}
}
r := mujica.BulkActionBar(c, n, mujica.BulkActionBarOptions{Disabled: s.disabled}, func() {
if mujica.Button(c, "Archive", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
log(fmt.Sprintf("bulk bar: archive %d", n))
}
if mujica.Button(c, "Delete", mujica.ButtonOptions{Variant: mujica.Danger}).Clicked() {
log(fmt.Sprintf("bulk bar: delete %d", n))
}
})
if r.Changed() {
s.picked = [6]bool{}
log("bulk bar: cleared")
}
})
}
func scrubInputDemo(c *ui.Context, log func(string)) {
type state struct {
opacity, size float64
disabled bool
}
s := ui.Local(c.Root(), "485", func() state { return state{opacity: 0.6, size: 14} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Checkbox(c, &s.disabled, "Disabled")
feedbackSection(c, "Drag a label sideways, or type", func() {
if mujica.ScrubInput(c, "Opacity", &s.opacity, mujica.ScrubInputOptions{Max: 1, Step: 0.05, Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("scrub: opacity %.2f", s.opacity))
}
if mujica.ScrubInput(c, "Size", &s.size, mujica.ScrubInputOptions{Min: 8, Max: 72, Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("scrub: size %.0f", s.size))
}
})
})
}
func currencyInputDemo(c *ui.Context, log func(string)) {
type state struct {
cents, yen int64
disabled bool
}
s := ui.Local(c.Root(), "486", func() state { return state{cents: 123450, yen: 98000} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Checkbox(c, &s.disabled, "Disabled")
feedbackSection(c, "Live", func() {
ui.Row(c).Gap(12).Children(func() {
if mujica.CurrencyInput(c, &s.cents, mujica.CurrencyInputOptions{Symbol: "$", Label: "Price", Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("currency: %d cents", s.cents))
}
if mujica.CurrencyInput(c, &s.yen, mujica.CurrencyInputOptions{Symbol: "¥", NoFraction: true, Label: "Yen", Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("currency: %d yen", s.yen))
}
})
ui.Text(c, "Stored as "+mujica.CurrencyFormat(s.cents, 2, true)+" and "+mujica.CurrencyFormat(s.yen, 0, true))
})
})
}
func maskedInputDemo(c *ui.Context, log func(string)) {
type state struct {
mask int
value string
disabled bool
}
masks := []string{"(999) 999-9999", "9999 9999 9999 9999", "99/99/9999", "aa-9999"}
s := ui.Local(c.Root(), "487", func() state { return state{} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Parameters", func() {
if ui.Segmented(c, &s.mask, masks...).Changed() {
s.value = ""
log("masked: mask " + masks[s.mask])
}
ui.Checkbox(c, &s.disabled, "Disabled")
})
feedbackSection(c, "Live (try an input method: nothing commits until it does)", func() {
r := mujica.MaskedInput(c, &s.value, mujica.MaskedInputOptions{Mask: masks[s.mask], Label: "Masked field", Disabled: s.disabled})
r.Element.Width(260)
if r.Changed() {
log(fmt.Sprintf("masked: %d characters filled", len([]rune(s.value))))
}
if r.Submitted() {
log("masked: submitted")
}
})
})
}
func choiceChipsDemo(c *ui.Context, log func(string)) {
type state struct {
value []string
multiple, disabled bool
}
s := ui.Local(c.Root(), "489", func() state { return state{multiple: true, value: []string{"open"}} })
items := []mujica.ToggleItem[string]{{Value: "open", Label: "Open"}, {Value: "draft", Label: "Draft"}, {Value: "review", Label: "In review"}, {Value: "closed", Label: "Closed", Disabled: true}, {Value: "merged", Label: "Merged"}}
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Parameters", func() {
ui.Row(c).Gap(16).Children(func() {
if ui.Checkbox(c, &s.multiple, "Multiple").Changed() && !s.multiple && len(s.value) > 1 {
s.value = s.value[:1]
}
ui.Checkbox(c, &s.disabled, "Disabled")
})
})
feedbackSection(c, "Live", func() {
if mujica.ChoiceChips(c, &s.value, items, mujica.ChoiceChipsOptions{Multiple: s.multiple, Disabled: s.disabled, Label: "Status"}).Changed() {
log(fmt.Sprintf("chips: %v", s.value))
}
})
})
}
func knobDemo(c *ui.Context, log func(string)) {
type state struct {
gain, mix float64
disabled bool
}
s := ui.Local(c.Root(), "490", func() state { return state{gain: 35, mix: 0.5} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Checkbox(c, &s.disabled, "Disabled")
ui.Row(c).Gap(32).Children(func() {
if mujica.Knob(c, &s.gain, mujica.KnobOptions{Label: "Gain", Ticks: 11, Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("knob: gain %.1f", s.gain))
}
if mujica.Knob(c, &s.mix, mujica.KnobOptions{Max: 1, Step: 0.1, Size: 72, Label: "Mix", Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("knob: mix %.1f", s.mix))
}
})
})
}
func verticalSliderDemo(c *ui.Context, log func(string)) {
type state struct {
levels [4]float64
showValue, disable bool
}
s := ui.Local(c.Root(), "491", func() state { return state{levels: [4]float64{40, 70, 20, 90}, showValue: true} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Row(c).Gap(16).Children(func() {
ui.Checkbox(c, &s.showValue, "Show value")
ui.Checkbox(c, &s.disable, "Disabled")
})
ui.Row(c).Gap(16).Children(func() {
for i := range s.levels {
opts := mujica.VerticalSliderOptions{Step: 10, ShowValue: s.showValue, Disabled: s.disable, Label: fmt.Sprintf("Band %d", i+1)}
if i == 0 {
opts.Marks = []mujica.SliderMark{{Value: 0, Label: "0"}, {Value: 50, Label: "50"}, {Value: 100, Label: "100"}}
}
if mujica.VerticalSlider(c, &s.levels[i], opts).Changed() {
log(fmt.Sprintf("vertical slider: band %d = %.0f", i+1, s.levels[i]))
}
}
})
})
}
+292
View File
@@ -0,0 +1,292 @@
package main
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/egoist/mygo/ui"
mujica "mujicaui"
)
func mentionInputDemo(c *ui.Context, log func(string)) {
type state struct {
text string
mentions []mujica.Mention
disabled bool
}
people := []mujica.MentionCandidate{{ID: "u1", Name: "Alice"}, {ID: "u2", Name: "Albert"}, {ID: "u3", Name: "Bob"}, {ID: "u4", Name: "Sakiko"}, {ID: "u5", Name: "Mutsumi"}}
s := ui.Local(c.Root(), "488", func() state { return state{} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Checkbox(c, &s.disabled, "Disabled")
feedbackSection(c, "Type @ to mention someone", func() {
r := mujica.MentionInput(c, &s.text, &s.mentions, mujica.MentionInputOptions{
Placeholder: "Write a comment…", Label: "Comment", Disabled: s.disabled,
Candidates: func(q string) []mujica.MentionCandidate {
var out []mujica.MentionCandidate
for _, p := range people {
if strings.HasPrefix(strings.ToLower(p.Name), strings.ToLower(q)) {
out = append(out, p)
}
}
return out
},
})
r.Element.Width(420)
if r.Changed() {
log(fmt.Sprintf("mention: %d mentions", len(s.mentions)))
}
if r.Submitted() {
log("mention: submitted")
}
for _, m := range s.mentions {
ui.Text(c, fmt.Sprintf("@%s → %s", m.Name, m.ID)).Key(m.ID)
}
})
})
}
func colorPaletteDemo(c *ui.Context, log func(string)) {
type state struct {
color ui.Color
custom, disabled bool
}
s := ui.Local(c.Root(), "492", func() state { return state{color: ui.Hex("#7D2034"), custom: true} })
swatches := []mujica.ColorSwatch{
{Color: ui.Hex("#7D2034"), Name: "Wine"}, {Color: ui.Hex("#A12635"), Name: "Crimson"}, {Color: ui.Hex("#AD9164"), Name: "Old gold"},
{Color: ui.Hex("#805714"), Name: "Bronze"}, {Color: ui.Hex("#326247"), Name: "Ivy"}, {Color: ui.Hex("#355E80"), Name: "Slate blue"},
{Color: ui.Hex("#4B3A5A"), Name: "Plum"}, {Color: ui.Hex("#211C1D"), Name: "Ink"}, {Color: ui.Hex("#6A5B55"), Name: "Taupe"},
{Color: ui.Hex("#D2C3AF"), Name: "Parchment"}, {Color: ui.Hex("#F2EBDD"), Name: "Ivory"}, {Color: ui.Hex("#FFFFFF"), Name: "White"},
}
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Row(c).Gap(16).Children(func() {
ui.Checkbox(c, &s.custom, "Custom entry")
ui.Checkbox(c, &s.disabled, "Disabled")
})
if mujica.ColorPalette(c, &s.color, swatches, mujica.ColorPaletteOptions{Columns: 6, Custom: s.custom, Disabled: s.disabled, Label: "Tag color"}).Changed() {
log(fmt.Sprintf("palette: %v", s.color))
}
})
}
func signaturePadDemo(c *ui.Context, log func(string)) {
type state struct {
strokes [][]mujica.SignaturePoint
disabled bool
}
s := ui.Local(c.Root(), "493", func() state { return state{} })
ui.Column(c).Gap(16).Width(480).Children(func() {
ui.Checkbox(c, &s.disabled, "Disabled")
if mujica.SignaturePad(c, &s.strokes, mujica.SignaturePadOptions{Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("signature: %d strokes, %d bytes of path", len(s.strokes), len(mujica.SignaturePath(s.strokes))))
}
})
}
func bannerDemo(c *ui.Context, log func(string)) {
type state struct {
sev int
action, closable, op bool
}
s := ui.Local(c.Root(), "494", func() state { return state{sev: 2, action: true, closable: true, op: true} })
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Parameters", func() {
ui.Segmented(c, &s.sev, "Info", "Success", "Warning", "Error")
ui.Row(c).Gap(16).Children(func() {
ui.Checkbox(c, &s.action, "Action")
ui.Checkbox(c, &s.closable, "Closable")
})
})
opts := mujica.BannerOptions{Severity: feedbackSeverities[s.sev]}
if s.action {
opts.Action = "Details"
}
var open *bool
if s.closable {
open = &s.op
}
r := mujica.Banner(c, open, "Scheduled maintenance tonight from 22:00 to 23:00.", opts)
if r.Action {
log("banner: action")
}
if r.Closed {
log("banner: closed")
}
if !s.op && mujica.Button(c, "Show again", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
s.op = true
log("banner: reopened")
}
})
}
func undoToastDemo(c *ui.Context, log func(string)) {
type state struct {
notes []string
removed string
open bool
seconds float64
}
s := ui.Local(c.Root(), "495", func() state {
return state{notes: []string{"Call the florist", "Book the hall", "Order candles"}, seconds: 5}
})
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
ui.Text(c, fmt.Sprintf("Countdown %.0f s", s.seconds))
ui.Slider(c, &s.seconds, 2, 10).Width(200)
})
for i, n := range s.notes {
ui.Row(c).Key(n).Gap(12).AlignItems(ui.Center).Children(func() {
ui.Text(c, n).Grow(1)
if mujica.Button(c, "Delete", mujica.ButtonOptions{Variant: mujica.Ghost}).Clicked() {
s.removed, s.open = n, true
s.notes = append(s.notes[:i:i], s.notes[i+1:]...)
log("undo toast: deleted " + n)
}
})
}
})
if !s.open {
return
}
removed := s.removed
r := mujica.UndoToast(c, &s.open, "Deleted “"+removed+"”", mujica.UndoToastOptions{
Duration: time.Duration(s.seconds * float64(time.Second)),
OnUndo: func() { s.notes = append(s.notes, removed) },
})
if r.Undone {
log("undo toast: undone " + removed)
}
if r.Expired {
log("undo toast: expired")
}
}
func masonryDemo(c *ui.Context, log func(string)) {
type state struct {
count, width float64
}
s := ui.Local(c.Root(), "496", func() state { return state{count: 40, width: 180} })
k := mujica.Tokens(c)
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
ui.Text(c, fmt.Sprintf("Items %.0f", s.count))
if ui.Slider(c, &s.count, 1, 10000).Width(200).Changed() {
log(fmt.Sprintf("masonry: %d items", int(s.count)))
}
ui.Text(c, fmt.Sprintf("Column %.0f", s.width))
ui.Slider(c, &s.width, 120, 320).Width(160)
})
mujica.Masonry(c, int(s.count), mujica.MasonryOptions{Key: strconv.Itoa, ColumnWidth: float32(s.width)}, func(i int) {
card := ui.Column(c).Height(float32(60+i*37%90)).Padding(10).Background(k.Surface).Border(1, k.Border).Radius(4).Cursor(ui.CursorPointer)
card.Children(func() { ui.Text(c, "Card "+strconv.Itoa(i+1)) })
if card.Clicked() {
log("masonry: clicked card " + strconv.Itoa(i+1))
}
}).FillWidth().Height(420)
})
}
func resizablePanelGroupDemo(c *ui.Context, log func(string)) {
type state struct {
sizes []float32
vertical, disabled bool
}
s := ui.Local(c.Root(), "497", func() state { return state{} })
k := mujica.Tokens(c)
pane := func(name string) func() {
return func() {
ui.Column(c).Fill().Padding(12).Background(k.Surface).Children(func() { ui.Text(c, name) })
}
}
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Row(c).Gap(16).Children(func() {
if ui.Checkbox(c, &s.vertical, "Vertical").Changed() {
s.sizes = nil
}
ui.Checkbox(c, &s.disabled, "Disabled")
})
r := mujica.ResizablePanelGroup(c, &s.sizes, []mujica.ResizablePanel{
{Label: "Files", Collapsible: true, Build: pane("Files")},
{Label: "Editor", Min: 160, Build: pane("Editor")},
{Label: "Outline", Collapsible: true, Build: pane("Outline")},
}, mujica.ResizablePanelGroupOptions{Vertical: s.vertical, Disabled: s.disabled})
r.Element.FillWidth().Height(320)
if r.Committed {
log(fmt.Sprintf("panels: save %.2f", s.sizes))
}
})
}
func dockPanelDemo(c *ui.Context, log func(string)) {
type state struct {
layout mujica.DockLayout
disabled bool
}
s := ui.Local(c.Root(), "498", func() state {
return state{layout: mujica.DockLayout{Zones: [4][]string{{"editor"}, {"files"}, {"outline"}, {"terminal", "problems"}}}}
})
var items []mujica.DockItem
for _, id := range []string{"files", "editor", "outline", "terminal", "problems"} {
items = append(items, mujica.DockItem{ID: id, Title: strings.ToUpper(id[:1]) + id[1:], Build: func() { ui.Text(c, "Contents of "+id) }})
}
ui.Column(c).Gap(16).FillWidth().Children(func() {
ui.Checkbox(c, &s.disabled, "Disabled")
r := mujica.DockPanel(c, &s.layout, items, mujica.DockPanelOptions{Disabled: s.disabled})
r.Element.FillWidth().Height(440)
if r.Changed() {
log(fmt.Sprintf("dock: save %v", s.layout.Zones))
}
})
}
func qrCodeDemo(c *ui.Context, log func(string)) {
type state struct {
text string
level int
size float64
}
s := ui.Local(c.Root(), "499", func() state { return state{text: "https://example.com/mujica", level: 1, size: 200} })
levels := []mujica.QRLevel{mujica.QRLow, mujica.QRMedium, mujica.QRQuartile, mujica.QRHigh}
ui.Column(c).Gap(16).FillWidth().Children(func() {
feedbackSection(c, "Parameters", func() {
if ui.TextInput(c, &s.text).Width(360).Changed() {
log(fmt.Sprintf("qr: %d bytes", len(s.text)))
}
if ui.Segmented(c, &s.level, "L", "M", "Q", "H").Changed() {
log("qr: level " + levels[s.level].String())
}
ui.Row(c).Gap(12).AlignItems(ui.Center).Children(func() {
ui.Text(c, fmt.Sprintf("Size %.0f", s.size))
ui.Slider(c, &s.size, 100, 320).Width(200)
})
})
mujica.QRCode(c, s.text, mujica.QRCodeOptions{Level: levels[s.level], Size: float32(s.size)})
if m, err := mujica.EncodeQR([]byte(s.text), levels[s.level]); err == nil {
ui.Text(c, fmt.Sprintf("Version %d, mask %d, %d × %d modules", m.Version, m.Mask, m.Size(), m.Size()))
}
})
}
func activityFeedDemo(c *ui.Context, log func(string)) {
type state struct{ events []mujica.Activity }
s := ui.Local(c.Root(), "500", func() state {
now := time.Now()
var ev []mujica.Activity
actors := []string{"Sakiko", "Mutsumi", "Uika", "Umiri", "Nyamu"}
verbs := []string{"commented on", "edited", "approved", "closed", "opened"}
for i := range 400 {
ev = append(ev, mujica.Activity{ID: strconv.Itoa(i), Actor: actors[i%5], Action: verbs[i*3%5], Object: fmt.Sprintf("Score #%d", 400-i), Time: now.Add(-time.Duration(i) * 97 * time.Minute)})
}
return state{events: ev}
})
ui.Column(c).Gap(16).FillWidth().Children(func() {
if mujica.Button(c, "Add event", mujica.ButtonOptions{Variant: mujica.Secondary}).AlignSelf(ui.Start).Clicked() {
e := mujica.Activity{ID: fmt.Sprint("n", len(s.events)), Actor: "You", Action: "added", Object: "a new note", Time: time.Now()}
s.events = append([]mujica.Activity{e}, s.events...)
log("activity: added " + e.ID)
}
mujica.ActivityFeed(c, s.events, mujica.ActivityFeedOptions{}).FillWidth().Height(420)
})
}