staffa 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -27
- package/dist/components/autocomplete.js +20 -20
- package/dist/components/box.d.ts +8 -6
- package/dist/components/box.js +15 -11
- package/dist/components/button.d.ts +10 -33
- package/dist/components/button.js +17 -38
- package/dist/components/buttonChooser.d.ts +42 -0
- package/dist/components/buttonChooser.js +38 -0
- package/dist/components/buttonGroup.d.ts +3 -3
- package/dist/components/buttonGroup.js +18 -18
- package/dist/components/checkbox.js +7 -7
- package/dist/components/dialog.d.ts +20 -25
- package/dist/components/dialog.js +81 -91
- package/dist/components/field.d.ts +8 -6
- package/dist/components/field.js +18 -17
- package/dist/components/form.d.ts +3 -3
- package/dist/components/form.js +4 -4
- package/dist/components/main.d.ts +7 -5
- package/dist/components/main.js +25 -23
- package/dist/components/select.d.ts +1 -1
- package/dist/components/select.js +5 -5
- package/dist/components/tabs.d.ts +5 -3
- package/dist/components/tabs.js +25 -19
- package/dist/components/textarea.js +4 -4
- package/dist/components/textline.js +1 -1
- package/dist/core.d.ts +26 -39
- package/dist/core.js +6 -5
- package/dist/index.d.ts +10 -8
- package/dist/index.js +9 -8
- package/dist/staffa.esm.js +1 -0
- package/dist/theme.d.ts +9 -75
- package/dist/theme.js +178 -82
- package/package.json +3 -2
- package/src/components/autocomplete.ts +20 -20
- package/src/components/box.ts +20 -14
- package/src/components/button.ts +24 -72
- package/src/components/buttonChooser.ts +66 -0
- package/src/components/buttonGroup.ts +18 -18
- package/src/components/checkbox.ts +7 -7
- package/src/components/dialog.ts +101 -102
- package/src/components/field.ts +24 -21
- package/src/components/form.ts +7 -7
- package/src/components/main.ts +30 -26
- package/src/components/select.ts +4 -4
- package/src/components/tabs.ts +29 -22
- package/src/components/textarea.ts +4 -4
- package/src/components/textline.ts +1 -1
- package/src/core.ts +26 -40
- package/src/index.ts +10 -8
- package/src/theme.ts +190 -135
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Content, type Slot, type Attributes } from "../core.js";
|
|
2
2
|
/** Options for {@link main}. */
|
|
3
|
-
export interface MainOptions
|
|
3
|
+
export interface MainOptions {
|
|
4
|
+
/** Aberdeen attr/style string applied to the outermost shell element. */
|
|
5
|
+
attrs?: Attributes;
|
|
4
6
|
/** App/page title shown in the top bar. */
|
|
5
7
|
title?: Slot;
|
|
6
8
|
/** Secondary line under the title. */
|
|
@@ -20,9 +22,9 @@ export interface MainOptions extends BaseOptions {
|
|
|
20
22
|
*/
|
|
21
23
|
maxWidth?: string;
|
|
22
24
|
/** Aberdeen attr/style string applied to the content sheet. */
|
|
23
|
-
|
|
25
|
+
contentAttrs?: Attributes;
|
|
24
26
|
/** Aberdeen attr/style string applied to the top bar. */
|
|
25
|
-
|
|
27
|
+
topbarAttrs?: Attributes;
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
28
30
|
* An application shell that wires up the things almost every app needs: a sticky
|
|
@@ -35,7 +37,7 @@ export interface MainOptions extends BaseOptions {
|
|
|
35
37
|
* ```ts
|
|
36
38
|
* S.main({
|
|
37
39
|
* icon: "✦",
|
|
38
|
-
* title: "
|
|
40
|
+
* title: "Staffa Demo",
|
|
39
41
|
* subtitle: "Component playground",
|
|
40
42
|
* maxWidth: "56rem",
|
|
41
43
|
* menu: () => S.button({ text: "New", size: "sm" }),
|
package/dist/components/main.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import A from "aberdeen";
|
|
2
2
|
import { drawSlot } from "../core.js";
|
|
3
3
|
A.insertGlobalCss({
|
|
4
|
-
".
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"> header
|
|
9
|
-
"> header .
|
|
10
|
-
"> header .
|
|
11
|
-
"> header .
|
|
4
|
+
".s-main": {
|
|
5
|
+
// Colours come from the surface classes added in main(): the shell is a
|
|
6
|
+
// filled base, the bars are raised, the framed sheet is a panel.
|
|
7
|
+
"&": "display:flex flex-direction:column min-height:100vh max-height:100vh",
|
|
8
|
+
"> header": "display:flex align-items:center gap:$3 padding: $2 $3; border-bottom: 1px solid $s-border; position:sticky top:0 z-index:10",
|
|
9
|
+
"> header .s-icon": "display:flex align-items:center font-size:1.4em",
|
|
10
|
+
"> header .s-titles": "display:flex flex-direction:column min-width:0 flex:1",
|
|
11
|
+
"> header .s-title": "font-weight:700 font-size:1.1em line-height:1.2 overflow:hidden text-overflow:ellipsis white-space:nowrap",
|
|
12
|
+
"> header .s-subtitle": "fg:$s-fg-muted font-size:0.85em overflow:hidden text-overflow:ellipsis white-space:nowrap",
|
|
13
|
+
"> header .s-menu": "display:flex align-items:center gap:$2",
|
|
12
14
|
"> main": "flex:1 overflow-y:auto display:flex flex-direction:column",
|
|
13
|
-
"> main > .
|
|
14
|
-
"> main > .
|
|
15
|
-
"> main > .
|
|
16
|
-
"> footer": "display:flex align-items:center gap:$2 padding: $2 $3;
|
|
15
|
+
"> main > .s-content": "width:100% flex:1",
|
|
16
|
+
"> main > .s-content.s-framed": "margin: $3 auto; border: 1px solid $s-border; r:$s-radius-lg box-shadow:$s-shadow p:$4",
|
|
17
|
+
"> main > .s-content.s-plain": "p:$3",
|
|
18
|
+
"> footer": "display:flex align-items:center gap:$2 padding: $2 $3; border-top: 1px solid $s-border; fg:$s-fg-muted",
|
|
17
19
|
},
|
|
18
20
|
});
|
|
19
21
|
/**
|
|
@@ -27,7 +29,7 @@ A.insertGlobalCss({
|
|
|
27
29
|
* ```ts
|
|
28
30
|
* S.main({
|
|
29
31
|
* icon: "✦",
|
|
30
|
-
* title: "
|
|
32
|
+
* title: "Staffa Demo",
|
|
31
33
|
* subtitle: "Component playground",
|
|
32
34
|
* maxWidth: "56rem",
|
|
33
35
|
* menu: () => S.button({ text: "New", size: "sm" }),
|
|
@@ -37,44 +39,44 @@ A.insertGlobalCss({
|
|
|
37
39
|
* ```
|
|
38
40
|
*/
|
|
39
41
|
export function main(opts = {}) {
|
|
40
|
-
A("div.
|
|
42
|
+
A("div.s-main.s-s.base", opts.attrs, () => {
|
|
41
43
|
// Top bar — only rendered when there's something to show in it.
|
|
42
44
|
A(() => {
|
|
43
45
|
const hasBar = opts.title != null || opts.subtitle != null || opts.icon != null || opts.menu != null;
|
|
44
46
|
if (!hasBar)
|
|
45
47
|
return;
|
|
46
|
-
A("header", opts.
|
|
48
|
+
A("header.s-s.raised", opts.topbarAttrs, () => {
|
|
47
49
|
A(() => {
|
|
48
50
|
if (opts.icon != null)
|
|
49
|
-
A("div.
|
|
51
|
+
A("div.s-icon", () => drawSlot(opts.icon));
|
|
50
52
|
});
|
|
51
|
-
A("div.
|
|
53
|
+
A("div.s-titles", () => {
|
|
52
54
|
A(() => {
|
|
53
55
|
if (opts.title != null)
|
|
54
|
-
A("div.
|
|
56
|
+
A("div.s-title", () => drawSlot(opts.title));
|
|
55
57
|
});
|
|
56
58
|
A(() => {
|
|
57
59
|
if (opts.subtitle != null)
|
|
58
|
-
A("div.
|
|
60
|
+
A("div.s-subtitle", () => drawSlot(opts.subtitle));
|
|
59
61
|
});
|
|
60
62
|
});
|
|
61
63
|
A(() => {
|
|
62
64
|
if (opts.menu)
|
|
63
|
-
A("div.
|
|
65
|
+
A("div.s-menu", () => opts.menu?.());
|
|
64
66
|
});
|
|
65
67
|
});
|
|
66
68
|
});
|
|
67
69
|
// Scrollable main region with the (optionally framed) content sheet.
|
|
68
70
|
A("main", () => {
|
|
69
|
-
A("div.
|
|
71
|
+
A("div.s-content", opts.contentAttrs, () => {
|
|
70
72
|
// Framing applied in its own scope so changing maxWidth doesn't
|
|
71
73
|
// recreate the content (which holds the whole page).
|
|
72
74
|
A(() => {
|
|
73
75
|
const max = opts.maxWidth;
|
|
74
76
|
if (max != null)
|
|
75
|
-
A(".
|
|
77
|
+
A(".s-framed.s-s.panel max-width:", max);
|
|
76
78
|
else
|
|
77
|
-
A(".
|
|
79
|
+
A(".s-plain");
|
|
78
80
|
});
|
|
79
81
|
if (opts.content)
|
|
80
82
|
opts.content();
|
|
@@ -16,7 +16,7 @@ export interface SelectOptions extends FieldOptions {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* A single-select dropdown backed by a native `<select>` element. Looks like the
|
|
19
|
-
* other
|
|
19
|
+
* other Staffa inputs but delegates all focus management, keyboard navigation, and
|
|
20
20
|
* mobile-native picker behaviour to the browser.
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
@@ -2,15 +2,15 @@ import A from "aberdeen";
|
|
|
2
2
|
import { applyControlAttrs, drawField } from "./field.js";
|
|
3
3
|
// Wrapper provides the chevron via ::after (pseudo-elements on <select> are unreliable).
|
|
4
4
|
A.insertGlobalCss({
|
|
5
|
-
".
|
|
5
|
+
".s-select_wrap": {
|
|
6
6
|
"&": "position:relative display:block",
|
|
7
7
|
"select": "w:100% cursor:pointer padding-right:2.2em; appearance:none",
|
|
8
|
-
"&::after": "content: '▾'; position:absolute right:0.7em top:50%; transform: translateY(-50%); pointer-events:none fg:$
|
|
8
|
+
"&::after": "content: '▾'; position:absolute right:0.7em top:50%; transform: translateY(-50%); pointer-events:none fg:$s-fg-muted font-size:0.85em",
|
|
9
9
|
},
|
|
10
10
|
});
|
|
11
11
|
/**
|
|
12
12
|
* A single-select dropdown backed by a native `<select>` element. Looks like the
|
|
13
|
-
* other
|
|
13
|
+
* other Staffa inputs but delegates all focus management, keyboard navigation, and
|
|
14
14
|
* mobile-native picker behaviour to the browser.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
@@ -20,8 +20,8 @@ A.insertGlobalCss({
|
|
|
20
20
|
*/
|
|
21
21
|
export function select(opts) {
|
|
22
22
|
drawField(opts, (id, isInvalid) => {
|
|
23
|
-
A("div.
|
|
24
|
-
A("select.
|
|
23
|
+
A("div.s-select_wrap", opts.inputAttrs, () => {
|
|
24
|
+
A("select.s-input", () => {
|
|
25
25
|
applyControlAttrs(opts, id, isInvalid);
|
|
26
26
|
A("change=", (e) => {
|
|
27
27
|
if (opts.bind)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Bindable, type Content, type Slot, type Attributes } from "../core.js";
|
|
2
2
|
/** A single tab definition. */
|
|
3
3
|
export interface Tab {
|
|
4
4
|
/** Stable id used as the selection value. Falls back to the array index. */
|
|
@@ -13,7 +13,9 @@ export interface Tab {
|
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
}
|
|
15
15
|
/** Options for {@link tabs}. */
|
|
16
|
-
export interface TabsOptions
|
|
16
|
+
export interface TabsOptions {
|
|
17
|
+
/** Aberdeen attr/style string applied to the outermost element. */
|
|
18
|
+
attrs?: Attributes;
|
|
17
19
|
/** The tabs to display. */
|
|
18
20
|
tabs: Tab[];
|
|
19
21
|
/**
|
|
@@ -24,7 +26,7 @@ export interface TabsOptions extends BaseOptions {
|
|
|
24
26
|
/** Visual style of the tab strip. Defaults to `"underline"`. */
|
|
25
27
|
variant?: "underline" | "pills";
|
|
26
28
|
/** Aberdeen attr/style string applied to the active panel. */
|
|
27
|
-
|
|
29
|
+
contentAttrs?: Attributes;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* A tabbed view. Renders an ARIA `tablist` of buttons and a single live panel
|
package/dist/components/tabs.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import A from "aberdeen";
|
|
2
2
|
import { drawSlot, uniqueId } from "../core.js";
|
|
3
3
|
A.insertGlobalCss({
|
|
4
|
-
".
|
|
4
|
+
".s-tabs": {
|
|
5
5
|
"&": "display:flex flex-direction:column gap:$3",
|
|
6
|
-
".
|
|
7
|
-
".
|
|
8
|
-
"border:0 fg
|
|
6
|
+
".s-tablist": "display:flex gap:$1 align-items:stretch",
|
|
7
|
+
".s-tab": "display:inline-flex align-items:center gap:$2 cursor:pointer background:transparent " +
|
|
8
|
+
"border:0 color: $s-fg-muted; font-weight:600 padding: 0.6em 0.9em; " +
|
|
9
9
|
"transition: color 0.15s, background 0.15s, border-color 0.15s;",
|
|
10
|
-
".
|
|
11
|
-
".
|
|
12
|
-
".
|
|
13
|
-
// Underline variant.
|
|
14
|
-
"&.
|
|
15
|
-
"&.
|
|
16
|
-
"&.
|
|
17
|
-
// Pills variant.
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
".s-tab:hover:not(:disabled)": "color: $s-fg;",
|
|
11
|
+
".s-tab:disabled": "opacity:0.5 cursor:not-allowed",
|
|
12
|
+
".s-tab:focus-visible": "outline:none box-shadow: 0 0 0 3px $s-focus; r: $s-radius;",
|
|
13
|
+
// Underline variant — the active marker uses the contextual brand accent.
|
|
14
|
+
"&.s-underline .s-tablist": "border-bottom: 1px solid $s-border;",
|
|
15
|
+
"&.s-underline .s-tab": "border-bottom: 2px solid transparent; margin-bottom:-1px",
|
|
16
|
+
"&.s-underline .s-tab[aria-selected=true]": "color: $s-fg; border-bottom-color: $s-accent;",
|
|
17
|
+
// Pills variant — the active pill gets the `.s-s.primary` surface (added in
|
|
18
|
+
// tabs()), so its `--s-bg`/`--s-fg` are the brand surface's; we just paint
|
|
19
|
+
// (the `.s-tab` rule's transparent background would otherwise win on it).
|
|
20
|
+
"&.s-pills .s-tab": "r: $s-radius;",
|
|
21
|
+
"&.s-pills .s-tab[aria-selected=true]": "background: $s-bg; color: $s-fg;",
|
|
20
22
|
// The panel has no enclosing box, so no default padding — its content
|
|
21
23
|
// aligns flush with the tab strip. Callers add padding/flex via `inner`.
|
|
22
|
-
".
|
|
24
|
+
".s-tabpanel": "display:block",
|
|
23
25
|
},
|
|
24
26
|
});
|
|
25
27
|
/**
|
|
@@ -46,16 +48,20 @@ export function tabs(opts) {
|
|
|
46
48
|
return;
|
|
47
49
|
$sel.value = keyOf(tab, index);
|
|
48
50
|
};
|
|
49
|
-
A(`div.
|
|
50
|
-
A("div.
|
|
51
|
+
A(`div.s-tabs.s-${variant}`, opts.attrs, () => {
|
|
52
|
+
A("div.s-tablist role=tablist", () => {
|
|
51
53
|
opts.tabs.forEach((tab, index) => {
|
|
52
54
|
const key = keyOf(tab, index);
|
|
53
|
-
A("button.
|
|
55
|
+
A("button.s-tab type=button role=tab", () => {
|
|
54
56
|
A(`id=${groupId}-tab-${key} aria-controls=${groupId}-panel-${key}`);
|
|
55
57
|
A(() => {
|
|
56
58
|
const selected = $sel.value === key;
|
|
57
59
|
A("aria-selected=", selected ? "true" : "false");
|
|
58
60
|
A("tabindex=", selected ? "0" : "-1");
|
|
61
|
+
// The active pill is a filled brand surface; the classes flip
|
|
62
|
+
// with selection (Aberdeen removes them when this scope re-runs).
|
|
63
|
+
if (selected && variant === "pills")
|
|
64
|
+
A(".s-s.primary");
|
|
59
65
|
});
|
|
60
66
|
if (tab.disabled)
|
|
61
67
|
A("disabled=true");
|
|
@@ -66,7 +72,7 @@ export function tabs(opts) {
|
|
|
66
72
|
});
|
|
67
73
|
});
|
|
68
74
|
});
|
|
69
|
-
A("div.
|
|
75
|
+
A("div.s-tabpanel role=tabpanel", opts.contentAttrs, () => {
|
|
70
76
|
A(() => {
|
|
71
77
|
const selKey = $sel.value;
|
|
72
78
|
const index = opts.tabs.findIndex((t, i) => keyOf(t, i) === selKey);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import A from "aberdeen";
|
|
2
2
|
import { applyControlAttrs, drawField } from "./field.js";
|
|
3
3
|
A.insertGlobalCss({
|
|
4
|
-
"textarea.
|
|
5
|
-
"textarea.
|
|
4
|
+
"textarea.s-input": "resize:vertical min-height:3em line-height:1.45",
|
|
5
|
+
"textarea.s-input.s-autoGrow": "resize:none min-height:2.5em overflow-y:hidden",
|
|
6
6
|
});
|
|
7
7
|
/**
|
|
8
8
|
* A multi-line text input. Shares the field chrome and styling of
|
|
@@ -16,9 +16,9 @@ A.insertGlobalCss({
|
|
|
16
16
|
export function textarea(opts = {}) {
|
|
17
17
|
const grow = opts.autoGrow !== false;
|
|
18
18
|
drawField(opts, (id, isInvalid) => {
|
|
19
|
-
const el = A("textarea.
|
|
19
|
+
const el = A("textarea.s-input", opts.inputAttrs, () => {
|
|
20
20
|
if (grow) {
|
|
21
|
-
A(".
|
|
21
|
+
A(".s-autoGrow");
|
|
22
22
|
A("input=", (e) => {
|
|
23
23
|
fitToContent(e.currentTarget);
|
|
24
24
|
if (opts.input)
|
|
@@ -14,7 +14,7 @@ import { applyControlAttrs, drawField } from "./field.js";
|
|
|
14
14
|
*/
|
|
15
15
|
export function textline(opts = {}) {
|
|
16
16
|
drawField(opts, (id, isInvalid) => {
|
|
17
|
-
A("input.
|
|
17
|
+
A("input.s-input", opts.inputAttrs, () => {
|
|
18
18
|
A("type=", opts.type ?? "text");
|
|
19
19
|
if (opts.placeholder != null)
|
|
20
20
|
A("placeholder=", opts.placeholder);
|
package/dist/core.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared building blocks for the
|
|
2
|
+
* Shared building blocks for the Staffa component library.
|
|
3
3
|
*
|
|
4
|
-
* Every component in
|
|
4
|
+
* Every component in Staffa is "just an Aberdeen draw function": a plain function
|
|
5
5
|
* that takes a single, strongly typed options object and emits DOM through
|
|
6
6
|
* Aberdeen's {@link A} function. This module defines the option-type hierarchy
|
|
7
7
|
* that all components build on, plus a couple of tiny helpers.
|
|
@@ -9,15 +9,19 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* An Aberdeen attribute/style/class string, e.g. `"display:flex gap:$3 .my-class"`.
|
|
11
11
|
*
|
|
12
|
+
* Common values are our surface modifier classes:
|
|
13
|
+
* - for colors: `.panel` `.raised` `.neutral` `.primary` `.danger` `.success` and `.warning`
|
|
14
|
+
* - for variant: `.filled` `.tonal` and `.outlined`
|
|
15
|
+
*
|
|
12
16
|
* These strings are passed straight through to {@link A} as positional
|
|
13
17
|
* arguments, so they accept the full Aberdeen shorthand syntax: CSS shortcuts
|
|
14
18
|
* (`p`, `mt`, `bg`, `r`, ...), spacing variables (`$1`..`$12`), CSS custom
|
|
15
|
-
* properties (`$
|
|
19
|
+
* properties (`$s-primary`), classes (`.foo`) and attributes (`aria-label=Hi`).
|
|
16
20
|
*
|
|
17
21
|
* Note: because Aberdeen interprets a leading bare word as an element name, write
|
|
18
22
|
* `display:flex` rather than just `flex`.
|
|
19
23
|
*/
|
|
20
|
-
export type
|
|
24
|
+
export type Attributes = string;
|
|
21
25
|
/** A reactive "value box", such as the result of `A.proxy(x)` or `A.ref(obj, key)`. */
|
|
22
26
|
export type Bindable<T> = {
|
|
23
27
|
value: T;
|
|
@@ -25,49 +29,32 @@ export type Bindable<T> = {
|
|
|
25
29
|
/** A content function. It runs inside the relevant element's reactive scope. */
|
|
26
30
|
export type Content = () => void;
|
|
27
31
|
/**
|
|
28
|
-
* Something that renders a small piece of content: either a plain string
|
|
29
|
-
*
|
|
30
|
-
*/
|
|
31
|
-
export type Slot = string | Content;
|
|
32
|
-
/**
|
|
33
|
-
* Options shared by *every* Skye component.
|
|
32
|
+
* Something that renders a small piece of content: either a plain string or a
|
|
33
|
+
* draw function (for icons, badges, custom markup, ...).
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* A string is drawn as **rich text** (see {@link drawSlot}): Aberdeen's `rich`
|
|
36
|
+
* markup is applied, so `*italic*`, `**bold**`, `` `code` `` and
|
|
37
|
+
* `[links](/path)` render as inline elements (text is safely escaped).
|
|
38
|
+
*
|
|
39
|
+
* The optional `Args` type parameter lets a slot's draw-function receive
|
|
40
|
+
* arguments — e.g. a dialog body is a `Slot<[close: () => void]>`.
|
|
38
41
|
*/
|
|
39
|
-
export
|
|
40
|
-
/**
|
|
41
|
-
* Aberdeen attr/style string applied to the widget's root element.
|
|
42
|
-
*
|
|
43
|
-
* It is passed as a positional argument to {@link A}, so a *change* to it on a
|
|
44
|
-
* proxied options object re-runs the caller's scope (recreating the widget).
|
|
45
|
-
* That's fine for `root` — it rarely changes at runtime.
|
|
46
|
-
*/
|
|
47
|
-
root?: Styling;
|
|
48
|
-
}
|
|
42
|
+
export type Slot<Args extends unknown[] = []> = string | ((...args: Args) => void);
|
|
49
43
|
/**
|
|
50
|
-
* Options for components that wrap a single block of caller-provided content
|
|
51
|
-
*
|
|
52
|
-
* Such components render an *inner* element (the one that actually holds the
|
|
53
|
-
* children) which is given sensible default padding and `gap` in CSS. Override
|
|
54
|
-
* those via {@link ContentOptions.inner | inner}, whose declarations win because
|
|
55
|
-
* they're applied as inline styles.
|
|
44
|
+
* Options for components that wrap a single block of caller-provided content,
|
|
45
|
+
* with an `attrs` escape hatch on the outermost element.
|
|
56
46
|
*/
|
|
57
|
-
export interface ContentOptions
|
|
47
|
+
export interface ContentOptions {
|
|
48
|
+
/** Aberdeen attr/style string applied to the widget's outermost element. */
|
|
49
|
+
attrs?: Attributes;
|
|
58
50
|
/** Draws the children of this component. */
|
|
59
51
|
content?: Content;
|
|
60
|
-
/**
|
|
61
|
-
* Aberdeen attr/style string applied to the inner (content-holding) element.
|
|
62
|
-
* Add `display:flex` here if you want the children laid out as a flex
|
|
63
|
-
* row/column.
|
|
64
|
-
*/
|
|
65
|
-
inner?: Styling;
|
|
66
52
|
}
|
|
67
53
|
/** Generates a process-unique id, used to wire `<label for>` to its control. */
|
|
68
54
|
export declare function uniqueId(prefix?: string): string;
|
|
69
55
|
/**
|
|
70
|
-
* Draw a {@link Slot} into the current element: call it
|
|
71
|
-
* otherwise emit
|
|
56
|
+
* Draw a {@link Slot} into the current element: call it (with any extra `args`)
|
|
57
|
+
* if it's a function, otherwise emit the string as **rich text** via Aberdeen's
|
|
58
|
+
* `rich` markup (`*italic*`, `**bold**`, `` `code` ``, `[link](/path)`).
|
|
72
59
|
*/
|
|
73
|
-
export declare function drawSlot(slot: Slot | undefined): void;
|
|
60
|
+
export declare function drawSlot<Args extends unknown[] = []>(slot: Slot<Args> | undefined, ...args: Args): void;
|
package/dist/core.js
CHANGED
|
@@ -5,14 +5,15 @@ export function uniqueId(prefix = "s") {
|
|
|
5
5
|
return `${prefix}-${++idCounter}`;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Draw a {@link Slot} into the current element: call it
|
|
9
|
-
* otherwise emit
|
|
8
|
+
* Draw a {@link Slot} into the current element: call it (with any extra `args`)
|
|
9
|
+
* if it's a function, otherwise emit the string as **rich text** via Aberdeen's
|
|
10
|
+
* `rich` markup (`*italic*`, `**bold**`, `` `code` ``, `[link](/path)`).
|
|
10
11
|
*/
|
|
11
|
-
export function drawSlot(slot) {
|
|
12
|
+
export function drawSlot(slot, ...args) {
|
|
12
13
|
if (slot == null)
|
|
13
14
|
return;
|
|
14
15
|
if (typeof slot === "function")
|
|
15
|
-
slot();
|
|
16
|
+
slot(...args);
|
|
16
17
|
else
|
|
17
|
-
A("
|
|
18
|
+
A("rich=", slot);
|
|
18
19
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Staffa — a small, opinionated component library for the
|
|
3
3
|
* {@link https://aberdeenjs.org | Aberdeen} reactive UI library.
|
|
4
4
|
*
|
|
5
5
|
* Import the default `S` object and call its component functions:
|
|
6
6
|
*
|
|
7
7
|
* ```ts
|
|
8
|
-
* import S from "
|
|
8
|
+
* import S from "staffa";
|
|
9
9
|
*
|
|
10
10
|
* S.main({
|
|
11
11
|
* title: "Hello",
|
|
@@ -33,6 +33,7 @@ import { setDarkMode, getDarkMode } from "./theme.js";
|
|
|
33
33
|
import { autocomplete } from "./components/autocomplete.js";
|
|
34
34
|
import { box } from "./components/box.js";
|
|
35
35
|
import { button } from "./components/button.js";
|
|
36
|
+
import { buttonChooser } from "./components/buttonChooser.js";
|
|
36
37
|
import { buttonGroup } from "./components/buttonGroup.js";
|
|
37
38
|
import { checkbox } from "./components/checkbox.js";
|
|
38
39
|
import { form } from "./components/form.js";
|
|
@@ -42,7 +43,7 @@ import { select } from "./components/select.js";
|
|
|
42
43
|
import { tabs } from "./components/tabs.js";
|
|
43
44
|
import { textarea } from "./components/textarea.js";
|
|
44
45
|
import { textline } from "./components/textline.js";
|
|
45
|
-
/** The
|
|
46
|
+
/** The Staffa component namespace. */
|
|
46
47
|
export declare const S: {
|
|
47
48
|
main: typeof main;
|
|
48
49
|
box: typeof box;
|
|
@@ -56,17 +57,17 @@ export declare const S: {
|
|
|
56
57
|
checkbox: typeof checkbox;
|
|
57
58
|
tabs: typeof tabs;
|
|
58
59
|
button: typeof button;
|
|
60
|
+
buttonChooser: typeof buttonChooser;
|
|
59
61
|
buttonGroup: typeof buttonGroup;
|
|
60
62
|
autocomplete: typeof autocomplete;
|
|
61
63
|
select: typeof select;
|
|
62
|
-
darkTheme: import("./theme.js").Theme;
|
|
63
|
-
lightTheme: import("./theme.js").Theme;
|
|
64
64
|
setDarkMode: typeof setDarkMode;
|
|
65
65
|
getDarkMode: typeof getDarkMode;
|
|
66
66
|
};
|
|
67
67
|
export default S;
|
|
68
|
-
export {
|
|
69
|
-
export type {
|
|
68
|
+
export { setDarkMode, getDarkMode } from "./theme.js";
|
|
69
|
+
export type { SurfaceRole, Variant } from "./theme.js";
|
|
70
|
+
export type { ContentOptions, Bindable, Content, Slot, Attributes as Styling, } from "./core.js";
|
|
70
71
|
export { drawSlot, uniqueId } from "./core.js";
|
|
71
72
|
export type { FieldOptions } from "./components/field.js";
|
|
72
73
|
export type { MainOptions } from "./components/main.js";
|
|
@@ -76,7 +77,8 @@ export type { TextlineOptions, TextlineType } from "./components/textline.js";
|
|
|
76
77
|
export type { TextareaOptions } from "./components/textarea.js";
|
|
77
78
|
export type { CheckboxOptions } from "./components/checkbox.js";
|
|
78
79
|
export type { Tab, TabsOptions } from "./components/tabs.js";
|
|
79
|
-
export type { ButtonOptions
|
|
80
|
+
export type { ButtonOptions } from "./components/button.js";
|
|
81
|
+
export type { ButtonChooserOptions } from "./components/buttonChooser.js";
|
|
80
82
|
export type { ButtonGroupOptions } from "./components/buttonGroup.js";
|
|
81
83
|
export type { AutocompleteOptions, AutocompleteOptionInput } from "./components/autocomplete.js";
|
|
82
84
|
export type { SelectOptions, SelectOptionInput } from "./components/select.js";
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Staffa — a small, opinionated component library for the
|
|
3
3
|
* {@link https://aberdeenjs.org | Aberdeen} reactive UI library.
|
|
4
4
|
*
|
|
5
5
|
* Import the default `S` object and call its component functions:
|
|
6
6
|
*
|
|
7
7
|
* ```ts
|
|
8
|
-
* import S from "
|
|
8
|
+
* import S from "staffa";
|
|
9
9
|
*
|
|
10
10
|
* S.main({
|
|
11
11
|
* title: "Hello",
|
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
* mutate it. See `AGENTS.md` for the design philosophy.
|
|
31
31
|
*/
|
|
32
32
|
// Importing the theme module installs spacing vars, the reactive theme and the
|
|
33
|
-
// base stylesheet. Customise
|
|
34
|
-
|
|
33
|
+
// base stylesheet. Customise it from your app with A.insertGlobalCss (see
|
|
34
|
+
// theme.ts); toggle modes with setDarkMode / getDarkMode.
|
|
35
|
+
import { setDarkMode, getDarkMode } from "./theme.js";
|
|
35
36
|
import { autocomplete } from "./components/autocomplete.js";
|
|
36
37
|
import { box } from "./components/box.js";
|
|
37
38
|
import { button } from "./components/button.js";
|
|
39
|
+
import { buttonChooser } from "./components/buttonChooser.js";
|
|
38
40
|
import { buttonGroup } from "./components/buttonGroup.js";
|
|
39
41
|
import { checkbox } from "./components/checkbox.js";
|
|
40
42
|
import { form } from "./components/form.js";
|
|
@@ -44,7 +46,7 @@ import { select } from "./components/select.js";
|
|
|
44
46
|
import { tabs } from "./components/tabs.js";
|
|
45
47
|
import { textarea } from "./components/textarea.js";
|
|
46
48
|
import { textline } from "./components/textline.js";
|
|
47
|
-
/** The
|
|
49
|
+
/** The Staffa component namespace. */
|
|
48
50
|
export const S = {
|
|
49
51
|
main,
|
|
50
52
|
box,
|
|
@@ -58,15 +60,14 @@ export const S = {
|
|
|
58
60
|
checkbox,
|
|
59
61
|
tabs,
|
|
60
62
|
button,
|
|
63
|
+
buttonChooser,
|
|
61
64
|
buttonGroup,
|
|
62
65
|
autocomplete,
|
|
63
66
|
select,
|
|
64
|
-
darkTheme,
|
|
65
|
-
lightTheme,
|
|
66
67
|
setDarkMode,
|
|
67
68
|
getDarkMode,
|
|
68
69
|
};
|
|
69
70
|
export default S;
|
|
70
71
|
// Re-export theming and shared types for advanced use.
|
|
71
|
-
export {
|
|
72
|
+
export { setDarkMode, getDarkMode } from "./theme.js";
|
|
72
73
|
export { drawSlot, uniqueId } from "./core.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import S from"aberdeen";var K="staffa:darkMode",U=S.proxy({value:le()});function le(){try{let e=localStorage.getItem(K);if(e==="dark")return!0;if(e==="light")return!1}catch{}}function P(e){U.value=e;try{e===void 0?localStorage.removeItem(K):localStorage.setItem(K,e?"dark":"light")}catch{}}function M(e=!1){let t=U.value;return t===void 0&&!e?S.darkMode():t}S(()=>{M()?S.insertGlobalCss({":root":"--s-primary:#8b7bff --s-danger:#ff6b6b --s-success:#46d39a --s-warning:#fbbf24 --s-neutral:#3c4352 --s-page:#0e1015 --s-panel:#181b22 --s-raised:#222632 --s-ink:#e8eaf0 --s-on-accent:#0c0a14 --s-focus:rgba(139,123,255,0.45) --s-radius:10px --s-radius-lg:16px --s-shadow: 0 8px 30px rgba(0,0,0,0.45);",":root, .s-s.base, .s-s.panel, .s-s.raised, .s-s.neutral":"--s-link:#6db3ff"}):S.insertGlobalCss({":root":"--s-primary:#6c5ce7 --s-danger:#e23b3b --s-success:#1f9d6b --s-warning:#d97706 --s-neutral:#c7ccda --s-page:#f3f4f8 --s-panel:#ffffff --s-raised:#eceef4 --s-ink:#1b1e27 --s-on-accent:#ffffff --s-focus:rgba(108,92,231,0.35) --s-radius:10px --s-radius-lg:16px --s-shadow: 0 6px 24px rgba(20,24,40,0.12);",":root, .s-s.base, .s-s.panel, .s-s.raised, .s-s.neutral":"--s-link:#2563eb"})});S.setSpacingCssVars();S.insertGlobalCss({":root, .s-s.base":"--s-a:$s-ink --s-b:$s-page",".s-s.panel":"--s-a:$s-ink --s-b:$s-panel",".s-s.raised":"--s-a:$s-ink --s-b:$s-raised",".s-s.neutral":"--s-a:$s-ink --s-b:$s-neutral",".s-s.primary":"--s-a:$s-on-accent --s-b:$s-primary",".s-s.danger":"--s-a:$s-on-accent --s-b:$s-danger",".s-s.success":"--s-a:$s-on-accent --s-b:$s-success",".s-s.warning":"--s-a:$s-on-accent --s-b:$s-warning",":root, .s-s":"--s-fg:$s-a --s-bg:$s-b --s-fg-muted: color-mix(in oklab, $s-fg, $s-bg 42%); --s-fg-faint: color-mix(in oklab, $s-fg, $s-bg 64%); --s-border: color-mix(in oklab, $s-fg, $s-bg 82%); --s-border-strong: color-mix(in oklab, $s-fg, $s-bg 68%); background:$s-bg color:$s-fg",".s-s.tonal":"--s-fg:$s-b --s-bg: color-mix(in srgb, $s-b 16%, transparent);",".s-s.outlined":"--s-fg:$s-b --s-bg:inherit background:transparent --s-border: color-mix(in srgb, $s-fg 55%, $s-bg);",".s-s.filled":"--s-fg:$s-a --s-bg:$s-b background:$s-bg;",":root, .s-s.base, .s-s.panel, .s-s.raised, .s-s.neutral":"--s-accent:$s-primary",".s-s.primary, .s-s.danger, .s-s.success, .s-s.warning":"--s-accent:$s-fg --s-link:$s-fg"});S.insertGlobalCss({"*, *::before, *::after":"box-sizing:border-box",html:"text-size-adjust:100%",body:"m:0 line-height:1.5 font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -webkit-font-smoothing:antialiased",a:"color: $s-link; text-decoration:underline text-underline-offset:2px","a:hover":"filter: brightness(1.15)","input, button, textarea, select":"font:inherit color:inherit","code, kbd, samp, pre":"font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;",code:"background: color-mix(in oklab, $s-fg, $s-bg 86%); padding: 0.12em 0.34em; r:4px font-size:0.9em",pre:"background: color-mix(in oklab, $s-fg, $s-bg 92%); p:$3 r: $s-radius; overflow:auto","pre code":"background:transparent p:0","img, svg, video, canvas":"max-width:100% h:auto",hr:"border:0 border-top: 1px solid $s-border; margin: $3 0;","::placeholder":"color: $s-fg-faint; opacity:1",":focus-visible":"outline: 2px solid $s-focus; outline-offset:2px",small:"color:$s-fg-muted font-size:0.9em"});import o from"aberdeen";import de from"aberdeen";var ce=0;function O(e="s"){return`${e}-${++ce}`}function c(e,...t){e!=null&&(typeof e=="function"?e(...t):de("rich=",e))}import g from"aberdeen";g.insertGlobalCss({".s-field":{"&":"display:flex flex-direction:column gap:$1","> label":"font-weight:600 font-size:0.9em fg:$s-fg user-select:none"},".s-req":"fg:$s-danger margin-left:2px",".s-help":"font-size:0.82em fg:$s-fg-muted",".s-error":"font-size:0.82em fg:$s-danger",".s-input":{"&":"w:100% bg:$s-panel fg:$s-ink border: 1px solid $s-border; r:$s-radius padding: 0.55em 0.7em; transition: border-color 0.15s, box-shadow 0.15s;","&:hover:not(:disabled)":"border-color:$s-border-strong","&:focus-visible":"border-color:$s-accent box-shadow: 0 0 0 3px $s-focus; outline:none","&:disabled":"opacity:0.6 cursor:not-allowed","&[aria-invalid=true]":"border-color:$s-danger"}});function T(e,t){let n=e.id??O("field"),i=()=>!!e.error;g("div.s-field",e.attrs,()=>{g(()=>{e.label!=null&&g(`label for=${n}`,()=>{c(e.label),e.required&&g("span.s-req aria-hidden=true #*")})}),t(n,i),g(()=>{e.help!=null&&!e.error&&g("div.s-help",()=>c(e.help))}),g(()=>{e.error&&g("div.s-error role=alert #",e.error)})})}function F(e,t,n,i){g(`id=${t}`),e.name&&g(`name=${e.name}`),g(()=>{e.disabled&&g("disabled=true")}),g(()=>{e.required&&g("aria-required=true")}),g(()=>g("aria-invalid=",n()?"true":"false")),i&&g("bind=",i)}o.insertGlobalCss({".s-ac":{"&":"position:relative","> .s-control":"display:flex flex-wrap:wrap align-items:center gap:$1 bg:$s-panel fg:$s-ink border: 1px solid $s-border; r:$s-radius padding: 0.3em 0.4em; cursor:text; transition: border-color 0.15s, box-shadow 0.15s;","> .s-control:hover":"border-color:$s-border-strong","> .s-control:focus-within":"border-color:$s-accent box-shadow: 0 0 0 3px $s-focus;","&[aria-invalid=true] > .s-control":"border-color:$s-danger",".s-chip":"display:inline-flex align-items:center gap:$1 font-size:0.85em bg:$s-raised border: 1px solid $s-border; r:$s-radius padding: 0.1em 0.2em 0.1em 0.5em;",".s-chip > button":"cursor:pointer border:0 background:transparent fg:$s-fg-muted font-size:1.1em line-height:1 padding: 0 0.2em; r:4px",".s-chip > button:hover":"fg:$s-fg background:$s-border",input:"flex:1 min-width:6ch border:0 background:transparent color:inherit outline:none padding:0.25em","> .s-menu":"position:absolute top:100% left:0 right:0 z-index:20 margin-top:4px max-height:15rem overflow-y:auto list-style:none p:$1 margin-bottom:0 bg:$s-panel border: 1px solid $s-border; r:$s-radius box-shadow:$s-shadow",".s-option":"padding: 0.45em 0.6em; r:6px cursor:pointer",".s-option[aria-selected=true]":"background:$s-raised",".s-add":"fg:$s-accent font-style:italic",".s-empty":"padding: 0.45em 0.6em; fg:$s-fg-muted"}});function ue(e){return typeof e=="string"?{value:e,label:e}:{value:e.value,label:e.label??e.value}}function _(e){let t=O("ac-menu"),n=o.proxy({query:"",open:!1,active:0}),i=()=>(typeof e.options=="function"?e.options():e.options).map(ue),r=()=>{let s=e.bind?.value;return s==null||s===""?[]:Array.isArray(s)?s:[s]},l=s=>i().find(x=>x.value===s)?.label??s;if(!e.multi){let s=e.bind?o.peek(e.bind,"value"):void 0;typeof s=="string"&&s&&(n.query=o.peek(()=>l(s)))}let a=()=>{let s=new Set(r()),x=i();e.multi&&(x=x.filter(m=>!s.has(m.value)));let b=n.query.trim().toLowerCase();return b&&(x=x.filter(m=>m.label.toLowerCase().includes(b))),x},u=(s,x)=>{if(e.multi){let b=Array.isArray(e.bind?.value)?[...e.bind.value]:[];b.includes(s)||b.push(s),e.bind&&(e.bind.value=b),n.query=""}else e.bind&&(e.bind.value=s),n.query=l(s),n.open=!1;n.active=0,x?.focus()},$=s=>{if(!e.bind)return;let x=e.bind.value??[];e.bind.value=x.filter(b=>b!==s)};T(e,(s,x)=>{o("div.s-ac",e.inputAttrs,()=>{o(()=>o("aria-invalid=",x()?"true":"false"));let b;o("div.s-control",()=>{o("click=",()=>b?.focus()),o(()=>{if(e.multi)for(let m of r())o("span.s-chip",()=>{o("span #",o.peek(()=>l(m))),o("button type=button aria-label=",`Remove ${m}`,()=>{o("#\xD7"),o("click=",v=>{v.stopPropagation(),$(m),b?.focus()})})})}),b=o("input type=text role=combobox autocomplete=off",()=>{o(`id=${s} aria-controls=${t} aria-autocomplete=list`),e.placeholder!=null&&o("placeholder=",e.placeholder),e.disabled&&o("disabled=true"),e.required&&o("aria-required=true"),o("bind=",o.ref(n,"query")),o(()=>o("aria-expanded=",n.open?"true":"false")),o(()=>{let v=a()[n.active];o("aria-activedescendant=",n.open&&v?`${t}-opt-${n.active}`:"")}),o("input=",()=>{n.open=!0,n.active=0}),o("focus=",()=>{n.open=!0}),o("blur=",()=>{setTimeout(()=>L(),150)}),o("keydown=",m=>A(m,b))})}),o(()=>{if(!n.open)return;let m=a(),v=n.query.trim(),W=e.allowCustom!==!1&&v!==""&&!m.some(G=>G.label.toLowerCase()===v.toLowerCase());o("ul.s-menu role=listbox",`id=${t}`,()=>{m.forEach((G,H)=>{o("li.s-option role=option",`id=${t}-opt-${H}`,()=>{o(()=>o("aria-selected=",n.active===H?"true":"false")),o("#",G.label),o("mousedown=",ae=>ae.preventDefault()),o("click=",()=>u(G.value,b)),o("mousemove=",()=>{n.active=H})})}),W&&o("li.s-option.s-add role=option",()=>{o("#",`Add "${v}"`),o("mousedown=",G=>G.preventDefault()),o("click=",()=>u(v,b))}),m.length===0&&!W&&o("li.s-empty #No matches")})}),o(()=>{if(e.name)if(e.multi)for(let m of r())o("input type=hidden",()=>{o("name=",e.name),o("value=",m)});else o("input type=hidden",()=>{o("name=",e.name),o("value=",r()[0]??"")})})})});function A(s,x){let b=a(),m=b.length-1;if(s.key==="ArrowDown")s.preventDefault(),n.open=!0,n.active=Math.min(m,n.active+1);else if(s.key==="ArrowUp")s.preventDefault(),n.active=Math.max(0,n.active-1);else if(s.key==="Enter"){s.preventDefault();let v=b[n.active];v?u(v.value,x):e.allowCustom!==!1&&n.query.trim()?u(n.query.trim(),x):n.open&&(n.open=!1)}else if(s.key==="Escape")n.open=!1,e.multi||(n.query=l(r()[0]??""));else if(s.key==="Backspace"&&e.multi&&n.query===""){let v=r();v.length&&$(v[v.length-1])}}function L(){n.open=!1,e.multi?n.query="":e.allowCustom!==!1&&n.query.trim()?u(n.query.trim()):n.query=l(r()[0]??"")}}import B from"aberdeen";B.insertGlobalCss({".s-box":{"&":"display:flex flex-direction:column border: 1px solid $s-border; r: $s-radius; overflow:hidden","> header":"display:flex align-items:center gap:$2 padding: $2 $3; border-bottom: 1px solid $s-border; font-weight:600","> footer":"display:flex align-items:center gap:$2 padding: $2 $3; border-top: 1px solid $s-border;","> div":"p:$3 gap:$3"}});function Y(e={}){let t=typeof e=="function"?{content:e}:e;B("section.s-box.s-s.panel",t.attrs,()=>{B(()=>{t.header!=null&&B("header.s-s.raised",t.headerAttrs,()=>c(t.header))}),B("div",t.contentAttrs,()=>{t.content&&t.content()}),B(()=>{t.footer!=null&&B("footer.s-s.raised",t.footerAttrs,()=>c(t.footer))})})}import C from"aberdeen";C.insertGlobalCss({".s-btn":{"&":"display:inline-flex align-items:center justify-content:center gap:$2 font-weight:600 line-height:1.2 white-space:nowrap cursor:pointer text-decoration:none border: 1px solid $s-border; r: $s-radius; padding: 0.5em 1em; transition: background 0.15s, border-color 0.15s, filter 0.15s, box-shadow 0.15s;","&:focus-visible":"outline:none box-shadow: 0 0 0 3px $s-focus;","&:disabled, &[aria-disabled=true]":"opacity:0.45 cursor:not-allowed pointer-events:none filter:saturate(0.6)","&:hover":"filter: brightness(1.08)","&.tonal:hover, &.outlined:hover":"background: color-mix(in srgb, $s-b 26%, transparent);","&.s-sm":"padding: 0.32em 0.7em; font-size:0.85em","&.s-lg":"padding: 0.66em 1.3em; font-size:1.1em"}});function w(e={}){let t=typeof e=="string"?{text:e}:typeof e=="function"?{content:e}:e,n=t.href!=null?"a":"button",i=t.size!=null?`.s-${t.size}`:"";C(`${n}.s-btn.s-s.primary${i}`,t.attrs,()=>{t.href!=null?(C(`href=${t.href} role=button`),t.disabled&&C("aria-disabled=true")):(C("type=",t.type??"button"),t.disabled&&C("disabled=true")),t.ariaLabel&&C("aria-label=",t.ariaLabel),t.click&&C("click=",t.click),c(t.icon),t.content?t.content():t.text!=null&&C("#",t.text)})}import R from"aberdeen";import N from"aberdeen";N.insertGlobalCss({".s-bgroup":{"&":"display:inline-flex align-items:stretch","&.s-spaced":"gap:$2 flex-wrap:wrap","&.s-vertical":"flex-direction:column","&.s-attached":"gap:0","&.s-attached:not(.s-vertical) > .s-btn:not(:first-child)":"margin-left:-1px","&.s-attached:not(.s-vertical) > .s-btn:not(:first-child):not(:last-child)":"r:0","&.s-attached:not(.s-vertical) > .s-btn:first-child:not(:last-child)":"border-top-right-radius:0 border-bottom-right-radius:0","&.s-attached:not(.s-vertical) > .s-btn:last-child:not(:first-child)":"border-top-left-radius:0 border-bottom-left-radius:0","&.s-attached.s-vertical > .s-btn:not(:first-child)":"margin-top:-1px","&.s-attached.s-vertical > .s-btn:not(:first-child):not(:last-child)":"r:0","&.s-attached.s-vertical > .s-btn:first-child:not(:last-child)":"border-bottom-left-radius:0 border-bottom-right-radius:0","&.s-attached.s-vertical > .s-btn:last-child:not(:first-child)":"border-top-left-radius:0 border-top-right-radius:0","&.s-attached > .s-btn:hover, &.s-attached > .s-btn:focus-visible":"z-index:1"}});function E(e={}){let n=`.s-${e.layout??"attached"}${e.vertical?".s-vertical":""}`;N(`div.s-bgroup${n} role=group`,e.attrs,()=>{if(e.buttons)for(let i of e.buttons)w(i);e.content&&e.content()})}function J(e){R(()=>{let t=e.bind.value;E({attrs:e.attrs,buttons:Object.entries(e.options).map(([n,i])=>({text:i,size:e.size,attrs:t===n?".primary":".neutral .outlined",click:()=>{e.bind.value=e.allowDeselect&&t===n?null:n}}))})}),e.name&&R(()=>R(`input type=hidden name=${e.name} value=`,e.bind.value??""))}import p from"aberdeen";p.insertGlobalCss({".s-check":{"&":"display:flex flex-direction:column gap:$1","> label":"display:flex align-items:center gap:$2 cursor:pointer user-select:none","> label:has(input:disabled)":"cursor:not-allowed opacity:0.6",input:"width:1.15em height:1.15em accent-color:$s-accent cursor:inherit m:0"}});function Q(e={}){let t=e.id??O("check");p("div.s-check",e.attrs,()=>{p(`label for=${t}`,()=>{p("input type=checkbox",e.inputAttrs,()=>{p(`id=${t}`),e.name&&p(`name=${e.name}`),e.checked&&!e.bind&&p("checked=true"),e.change&&p("change=",e.change),p(()=>{e.disabled&&p("disabled=true")}),p(()=>{e.required&&p("aria-required=true")}),e.bind&&p("bind=",e.bind)}),p(()=>{e.label!=null&&c(e.label),e.required&&p("span.s-req aria-hidden=true #*")})}),p(()=>{e.help!=null&&!e.error&&p("div.s-help",()=>c(e.help))}),p(()=>{e.error&&p("div.s-error role=alert #",e.error)})})}import q from"aberdeen";q.insertGlobalCss({".s-form":{"&":"display:flex flex-direction:column gap:$3","&.grid":"display:grid grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap:$3","&.grid > .s-wide, &.grid > footer":"grid-column: 1 / -1;","> footer":"display:flex align-items:center gap:$2 flex-wrap:wrap margin-top:$1"}});function X(e={}){let t=typeof e=="function"?{content:e}:e;q("form.s-form",t.attrs,()=>{q(()=>{q(".grid=",t.layout==="grid")}),q("submit=",n=>{if(n.preventDefault(),t.submit){let i=new FormData(n.target),r={};for(let l of new Set(i.keys())){let a=i.getAll(l);r[l]=a.length===1?a[0]:a}t.submit(r,n)}}),t.content&&t.content(),q(()=>{t.actions&&q("footer",t.actionsAttrs,()=>t.actions?.())})})}import f from"aberdeen";f.insertGlobalCss({".s-main":{"&":"display:flex flex-direction:column min-height:100vh max-height:100vh","> header":"display:flex align-items:center gap:$3 padding: $2 $3; border-bottom: 1px solid $s-border; position:sticky top:0 z-index:10","> header .s-icon":"display:flex align-items:center font-size:1.4em","> header .s-titles":"display:flex flex-direction:column min-width:0 flex:1","> header .s-title":"font-weight:700 font-size:1.1em line-height:1.2 overflow:hidden text-overflow:ellipsis white-space:nowrap","> header .s-subtitle":"fg:$s-fg-muted font-size:0.85em overflow:hidden text-overflow:ellipsis white-space:nowrap","> header .s-menu":"display:flex align-items:center gap:$2","> main":"flex:1 overflow-y:auto display:flex flex-direction:column","> main > .s-content":"width:100% flex:1","> main > .s-content.s-framed":"margin: $3 auto; border: 1px solid $s-border; r:$s-radius-lg box-shadow:$s-shadow p:$4","> main > .s-content.s-plain":"p:$3","> footer":"display:flex align-items:center gap:$2 padding: $2 $3; border-top: 1px solid $s-border; fg:$s-fg-muted"}});function Z(e={}){f("div.s-main.s-s.base",e.attrs,()=>{f(()=>{(e.title!=null||e.subtitle!=null||e.icon!=null||e.menu!=null)&&f("header.s-s.raised",e.topbarAttrs,()=>{f(()=>{e.icon!=null&&f("div.s-icon",()=>c(e.icon))}),f("div.s-titles",()=>{f(()=>{e.title!=null&&f("div.s-title",()=>c(e.title))}),f(()=>{e.subtitle!=null&&f("div.s-subtitle",()=>c(e.subtitle))})}),f(()=>{e.menu&&f("div.s-menu",()=>e.menu?.())})})}),f("main",()=>{f("div.s-content",e.contentAttrs,()=>{f(()=>{let t=e.maxWidth;t!=null?f(".s-framed.s-s.panel max-width:",t):f(".s-plain")}),e.content&&e.content()})}),f(()=>{e.footer!=null&&f("footer",()=>c(e.footer))})})}import d from"aberdeen";import z from"aberdeen";function j(e={}){T(e,(t,n)=>{z("input.s-input",e.inputAttrs,()=>{z("type=",e.type??"text"),e.placeholder!=null&&z("placeholder=",e.placeholder),e.autocomplete!=null&&z("autocomplete=",e.autocomplete),e.value!=null&&!e.bind&&z("value=",e.value),e.input&&z("input=",e.input),e.change&&z("change=",e.change),F(e,t,n,e.bind)})})}d.insertGlobalCss({".s-backdrop":{"&":"position:fixed inset:0 z-index:200 display:block background: rgba(0,0,0,0.55); transition: opacity 0.4s ease-in-out;","&.hidden":"opacity:0 pointer-events:none"},".s-dialog":{"&":"position:fixed z-index:200 top:50% left:50% display:flex flex-direction:column transform:translate(-50%,-50%) min-width:20rem max-width:min(90vw,44rem) max-height:min(88vh,800px) border: 1px solid $s-border; r: $s-radius-lg; box-shadow: $s-shadow; overflow:hidden transition: opacity 0.2s ease-out, transform 0.2s ease-out;","> header":"display:flex align-items:center gap:$2 padding: $2 $3; border-bottom: 1px solid $s-border; font-weight:600 flex-shrink:0","> footer":"display:flex align-items:center gap:$2 padding: $2 $3; border-top: 1px solid $s-border; flex-shrink:0","> div":"p:$3 gap:$3 display:flex flex-direction:column overflow-y:auto flex:1 min-height:0","&.hidden":"opacity:0 pointer-events:none transform: translate(-50%, calc(-50% + 20px)); pointer-events:none"}});var I=d.proxy({}),V=0,fe=d.derive(()=>{let e=Object.keys(I);if(e.length)return e[e.length-1]});d.mount(document.body,()=>{d.onEach(I,({resolve:e,opts:t},n)=>{let i=()=>{delete I[n]};d.clean(()=>{t.onClose?.(),e()});let r=d.derive(()=>fe.value!=n);d("div.s-backdrop create=hidden destroy=hidden .hidden=",r,"click=",()=>{t.allowCancel!==!1&&i()}),d("div.s-dialog.s-s.panel create=hidden destroy=hidden",t.attrs,()=>{d(()=>{t.header!=null&&d("header.s-s.raised",t.headerAttrs,()=>c(t.header))}),d("div",t.contentAttrs,()=>{c(t.content,i)}),d(()=>{t.footer!=null&&d("footer.s-s.raised",t.footerAttrs,()=>c(t.footer))})})})});function D(e){V||document.addEventListener("keydown",n=>{if(n.key==="Escape"&&e.allowCancel!==!1){let i=d.unproxy(I);for(let r=V;r>0;r--)if(i[r]){i[r].opts.allowCancel!==!1&&delete I[r];break}}});let t=++V;return e.cancelWithScope!==!1&&d.clean(()=>{delete I[t]}),new Promise(n=>{I[t]={resolve:n,opts:e}})}function ee(e,t={}){return D({header:"Alert",allowCancel:!0,content:n=>{d("p",()=>{d("#",e)}),E({layout:"spaced",attrs:"align-self:flex-end",content:()=>{w({text:"OK",click:n})}})},...t})}function te(e,t={}){return new Promise(n=>{let i=!1;D({header:"Confirm",allowCancel:!0,content:r=>{d("p",()=>{d("#",e)}),E({layout:"spaced",attrs:"align-self:flex-end",content:()=>{w({text:"Cancel",attrs:".neutral .outlined",click:r}),w({text:"OK",click:()=>{i=!0,r()}})}})},...t,onClose:()=>{n(i),t.onClose?.()}})})}function ne(e,t="",n={}){return new Promise(i=>{let r=null;D({header:"Input",allowCancel:!0,content:l=>{d("p",()=>{d("#",e)});let a=d.proxy({value:t});d("form display:contents",()=>{d("submit=",u=>{u.preventDefault(),r=a.value,l()}),j({bind:d.ref(a,"value")}),E({layout:"spaced",attrs:"align-self:flex-end",content:()=>{w({text:"Cancel",attrs:".neutral .outlined",type:"button",click:l}),w({text:"OK",type:"submit"})}})})},...n,onClose:()=>{i(r),n.onClose?.()}})})}import y from"aberdeen";y.insertGlobalCss({".s-select_wrap":{"&":"position:relative display:block",select:"w:100% cursor:pointer padding-right:2.2em; appearance:none","&::after":"content: '\u25BE'; position:absolute right:0.7em top:50%; transform: translateY(-50%); pointer-events:none fg:$s-fg-muted font-size:0.85em"}});function oe(e){T(e,(t,n)=>{y("div.s-select_wrap",e.inputAttrs,()=>{y("select.s-input",()=>{F(e,t,n),y("change=",i=>{e.bind&&(e.bind.value=i.target.value)}),y(()=>{let i=typeof e.options=="function"?e.options():e.options,r=e.bind?.value??"";e.placeholder!=null&&y("option",()=>{y("value= disabled=true hidden=true"),r||y("selected=true"),y("#",e.placeholder)});for(let l of i){let a=typeof l=="string"?{value:l,label:l}:{value:l.value,label:l.label??l.value};y("option",()=>{y("value=",a.value),a.value===r&&y("selected=true"),y("#",a.label)})}})})})})}import h from"aberdeen";h.insertGlobalCss({".s-tabs":{"&":"display:flex flex-direction:column gap:$3",".s-tablist":"display:flex gap:$1 align-items:stretch",".s-tab":"display:inline-flex align-items:center gap:$2 cursor:pointer background:transparent border:0 color: $s-fg-muted; font-weight:600 padding: 0.6em 0.9em; transition: color 0.15s, background 0.15s, border-color 0.15s;",".s-tab:hover:not(:disabled)":"color: $s-fg;",".s-tab:disabled":"opacity:0.5 cursor:not-allowed",".s-tab:focus-visible":"outline:none box-shadow: 0 0 0 3px $s-focus; r: $s-radius;","&.s-underline .s-tablist":"border-bottom: 1px solid $s-border;","&.s-underline .s-tab":"border-bottom: 2px solid transparent; margin-bottom:-1px","&.s-underline .s-tab[aria-selected=true]":"color: $s-fg; border-bottom-color: $s-accent;","&.s-pills .s-tab":"r: $s-radius;","&.s-pills .s-tab[aria-selected=true]":"background: $s-bg; color: $s-fg;",".s-tabpanel":"display:block"}});function re(e){let t=e.variant??"underline",n=O("tabs"),i=(a,u)=>a.id??String(u),r=e.bind??h.proxy(i(e.tabs[0]??{label:""},0)),l=(a,u)=>{a.disabled||(r.value=i(a,u))};h(`div.s-tabs.s-${t}`,e.attrs,()=>{h("div.s-tablist role=tablist",()=>{e.tabs.forEach((a,u)=>{let $=i(a,u);h("button.s-tab type=button role=tab",()=>{h(`id=${n}-tab-${$} aria-controls=${n}-panel-${$}`),h(()=>{let A=r.value===$;h("aria-selected=",A?"true":"false"),h("tabindex=",A?"0":"-1"),A&&t==="pills"&&h(".s-s.primary")}),a.disabled&&h("disabled=true"),h("click=",()=>l(a,u)),h("keydown=",A=>pe(A,e.tabs,u,l)),c(a.icon),c(a.label)})})}),h("div.s-tabpanel role=tabpanel",e.contentAttrs,()=>{h(()=>{let a=r.value,u=e.tabs.findIndex((A,L)=>i(A,L)===a),$=e.tabs[u]??e.tabs[0];$&&(h(`id=${n}-panel-${i($,u)} aria-labelledby=${n}-tab-${i($,u)}`),$.content?.())})})})}function pe(e,t,n,i){let r=n;if(e.key==="ArrowRight"||e.key==="ArrowDown")r=(n+1)%t.length;else if(e.key==="ArrowLeft"||e.key==="ArrowUp")r=(n-1+t.length)%t.length;else if(e.key==="Home")r=0;else if(e.key==="End")r=t.length-1;else return;e.preventDefault();let l=r>=n?1:-1;for(let a=0;a<t.length;a++){let u=t[r];if(u&&!u.disabled){i(u,r),e.currentTarget?.parentElement?.children[r]?.focus();return}r=(r+l+t.length)%t.length}}import k from"aberdeen";k.insertGlobalCss({"textarea.s-input":"resize:vertical min-height:3em line-height:1.45","textarea.s-input.s-autoGrow":"resize:none min-height:2.5em overflow-y:hidden"});function se(e={}){let t=e.autoGrow!==!1;T(e,(n,i)=>{let r=k("textarea.s-input",e.inputAttrs,()=>{t?(k(".s-autoGrow"),k("input=",l=>{ie(l.currentTarget),e.input&&e.input(l)})):(k("rows=",e.rows??4),k("resize:",e.resize??"vertical"),e.input&&k("input=",e.input)),e.placeholder!=null&&k("placeholder=",e.placeholder),e.value!=null&&!e.bind&&k("value=",e.value),e.change&&k("change=",e.change),F(e,n,i,e.bind)});t&&requestAnimationFrame(()=>ie(r))})}function ie(e){e.style.height="auto",e.style.height=`${e.scrollHeight}px`}var be={main:Z,box:Y,dialog:D,alert:ee,confirm:te,prompt:ne,form:X,textline:j,textarea:se,checkbox:Q,tabs:re,button:w,buttonChooser:J,buttonGroup:E,autocomplete:_,select:oe,setDarkMode:P,getDarkMode:M},At=be;export{be as S,At as default,c as drawSlot,M as getDarkMode,P as setDarkMode,O as uniqueId};
|