Reverted text-xs override. text-ss replaces the text-xs override while tailwind's existing text-xs replaces our text-2xs.
This commit is contained in:
@@ -232,7 +232,7 @@ func badgesSection() func() *VNode {
|
|||||||
ui.EnvBadge("staging"),
|
ui.EnvBadge("staging"),
|
||||||
// Production deliberately renders NOTHING — the badge exists to tell you that
|
// Production deliberately renders NOTHING — the badge exists to tell you that
|
||||||
// you are NOT in production, and a badge that is always there says nothing.
|
// you are NOT in production, and a badge that is always there says nothing.
|
||||||
Span(Attr("class", "text-xs text-ink-muted"),
|
Span(Attr("class", "text-ss text-ink-muted"),
|
||||||
Text("(production renders nothing — that is the point of it)")),
|
Text("(production renders nothing — that is the point of it)")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -473,7 +473,7 @@ func selectsSection() func() *VNode {
|
|||||||
field("Timezone", ui.FormTimezoneSelector(ui.FormSelectProps{
|
field("Timezone", ui.FormTimezoneSelector(ui.FormSelectProps{
|
||||||
Value: tz.Get(), OnChange: func(v string) { tz.Set(v) }})),
|
Value: tz.Get(), OnChange: func(v string) { tz.Set(v) }})),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("one="+orElse(one.Get(), "—")+" several="+orElse(strings.Join(langs.Get(), ","), "—"))),
|
Text("one="+orElse(one.Get(), "—")+" several="+orElse(strings.Join(langs.Get(), ","), "—"))),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -484,7 +484,7 @@ func selectsSection() func() *VNode {
|
|||||||
OnSelect: func(o ui.FormSelectOption) { picked.Set(o.Label + " <" + o.Value + ">") },
|
OnSelect: func(o ui.FormSelectOption) { picked.Set(o.Label + " <" + o.Value + ">") },
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("Two characters before it asks; 200 ms after you stop typing. A response for a query "+
|
Text("Two characters before it asks; 200 ms after you stop typing. A response for a query "+
|
||||||
"you have already typed past is DISCARDED rather than shown — which is the whole bug "+
|
"you have already typed past is DISCARDED rather than shown — which is the whole bug "+
|
||||||
"with hand-rolled autocompletes.")),
|
"with hand-rolled autocompletes.")),
|
||||||
@@ -500,7 +500,7 @@ func selectsSection() func() *VNode {
|
|||||||
ShowSelectAll: true,
|
ShowSelectAll: true,
|
||||||
OnChange: func(v []string) { tags.Set(v) },
|
OnChange: func(v []string) { tags.Set(v) },
|
||||||
}),
|
}),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("Same selection model as the field above; only the thing you click on differs.")),
|
Text("Same selection model as the field above; only the thing you click on differs.")),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -535,7 +535,7 @@ func togglesSection() func() *VNode {
|
|||||||
|
|
||||||
demo("Signature pad — "+itoa(len(signed.Get()))+" bytes of SVG",
|
demo("Signature pad — "+itoa(len(signed.Get()))+" bytes of SVG",
|
||||||
pad.Render(ui.SignaturePadProps{}),
|
pad.Render(ui.SignaturePadProps{}),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("Draw in it. Long strokes are smoothed; a single stray point is not a stroke and "+
|
Text("Draw in it. Long strokes are smoothed; a single stray point is not a stroke and "+
|
||||||
"is dropped, so a click does not leave a dot behind.")),
|
"is dropped, so a click does not leave a dot behind.")),
|
||||||
),
|
),
|
||||||
@@ -584,7 +584,7 @@ func datesSection() func() *VNode {
|
|||||||
OnNavigate: func(m time.Time) { month.Set(m) },
|
OnNavigate: func(m time.Time) { month.Set(m) },
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("The same grid the picker drops down, usable directly when you want it inline.")),
|
Text("The same grid the picker drops down, usable directly when you want it inline.")),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -676,7 +676,7 @@ func tablesSection() func() *VNode {
|
|||||||
SortDesc: gridDesc.Get(),
|
SortDesc: gridDesc.Get(),
|
||||||
SetSortDesc: func(b bool) { gridDesc.Set(b) },
|
SetSortDesc: func(b bool) { gridDesc.Set(b) },
|
||||||
}),
|
}),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("Click a Qty or Price cell and type. The grid does not own the rows — it tells you "+
|
Text("Click a Qty or Price cell and type. The grid does not own the rows — it tells you "+
|
||||||
"which cell changed and hands the value back; what you do with it is yours.")),
|
"which cell changed and hands the value back; what you do with it is yours.")),
|
||||||
),
|
),
|
||||||
@@ -801,7 +801,7 @@ func overlaysSection(push func(ui.ToastType, string)) func() *VNode {
|
|||||||
tipFocus.Render(Span(Text("Shown on FOCUS, not hover — tab to the field")),
|
tipFocus.Render(Span(Text("Shown on FOCUS, not hover — tab to the field")),
|
||||||
ui.FormInput(ui.FormInputProps{Placeholder: "Focus me"})),
|
ui.FormInput(ui.FormInputProps{Placeholder: "Focus me"})),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("Narrow the window and hover \"Right\": it flips to the left, and its arrow follows "+
|
Text("Narrow the window and hover \"Right\": it flips to the left, and its arrow follows "+
|
||||||
"it. A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.")),
|
"it. A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.")),
|
||||||
),
|
),
|
||||||
@@ -862,7 +862,7 @@ func overlaysSection(push func(ui.ToastType, string)) func() *VNode {
|
|||||||
hoverMenu.Item(ui.MenuItemProps{}, Text("Two")),
|
hoverMenu.Item(ui.MenuItemProps{}, Text("Two")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("Opening one closes the other — a single-open manager. Submenus are exempt, or a "+
|
Text("Opening one closes the other — a single-open manager. Submenus are exempt, or a "+
|
||||||
"submenu would close the very menu it belongs to as it opened. The items raise toasts, "+
|
"submenu would close the very menu it belongs to as it opened. The items raise toasts, "+
|
||||||
"which is how you can see that an item really does close its own menu.")),
|
"which is how you can see that an item really does close its own menu.")),
|
||||||
@@ -889,7 +889,7 @@ func overlaysSection(push func(ui.ToastType, string)) func() *VNode {
|
|||||||
}, ui.ModalOptions{Size: ui.ModalSmall})
|
}, ui.ModalOptions{Size: ui.ModalSmall})
|
||||||
}}),
|
}}),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("Open the modal, then the nested one inside it, and press Escape twice: modals unwind "+
|
Text("Open the modal, then the nested one inside it, and press Escape twice: modals unwind "+
|
||||||
"ONE LAYER per press rather than all at once.")),
|
"ONE LAYER per press rather than all at once.")),
|
||||||
|
|
||||||
@@ -994,7 +994,7 @@ func feedbackSection(toaster *ui.Toaster, tour *ui.Tutorial) func() *VNode {
|
|||||||
demo("The guided tour",
|
demo("The guided tour",
|
||||||
row("flex flex-wrap items-center gap-3",
|
row("flex flex-wrap items-center gap-3",
|
||||||
tour.StartButton(0, "", Text("Take the tour")),
|
tour.StartButton(0, "", Text("Take the tour")),
|
||||||
Span(Attr("class", "text-xs text-ink-muted"),
|
Span(Attr("class", "text-ss text-ink-muted"),
|
||||||
Text("It dims the page, cuts a hole around each target, and animates the spotlight from "+
|
Text("It dims the page, cuts a hole around each target, and animates the spotlight from "+
|
||||||
"one to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to.")),
|
"one to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to.")),
|
||||||
),
|
),
|
||||||
@@ -1006,7 +1006,7 @@ func feedbackSection(toaster *ui.Toaster, tour *ui.Tutorial) func() *VNode {
|
|||||||
OnClick: flash.Fire}),
|
OnClick: flash.Fire}),
|
||||||
ui.RemoteUpdateFlash(flash.Visible()),
|
ui.RemoteUpdateFlash(flash.Visible()),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("A two-second acknowledgement that data you are looking at was changed by somebody "+
|
Text("A two-second acknowledgement that data you are looking at was changed by somebody "+
|
||||||
"else. It is not a toast: it belongs next to the thing that moved, not in the corner.")),
|
"else. It is not a toast: it belongs next to the thing that moved, not in the corner.")),
|
||||||
),
|
),
|
||||||
@@ -1071,7 +1071,7 @@ func navigationSection() func() *VNode {
|
|||||||
demo("Accordion — one open at a time, or several",
|
demo("Accordion — one open at a time, or several",
|
||||||
row("grid gap-6 lg:grid-cols-2",
|
row("grid gap-6 lg:grid-cols-2",
|
||||||
row("flex flex-col gap-2",
|
row("flex flex-col gap-2",
|
||||||
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-ink-faint"),
|
P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-ink-faint"),
|
||||||
Text("SingleAccordion")),
|
Text("SingleAccordion")),
|
||||||
ui.SingleAccordion([]ui.AccordionItemData{
|
ui.SingleAccordion([]ui.AccordionItemData{
|
||||||
{Title: "What is Kjøl Wasm Web?", Content: panel("kjol's Go→WebAssembly UI engine.")},
|
{Title: "What is Kjøl Wasm Web?", Content: panel("kjol's Go→WebAssembly UI engine.")},
|
||||||
@@ -1080,7 +1080,7 @@ func navigationSection() func() *VNode {
|
|||||||
}, acc.Get(), func(i int) { acc.Set(i) }),
|
}, acc.Get(), func(i int) { acc.Set(i) }),
|
||||||
),
|
),
|
||||||
row("flex flex-col gap-2",
|
row("flex flex-col gap-2",
|
||||||
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-ink-faint"),
|
P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-ink-faint"),
|
||||||
Text("Accordion (several at once)")),
|
Text("Accordion (several at once)")),
|
||||||
ui.Accordion([]ui.AccordionItemData{
|
ui.Accordion([]ui.AccordionItemData{
|
||||||
{Title: "First", Content: panel("Open me.")},
|
{Title: "First", Content: panel("Open me.")},
|
||||||
@@ -1107,7 +1107,7 @@ func navigationSection() func() *VNode {
|
|||||||
}},
|
}},
|
||||||
}, func(id string) { side.Set(id) }, ""),
|
}, func(id string) { side.Set(id) }, ""),
|
||||||
),
|
),
|
||||||
P(Attr("class", "mt-3 text-xs text-ink-muted"),
|
P(Attr("class", "mt-3 text-ss text-ink-muted"),
|
||||||
Text("It reports the id you clicked and nothing else. The sidebar on THIS page is the "+
|
Text("It reports the id you clicked and nothing else. The sidebar on THIS page is the "+
|
||||||
"same idea, wired to scroll the section into view.")),
|
"same idea, wired to scroll the section into view.")),
|
||||||
),
|
),
|
||||||
@@ -1230,7 +1230,7 @@ func chartsSection() func() *VNode {
|
|||||||
row("mt-4 flex items-center gap-3",
|
row("mt-4 flex items-center gap-3",
|
||||||
ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Small: true, Text: "New data", OnClick: func() { seed.Set(seed.Get() + 1) }}),
|
ui.Button(ui.ButtonProps{Color: ui.ButtonPrimary, Small: true, Text: "New data", OnClick: func() { seed.Set(seed.Get() + 1) }}),
|
||||||
ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Small: true, Text: threeDLabel, OnClick: func() { threeD.Set(!threeD.Get()) }}),
|
ui.Button(ui.ButtonProps{Color: ui.ButtonNeutral, Small: true, Text: threeDLabel, OnClick: func() { threeD.Set(!threeD.Get()) }}),
|
||||||
Span(Attr("class", "text-xs text-ink-muted"), Text("Each chart takes a Title; a multi-series chart also draws a legend. Click a legend key (Errors, or a donut slice) to hide it — the scale and marks recompute. 3D extrudes the bars and tilts the donut.")),
|
Span(Attr("class", "text-ss text-ink-muted"), Text("Each chart takes a Title; a multi-series chart also draws a legend. Click a legend key (Errors, or a donut slice) to hide it — the scale and marks recompute. 3D extrudes the bars and tilts the donut.")),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -1247,7 +1247,7 @@ func chartsSection() func() *VNode {
|
|||||||
|
|
||||||
demo("US heatmap — a value per state, with proportional lat/lng points on top",
|
demo("US heatmap — a value per state, with proportional lat/lng points on top",
|
||||||
heat.Render(ui.USHeatmapProps{Data: usSignups, Points: usCities, Proportional: true}),
|
heat.Render(ui.USHeatmapProps{Data: usSignups, Points: usCities, Proportional: true}),
|
||||||
Span(Attr("class", "mt-3 block text-xs text-ink-muted"),
|
Span(Attr("class", "mt-3 block text-ss text-ink-muted"),
|
||||||
Text("webui.USHeatmap shades each state on the choropleth ramp and projects lat/lng points "+
|
Text("webui.USHeatmap shades each state on the choropleth ramp and projects lat/lng points "+
|
||||||
"with a Go albersUsa port — Anchorage and Honolulu land on the insets. Hover a state or a point.")),
|
"with a Go albersUsa port — Anchorage and Honolulu land on the insets. Hover a state or a point.")),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"kjol/lexer" // syntax highlighting for the code blocks — a string in, HTML out
|
"kjol/lexer" // syntax highlighting for the code blocks — a string in, HTML out
|
||||||
. "kjol/vdom"
|
. "kjol/vdom"
|
||||||
|
|
||||||
// The host API is dual-build — real under js/wasm, no-ops natively — so neutral page
|
// The host API is dual-build — real under js/wasm, no-ops natively — so neutral page
|
||||||
// code can measure the browser and still server-render. This page uses it for exactly
|
// code can measure the browser and still server-render. This page uses it for exactly
|
||||||
// one thing: reading the clock when hydration commits.
|
// one thing: reading the clock when hydration commits.
|
||||||
@@ -89,7 +90,7 @@ func docPage(eyebrow, title, lede string, sections ...*VNode) *VNode {
|
|||||||
mods := []Mod{Attr("class", "pb-16")}
|
mods := []Mod{Attr("class", "pb-16")}
|
||||||
mods = append(mods,
|
mods = append(mods,
|
||||||
Div(Attr("class", "border-b border-line pb-6"),
|
Div(Attr("class", "border-b border-line pb-6"),
|
||||||
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-accent"), Text(eyebrow)),
|
P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-accent"), Text(eyebrow)),
|
||||||
H1(Attr("class", "mt-2 text-3xl font-semibold tracking-tight text-text-heading"), Text(title)),
|
H1(Attr("class", "mt-2 text-3xl font-semibold tracking-tight text-text-heading"), Text(title)),
|
||||||
P(Attr("class", "mt-3 text-ink-muted leading-relaxed"), Text(lede)),
|
P(Attr("class", "mt-3 text-ink-muted leading-relaxed"), Text(lede)),
|
||||||
),
|
),
|
||||||
@@ -150,7 +151,7 @@ func codeLang(caption, lang, src string) *VNode {
|
|||||||
|
|
||||||
return Div(Attr("class", "mt-4 overflow-hidden rounded-default border border-neutral-800 bg-neutral-900"),
|
return Div(Attr("class", "mt-4 overflow-hidden rounded-default border border-neutral-800 bg-neutral-900"),
|
||||||
Div(Attr("class", "flex items-center gap-2 border-b border-neutral-800 px-4 py-2"),
|
Div(Attr("class", "flex items-center gap-2 border-b border-neutral-800 px-4 py-2"),
|
||||||
Span(Attr("class", "text-xs font-medium text-ink-faint font-mono"), Text(caption)),
|
Span(Attr("class", "text-ss font-medium text-ink-faint font-mono"), Text(caption)),
|
||||||
Span(Attr("class", "ml-auto rounded-full bg-white/5 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-faint"), Text(lang)),
|
Span(Attr("class", "ml-auto rounded-full bg-white/5 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-faint"), Text(lang)),
|
||||||
),
|
),
|
||||||
Pre(Attr("class", "overflow-x-auto px-4 py-3 text-[13px] leading-relaxed text-neutral-100 font-mono"), body),
|
Pre(Attr("class", "overflow-x-auto px-4 py-3 text-[13px] leading-relaxed text-neutral-100 font-mono"), body),
|
||||||
@@ -164,7 +165,7 @@ func demo(title string, body ...*VNode) *VNode {
|
|||||||
mods := []Mod{Attr("class", "mt-4 rounded-default border border-line bg-surface shadow-xs")}
|
mods := []Mod{Attr("class", "mt-4 rounded-default border border-line bg-surface shadow-xs")}
|
||||||
mods = append(mods,
|
mods = append(mods,
|
||||||
Div(Attr("class", "border-b border-line px-4 py-2"),
|
Div(Attr("class", "border-b border-line px-4 py-2"),
|
||||||
Span(Attr("class", "text-xs text-ink-muted"), Text(title)),
|
Span(Attr("class", "text-ss text-ink-muted"), Text(title)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
inner := []Mod{Attr("class", "p-4")}
|
inner := []Mod{Attr("class", "p-4")}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ func layerItem(d Deps, l Layer) *VNode {
|
|||||||
Span(Attr("class", "rounded-full bg-surface-raised px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-muted"),
|
Span(Attr("class", "rounded-full bg-surface-raised px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-muted"),
|
||||||
Text("reference")),
|
Text("reference")),
|
||||||
),
|
),
|
||||||
Span(Attr("class", "text-xs text-ink-muted"), Text(l.Tagline)),
|
Span(Attr("class", "text-ss text-ink-muted"), Text(l.Tagline)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,6 +257,6 @@ func layerItem(d Deps, l Layer) *VNode {
|
|||||||
}
|
}
|
||||||
return A(Attr("class", cls), Attr("href", l.Href),
|
return A(Attr("class", cls), Attr("href", l.Href),
|
||||||
Span(Attr("class", "text-sm font-medium text-ink"), Text(l.Name)),
|
Span(Attr("class", "text-sm font-medium text-ink"), Text(l.Name)),
|
||||||
Span(Attr("class", "text-xs text-ink-muted"), Text(l.Tagline)),
|
Span(Attr("class", "text-ss text-ink-muted"), Text(l.Tagline)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ serving "./wwwroot" on http://localhost:8085`
|
|||||||
func AboutPage(d Deps) func() *VNode {
|
func AboutPage(d Deps) func() *VNode {
|
||||||
return func() *VNode {
|
return func() *VNode {
|
||||||
return Div(Attr("class", "mx-auto max-w-3xl py-4"),
|
return Div(Attr("class", "mx-auto max-w-3xl py-4"),
|
||||||
P(Attr("class", "text-xs font-semibold uppercase tracking-widest text-accent"), Text("About")),
|
P(Attr("class", "text-ss font-semibold uppercase tracking-widest text-accent"), Text("About")),
|
||||||
H1(Attr("class", "mt-2 text-4xl font-semibold tracking-tight text-text-heading"), Text("Why this exists")),
|
H1(Attr("class", "mt-2 text-4xl font-semibold tracking-tight text-text-heading"), Text("Why this exists")),
|
||||||
|
|
||||||
P(Attr("class", "mt-6 text-lg leading-relaxed text-ink-soft"),
|
P(Attr("class", "mt-6 text-lg leading-relaxed text-ink-soft"),
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ function LayerItem(props: { layer: Layer; current?: Layer }) {
|
|||||||
reference
|
reference
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-ink-muted">{props.layer.tagline}</span>
|
<span class="text-ss text-ink-muted">{props.layer.tagline}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -113,7 +113,7 @@ function LayerItem(props: { layer: Layer; current?: Layer }) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span class="text-sm font-medium text-ink">{props.layer.name}</span>
|
<span class="text-sm font-medium text-ink">{props.layer.name}</span>
|
||||||
<span class="text-xs text-ink-muted">{props.layer.tagline}</span>
|
<span class="text-ss text-ink-muted">{props.layer.tagline}</span>
|
||||||
</a>
|
</a>
|
||||||
</Show>
|
</Show>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ function Panel(props: { title: string; children?: JSXElement }) {
|
|||||||
return (
|
return (
|
||||||
<div class="mt-4 rounded-default border border-line bg-surface shadow-xs">
|
<div class="mt-4 rounded-default border border-line bg-surface shadow-xs">
|
||||||
<div class="border-b border-line px-4 py-2">
|
<div class="border-b border-line px-4 py-2">
|
||||||
<span class="text-xs text-ink-muted">{props.title}</span>
|
<span class="text-ss text-ink-muted">{props.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4">{props.children}</div>
|
<div class="p-4">{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -153,7 +153,7 @@ function Body() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div class="border-b border-line pb-6">
|
<div class="border-b border-line pb-6">
|
||||||
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
<p class="text-ss font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
||||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">Components</h1>
|
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">Components</h1>
|
||||||
<p class="mt-3 leading-relaxed text-ink-muted">
|
<p class="mt-3 leading-relaxed text-ink-muted">
|
||||||
Every component in the Solid kit, running. Not a screenshot of one anywhere: each block
|
Every component in the Solid kit, running. Not a screenshot of one anywhere: each block
|
||||||
@@ -313,7 +313,7 @@ function Badges() {
|
|||||||
<span class="text-sm font-semibold text-ink">kjol</span>
|
<span class="text-sm font-semibold text-ink">kjol</span>
|
||||||
<EnvBadge />
|
<EnvBadge />
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
Nothing beside the wordmark? Then this build is production — which is what it is telling
|
Nothing beside the wordmark? Then this build is production — which is what it is telling
|
||||||
you.
|
you.
|
||||||
</p>
|
</p>
|
||||||
@@ -429,7 +429,7 @@ function Icons() {
|
|||||||
<Icon icon="star" size={18} solid />
|
<Icon icon="star" size={18} solid />
|
||||||
<Icon icon="star" size={18} solid={false} />
|
<Icon icon="star" size={18} solid={false} />
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
The last two are the same name in the solid and regular styles. Which one you get by
|
The last two are the same name in the solid and regular styles. Which one you get by
|
||||||
default is a CSS variable the app sets, read at runtime.
|
default is a CSS variable the app sets, read at runtime.
|
||||||
</p>
|
</p>
|
||||||
@@ -614,7 +614,7 @@ function Selects() {
|
|||||||
<FormTimezoneSelector value={tz()} onchange={(e: any) => setTz(e?.currentTarget?.value ?? e)} />
|
<FormTimezoneSelector value={tz()} onchange={(e: any) => setTz(e?.currentTarget?.value ?? e)} />
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
one: <span class="font-mono text-ink">{one() || "—"}</span> · several:{" "}
|
one: <span class="font-mono text-ink">{one() || "—"}</span> · several:{" "}
|
||||||
<span class="font-mono text-ink">{many().join(", ") || "—"}</span>
|
<span class="font-mono text-ink">{many().join(", ") || "—"}</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -635,7 +635,7 @@ function Selects() {
|
|||||||
onSelect={(value, option) => setPicked(option.label + " <" + value + ">")}
|
onSelect={(value, option) => setPicked(option.label + " <" + value + ">")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
Two characters before it asks; 200 ms after you stop typing. A response for a query you
|
Two characters before it asks; 200 ms after you stop typing. A response for a query you
|
||||||
have already typed past is DISCARDED rather than shown — which is the whole bug with
|
have already typed past is DISCARDED rather than shown — which is the whole bug with
|
||||||
hand-rolled autocompletes.
|
hand-rolled autocompletes.
|
||||||
@@ -655,7 +655,7 @@ function Selects() {
|
|||||||
searchable
|
searchable
|
||||||
showSelectAll
|
showSelectAll
|
||||||
/>
|
/>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
Same selection model as the field above; only the thing you click on differs.
|
Same selection model as the field above; only the thing you click on differs.
|
||||||
</p>
|
</p>
|
||||||
</Panel>
|
</Panel>
|
||||||
@@ -698,7 +698,7 @@ function Toggles() {
|
|||||||
|
|
||||||
<Panel title={"Signature pad — " + signed().length + " bytes of SVG"}>
|
<Panel title={"Signature pad — " + signed().length + " bytes of SVG"}>
|
||||||
<FormSignaturePad onchange={setSigned} />
|
<FormSignaturePad onchange={setSigned} />
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
Draw in it. Clearing it emits an empty string, so "did they sign?" is just a length check.
|
Draw in it. Clearing it emits an empty string, so "did they sign?" is just a length check.
|
||||||
</p>
|
</p>
|
||||||
</Panel>
|
</Panel>
|
||||||
@@ -735,7 +735,7 @@ function Dates() {
|
|||||||
<div class="max-w-xs">
|
<div class="max-w-xs">
|
||||||
<Calendar selected={day()} onSelect={setDay} />
|
<Calendar selected={day()} onSelect={setDay} />
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
The same grid the picker drops down, usable directly when you want it inline. Pass{" "}
|
The same grid the picker drops down, usable directly when you want it inline. Pass{" "}
|
||||||
<code class="font-mono">variant="month"</code> for the big version.
|
<code class="font-mono">variant="month"</code> for the big version.
|
||||||
</p>
|
</p>
|
||||||
@@ -859,7 +859,7 @@ function Tables() {
|
|||||||
sortDesc={sortDesc()}
|
sortDesc={sortDesc()}
|
||||||
setSortDesc={setSortDesc}
|
setSortDesc={setSortDesc}
|
||||||
/>
|
/>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
Click a Qty or Price cell and type; Tab and the arrow keys move between editable cells.
|
Click a Qty or Price cell and type; Tab and the arrow keys move between editable cells.
|
||||||
The grid does not own the rows — it tells you which cell changed and hands the value back.
|
The grid does not own the rows — it tells you which cell changed and hands the value back.
|
||||||
</p>
|
</p>
|
||||||
@@ -972,7 +972,7 @@ function Overlays() {
|
|||||||
<FormInput placeholder="Focus me" />
|
<FormInput placeholder="Focus me" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.
|
A tooltip that only answers to a mouse is a tooltip a keyboard user cannot read.
|
||||||
</p>
|
</p>
|
||||||
</Panel>
|
</Panel>
|
||||||
@@ -1048,7 +1048,7 @@ function Overlays() {
|
|||||||
</MenuContent>
|
</MenuContent>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
The items raise toasts, which is how you can see that an item really does close its own
|
The items raise toasts, which is how you can see that an item really does close its own
|
||||||
menu — and that the one marked closeOnClick={"{false}"} does not.
|
menu — and that the one marked closeOnClick={"{false}"} does not.
|
||||||
</p>
|
</p>
|
||||||
@@ -1064,7 +1064,7 @@ function Overlays() {
|
|||||||
Open wizard
|
Open wizard
|
||||||
</ButtonUI>
|
</ButtonUI>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
Open the modal, then the nested one inside it, and press Escape twice: modals unwind ONE
|
Open the modal, then the nested one inside it, and press Escape twice: modals unwind ONE
|
||||||
LAYER per press rather than all at once.
|
LAYER per press rather than all at once.
|
||||||
</p>
|
</p>
|
||||||
@@ -1190,7 +1190,7 @@ function Feedback() {
|
|||||||
Sticky (no timer)
|
Sticky (no timer)
|
||||||
</ButtonUI>
|
</ButtonUI>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
<code class="font-mono">useToast()</code> throws outside a{" "}
|
<code class="font-mono">useToast()</code> throws outside a{" "}
|
||||||
<code class="font-mono"><ToastProvider></code> — which is one of only two providers
|
<code class="font-mono"><ToastProvider></code> — which is one of only two providers
|
||||||
this kit has.
|
this kit has.
|
||||||
@@ -1200,7 +1200,7 @@ function Feedback() {
|
|||||||
<Panel title="The guided tour">
|
<Panel title="The guided tour">
|
||||||
<div class="flex flex-wrap items-center gap-3">
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
<StartTutorialButton>Take the tour</StartTutorialButton>
|
<StartTutorialButton>Take the tour</StartTutorialButton>
|
||||||
<span class="text-xs text-ink-muted">
|
<span class="text-ss text-ink-muted">
|
||||||
It dims the page, cuts a hole around each target, and animates the spotlight from one
|
It dims the page, cuts a hole around each target, and animates the spotlight from one
|
||||||
to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to.
|
to the next. Targets are CSS SELECTORS — the same section ids the sidebar jumps to.
|
||||||
</span>
|
</span>
|
||||||
@@ -1214,7 +1214,7 @@ function Feedback() {
|
|||||||
</ButtonUI>
|
</ButtonUI>
|
||||||
<RemoteUpdateFlash when={flash.visible()} />
|
<RemoteUpdateFlash when={flash.visible()} />
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
A brief acknowledgement that data you are looking at was changed by somebody else. It is
|
A brief acknowledgement that data you are looking at was changed by somebody else. It is
|
||||||
not a toast: it belongs next to the thing that moved, not in the corner.
|
not a toast: it belongs next to the thing that moved, not in the corner.
|
||||||
</p>
|
</p>
|
||||||
@@ -1269,7 +1269,7 @@ function Navigation() {
|
|||||||
<Panel title="Accordion — one open at a time, or several">
|
<Panel title="Accordion — one open at a time, or several">
|
||||||
<div class="grid gap-6 lg:grid-cols-2">
|
<div class="grid gap-6 lg:grid-cols-2">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-xs font-semibold uppercase tracking-widest text-ink-faint">
|
<p class="text-ss font-semibold uppercase tracking-widest text-ink-faint">
|
||||||
SingleAccordion
|
SingleAccordion
|
||||||
</p>
|
</p>
|
||||||
<SingleAccordion
|
<SingleAccordion
|
||||||
@@ -1281,7 +1281,7 @@ function Navigation() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-xs font-semibold uppercase tracking-widest text-ink-faint">
|
<p class="text-ss font-semibold uppercase tracking-widest text-ink-faint">
|
||||||
Accordion (several at once)
|
Accordion (several at once)
|
||||||
</p>
|
</p>
|
||||||
<Accordion
|
<Accordion
|
||||||
@@ -1314,7 +1314,7 @@ function Navigation() {
|
|||||||
onItemClick={setSide}
|
onItemClick={setSide}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
It scrolls the element whose <code class="font-mono">id</code> matches the item into view —
|
It scrolls the element whose <code class="font-mono">id</code> matches the item into view —
|
||||||
so these really do jump, because those sections really do exist on this page.
|
so these really do jump, because those sections really do exist on this page.
|
||||||
</p>
|
</p>
|
||||||
@@ -1353,7 +1353,7 @@ function Search() {
|
|||||||
onSelect={(value) => setHit(value)}
|
onSelect={(value) => setHit(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
The scorer is headless too: <code class="font-mono">rankFuzzyMatches</code> and{" "}
|
The scorer is headless too: <code class="font-mono">rankFuzzyMatches</code> and{" "}
|
||||||
<code class="font-mono">fuzzySegments</code> give you the ranking and the highlight runs,
|
<code class="font-mono">fuzzySegments</code> give you the ranking and the highlight runs,
|
||||||
and you render them however you like.
|
and you render them however you like.
|
||||||
@@ -1438,7 +1438,7 @@ function Charts() {
|
|||||||
<ButtonUI color={BUTTON_COLOR_NEUTRAL} small onclick={() => setThreeD(!threeD())}>
|
<ButtonUI color={BUTTON_COLOR_NEUTRAL} small onclick={() => setThreeD(!threeD())}>
|
||||||
{threeD() ? "Flat" : "3D"}
|
{threeD() ? "Flat" : "3D"}
|
||||||
</ButtonUI>
|
</ButtonUI>
|
||||||
<span class="text-xs text-ink-muted">
|
<span class="text-ss text-ink-muted">
|
||||||
Each chart carries a <code class="font-mono">title</code>; a multi-series chart also draws a
|
Each chart carries a <code class="font-mono">title</code>; a multi-series chart also draws a
|
||||||
legend. Click a legend key — say <em>Errors</em> or a donut slice — to hide it, and the scale
|
legend. Click a legend key — say <em>Errors</em> or a donut slice — to hide it, and the scale
|
||||||
and marks recompute from what's left. The <code class="font-mono">threeD</code> prop extrudes
|
and marks recompute from what's left. The <code class="font-mono">threeD</code> prop extrudes
|
||||||
@@ -1468,7 +1468,7 @@ function Charts() {
|
|||||||
{ name: "Retries", data: shuffle([5, 7, 3, 9]) },
|
{ name: "Retries", data: shuffle([5, 7, 3, 9]) },
|
||||||
]} height={260} />
|
]} height={260} />
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
<code class="font-mono">horizontal</code> runs the categories down the y-axis;{" "}
|
<code class="font-mono">horizontal</code> runs the categories down the y-axis;{" "}
|
||||||
<code class="font-mono">stacked</code> layers the series with a 2px surface gap between segments.
|
<code class="font-mono">stacked</code> layers the series with a 2px surface gap between segments.
|
||||||
</p>
|
</p>
|
||||||
@@ -1476,7 +1476,7 @@ function Charts() {
|
|||||||
|
|
||||||
<Panel title="US heatmap — a value per state, with proportional lat/lng points on top">
|
<Panel title="US heatmap — a value per state, with proportional lat/lng points on top">
|
||||||
<USHeatmap data={US_SIGNUPS} points={US_CITIES} proportional valueFormat={(v) => v.toLocaleString("en-US")} />
|
<USHeatmap data={US_SIGNUPS} points={US_CITIES} proportional valueFormat={(v) => v.toLocaleString("en-US")} />
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
<code class="font-mono">@ui/USHeatmap</code> shades each state on a themed sequential ramp and
|
<code class="font-mono">@ui/USHeatmap</code> shades each state on a themed sequential ramp and
|
||||||
projects <code class="font-mono">points</code> (latitude/longitude) with a dependency-free
|
projects <code class="font-mono">points</code> (latitude/longitude) with a dependency-free
|
||||||
albersUsa port — so Anchorage and Honolulu land on the Alaska and Hawaii insets. With{" "}
|
albersUsa port — so Anchorage and Honolulu land on the Alaska and Hawaii insets. With{" "}
|
||||||
@@ -1557,7 +1557,7 @@ function Theming() {
|
|||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-3 text-xs text-ink-muted">
|
<p class="mt-3 text-ss text-ink-muted">
|
||||||
The swatch class is written out in full in the source, not built as{" "}
|
The swatch class is written out in full in the source, not built as{" "}
|
||||||
<code class="font-mono">"bg-" + name</code>. Tailwind finds the classes it must compile by
|
<code class="font-mono">"bg-" + name</code>. Tailwind finds the classes it must compile by
|
||||||
scanning the source for literal strings — a concatenation is invisible to it, and every
|
scanning the source for literal strings — a concatenation is invisible to it, and every
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function Overview() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
<p class="text-ss font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
||||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
|
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
|
||||||
A Solid kit, built by a Go toolchain
|
A Solid kit, built by a Go toolchain
|
||||||
</h1>
|
</h1>
|
||||||
@@ -103,7 +103,7 @@ export function Overview() {
|
|||||||
function Stage(props: { n: string; title: string; body: string }) {
|
function Stage(props: { n: string; title: string; body: string }) {
|
||||||
return (
|
return (
|
||||||
<div class="flex gap-4 border-l-2 border-line pl-4">
|
<div class="flex gap-4 border-l-2 border-line pl-4">
|
||||||
<span class="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-surface-raised text-xs font-semibold text-ink-soft">
|
<span class="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-surface-raised text-ss font-semibold text-ink-soft">
|
||||||
{props.n}
|
{props.n}
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function Ssr() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="page-ssr mx-auto max-w-2xl px-4 py-14">
|
<div class="page-ssr mx-auto max-w-2xl px-4 py-14">
|
||||||
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
<p class="text-ss font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
||||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
|
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
|
||||||
This page was rendered by Go
|
This page was rendered by Go
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@@ -39,8 +39,7 @@
|
|||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
/* Custom text sizes */
|
/* Custom text sizes */
|
||||||
--text-2xs: 0.75rem; /* Same size as Tailwind's default text-xs */
|
--text-ss: 0.8rem; /* Override: Tailwind default is 0.75rem */
|
||||||
--text-xs: 0.8rem; /* Override: Tailwind default is 0.75rem */
|
|
||||||
|
|
||||||
/* Custom breakpoints (Tailwind defaults + xs + ultrawide) */
|
/* Custom breakpoints (Tailwind defaults + xs + ultrawide) */
|
||||||
--breakpoint-xs: 375px;
|
--breakpoint-xs: 375px;
|
||||||
|
|||||||
@@ -560,14 +560,14 @@ const HEADER_SORT_ICON_CLS: Record<AutoTableHeaderColor, string> = {
|
|||||||
export const HEADER_PADDING_CLS: Record<AutoTableSize, string> = {
|
export const HEADER_PADDING_CLS: Record<AutoTableSize, string> = {
|
||||||
[AUTOTABLE_SIZE_DEFAULT]: "p-4 text-sm",
|
[AUTOTABLE_SIZE_DEFAULT]: "p-4 text-sm",
|
||||||
[AUTOTABLE_SIZE_COMPACT]: "py-1.5 px-2 text-sm",
|
[AUTOTABLE_SIZE_COMPACT]: "py-1.5 px-2 text-sm",
|
||||||
[AUTOTABLE_SIZE_SUPERCOMPACT]: "py-1 px-2 text-xs",
|
[AUTOTABLE_SIZE_SUPERCOMPACT]: "py-1 px-2 text-ss",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Body cell padding per table size — applied via [&_td]: on the tbody.
|
// Body cell padding per table size — applied via [&_td]: on the tbody.
|
||||||
export const BODY_PADDING_CLS: Record<AutoTableSize, string> = {
|
export const BODY_PADDING_CLS: Record<AutoTableSize, string> = {
|
||||||
[AUTOTABLE_SIZE_DEFAULT]: "text-sm [&_td]:p-4",
|
[AUTOTABLE_SIZE_DEFAULT]: "text-sm [&_td]:p-4",
|
||||||
[AUTOTABLE_SIZE_COMPACT]: "text-sm [&_td]:py-1 [&_td]:px-2",
|
[AUTOTABLE_SIZE_COMPACT]: "text-sm [&_td]:py-1 [&_td]:px-2",
|
||||||
[AUTOTABLE_SIZE_SUPERCOMPACT]: "text-xs [&_td]:py-0.5 [&_td]:px-2",
|
[AUTOTABLE_SIZE_SUPERCOMPACT]: "text-ss [&_td]:py-0.5 [&_td]:px-2",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pagination padding per table size.
|
// Pagination padding per table size.
|
||||||
@@ -651,7 +651,7 @@ const FORMULA_PLACEHOLDER = `<span class="text-ink-faint">e.g. [Revenue] / SUM({
|
|||||||
|
|
||||||
// Mobile: filters collapse behind a toggle; lg+ always shows the filter row inline.
|
// Mobile: filters collapse behind a toggle; lg+ always shows the filter row inline.
|
||||||
const FILTERS_TOGGLE = "lg:hidden inline-flex w-full items-center justify-between gap-2 py-1 px-3 text-sm font-normal text-ink bg-surface-muted border border-line-strong rounded-default shadow-xs cursor-pointer hover:bg-surface-raised";
|
const FILTERS_TOGGLE = "lg:hidden inline-flex w-full items-center justify-between gap-2 py-1 px-3 text-sm font-normal text-ink bg-surface-muted border border-line-strong rounded-default shadow-xs cursor-pointer hover:bg-surface-raised";
|
||||||
const FILTERS_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1.5 text-xs font-semibold text-white bg-sky-700 rounded-full";
|
const FILTERS_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1.5 text-ss font-semibold text-white bg-sky-700 rounded-full";
|
||||||
const FILTERS_BODY_BASE = "grow flex flex-col gap-3 min-w-0 max-w-full items-stretch overflow-visible lg:items-end max-lg:[&>*]:w-full max-lg:[&>*]:min-w-0 max-lg:[&_.search-fields]:w-full max-lg:[&_.search-fields]:flex-col [&>.quick-date-tags]:w-full";
|
const FILTERS_BODY_BASE = "grow flex flex-col gap-3 min-w-0 max-w-full items-stretch overflow-visible lg:items-end max-lg:[&>*]:w-full max-lg:[&>*]:min-w-0 max-lg:[&_.search-fields]:w-full max-lg:[&_.search-fields]:flex-col [&>.quick-date-tags]:w-full";
|
||||||
const FILTERS_BODY_INLINE = FILTERS_BODY_BASE
|
const FILTERS_BODY_INLINE = FILTERS_BODY_BASE
|
||||||
+ " lg:flex lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5"
|
+ " lg:flex lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5"
|
||||||
@@ -668,14 +668,14 @@ export const SEARCH_FIELDS = "search-fields flex flex-col items-stretch gap-3 w-
|
|||||||
export const SEARCH_FIELDS_SPACER = "search-fields-spacer hidden lg:block flex-1 min-w-4 basis-full lg:basis-auto lg:min-w-[1rem]";
|
export const SEARCH_FIELDS_SPACER = "search-fields-spacer hidden lg:block flex-1 min-w-4 basis-full lg:basis-auto lg:min-w-[1rem]";
|
||||||
|
|
||||||
/** Combobox / custom filter control wrapper — pair with FormCombobox fieldWidth="grow". */
|
/** Combobox / custom filter control wrapper — pair with FormCombobox fieldWidth="grow". */
|
||||||
export const AUTOTABLE_FILTER_FIELD = "flex flex-col gap-1 min-w-0 w-full overflow-visible [&>label]:text-xs [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none";
|
export const AUTOTABLE_FILTER_FIELD = "flex flex-col gap-1 min-w-0 w-full overflow-visible [&>label]:text-ss [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none";
|
||||||
|
|
||||||
/** Sales report toolbar wrappers (presets row 1, filters + export row 2). */
|
/** Sales report toolbar wrappers (presets row 1, filters + export row 2). */
|
||||||
export const SALES_REPORT_TOOLBAR = "sales-report-toolbar flex flex-col gap-2.5 w-full min-w-0 lg:contents";
|
export const SALES_REPORT_TOOLBAR = "sales-report-toolbar flex flex-col gap-2.5 w-full min-w-0 lg:contents";
|
||||||
export const SALES_REPORT_PRESETS = "sales-report-toolbar__presets min-w-0 w-full [&_.quick-date-tags]:gap-1.5";
|
export const SALES_REPORT_PRESETS = "sales-report-toolbar__presets min-w-0 w-full [&_.quick-date-tags]:gap-1.5";
|
||||||
export const SALES_REPORT_FILTERS = "search-fields sales-report-toolbar__filters flex flex-col items-stretch gap-3 w-full overflow-visible lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5 lg:w-auto lg:min-w-0 min-w-0 pt-0.5 border-t border-line [&>*]:min-w-0 [&>*]:max-lg:w-full lg:[&>*]:w-auto lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:flex-[1_1_8rem] lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:max-w-[12rem] [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none";
|
export const SALES_REPORT_FILTERS = "search-fields sales-report-toolbar__filters flex flex-col items-stretch gap-3 w-full overflow-visible lg:flex-row lg:flex-wrap lg:items-end lg:gap-x-3.5 lg:gap-y-2.5 lg:w-auto lg:min-w-0 min-w-0 pt-0.5 border-t border-line [&>*]:min-w-0 [&>*]:max-lg:w-full lg:[&>*]:w-auto lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:flex-[1_1_8rem] lg:[&>*:not(.sales-report-filter-date):not(.sales-report-filter-ref)]:max-w-[12rem] [&_.relative]:w-full [&_.relative]:min-w-0 [&_.relative]:flex-none";
|
||||||
|
|
||||||
export const AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 w-full [&>label]:text-xs [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0";
|
export const AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 w-full [&>label]:text-ss [&>label]:font-medium [&>label]:text-ink [&_.relative]:w-full [&_.relative]:min-w-0";
|
||||||
export const AUTOTABLE_SEARCH_FIELD_WIDE = AUTOTABLE_SEARCH_FIELD + " lg:flex-[2_1_12rem] lg:max-w-[20rem]";
|
export const AUTOTABLE_SEARCH_FIELD_WIDE = AUTOTABLE_SEARCH_FIELD + " lg:flex-[2_1_12rem] lg:max-w-[20rem]";
|
||||||
export const AUTOTABLE_SEARCH_FIELD_NARROW = AUTOTABLE_SEARCH_FIELD + " lg:flex-[0_1_auto] lg:max-w-[11rem] lg:min-w-[7rem]";
|
export const AUTOTABLE_SEARCH_FIELD_NARROW = AUTOTABLE_SEARCH_FIELD + " lg:flex-[0_1_auto] lg:max-w-[11rem] lg:min-w-[7rem]";
|
||||||
export const AUTOTABLE_DATE_SEARCH_FIELD = AUTOTABLE_SEARCH_FIELD + " lg:flex-[1_1_10rem] lg:max-w-none";
|
export const AUTOTABLE_DATE_SEARCH_FIELD = AUTOTABLE_SEARCH_FIELD + " lg:flex-[1_1_10rem] lg:max-w-none";
|
||||||
@@ -1831,8 +1831,8 @@ function FormulaField(props: FormulaFieldProps) {
|
|||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
// -- Insert menus (Advanced mode), shown in a row above the formula input ----
|
// -- Insert menus (Advanced mode), shown in a row above the formula input ----
|
||||||
const MENU_TRIGGER = "inline-flex items-center gap-1 px-2 py-1 text-xs rounded-default border border-line-strong bg-surface-muted hover:bg-surface-raised text-ink cursor-pointer leading-none";
|
const MENU_TRIGGER = "inline-flex items-center gap-1 px-2 py-1 text-ss rounded-default border border-line-strong bg-surface-muted hover:bg-surface-raised text-ink cursor-pointer leading-none";
|
||||||
const MENU_SEARCH = "w-full text-xs p-1 border border-line-strong rounded-default focus:outline-2 focus:outline-sky-500 focus:outline-offset-1";
|
const MENU_SEARCH = "w-full text-ss p-1 border border-line-strong rounded-default focus:outline-2 focus:outline-sky-500 focus:outline-offset-1";
|
||||||
|
|
||||||
const columnMenu = () => <Show when={operandOptions.length}>
|
const columnMenu = () => <Show when={operandOptions.length}>
|
||||||
<Popover placement="bottom-start" standalone={true} open={colMenuOpen()} onOpenChange={setColMenuOpen}>
|
<Popover placement="bottom-start" standalone={true} open={colMenuOpen()} onOpenChange={setColMenuOpen}>
|
||||||
@@ -1850,17 +1850,17 @@ function FormulaField(props: FormulaFieldProps) {
|
|||||||
class="flex items-center rounded-default hover:bg-surface-raised"
|
class="flex items-center rounded-default hover:bg-surface-raised"
|
||||||
onMouseEnter={() => highlight([o.value])} onMouseLeave={() => highlight(null)}>
|
onMouseEnter={() => highlight([o.value])} onMouseLeave={() => highlight(null)}>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="flex-1 min-w-0 text-left truncate px-2 py-1 font-mono text-xs text-sky-600 dark:text-sky-400 bg-transparent border-0 cursor-pointer"
|
class="flex-1 min-w-0 text-left truncate px-2 py-1 font-mono text-ss text-sky-600 dark:text-sky-400 bg-transparent border-0 cursor-pointer"
|
||||||
title={"Insert [" + o.label + "] — this row's cell"}
|
title={"Insert [" + o.label + "] — this row's cell"}
|
||||||
onclick={(_e: MouseEvent) => { insertRef("[" + o.label + "]"); setColMenuOpen(false); }}>[{o.label}]</button>
|
onclick={(_e: MouseEvent) => { insertRef("[" + o.label + "]"); setColMenuOpen(false); }}>[{o.label}]</button>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="px-2 py-1 text-xs font-mono text-violet-600 hover:text-violet-800 bg-transparent border-0 cursor-pointer"
|
class="px-2 py-1 text-ss font-mono text-violet-600 hover:text-violet-800 bg-transparent border-0 cursor-pointer"
|
||||||
title={"Insert {" + o.label + "} — whole column"}
|
title={"Insert {" + o.label + "} — whole column"}
|
||||||
onclick={(_e: MouseEvent) => { insertRef("{" + o.label + "}"); setColMenuOpen(false); }}>{"{ }"}</button>
|
onclick={(_e: MouseEvent) => { insertRef("{" + o.label + "}"); setColMenuOpen(false); }}>{"{ }"}</button>
|
||||||
</div>}
|
</div>}
|
||||||
</For>
|
</For>
|
||||||
<Show when={filteredColumns().length === 0}>
|
<Show when={filteredColumns().length === 0}>
|
||||||
<div class="px-2 py-3 text-xs text-ink-muted text-center">No columns match.</div>
|
<div class="px-2 py-3 text-ss text-ink-muted text-center">No columns match.</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
@@ -1884,14 +1884,14 @@ function FormulaField(props: FormulaFieldProps) {
|
|||||||
{(f: { name: string; sig: string; desc: string }) => <button type="button"
|
{(f: { name: string; sig: string; desc: string }) => <button type="button"
|
||||||
class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex flex-col gap-0.5"
|
class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex flex-col gap-0.5"
|
||||||
onclick={(_e: MouseEvent) => { insertFunction(f.name); setFuncMenuOpen(false); }}>
|
onclick={(_e: MouseEvent) => { insertFunction(f.name); setFuncMenuOpen(false); }}>
|
||||||
<span class="font-mono text-xs text-ink" innerHTML={highlightFormula(f.sig)}></span>
|
<span class="font-mono text-ss text-ink" innerHTML={highlightFormula(f.sig)}></span>
|
||||||
<span class="text-[11px] text-ink-muted">{f.desc}</span>
|
<span class="text-[11px] text-ink-muted">{f.desc}</span>
|
||||||
</button>}
|
</button>}
|
||||||
</For>
|
</For>
|
||||||
</div>}
|
</div>}
|
||||||
</For>
|
</For>
|
||||||
<Show when={filteredFunctionGroups().length === 0}>
|
<Show when={filteredFunctionGroups().length === 0}>
|
||||||
<div class="px-2 py-3 text-xs text-ink-muted text-center">No functions match.</div>
|
<div class="px-2 py-3 text-ss text-ink-muted text-center">No functions match.</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
@@ -1911,12 +1911,12 @@ function FormulaField(props: FormulaFieldProps) {
|
|||||||
{(c: { name: string; desc: string }) => <button type="button"
|
{(c: { name: string; desc: string }) => <button type="button"
|
||||||
class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-center justify-between gap-2"
|
class="w-full text-left px-2 py-1 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-center justify-between gap-2"
|
||||||
onclick={(_e: MouseEvent) => { insertRef(c.name); setConstMenuOpen(false); }}>
|
onclick={(_e: MouseEvent) => { insertRef(c.name); setConstMenuOpen(false); }}>
|
||||||
<span class="font-mono text-xs text-amber-600 dark:text-amber-400">{c.name}</span>
|
<span class="font-mono text-ss text-amber-600 dark:text-amber-400">{c.name}</span>
|
||||||
<span class="text-[11px] text-ink-muted">{c.desc}</span>
|
<span class="text-[11px] text-ink-muted">{c.desc}</span>
|
||||||
</button>}
|
</button>}
|
||||||
</For>
|
</For>
|
||||||
<Show when={filteredConstants().length === 0}>
|
<Show when={filteredConstants().length === 0}>
|
||||||
<div class="px-2 py-3 text-xs text-ink-muted text-center">No constants match.</div>
|
<div class="px-2 py-3 text-ss text-ink-muted text-center">No constants match.</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
@@ -1948,7 +1948,7 @@ function FormulaField(props: FormulaFieldProps) {
|
|||||||
<HoverPopoverTrigger class="inline-flex items-center text-ink-faint hover:text-ink-soft cursor-help bg-transparent border-0 p-0 leading-none">
|
<HoverPopoverTrigger class="inline-flex items-center text-ink-faint hover:text-ink-soft cursor-help bg-transparent border-0 p-0 leading-none">
|
||||||
<Icon icon="circle-info" size={14} />
|
<Icon icon="circle-info" size={14} />
|
||||||
</HoverPopoverTrigger>
|
</HoverPopoverTrigger>
|
||||||
<HoverPopoverContent class="p-3 w-80 text-xs text-ink-soft leading-relaxed flex flex-col gap-2">
|
<HoverPopoverContent class="p-3 w-80 text-ss text-ink-soft leading-relaxed flex flex-col gap-2">
|
||||||
<div>
|
<div>
|
||||||
<div class="font-medium text-ink mb-1">References</div>
|
<div class="font-medium text-ink mb-1">References</div>
|
||||||
<div class="grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 items-center">
|
<div class="grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 items-center">
|
||||||
@@ -2098,7 +2098,7 @@ function CalculatedColumnForm(props: CalculatedColumnFormProps) {
|
|||||||
onHighlight={highlight}
|
onHighlight={highlight}
|
||||||
apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} />
|
apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} />
|
||||||
<Show when={formulaError()}>
|
<Show when={formulaError()}>
|
||||||
<p class="text-xs text-red-600 dark:text-red-400 mt-1">{formulaError()}</p>
|
<p class="text-ss text-red-600 dark:text-red-400 mt-1">{formulaError()}</p>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@@ -2130,11 +2130,11 @@ function CalculatedColumnForm(props: CalculatedColumnFormProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Show when={errorMsg()}>
|
<Show when={errorMsg()}>
|
||||||
<p class="text-xs text-red-600 dark:text-red-400">{errorMsg()}</p>
|
<p class="text-ss text-red-600 dark:text-red-400">{errorMsg()}</p>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="flex gap-2 items-center pt-1">
|
<div class="flex gap-2 items-center pt-1">
|
||||||
<Show when={!!column() && !!props.onRemove}>
|
<Show when={!!column() && !!props.onRemove}>
|
||||||
<ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-xs">Remove</span></ButtonLinkRed>
|
<ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-ss">Remove</span></ButtonLinkRed>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="flex gap-2 items-center ml-auto">
|
<div class="flex gap-2 items-center ml-auto">
|
||||||
<ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI>
|
<ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI>
|
||||||
@@ -2235,7 +2235,7 @@ function SummaryRowForm(props: SummaryRowFormProps) {
|
|||||||
onHighlight={highlight}
|
onHighlight={highlight}
|
||||||
apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} />
|
apiRef={(api: FormulaFieldApi) => { formulaApi = api; }} />
|
||||||
<Show when={formulaError()}>
|
<Show when={formulaError()}>
|
||||||
<p class="text-xs text-red-600 dark:text-red-400 mt-1">{formulaError()}</p>
|
<p class="text-ss text-red-600 dark:text-red-400 mt-1">{formulaError()}</p>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@@ -2257,11 +2257,11 @@ function SummaryRowForm(props: SummaryRowFormProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Show when={errorMsg()}>
|
<Show when={errorMsg()}>
|
||||||
<p class="text-xs text-red-600 dark:text-red-400">{errorMsg()}</p>
|
<p class="text-ss text-red-600 dark:text-red-400">{errorMsg()}</p>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="flex gap-2 items-center pt-1">
|
<div class="flex gap-2 items-center pt-1">
|
||||||
<Show when={!!row() && !!props.onRemove}>
|
<Show when={!!row() && !!props.onRemove}>
|
||||||
<ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-xs">Remove</span></ButtonLinkRed>
|
<ButtonLinkRed onclick={(_e: MouseEvent) => handleRemove()}><span class="text-ss">Remove</span></ButtonLinkRed>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="flex gap-2 items-center ml-auto">
|
<div class="flex gap-2 items-center ml-auto">
|
||||||
<ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI>
|
<ButtonUI small={true} color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={(_e: MouseEvent) => close()}>Cancel</ButtonUI>
|
||||||
@@ -2303,7 +2303,7 @@ function AddCalcMenu(props: AddCalcMenuProps) {
|
|||||||
<Icon icon="table-columns" size={16} class="mt-0.5 text-ink-muted" />
|
<Icon icon="table-columns" size={16} class="mt-0.5 text-ink-muted" />
|
||||||
<span class="flex flex-col">
|
<span class="flex flex-col">
|
||||||
<span class="text-sm font-medium text-ink">Calculated column</span>
|
<span class="text-sm font-medium text-ink">Calculated column</span>
|
||||||
<span class="text-xs text-ink-muted">A new column computed for each row</span>
|
<span class="text-ss text-ink-muted">A new column computed for each row</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</Show>
|
</Show>
|
||||||
@@ -2312,7 +2312,7 @@ function AddCalcMenu(props: AddCalcMenuProps) {
|
|||||||
<Icon icon="list-ol" size={16} class="mt-0.5 text-ink-muted" />
|
<Icon icon="list-ol" size={16} class="mt-0.5 text-ink-muted" />
|
||||||
<span class="flex flex-col">
|
<span class="flex flex-col">
|
||||||
<span class="text-sm font-medium text-ink">Summary row</span>
|
<span class="text-sm font-medium text-ink">Summary row</span>
|
||||||
<span class="text-xs text-ink-muted">A total or aggregate shown in the footer</span>
|
<span class="text-ss text-ink-muted">A total or aggregate shown in the footer</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const BADGE_AMBER = "amber";
|
|||||||
export const BADGE_NEUTRAL = "neutral";
|
export const BADGE_NEUTRAL = "neutral";
|
||||||
export const BADGE_MUTED = "muted";
|
export const BADGE_MUTED = "muted";
|
||||||
|
|
||||||
const BASE = "inline-flex items-center gap-1 text-xs font-semibold py-0.5 px-2 rounded-default whitespace-nowrap";
|
const BASE = "inline-flex items-center gap-1 text-ss font-semibold py-0.5 px-2 rounded-default whitespace-nowrap";
|
||||||
|
|
||||||
const COLORS: Record<string, string> = {
|
const COLORS: Record<string, string> = {
|
||||||
"green": "text-white bg-green-700",
|
"green": "text-white bg-green-700",
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export function SegmentedButtons(props: SegmentedButtonsProps) {
|
|||||||
const innerRadius = "rounded-default"; // chips: 4px
|
const innerRadius = "rounded-default"; // chips: 4px
|
||||||
const outerRadius = "rounded-md"; // track: 4px + 2px = 6px
|
const outerRadius = "rounded-md"; // track: 4px + 2px = 6px
|
||||||
|
|
||||||
const sizeCls = () => props.small ? "py-0.5 px-2 text-xs" : "py-1 px-3 text-sm";
|
const sizeCls = () => props.small ? "py-0.5 px-2 text-ss" : "py-1 px-3 text-sm";
|
||||||
const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed";
|
const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed";
|
||||||
const activeCls = "bg-surface text-ink shadow-sm";
|
const activeCls = "bg-surface text-ink shadow-sm";
|
||||||
const inactiveCls = "text-ink-muted hover:text-ink";
|
const inactiveCls = "text-ink-muted hover:text-ink";
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ export const CAL_MY_MONTH = "text-lg font-heading mx-4 flex-1 text-center font-s
|
|||||||
|
|
||||||
export const CAL_WEEKDAYS_PICKER = "grid grid-cols-7 gap-[2px] mb-1";
|
export const CAL_WEEKDAYS_PICKER = "grid grid-cols-7 gap-[2px] mb-1";
|
||||||
export const CAL_WEEKDAYS_MONTH = "grid grid-cols-7 border-b border-line";
|
export const CAL_WEEKDAYS_MONTH = "grid grid-cols-7 border-b border-line";
|
||||||
export const CAL_WEEKDAY_PICKER = "text-center text-xs font-semibold text-ink-muted p-1";
|
export const CAL_WEEKDAY_PICKER = "text-center text-ss font-semibold text-ink-muted p-1";
|
||||||
export const CAL_WEEKDAY_MONTH = "text-center text-xs font-semibold text-ink-muted p-2 uppercase tracking-wider";
|
export const CAL_WEEKDAY_MONTH = "text-center text-ss font-semibold text-ink-muted p-2 uppercase tracking-wider";
|
||||||
|
|
||||||
export const CAL_DAYS_PICKER = "grid grid-cols-7 gap-[2px]";
|
export const CAL_DAYS_PICKER = "grid grid-cols-7 gap-[2px]";
|
||||||
export const CAL_DAYS_MONTH = "grid grid-cols-7";
|
export const CAL_DAYS_MONTH = "grid grid-cols-7";
|
||||||
|
|
||||||
export const CAL_DAY_PICKER_BASE = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-ink p-0";
|
export const CAL_DAY_PICKER_BASE = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-ink p-0";
|
||||||
export const CAL_DAY_MONTH_BASE = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-xs bg-transparent cursor-pointer";
|
export const CAL_DAY_MONTH_BASE = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-ss bg-transparent cursor-pointer";
|
||||||
|
|
||||||
export const CAL_SELECT = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-ink cursor-pointer focus:outline-hidden focus:border-primary";
|
export const CAL_SELECT = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-ink cursor-pointer focus:outline-hidden focus:border-primary";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createSignal, createMemo, untrack, Show, For, JSXElement } from "solid-js";
|
import { createSignal, createMemo, untrack, Show, For, JSXElement } from "solid-js";
|
||||||
import { Icon } from "./Icons.tsx";
|
import { Icon } from "./Icons.tsx";
|
||||||
|
|
||||||
export const GRID_HEADER_CLS = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-xs font-bold uppercase text-ink whitespace-nowrap last:border-r-0";
|
export const GRID_HEADER_CLS = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-ss font-bold uppercase text-ink whitespace-nowrap last:border-r-0";
|
||||||
|
|
||||||
interface SortableHeaderProps {
|
interface SortableHeaderProps {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -411,7 +411,7 @@ export function CellGrid(props: CellGridProps) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableCls = () => "min-w-full w-max border-collapse " + (dense() ? "text-xs" : "text-sm");
|
const tableCls = () => "min-w-full w-max border-collapse " + (dense() ? "text-ss" : "text-sm");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums">
|
<div class="relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums">
|
||||||
|
|||||||
@@ -586,7 +586,7 @@ function CartesianTooltip(p: {
|
|||||||
});
|
});
|
||||||
const rows = () => p.props.series.map((s, i) => ({ s, i })).filter(({ i }) => !p.hidden.has(i));
|
const rows = () => p.props.series.map((s, i) => ({ s, i })).filter(({ i }) => !p.hidden.has(i));
|
||||||
return (
|
return (
|
||||||
<div class="pointer-events-none absolute z-10 min-w-32 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg"
|
<div class="pointer-events-none absolute z-10 min-w-32 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
|
||||||
style={style()}>
|
style={style()}>
|
||||||
<div class="mb-1 font-medium text-ink">{p.label}</div>
|
<div class="mb-1 font-medium text-ink">{p.label}</div>
|
||||||
<For each={rows()}>{(row) => (
|
<For each={rows()}>{(row) => (
|
||||||
@@ -704,7 +704,7 @@ function RadialChart(p: SubProps): JSXElement {
|
|||||||
<svg width={p.width} height={p.height} viewBox={`0 0 ${p.width} ${p.height}`} class="block" role="img"
|
<svg width={p.width} height={p.height} viewBox={`0 0 ${p.width} ${p.height}`} class="block" role="img"
|
||||||
innerHTML={body()} onpointermove={onMove} onpointerleave={() => setHover(null)} />
|
innerHTML={body()} onpointermove={onMove} onpointerleave={() => setHover(null)} />
|
||||||
<Show when={p.props.tooltip !== false && hover() !== null}>
|
<Show when={p.props.tooltip !== false && hover() !== null}>
|
||||||
<div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg"
|
<div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
|
||||||
style={{
|
style={{
|
||||||
left: `${clamp(pointer()[0], 8, p.width - 8)}px`,
|
left: `${clamp(pointer()[0], 8, p.width - 8)}px`,
|
||||||
top: `${clamp(pointer()[1], 8, p.height - 8)}px`,
|
top: `${clamp(pointer()[1], 8, p.height - 8)}px`,
|
||||||
@@ -752,7 +752,7 @@ function Legend(p: { props: ChartProps; colorOf: (i: number) => string; hidden:
|
|||||||
}>
|
}>
|
||||||
<span class="inline-block h-0.5 w-4 rounded-full" style={{ "background-color": p.colorOf(it.i), opacity: off() ? 0.35 : 1 }} />
|
<span class="inline-block h-0.5 w-4 rounded-full" style={{ "background-color": p.colorOf(it.i), opacity: off() ? 0.35 : 1 }} />
|
||||||
</Show>
|
</Show>
|
||||||
<span class={"text-xs " + (off() ? "text-ink-faint line-through" : "text-ink-soft")}>{it.name}</span>
|
<span class={"text-ss " + (off() ? "text-ink-faint line-through" : "text-ink-soft")}>{it.name}</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}}</For>
|
}}</For>
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ const CRM_TAB_ROW = "flex w-full overflow-x-auto text-sm";
|
|||||||
const CRM_TAB_BASE = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors";
|
const CRM_TAB_BASE = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors";
|
||||||
const CRM_TAB_INACTIVE = "border-b text-ink-muted hover:text-ink";
|
const CRM_TAB_INACTIVE = "border-b text-ink-muted hover:text-ink";
|
||||||
const CRM_TAB_ACTIVE = "border-x border-t-2 border-t-sky-700 text-primary";
|
const CRM_TAB_ACTIVE = "border-x border-t-2 border-t-sky-700 text-primary";
|
||||||
const CRM_TAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full";
|
const CRM_TAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full";
|
||||||
|
|
||||||
export function CrmTabGroup(props: CrmTabGroupProps) {
|
export function CrmTabGroup(props: CrmTabGroupProps) {
|
||||||
// Memoize props.items: callers pass items={[{content: <Comp/>}]} where each
|
// Memoize props.items: callers pass items={[{content: <Comp/>}]} where each
|
||||||
@@ -148,7 +148,7 @@ const CRM_SUBTAB_BASE = "flex items-center gap-1.5 py-1 px-3 cursor-pointer font
|
|||||||
const CRM_SUBTAB_DIVIDER = "border-l border-line-strong";
|
const CRM_SUBTAB_DIVIDER = "border-l border-line-strong";
|
||||||
const CRM_SUBTAB_ACTIVE = "bg-neutral-500 text-white";
|
const CRM_SUBTAB_ACTIVE = "bg-neutral-500 text-white";
|
||||||
const CRM_SUBTAB_INACTIVE = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink";
|
const CRM_SUBTAB_INACTIVE = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink";
|
||||||
const CRM_SUBTAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-black/10 text-current rounded-full";
|
const CRM_SUBTAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-black/10 text-current rounded-full";
|
||||||
|
|
||||||
export function CrmSubTabGroup(props: CrmTabGroupProps) {
|
export function CrmSubTabGroup(props: CrmTabGroupProps) {
|
||||||
// See CrmTabGroup: memoize eager `content` JSX so it's built once, in-owner.
|
// See CrmTabGroup: memoize eager `content` JSX so it's built once, in-owner.
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ function _prefixCls(small?: boolean, error?: unknown, onDark?: boolean): string
|
|||||||
+ (small ? " p-1 text-sm" : " p-2");
|
+ (small ? " p-1 text-sm" : " p-2");
|
||||||
}
|
}
|
||||||
|
|
||||||
const FORM_ERROR = "block text-red-600 text-xs mt-1";
|
const FORM_ERROR = "block text-red-600 text-ss mt-1";
|
||||||
const FORM_SUCCESS = "block text-green-600 text-xs mt-1";
|
const FORM_SUCCESS = "block text-green-600 text-ss mt-1";
|
||||||
const INPUT_GROUP = "flex flex-row items-stretch w-full text-sm";
|
const INPUT_GROUP = "flex flex-row items-stretch w-full text-sm";
|
||||||
|
|
||||||
const TRIGGER_BASE = "bg-white border border-neutral-300 rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-neutral-100 disabled:cursor-not-allowed";
|
const TRIGGER_BASE = "bg-white border border-neutral-300 rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-neutral-100 disabled:cursor-not-allowed";
|
||||||
@@ -662,8 +662,8 @@ export function FormSignaturePad(props: { value?: () => string; onchange?: (svg:
|
|||||||
onTouchEnd={stopDrawing}
|
onTouchEnd={stopDrawing}
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center justify-between px-2 py-1 border-t border-neutral-200 bg-neutral-50">
|
<div class="flex items-center justify-between px-2 py-1 border-t border-neutral-200 bg-neutral-50">
|
||||||
<span class="text-xs text-neutral-400 italic">{isEmpty() ? "Sign above" : ""}</span>
|
<span class="text-ss text-neutral-400 italic">{isEmpty() ? "Sign above" : ""}</span>
|
||||||
<button type="button" class="text-xs font-medium text-neutral-500 bg-transparent border-none cursor-pointer px-1 py-0.5 rounded-default hover:text-red-600 hover:bg-red-50 disabled:opacity-40 disabled:cursor-not-allowed" onclick={clearSignature} disabled={isEmpty()}>Clear</button>
|
<button type="button" class="text-ss font-medium text-neutral-500 bg-transparent border-none cursor-pointer px-1 py-0.5 rounded-default hover:text-red-600 hover:bg-red-50 disabled:opacity-40 disabled:cursor-not-allowed" onclick={clearSignature} disabled={isEmpty()}>Clear</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
@@ -1537,7 +1537,7 @@ export function FormMultiSelect(props: FormMultiSelectProps) {
|
|||||||
}
|
}
|
||||||
return <div ref={(el) => tagsContainerRef = el} class="flex flex-nowrap gap-1 overflow-hidden items-center">
|
return <div ref={(el) => tagsContainerRef = el} class="flex flex-nowrap gap-1 overflow-hidden items-center">
|
||||||
<For each={selectedOptions()}>
|
<For each={selectedOptions()}>
|
||||||
{(option) => <span class={"inline-flex items-center gap-0.5 bg-neutral-200 rounded-default whitespace-nowrap leading-none" + (props.small ? " py-0.5 px-1.5 text-xs" : " py-0.5 px-2 text-sm")}>
|
{(option) => <span class={"inline-flex items-center gap-0.5 bg-neutral-200 rounded-default whitespace-nowrap leading-none" + (props.small ? " py-0.5 px-1.5 text-ss" : " py-0.5 px-2 text-sm")}>
|
||||||
{option.label}
|
{option.label}
|
||||||
<button type="button" class="bg-transparent border-none p-0 cursor-pointer flex items-center hover:text-neutral-900" onclick={(e) => removeOption(option.value, e)}>
|
<button type="button" class="bg-transparent border-none p-0 cursor-pointer flex items-center hover:text-neutral-900" onclick={(e) => removeOption(option.value, e)}>
|
||||||
<IconInline icon="xmark" size={10} />
|
<IconInline icon="xmark" size={10} />
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ export function FuzzyMatch(props: FuzzyMatchProps) {
|
|||||||
|
|
||||||
const ScoreBadge = (p: { score: number }) =>
|
const ScoreBadge = (p: { score: number }) =>
|
||||||
<Show when={props.showScores}>
|
<Show when={props.showScores}>
|
||||||
<span class="ml-3 shrink-0 text-xs text-ink-faint">{p.score}</span>
|
<span class="ml-3 shrink-0 text-ss text-ink-faint">{p.score}</span>
|
||||||
</Show>;
|
</Show>;
|
||||||
|
|
||||||
return <div ref={containerRef} class={"relative " + (props.class ?? "")}>
|
return <div ref={containerRef} class={"relative " + (props.class ?? "")}>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ interface CodeBoxProps {
|
|||||||
|
|
||||||
export function CodeBox(props: CodeBoxProps): JSXElement {
|
export function CodeBox(props: CodeBoxProps): JSXElement {
|
||||||
return (
|
return (
|
||||||
<div class={"text-xs p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default " + (props.class || "")}>
|
<div class={"text-ss p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default " + (props.class || "")}>
|
||||||
<pre><code>{props.code}</code></pre>
|
<pre><code>{props.code}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -79,7 +79,7 @@ interface BreadcrumbsProps {
|
|||||||
|
|
||||||
export function Breadcrumbs(props: BreadcrumbsProps): JSXElement {
|
export function Breadcrumbs(props: BreadcrumbsProps): JSXElement {
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-row items-center text-ink-faint text-xs">
|
<div class="flex flex-row items-center text-ink-faint text-ss">
|
||||||
<For each={props.items}>{(crumb, index) => (
|
<For each={props.items}>{(crumb, index) => (
|
||||||
index() !== props.items.length - 1
|
index() !== props.items.length - 1
|
||||||
? (
|
? (
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const MODAL_SIZES: Record<ModalSize, string> = {
|
|||||||
"2xlarge":"max-w-5xl",
|
"2xlarge":"max-w-5xl",
|
||||||
"3xlarge":"max-w-6xl",
|
"3xlarge":"max-w-6xl",
|
||||||
"4xlarge":"max-w-7xl",
|
"4xlarge":"max-w-7xl",
|
||||||
"5xlarge":"max-w-[90rem]",
|
"5xlarge":"max-w-8xl",
|
||||||
full: "max-w-none",
|
full: "max-w-none",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,12 +117,12 @@ const CONFIRM_OK_VARIANTS = {
|
|||||||
const WIZARD_HEADER = "flex flex-col items-center gap-2 flex-1";
|
const WIZARD_HEADER = "flex flex-col items-center gap-2 flex-1";
|
||||||
const WIZARD_TITLE_ROW = "flex items-center justify-between w-full";
|
const WIZARD_TITLE_ROW = "flex items-center justify-between w-full";
|
||||||
const WIZARD_TITLE = "text-xl";
|
const WIZARD_TITLE = "text-xl";
|
||||||
const WIZARD_STEP_NAME = "text-xs font-semibold text-ink-soft uppercase tracking-wider";
|
const WIZARD_STEP_NAME = "text-ss font-semibold text-ink-soft uppercase tracking-wider";
|
||||||
const WIZARD_STEPS = "flex items-center justify-between relative w-full max-w-64";
|
const WIZARD_STEPS = "flex items-center justify-between relative w-full max-w-64";
|
||||||
const WIZARD_TRACK = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2";
|
const WIZARD_TRACK = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2";
|
||||||
const WIZARD_TRACK_FILL = "h-full bg-primary transition-[width] duration-300 ease-in-out";
|
const WIZARD_TRACK_FILL = "h-full bg-primary transition-[width] duration-300 ease-in-out";
|
||||||
const WIZARD_STEP_WRAP = "relative z-[1]";
|
const WIZARD_STEP_WRAP = "relative z-[1]";
|
||||||
const STEP_INDICATOR_BASE = "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out";
|
const STEP_INDICATOR_BASE = "w-7 h-7 rounded-full flex items-center justify-center text-ss font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out";
|
||||||
const STEP_INDICATOR_PENDING = "border-line-strong text-ink-faint bg-surface";
|
const STEP_INDICATOR_PENDING = "border-line-strong text-ink-faint bg-surface";
|
||||||
const STEP_INDICATOR_ACTIVE = "bg-primary text-white border-primary";
|
const STEP_INDICATOR_ACTIVE = "bg-primary text-white border-primary";
|
||||||
const STEP_INDICATOR_COMPLETED = "bg-primary text-white border-primary";
|
const STEP_INDICATOR_COMPLETED = "bg-primary text-white border-primary";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { For, createSignal, Show, JSXElement } from "solid-js";
|
|||||||
const NAV_ROOT = "bg-surface rounded-default shadow-sm border border-line py-2";
|
const NAV_ROOT = "bg-surface rounded-default shadow-sm border border-line py-2";
|
||||||
const NAV_LIST = "list-none m-0 p-0";
|
const NAV_LIST = "list-none m-0 p-0";
|
||||||
const NAV_BTN = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted";
|
const NAV_BTN = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted";
|
||||||
const NAV_SUBBTN = "w-full text-left py-1.5 pr-3 pl-8 text-xs cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted";
|
const NAV_SUBBTN = "w-full text-left py-1.5 pr-3 pl-8 text-ss cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted";
|
||||||
const NAV_ICON = "mr-2";
|
const NAV_ICON = "mr-2";
|
||||||
|
|
||||||
const LAYOUT_ROOT = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12";
|
const LAYOUT_ROOT = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12";
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export function TabGroup(props: TabGroupProps) {
|
|||||||
const b = resolveBadge(item.badge);
|
const b = resolveBadge(item.badge);
|
||||||
return b != null && b > 0;
|
return b != null && b > 0;
|
||||||
})()}>
|
})()}>
|
||||||
<span class="inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full">{resolveBadge(item.badge)}</span>
|
<span class="inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full">{resolveBadge(item.badge)}</span>
|
||||||
</Show>
|
</Show>
|
||||||
</button>
|
</button>
|
||||||
)}</For>
|
)}</For>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export function ToggleSwitch(props: ToggleSwitchProps) {
|
|||||||
class={"text-sm select-none " + (isDisabled() ? "text-ink-faint" : "text-ink")}
|
class={"text-sm select-none " + (isDisabled() ? "text-ink-faint" : "text-ink")}
|
||||||
onclick={(_e: MouseEvent) => toggle()}
|
onclick={(_e: MouseEvent) => toggle()}
|
||||||
>{resolve(props.label)}</span>}
|
>{resolve(props.label)}</span>}
|
||||||
{props.description !== undefined && <span class="text-xs text-ink-muted">{resolve(props.description)}</span>}
|
{props.description !== undefined && <span class="text-ss text-ink-muted">{resolve(props.description)}</span>}
|
||||||
</div>}
|
</div>}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ function TutorialPopover() {
|
|||||||
<Show when={displayedStep()?.title}>
|
<Show when={displayedStep()?.title}>
|
||||||
<span class="font-medium text-ink">{displayedStep()?.title}</span>
|
<span class="font-medium text-ink">{displayedStep()?.title}</span>
|
||||||
</Show>
|
</Show>
|
||||||
<span class="text-xs text-ink-muted">
|
<span class="text-ss text-ink-muted">
|
||||||
{(displayedStepIndex() + 1) + " of " + ctx.totalSteps()}
|
{(displayedStepIndex() + 1) + " of " + ctx.totalSteps()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ export function USHeatmap(props: USHeatmapProps): JSXElement {
|
|||||||
|
|
||||||
<Show when={props.tooltip !== false && tip()}>
|
<Show when={props.tooltip !== false && tip()}>
|
||||||
{(t) => (
|
{(t) => (
|
||||||
<div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg"
|
<div class="pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
|
||||||
style={{
|
style={{
|
||||||
left: `${clamp(pointer()[0], 8, 100000)}px`,
|
left: `${clamp(pointer()[0], 8, 100000)}px`,
|
||||||
top: `${Math.max(8, pointer()[1])}px`,
|
top: `${Math.max(8, pointer()[1])}px`,
|
||||||
@@ -222,7 +222,7 @@ export function USHeatmap(props: USHeatmapProps): JSXElement {
|
|||||||
function ChoroplethLegend(p: { min: number; max: number; steps: number; fmt: (v: number) => string }): JSXElement {
|
function ChoroplethLegend(p: { min: number; max: number; steps: number; fmt: (v: number) => string }): JSXElement {
|
||||||
const swatches = () => Array.from({ length: p.steps }, (_, i) => i + 1);
|
const swatches = () => Array.from({ length: p.steps }, (_, i) => i + 1);
|
||||||
return (
|
return (
|
||||||
<div class="mt-3 flex items-center gap-2 text-xs text-ink-muted">
|
<div class="mt-3 flex items-center gap-2 text-ss text-ink-muted">
|
||||||
<span style={{ "font-variant-numeric": "tabular-nums" }}>{p.fmt(p.min)}</span>
|
<span style={{ "font-variant-numeric": "tabular-nums" }}>{p.fmt(p.min)}</span>
|
||||||
<div class="flex overflow-hidden rounded-xs">
|
<div class="flex overflow-hidden rounded-xs">
|
||||||
<For each={swatches()}>{(k) => (
|
<For each={swatches()}>{(k) => (
|
||||||
|
|||||||
@@ -118,14 +118,14 @@ var atHeaderSortIconCls = map[AutoTableHeaderColor]string{
|
|||||||
var HEADER_PADDING_CLS = map[AutoTableSize]string{
|
var HEADER_PADDING_CLS = map[AutoTableSize]string{
|
||||||
AUTOTABLE_SIZE_DEFAULT: "p-4 text-sm",
|
AUTOTABLE_SIZE_DEFAULT: "p-4 text-sm",
|
||||||
AUTOTABLE_SIZE_COMPACT: "py-1.5 px-2 text-sm",
|
AUTOTABLE_SIZE_COMPACT: "py-1.5 px-2 text-sm",
|
||||||
AUTOTABLE_SIZE_SUPERCOMPACT: "py-1 px-2 text-xs",
|
AUTOTABLE_SIZE_SUPERCOMPACT: "py-1 px-2 text-ss",
|
||||||
}
|
}
|
||||||
|
|
||||||
// BODY_PADDING_CLS is the body cell padding per table size (applied via [&_td]:).
|
// BODY_PADDING_CLS is the body cell padding per table size (applied via [&_td]:).
|
||||||
var BODY_PADDING_CLS = map[AutoTableSize]string{
|
var BODY_PADDING_CLS = map[AutoTableSize]string{
|
||||||
AUTOTABLE_SIZE_DEFAULT: "text-sm [&_td]:p-4",
|
AUTOTABLE_SIZE_DEFAULT: "text-sm [&_td]:p-4",
|
||||||
AUTOTABLE_SIZE_COMPACT: "text-sm [&_td]:py-1 [&_td]:px-2",
|
AUTOTABLE_SIZE_COMPACT: "text-sm [&_td]:py-1 [&_td]:px-2",
|
||||||
AUTOTABLE_SIZE_SUPERCOMPACT: "text-xs [&_td]:py-0.5 [&_td]:px-2",
|
AUTOTABLE_SIZE_SUPERCOMPACT: "text-ss [&_td]:py-0.5 [&_td]:px-2",
|
||||||
}
|
}
|
||||||
|
|
||||||
// atPaginationPaddingCls is the pagination bar padding per table size.
|
// atPaginationPaddingCls is the pagination bar padding per table size.
|
||||||
@@ -2184,7 +2184,7 @@ const (
|
|||||||
AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 grow sm:grow-0 sm:w-48"
|
AUTOTABLE_SEARCH_FIELD = "flex flex-col gap-1 min-w-0 grow sm:grow-0 sm:w-48"
|
||||||
AUTOTABLE_SEARCH_SELECT = "w-full rounded-default border border-line-strong bg-surface px-3 py-2 text-sm"
|
AUTOTABLE_SEARCH_SELECT = "w-full rounded-default border border-line-strong bg-surface px-3 py-2 text-sm"
|
||||||
AUTOTABLE_FILTERS_TOGGLE = "sm:hidden inline-flex items-center gap-2 rounded-default border border-line-strong px-3 py-2 text-sm"
|
AUTOTABLE_FILTERS_TOGGLE = "sm:hidden inline-flex items-center gap-2 rounded-default border border-line-strong px-3 py-2 text-sm"
|
||||||
AUTOTABLE_FILTERS_BADGE = "ml-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-sky-600 px-1.5 text-xs font-semibold text-white"
|
AUTOTABLE_FILTERS_BADGE = "ml-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-sky-600 px-1.5 text-ss font-semibold text-white"
|
||||||
AUTOTABLE_TOOLBAR = "flex flex-wrap items-end justify-between gap-3 pb-3"
|
AUTOTABLE_TOOLBAR = "flex flex-wrap items-end justify-between gap-3 pb-3"
|
||||||
AUTOTABLE_ASIDE = "w-full sm:w-64 shrink-0 rounded-default border border-line-strong bg-surface p-3"
|
AUTOTABLE_ASIDE = "w-full sm:w-64 shrink-0 rounded-default border border-line-strong bg-surface p-3"
|
||||||
AUTOTABLE_ACCORDION_CELL = "w-10 text-center"
|
AUTOTABLE_ACCORDION_CELL = "w-10 text-center"
|
||||||
@@ -3141,7 +3141,7 @@ const (
|
|||||||
|
|
||||||
formulaPlaceholder = `<span class="text-ink-faint">e.g. [Revenue] / SUM({Revenue}) * 100</span>`
|
formulaPlaceholder = `<span class="text-ink-faint">e.g. [Revenue] / SUM({Revenue}) * 100</span>`
|
||||||
|
|
||||||
calcMenuTrigger = "inline-flex items-center gap-1 rounded-default border border-line-strong bg-surface-muted px-2 py-1 text-xs leading-none text-ink-soft cursor-pointer hover:bg-surface-raised"
|
calcMenuTrigger = "inline-flex items-center gap-1 rounded-default border border-line-strong bg-surface-muted px-2 py-1 text-ss leading-none text-ink-soft cursor-pointer hover:bg-surface-raised"
|
||||||
calcChooserItem = "w-full text-left px-2 py-2 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-start gap-2.5"
|
calcChooserItem = "w-full text-left px-2 py-2 rounded-default hover:bg-surface-raised cursor-pointer border-0 bg-transparent flex items-start gap-2.5"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -3630,7 +3630,7 @@ func calcChooserButton(icon, title, subtitle string, onClick func()) *vdom.VNode
|
|||||||
Icon(icon, 16, "mt-0.5 text-ink-muted"),
|
Icon(icon, 16, "mt-0.5 text-ink-muted"),
|
||||||
vdom.Span(vdom.Attr("class", "flex flex-col"),
|
vdom.Span(vdom.Attr("class", "flex flex-col"),
|
||||||
vdom.Span(vdom.Attr("class", "text-sm font-medium text-ink"), vdom.Text(title)),
|
vdom.Span(vdom.Attr("class", "text-sm font-medium text-ink"), vdom.Text(title)),
|
||||||
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(subtitle)),
|
vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(subtitle)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3639,7 +3639,7 @@ func calcListRow(title, subtitle string, onEdit, onDelete func()) *vdom.VNode {
|
|||||||
return vdom.Div(vdom.Attr("class", "flex items-center gap-2 rounded-default px-2 py-1 hover:bg-surface-muted"),
|
return vdom.Div(vdom.Attr("class", "flex items-center gap-2 rounded-default px-2 py-1 hover:bg-surface-muted"),
|
||||||
vdom.Div(vdom.Attr("class", "min-w-0 grow"),
|
vdom.Div(vdom.Attr("class", "min-w-0 grow"),
|
||||||
vdom.Div(vdom.Attr("class", "truncate text-sm font-medium text-ink"), vdom.Text(title)),
|
vdom.Div(vdom.Attr("class", "truncate text-sm font-medium text-ink"), vdom.Text(title)),
|
||||||
vdom.Div(vdom.Attr("class", "truncate font-mono text-xs text-ink-muted"), vdom.Text(subtitle)),
|
vdom.Div(vdom.Attr("class", "truncate font-mono text-ss text-ink-muted"), vdom.Text(subtitle)),
|
||||||
),
|
),
|
||||||
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "pencil", Title: "Edit", OnClick: onEdit}),
|
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "pencil", Title: "Edit", OnClick: onEdit}),
|
||||||
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "trash", Title: "Delete", OnClick: onDelete}),
|
Button(ButtonProps{Color: ButtonLightNeutral, Small: true, Icon: "trash", Title: "Delete", OnClick: onDelete}),
|
||||||
@@ -3793,7 +3793,7 @@ func (s *AutoTableState) calcForm(e *calcEditor, summary bool) *vdom.VNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if msg := e.errorMsg.Get(); msg != "" {
|
if msg := e.errorMsg.Get(); msg != "" {
|
||||||
fields = append(fields, vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(msg)))
|
fields = append(fields, vdom.P(vdom.Attr("class", "text-ss text-red-600 dark:text-red-400"), vdom.Text(msg)))
|
||||||
}
|
}
|
||||||
|
|
||||||
saveLabel := "Add"
|
saveLabel := "Add"
|
||||||
@@ -3897,7 +3897,7 @@ func (s *AutoTableState) calcBasicEditor(e *calcEditor, summary bool) *vdom.VNod
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
children = append(children, vdom.P(vdom.Attr("class", "text-xs text-ink-muted"),
|
children = append(children, vdom.P(vdom.Attr("class", "text-ss text-ink-muted"),
|
||||||
vdom.Text(calcBasicHint(e, summary))))
|
vdom.Text(calcBasicHint(e, summary))))
|
||||||
|
|
||||||
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-2")}, children)...)
|
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-2")}, children)...)
|
||||||
@@ -3929,11 +3929,11 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
|
|||||||
colItems = append(colItems,
|
colItems = append(colItems,
|
||||||
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("[" + label + "]") }},
|
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("[" + label + "]") }},
|
||||||
vdom.Span(vdom.Attr("class", "font-mono text-sky-600 dark:text-sky-400"), vdom.Text("["+label+"]")),
|
vdom.Span(vdom.Attr("class", "font-mono text-sky-600 dark:text-sky-400"), vdom.Text("["+label+"]")),
|
||||||
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-ink-muted"), vdom.Text("this row")),
|
vdom.Span(vdom.Attr("class", "ml-2 text-ss text-ink-muted"), vdom.Text("this row")),
|
||||||
),
|
),
|
||||||
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("{" + label + "}") }},
|
e.colMenu.Item(MenuItemProps{OnClick: func() { e.insertAtCaret("{" + label + "}") }},
|
||||||
vdom.Span(vdom.Attr("class", "font-mono text-violet-600 dark:text-violet-400"), vdom.Text("{"+label+"}")),
|
vdom.Span(vdom.Attr("class", "font-mono text-violet-600 dark:text-violet-400"), vdom.Text("{"+label+"}")),
|
||||||
vdom.Span(vdom.Attr("class", "ml-2 text-xs text-ink-muted"), vdom.Text("whole column")),
|
vdom.Span(vdom.Attr("class", "ml-2 text-ss text-ink-muted"), vdom.Text("whole column")),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3945,7 +3945,7 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
|
|||||||
groups := filterFormulaGroups(e.fnSearch.Get())
|
groups := filterFormulaGroups(e.fnSearch.Get())
|
||||||
if len(groups) == 0 {
|
if len(groups) == 0 {
|
||||||
fnItems = append(fnItems, vdom.Div(
|
fnItems = append(fnItems, vdom.Div(
|
||||||
vdom.Attr("class", "px-2 py-3 text-center text-xs text-ink-muted"),
|
vdom.Attr("class", "px-2 py-3 text-center text-ss text-ink-muted"),
|
||||||
vdom.Text("No functions match"),
|
vdom.Text("No functions match"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -3959,8 +3959,8 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
|
|||||||
vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5 min-w-0"),
|
vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5 min-w-0"),
|
||||||
// The signature is syntax-highlighted with the same highlighter the
|
// The signature is syntax-highlighted with the same highlighter the
|
||||||
// formula box uses, so the menu and the editor speak one language.
|
// formula box uses, so the menu and the editor speak one language.
|
||||||
vdom.Span(vdom.Attr("class", "font-mono text-xs"), vdom.Raw(HighlightFormula(f.Sig))),
|
vdom.Span(vdom.Attr("class", "font-mono text-ss"), vdom.Raw(HighlightFormula(f.Sig))),
|
||||||
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(f.Desc)),
|
vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(f.Desc)),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -3973,8 +3973,8 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
|
|||||||
OnClick: func() { e.insertAtCaret(cc.Name) },
|
OnClick: func() { e.insertAtCaret(cc.Name) },
|
||||||
},
|
},
|
||||||
vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5"),
|
vdom.Div(vdom.Attr("class", "flex flex-col items-start gap-0.5"),
|
||||||
vdom.Span(vdom.Attr("class", "font-mono text-xs text-amber-600 dark:text-amber-400"), vdom.Text(cc.Name)),
|
vdom.Span(vdom.Attr("class", "font-mono text-ss text-amber-600 dark:text-amber-400"), vdom.Text(cc.Name)),
|
||||||
vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(cc.Desc)),
|
vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(cc.Desc)),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -4028,7 +4028,7 @@ func (s *AutoTableState) calcAdvancedEditor(e *calcEditor, summary bool) *vdom.V
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
help := vdom.P(vdom.Attr("class", "text-xs text-ink-muted"),
|
help := vdom.P(vdom.Attr("class", "text-ss text-ink-muted"),
|
||||||
vdom.Text("[Column] is this row's cell · {Column} is the whole column · {Column:1:ROW()} is a running total."))
|
vdom.Text("[Column] is this row's cell · {Column} is the whole column · {Column:1:ROW()} is a running total."))
|
||||||
|
|
||||||
children := []*vdom.VNode{menus, editor, help}
|
children := []*vdom.VNode{menus, editor, help}
|
||||||
@@ -4048,7 +4048,7 @@ func (e *calcEditor) fnSearchBox() *vdom.VNode {
|
|||||||
vdom.Input(
|
vdom.Input(
|
||||||
vdom.WithRef(e.fnSearchRef),
|
vdom.WithRef(e.fnSearchRef),
|
||||||
vdom.Attr("type", "text"),
|
vdom.Attr("type", "text"),
|
||||||
vdom.Attr("class", "w-full rounded-default border border-line-strong p-1 text-xs focus:outline-2 focus:outline-sky-500"),
|
vdom.Attr("class", "w-full rounded-default border border-line-strong p-1 text-ss focus:outline-2 focus:outline-sky-500"),
|
||||||
vdom.Attr("placeholder", "Search functions…"),
|
vdom.Attr("placeholder", "Search functions…"),
|
||||||
vdom.Attr("spellcheck", "false"),
|
vdom.Attr("spellcheck", "false"),
|
||||||
vdom.Prop("value", e.fnSearch.Get()),
|
vdom.Prop("value", e.fnSearch.Get()),
|
||||||
@@ -4089,12 +4089,12 @@ func (s *AutoTableState) calcPreview(e *calcEditor, src string, summary bool) *v
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if _, err := CompileFormula(src); err != nil {
|
if _, err := CompileFormula(src); err != nil {
|
||||||
return vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
|
return vdom.P(vdom.Attr("class", "text-ss text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
rows := s.FilteredRows()
|
rows := s.FilteredRows()
|
||||||
if len(rows) == 0 {
|
if len(rows) == 0 {
|
||||||
return vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text("Formula is valid."))
|
return vdom.P(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text("Formula is valid."))
|
||||||
}
|
}
|
||||||
ctx := NewCalcContext(rows, s.cols, s.Calculated(), s.read)
|
ctx := NewCalcContext(rows, s.cols, s.Calculated(), s.read)
|
||||||
|
|
||||||
@@ -4107,10 +4107,10 @@ func (s *AutoTableState) calcPreview(e *calcEditor, src string, summary bool) *v
|
|||||||
|
|
||||||
n, err := EvalFormula(src, target)
|
n, err := EvalFormula(src, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vdom.P(vdom.Attr("class", "text-xs text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
|
return vdom.P(vdom.Attr("class", "text-ss text-red-600 dark:text-red-400"), vdom.Text(err.Error()))
|
||||||
}
|
}
|
||||||
out := FormatCalcResult(n, CalculatedDataType(e.dataType.Get()), calcPrecisionOf(e.precision.Get()), "", "", "")
|
out := FormatCalcResult(n, CalculatedDataType(e.dataType.Get()), calcPrecisionOf(e.precision.Get()), "", "", "")
|
||||||
return vdom.P(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(prefix+out))
|
return vdom.P(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(prefix+out))
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertAtCaret drops text where the cursor is, rather than at the end — which is
|
// insertAtCaret drops text where the cursor is, rather than at the end — which is
|
||||||
@@ -4141,7 +4141,7 @@ func (e *calcEditor) insertAtCaretOffset(text string, back int) {
|
|||||||
|
|
||||||
func calcField(label string, children ...*vdom.VNode) *vdom.VNode {
|
func calcField(label string, children ...*vdom.VNode) *vdom.VNode {
|
||||||
nodes := []*vdom.VNode{
|
nodes := []*vdom.VNode{
|
||||||
vdom.Span(vdom.Attr("class", "text-xs font-medium text-ink-soft"), vdom.Text(label)),
|
vdom.Span(vdom.Attr("class", "text-ss font-medium text-ink-soft"), vdom.Text(label)),
|
||||||
}
|
}
|
||||||
nodes = append(nodes, children...)
|
nodes = append(nodes, children...)
|
||||||
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-1")}, nodes)...)
|
return vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex flex-col gap-1")}, nodes)...)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const (
|
|||||||
BadgeMuted = "muted"
|
BadgeMuted = "muted"
|
||||||
)
|
)
|
||||||
|
|
||||||
const badgeBase = "inline-flex items-center gap-1 text-xs font-semibold py-0.5 px-2 rounded-default whitespace-nowrap"
|
const badgeBase = "inline-flex items-center gap-1 text-ss font-semibold py-0.5 px-2 rounded-default whitespace-nowrap"
|
||||||
|
|
||||||
var badgeColors = map[string]string{
|
var badgeColors = map[string]string{
|
||||||
"green": "text-white bg-green-700",
|
"green": "text-white bg-green-700",
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ type SegmentedButtonOption struct {
|
|||||||
func SegmentedButtons(options []SegmentedButtonOption, value string, onChange func(string), small bool, class string) *vdom.VNode {
|
func SegmentedButtons(options []SegmentedButtonOption, value string, onChange func(string), small bool, class string) *vdom.VNode {
|
||||||
sizeCls := "py-1 px-3 text-sm"
|
sizeCls := "py-1 px-3 text-sm"
|
||||||
if small {
|
if small {
|
||||||
sizeCls = "py-0.5 px-2 text-xs"
|
sizeCls = "py-0.5 px-2 text-ss"
|
||||||
}
|
}
|
||||||
const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed"
|
const baseCls = "inline-flex items-center justify-center gap-1.5 flex-1 cursor-pointer font-medium transition-colors whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
const activeCls = "bg-surface text-text-heading shadow-sm"
|
const activeCls = "bg-surface text-text-heading shadow-sm"
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ const calMyMonth = "text-lg font-heading mx-4 flex-1 text-center font-semibold t
|
|||||||
|
|
||||||
const calWeekdaysPicker = "grid grid-cols-7 gap-[2px] mb-1"
|
const calWeekdaysPicker = "grid grid-cols-7 gap-[2px] mb-1"
|
||||||
const calWeekdaysMonth = "grid grid-cols-7 border-b border-line"
|
const calWeekdaysMonth = "grid grid-cols-7 border-b border-line"
|
||||||
const calWeekdayPicker = "text-center text-xs font-semibold text-text-muted p-1"
|
const calWeekdayPicker = "text-center text-ss font-semibold text-text-muted p-1"
|
||||||
const calWeekdayMonth = "text-center text-xs font-semibold text-text-muted p-2 uppercase tracking-wider"
|
const calWeekdayMonth = "text-center text-ss font-semibold text-text-muted p-2 uppercase tracking-wider"
|
||||||
|
|
||||||
const calDaysPicker = "grid grid-cols-7 gap-[2px]"
|
const calDaysPicker = "grid grid-cols-7 gap-[2px]"
|
||||||
const calDaysMonth = "grid grid-cols-7"
|
const calDaysMonth = "grid grid-cols-7"
|
||||||
|
|
||||||
const calDayPickerBase = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-text-body p-0"
|
const calDayPickerBase = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-text-body p-0"
|
||||||
const calDayMonthBase = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-xs bg-transparent cursor-pointer"
|
const calDayMonthBase = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-line text-left gap-1 text-ss bg-transparent cursor-pointer"
|
||||||
|
|
||||||
const calSelect = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-text-heading cursor-pointer focus:outline-hidden focus:border-primary"
|
const calSelect = "flex-1 py-1 px-2 text-sm font-semibold border border-line rounded-sm bg-surface text-text-heading cursor-pointer focus:outline-hidden focus:border-primary"
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
|
|
||||||
// GridHeaderCls is the base <th> class for CellGrid headers (exported, matching
|
// GridHeaderCls is the base <th> class for CellGrid headers (exported, matching
|
||||||
// the TSX GRID_HEADER_CLS).
|
// the TSX GRID_HEADER_CLS).
|
||||||
const GridHeaderCls = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-xs font-bold uppercase text-ink whitespace-nowrap last:border-r-0"
|
const GridHeaderCls = "border-b border-r border-line-strong bg-surface-muted px-1.5 py-1.5 text-left text-ss font-bold uppercase text-ink whitespace-nowrap last:border-r-0"
|
||||||
|
|
||||||
var cgLeadingDigits = regexp.MustCompile(`^\d+`)
|
var cgLeadingDigits = regexp.MustCompile(`^\d+`)
|
||||||
var cgLeadingFloat = regexp.MustCompile(`^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?`)
|
var cgLeadingFloat = regexp.MustCompile(`^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?`)
|
||||||
@@ -467,7 +467,7 @@ func CellGrid(p CellGridProps) *vdom.VNode {
|
|||||||
|
|
||||||
tableCls := "min-w-full w-max border-collapse text-sm"
|
tableCls := "min-w-full w-max border-collapse text-sm"
|
||||||
if p.Dense {
|
if p.Dense {
|
||||||
tableCls = "min-w-full w-max border-collapse text-xs"
|
tableCls = "min-w-full w-max border-collapse text-ss"
|
||||||
}
|
}
|
||||||
return vdom.Div(vdom.Attr("class", "relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums"),
|
return vdom.Div(vdom.Attr("class", "relative max-w-full overflow-x-auto border border-line-strong rounded-default bg-surface tabular-nums"),
|
||||||
vdom.Table(vdom.Attr("class", tableCls), thead, tbody),
|
vdom.Table(vdom.Attr("class", tableCls), thead, tbody),
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ func (c *Chart) positionTip(left, top float64, transform string) {
|
|||||||
// pass so its ref stays valid; onMove positions it imperatively. When there is no hover it
|
// pass so its ref stays valid; onMove positions it imperatively. When there is no hover it
|
||||||
// is present but hidden — the same slot each render keeps the reconciler's diff stable.
|
// is present but hidden — the same slot each render keeps the reconciler's diff stable.
|
||||||
func (c *Chart) tooltipNode(p ChartProps, hv int) *vdom.VNode {
|
func (c *Chart) tooltipNode(p ChartProps, hv int) *vdom.VNode {
|
||||||
base := "pointer-events-none absolute z-10 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg"
|
base := "pointer-events-none absolute z-10 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
|
||||||
if p.NoTooltip || hv < 0 || hv >= chartN(p) {
|
if p.NoTooltip || hv < 0 || hv >= chartN(p) {
|
||||||
return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden")))
|
return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden")))
|
||||||
}
|
}
|
||||||
@@ -1086,10 +1086,10 @@ func (c *Chart) legend(p ChartProps) *vdom.VNode {
|
|||||||
it := it // capture per iteration for the click closure
|
it := it // capture per iteration for the click closure
|
||||||
off := !chartShown(p, it.i)
|
off := !chartShown(p, it.i)
|
||||||
swatchStyle := "background-color:" + chartColor(p, it.i)
|
swatchStyle := "background-color:" + chartColor(p, it.i)
|
||||||
labelClass := "text-xs text-ink-soft"
|
labelClass := "text-ss text-ink-soft"
|
||||||
if off {
|
if off {
|
||||||
swatchStyle += ";opacity:0.35"
|
swatchStyle += ";opacity:0.35"
|
||||||
labelClass = "text-xs text-ink-faint line-through"
|
labelClass = "text-ss text-ink-faint line-through"
|
||||||
}
|
}
|
||||||
nodes = append(nodes, vdom.El("button",
|
nodes = append(nodes, vdom.El("button",
|
||||||
vdom.Attr("type", "button"),
|
vdom.Attr("type", "button"),
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const crmTabRow = "flex w-full overflow-x-auto text-sm"
|
|||||||
const crmTabBase = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors"
|
const crmTabBase = "flex items-center gap-1.5 cursor-pointer p-4 font-medium border-line-strong transition-colors"
|
||||||
const crmTabInactive = "border-b text-ink-muted hover:text-ink"
|
const crmTabInactive = "border-b text-ink-muted hover:text-ink"
|
||||||
const crmTabActive = "border-x border-t-2 border-t-sky-700 text-accent"
|
const crmTabActive = "border-x border-t-2 border-t-sky-700 text-accent"
|
||||||
const crmTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full"
|
const crmTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full"
|
||||||
|
|
||||||
// CrmTabGroup renders boxed tabs with a sky-blue top accent; content sits flat
|
// CrmTabGroup renders boxed tabs with a sky-blue top accent; content sits flat
|
||||||
// beneath the row (no body panel), with a trailing filler extending the baseline.
|
// beneath the row (no body panel), with a trailing filler extending the baseline.
|
||||||
@@ -95,7 +95,7 @@ const crmSubTabBase = "flex items-center gap-1.5 py-1 px-3 cursor-pointer font-m
|
|||||||
const crmSubTabDivider = "border-l border-line-strong"
|
const crmSubTabDivider = "border-l border-line-strong"
|
||||||
const crmSubTabActive = "bg-neutral-500 text-white"
|
const crmSubTabActive = "bg-neutral-500 text-white"
|
||||||
const crmSubTabInactive = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink"
|
const crmSubTabInactive = "bg-surface text-ink-soft hover:bg-surface-raised hover:text-ink"
|
||||||
const crmSubTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-black/10 text-current rounded-full"
|
const crmSubTabBadge = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-black/10 text-current rounded-full"
|
||||||
|
|
||||||
// CrmSubTabGroup renders a left-aligned segmented control (interlocking
|
// CrmSubTabGroup renders a left-aligned segmented control (interlocking
|
||||||
// segments) with a full-width baseline separating the bar from the content.
|
// segments) with a full-width baseline separating the bar from the content.
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import (
|
|||||||
const formInputBase = "bg-surface block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:bg-surface-raised disabled:cursor-not-allowed"
|
const formInputBase = "bg-surface block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:bg-surface-raised disabled:cursor-not-allowed"
|
||||||
const formInputBaseDark = "bg-dark text-text-on-dark placeholder:text-text-on-dark-faint block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:opacity-50 disabled:cursor-not-allowed"
|
const formInputBaseDark = "bg-dark text-text-on-dark placeholder:text-text-on-dark-faint block w-full border rounded-default shadow-xs text-sm focus:outline-2 focus:outline-offset-1 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
|
||||||
const formErrorCls = "block text-red-600 dark:text-red-400 text-xs mt-1"
|
const formErrorCls = "block text-red-600 dark:text-red-400 text-ss mt-1"
|
||||||
const formSuccessCls = "block text-green-600 text-xs mt-1"
|
const formSuccessCls = "block text-green-600 text-ss mt-1"
|
||||||
const formInputGroupCls = "flex flex-row items-stretch w-full text-sm"
|
const formInputGroupCls = "flex flex-row items-stretch w-full text-sm"
|
||||||
|
|
||||||
const formTriggerBase = "bg-surface border border-line-strong rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-surface-raised disabled:cursor-not-allowed"
|
const formTriggerBase = "bg-surface border border-line-strong rounded-default shadow-xs text-sm w-full text-left flex items-center justify-between gap-2 cursor-pointer disabled:bg-surface-raised disabled:cursor-not-allowed"
|
||||||
@@ -1312,7 +1312,7 @@ func (m *MultiSelect) triggerFace(p FormMultiSelectProps, selected []FormSelectO
|
|||||||
|
|
||||||
pillCls := formMultiSelectTag
|
pillCls := formMultiSelectTag
|
||||||
if p.Small {
|
if p.Small {
|
||||||
pillCls = cx(pillCls, "px-1.5 text-xs")
|
pillCls = cx(pillCls, "px-1.5 text-ss")
|
||||||
} else {
|
} else {
|
||||||
pillCls = cx(pillCls, "px-2 text-sm")
|
pillCls = cx(pillCls, "px-2 text-sm")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ func fuzzyScoreBadge(show bool, score int) *vdom.VNode {
|
|||||||
if !show {
|
if !show {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return vdom.Span(vdom.Attr("class", "ml-3 shrink-0 text-xs text-ink-faint"), vdom.Text(strconv.Itoa(score)))
|
return vdom.Span(vdom.Attr("class", "ml-3 shrink-0 text-ss text-ink-faint"), vdom.Text(strconv.Itoa(score)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// fuzzyListView renders the inline "list" display. Before the user types, all
|
// fuzzyListView renders the inline "list" display. Before the user types, all
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func Divider() *vdom.VNode { return vdom.Hr(vdom.Attr("class", "text-line mt-1 m
|
|||||||
|
|
||||||
// CodeBox renders a dark monospace code block.
|
// CodeBox renders a dark monospace code block.
|
||||||
func CodeBox(code, class string) *vdom.VNode {
|
func CodeBox(code, class string) *vdom.VNode {
|
||||||
return vdom.Div(vdom.Attr("class", cx("text-xs p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default", class)),
|
return vdom.Div(vdom.Attr("class", cx("text-ss p-3 bg-neutral-800 text-neutral-100 border border-neutral-700 rounded-default", class)),
|
||||||
vdom.Pre(vdom.Code(vdom.Text(code))),
|
vdom.Pre(vdom.Code(vdom.Text(code))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ type BreadcrumbItem struct {
|
|||||||
// Breadcrumbs renders a chevron-separated crumb trail; the last item is bold and
|
// Breadcrumbs renders a chevron-separated crumb trail; the last item is bold and
|
||||||
// not linked.
|
// not linked.
|
||||||
func Breadcrumbs(items []BreadcrumbItem) *vdom.VNode {
|
func Breadcrumbs(items []BreadcrumbItem) *vdom.VNode {
|
||||||
mods := []vdom.Mod{vdom.Attr("class", "flex flex-row items-center text-ink-faint text-xs")}
|
mods := []vdom.Mod{vdom.Attr("class", "flex flex-row items-center text-ink-faint text-ss")}
|
||||||
for i, crumb := range items {
|
for i, crumb := range items {
|
||||||
if i != len(items)-1 {
|
if i != len(items)-1 {
|
||||||
mods = append(mods, vdom.Span(vdom.Attr("class", "flex items-center"),
|
mods = append(mods, vdom.Span(vdom.Attr("class", "flex items-center"),
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ var modalSizes = map[ModalSize]string{
|
|||||||
Modal2XLarge: "max-w-5xl",
|
Modal2XLarge: "max-w-5xl",
|
||||||
Modal3XLarge: "max-w-6xl",
|
Modal3XLarge: "max-w-6xl",
|
||||||
Modal4XLarge: "max-w-7xl",
|
Modal4XLarge: "max-w-7xl",
|
||||||
Modal5XLarge: "max-w-[90rem]",
|
Modal5XLarge: "max-w-8xl",
|
||||||
ModalFull: "max-w-none",
|
ModalFull: "max-w-none",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,12 +137,12 @@ var modalConfirmOkVariants = map[string]string{
|
|||||||
const modalWizardHeader = "flex flex-col items-center gap-2 flex-1"
|
const modalWizardHeader = "flex flex-col items-center gap-2 flex-1"
|
||||||
const modalWizardTitleRow = "flex items-center justify-between w-full"
|
const modalWizardTitleRow = "flex items-center justify-between w-full"
|
||||||
const modalWizardTitle = "text-xl"
|
const modalWizardTitle = "text-xl"
|
||||||
const modalWizardStepName = "text-xs font-semibold text-ink-soft uppercase tracking-wider"
|
const modalWizardStepName = "text-ss font-semibold text-ink-soft uppercase tracking-wider"
|
||||||
const modalWizardSteps = "flex items-center justify-between relative w-full max-w-64"
|
const modalWizardSteps = "flex items-center justify-between relative w-full max-w-64"
|
||||||
const modalWizardTrack = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2"
|
const modalWizardTrack = "absolute top-1/2 left-0 right-0 h-0.5 bg-surface-strong -translate-y-1/2"
|
||||||
const modalWizardTrackFill = "h-full bg-primary transition-[width] duration-300 ease-in-out"
|
const modalWizardTrackFill = "h-full bg-primary transition-[width] duration-300 ease-in-out"
|
||||||
const modalWizardStepWrap = "relative z-[1]"
|
const modalWizardStepWrap = "relative z-[1]"
|
||||||
const modalStepIndicatorBase = "w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out"
|
const modalStepIndicatorBase = "w-7 h-7 rounded-full flex items-center justify-center text-ss font-semibold border-2 shrink-0 transition-all duration-300 ease-in-out"
|
||||||
const modalStepIndicatorPending = "border-line-strong text-ink-faint bg-surface"
|
const modalStepIndicatorPending = "border-line-strong text-ink-faint bg-surface"
|
||||||
const modalStepIndicatorActive = "bg-primary text-white border-primary"
|
const modalStepIndicatorActive = "bg-primary text-white border-primary"
|
||||||
const modalStepIndicatorCompleted = "bg-primary text-white border-primary"
|
const modalStepIndicatorCompleted = "bg-primary text-white border-primary"
|
||||||
|
|||||||
@@ -80,13 +80,13 @@ var ptRowHoverCls = map[PrettyTableHeaderColor]string{
|
|||||||
var ptHeaderPaddingCls = map[PrettyTableSize]string{
|
var ptHeaderPaddingCls = map[PrettyTableSize]string{
|
||||||
PrettyTableSizeDefault: "p-4 text-sm",
|
PrettyTableSizeDefault: "p-4 text-sm",
|
||||||
PrettyTableSizeCompact: "py-1.5 px-2 text-sm",
|
PrettyTableSizeCompact: "py-1.5 px-2 text-sm",
|
||||||
PrettyTableSizeSuperCompact: "py-1 px-2 text-xs",
|
PrettyTableSizeSuperCompact: "py-1 px-2 text-ss",
|
||||||
}
|
}
|
||||||
|
|
||||||
var ptBodyPaddingCls = map[PrettyTableSize]string{
|
var ptBodyPaddingCls = map[PrettyTableSize]string{
|
||||||
PrettyTableSizeDefault: "text-sm [&_td]:p-4",
|
PrettyTableSizeDefault: "text-sm [&_td]:p-4",
|
||||||
PrettyTableSizeCompact: "text-sm [&_td]:py-1 [&_td]:px-2",
|
PrettyTableSizeCompact: "text-sm [&_td]:py-1 [&_td]:px-2",
|
||||||
PrettyTableSizeSuperCompact: "text-xs [&_td]:py-0.5 [&_td]:px-2",
|
PrettyTableSizeSuperCompact: "text-ss [&_td]:py-0.5 [&_td]:px-2",
|
||||||
}
|
}
|
||||||
|
|
||||||
var ptPosCls = map[PrettyTableColumnPosition]string{
|
var ptPosCls = map[PrettyTableColumnPosition]string{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import "kjol/vdom"
|
|||||||
const sidebarNavRoot = "bg-surface rounded-default shadow-sm border border-line py-2"
|
const sidebarNavRoot = "bg-surface rounded-default shadow-sm border border-line py-2"
|
||||||
const sidebarNavList = "list-none m-0 p-0"
|
const sidebarNavList = "list-none m-0 p-0"
|
||||||
const sidebarNavBtn = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted"
|
const sidebarNavBtn = "w-full text-left py-2 pr-3 pl-4 text-sm cursor-pointer bg-transparent text-ink-soft border-0 hover:text-ink hover:bg-surface-muted"
|
||||||
const sidebarNavSubBtn = "w-full text-left py-1.5 pr-3 pl-8 text-xs cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted"
|
const sidebarNavSubBtn = "w-full text-left py-1.5 pr-3 pl-8 text-ss cursor-pointer bg-transparent text-ink-muted border-0 hover:text-ink hover:bg-surface-muted"
|
||||||
const sidebarNavIcon = "mr-2"
|
const sidebarNavIcon = "mr-2"
|
||||||
|
|
||||||
const sidebarLayoutRoot = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12"
|
const sidebarLayoutRoot = "grid grid-cols-1 gap-8 min-h-screen items-start lg:grid-cols-12"
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ func (s *SignaturePad) Render(p SignaturePadProps) *vdom.VNode {
|
|||||||
}
|
}
|
||||||
clear := []vdom.Mod{
|
clear := []vdom.Mod{
|
||||||
vdom.Attr("type", "button"),
|
vdom.Attr("type", "button"),
|
||||||
vdom.Attr("class", "text-xs text-ink-muted hover:text-ink disabled:opacity-50"),
|
vdom.Attr("class", "text-ss text-ink-muted hover:text-ink disabled:opacity-50"),
|
||||||
vdom.Text(pick(p.ClearText, "Clear")),
|
vdom.Text(pick(p.ClearText, "Clear")),
|
||||||
}
|
}
|
||||||
if p.Disabled || s.IsEmpty() {
|
if p.Disabled || s.IsEmpty() {
|
||||||
@@ -145,7 +145,7 @@ func (s *SignaturePad) Render(p SignaturePadProps) *vdom.VNode {
|
|||||||
}
|
}
|
||||||
kids = append(kids, vdom.Div(
|
kids = append(kids, vdom.Div(
|
||||||
vdom.Attr("class", "flex items-center justify-between border-t border-line bg-surface-muted px-2 py-1"),
|
vdom.Attr("class", "flex items-center justify-between border-t border-line bg-surface-muted px-2 py-1"),
|
||||||
vdom.Span(vdom.Attr("class", "text-xs italic text-ink-faint"), vdom.Text(hint)),
|
vdom.Span(vdom.Attr("class", "text-ss italic text-ink-faint"), vdom.Text(hint)),
|
||||||
vdom.Button(clear...),
|
vdom.Button(clear...),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func TabGroup(p TabGroupProps) *vdom.VNode {
|
|||||||
vdom.Text(item.Title),
|
vdom.Text(item.Title),
|
||||||
}
|
}
|
||||||
if item.Badge > 0 {
|
if item.Badge > 0 {
|
||||||
btn = append(btn, vdom.Span(vdom.Attr("class", "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full"),
|
btn = append(btn, vdom.Span(vdom.Attr("class", "inline-flex items-center justify-center min-w-5 h-5 px-1 text-ss font-semibold bg-primary text-white rounded-full"),
|
||||||
vdom.Text(strconv.Itoa(item.Badge))))
|
vdom.Text(strconv.Itoa(item.Badge))))
|
||||||
}
|
}
|
||||||
header = append(header, vdom.Button(btn...))
|
header = append(header, vdom.Button(btn...))
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func ToggleSwitch(checked bool, onChange func(bool), label, description string,
|
|||||||
vdom.Text(label)))
|
vdom.Text(label)))
|
||||||
}
|
}
|
||||||
if description != "" {
|
if description != "" {
|
||||||
text = append(text, vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"), vdom.Text(description)))
|
text = append(text, vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"), vdom.Text(description)))
|
||||||
}
|
}
|
||||||
mods = append(mods, vdom.Div(text...))
|
mods = append(mods, vdom.Div(text...))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -704,7 +704,7 @@ func (t *Tutorial) content(step *TutorialStep, idx, total int) *vdom.VNode {
|
|||||||
headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "font-medium text-ink"),
|
headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "font-medium text-ink"),
|
||||||
vdom.Text(step.Title)))
|
vdom.Text(step.Title)))
|
||||||
}
|
}
|
||||||
headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "text-xs text-ink-muted"),
|
headerLeft = append(headerLeft, vdom.Span(vdom.Attr("class", "text-ss text-ink-muted"),
|
||||||
vdom.Text(strconv.Itoa(idx+1)+" of "+strconv.Itoa(total))))
|
vdom.Text(strconv.Itoa(idx+1)+" of "+strconv.Itoa(total))))
|
||||||
|
|
||||||
header := vdom.Div(vdom.Attr("class", "flex items-center justify-between p-4 pb-2"),
|
header := vdom.Div(vdom.Attr("class", "flex items-center justify-between p-4 pb-2"),
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ func (c *USHeatmap) onMove(e vdom.Event) {
|
|||||||
// tooltipNode is the HTML tooltip (absolute, pointer-events-none), rendered every pass so
|
// tooltipNode is the HTML tooltip (absolute, pointer-events-none), rendered every pass so
|
||||||
// its ref stays valid; onMove positions it imperatively. Hidden when nothing is hovered.
|
// its ref stays valid; onMove positions it imperatively. Hidden when nothing is hovered.
|
||||||
func (c *USHeatmap) tooltipNode(p USHeatmapProps, hv usHover, mn, mx float64, steps int) *vdom.VNode {
|
func (c *USHeatmap) tooltipNode(p USHeatmapProps, hv usHover, mn, mx float64, steps int) *vdom.VNode {
|
||||||
base := "pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-xs shadow-lg"
|
base := "pointer-events-none absolute z-10 min-w-28 max-w-64 rounded-default border border-line bg-surface px-3 py-2 text-ss shadow-lg"
|
||||||
if p.NoTooltip || hv.kind == "" {
|
if p.NoTooltip || hv.kind == "" {
|
||||||
return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden")))
|
return vdom.Div(vdom.WithRef(c.tipRef), vdom.Attr("class", cx(base, "hidden")))
|
||||||
}
|
}
|
||||||
@@ -387,7 +387,7 @@ func choroplethLegend(mn, mx float64, steps int, fmt func(float64) string) *vdom
|
|||||||
vdom.Attr("style", "background-color:var(--color-choropleth-"+strconv.Itoa(k)+")")))
|
vdom.Attr("style", "background-color:var(--color-choropleth-"+strconv.Itoa(k)+")")))
|
||||||
}
|
}
|
||||||
ramp := vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex overflow-hidden rounded-xs")}, swatches)...)
|
ramp := vdom.Div(kids([]vdom.Mod{vdom.Attr("class", "flex overflow-hidden rounded-xs")}, swatches)...)
|
||||||
return vdom.Div(vdom.Attr("class", "mt-3 flex items-center gap-2 text-xs text-ink-muted"),
|
return vdom.Div(vdom.Attr("class", "mt-3 flex items-center gap-2 text-ss text-ink-muted"),
|
||||||
vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mn))),
|
vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mn))),
|
||||||
ramp,
|
ramp,
|
||||||
vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mx))),
|
vdom.Span(vdom.Attr("style", "font-variant-numeric:tabular-nums"), vdom.Text(fmt(mx))),
|
||||||
|
|||||||
Reference in New Issue
Block a user