@adea-ai/ui 0.143.0 → 0.144.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/dist/components/composites/accent-picker/accent-picker.d.ts +1 -1
- package/dist/components/composites/accent-picker/accent-picker.d.ts.map +1 -1
- package/dist/components/composites/accent-picker/accent-picker.js +31 -21
- package/dist/components/composites/accent-picker/accent-picker.js.map +1 -1
- package/dist/components/composites/icon-picker/icon-picker.d.ts +16 -1
- package/dist/components/composites/icon-picker/icon-picker.d.ts.map +1 -1
- package/dist/components/composites/icon-picker/icon-picker.js +83 -5
- package/dist/components/composites/icon-picker/icon-picker.js.map +1 -1
- package/dist/components/composites/icon-picker/index.d.ts +1 -1
- package/dist/components/composites/icon-picker/index.d.ts.map +1 -1
- package/dist/components/composites/icon-picker/index.js +2 -2
- package/dist/components/composites/settings/settings-navigation.js +1 -1
- package/dist/components/composites/settings/settings-navigation.js.map +1 -1
- package/dist/components/composites/settings/settings.d.ts +8 -14
- package/dist/components/composites/settings/settings.d.ts.map +1 -1
- package/dist/components/composites/settings/settings.js +88 -37
- package/dist/components/composites/settings/settings.js.map +1 -1
- package/dist/components/composites/tree/tree.d.ts +6 -0
- package/dist/components/composites/tree/tree.d.ts.map +1 -1
- package/dist/components/composites/tree/tree.js +35 -14
- package/dist/components/composites/tree/tree.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/r/accent-picker.json +2 -2
- package/dist/r/icon-picker.json +1 -0
- package/dist/r/registry.json +6 -3
- package/dist/r/settings.json +3 -1
- package/dist/r/src/components/composites/accent-picker/accent-picker.tsx +37 -16
- package/dist/r/src/components/composites/icon-picker/icon-picker.tsx +65 -3
- package/dist/r/src/components/composites/icon-picker/index.ts +6 -1
- package/dist/r/src/components/composites/settings/settings-navigation.tsx +1 -1
- package/dist/r/src/components/composites/settings/settings.tsx +40 -5
- package/dist/r/src/components/composites/tree/tree.tsx +25 -0
- package/package.json +1 -1
- package/registry.json +6 -3
- package/src/components/composites/accent-picker/accent-picker.tsx +37 -16
- package/src/components/composites/icon-picker/icon-picker.tsx +65 -3
- package/src/components/composites/icon-picker/index.ts +6 -1
- package/src/components/composites/settings/settings-navigation.tsx +1 -1
- package/src/components/composites/settings/settings.tsx +40 -5
- package/src/components/composites/tree/tree.tsx +25 -0
|
@@ -19,7 +19,7 @@ export type AccentPickerProps = {
|
|
|
19
19
|
/** An accent preset id, or `null` for no override. */
|
|
20
20
|
value: string | null;
|
|
21
21
|
onValueChange(value: string | null): void;
|
|
22
|
-
/** What `null` means here, e.g. "Inherit workspace accent". Also the first swatch's name. */
|
|
22
|
+
/** What `null` means here, e.g. "Inherit workspace accent". Also the first swatch's name and tooltip. */
|
|
23
23
|
inheritLabel?: string;
|
|
24
24
|
disabled?: boolean;
|
|
25
25
|
class?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accent-picker.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/accent-picker/accent-picker.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"accent-picker.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/accent-picker/accent-picker.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAMnC;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IACzC,yGAAyG;IACzG,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,eA6BpD"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../../ui/tooltip/tooltip.js";
|
|
2
3
|
import { accents } from "../../../lib/accent-presets.js";
|
|
3
4
|
import { createComponent, effect, insert, memo, setStyleProperty, template } from "solid-js/web";
|
|
4
|
-
import { Ban } from "lucide-solid";
|
|
5
5
|
import { For } from "solid-js";
|
|
6
6
|
import { RadioGroup } from "@kobalte/core/radio-group";
|
|
7
7
|
//#region src/components/composites/accent-picker/accent-picker.tsx
|
|
8
8
|
var _tmpl$ = /*#__PURE__*/ template(`<span aria-hidden=true class="block size-full rounded-full bg-(--swatch-light) dark:bg-(--swatch-dark)">`);
|
|
9
|
-
var _tmpl$2 = /*#__PURE__*/ template(`<span class=
|
|
9
|
+
var _tmpl$2 = /*#__PURE__*/ template(`<span aria-hidden=true data-slot=swatch-inherit class="relative block size-full overflow-hidden rounded-full bg-(--swatch-inherit)"style=--swatch-inherit:#000><span class="absolute inset-x-0 top-1/2 h-0.5 -translate-y-1/2 -rotate-45 bg-destructive">`);
|
|
10
|
+
var _tmpl$3 = /*#__PURE__*/ template(`<span class=sr-only>`);
|
|
10
11
|
var NONE = "__none__";
|
|
11
12
|
function AccentPicker(props) {
|
|
12
13
|
return createComponent(RadioGroup, {
|
|
@@ -28,13 +29,10 @@ function AccentPicker(props) {
|
|
|
28
29
|
return [createComponent(SwatchItem, {
|
|
29
30
|
id: NONE,
|
|
30
31
|
get label() {
|
|
31
|
-
return props.inheritLabel ?? "
|
|
32
|
+
return props.inheritLabel ?? "Inherit (default)";
|
|
32
33
|
},
|
|
33
34
|
get children() {
|
|
34
|
-
return createComponent(
|
|
35
|
-
"class": "size-4 text-muted-foreground",
|
|
36
|
-
"aria-hidden": "true"
|
|
37
|
-
});
|
|
35
|
+
return createComponent(InheritSwatch, {});
|
|
38
36
|
}
|
|
39
37
|
}), createComponent(For, {
|
|
40
38
|
each: accents,
|
|
@@ -63,6 +61,14 @@ function AccentPicker(props) {
|
|
|
63
61
|
}
|
|
64
62
|
});
|
|
65
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The "inherit" swatch: a black disc with a red slash, the universal "no
|
|
66
|
+
* override" mark. It is drawn rather than painted from a token so it reads the
|
|
67
|
+
* same in every theme and appearance.
|
|
68
|
+
*/
|
|
69
|
+
function InheritSwatch() {
|
|
70
|
+
return _tmpl$2();
|
|
71
|
+
}
|
|
66
72
|
function SwatchItem(props) {
|
|
67
73
|
return createComponent(RadioGroup.Item, {
|
|
68
74
|
get value() {
|
|
@@ -73,21 +79,25 @@ function SwatchItem(props) {
|
|
|
73
79
|
},
|
|
74
80
|
"class": "contents",
|
|
75
81
|
get children() {
|
|
76
|
-
return [createComponent(RadioGroup.ItemInput, { "class": "peer" }), createComponent(
|
|
77
|
-
|
|
82
|
+
return [createComponent(RadioGroup.ItemInput, { "class": "peer" }), createComponent(Tooltip, { get children() {
|
|
83
|
+
return [createComponent(TooltipTrigger, {
|
|
84
|
+
get as() {
|
|
85
|
+
return RadioGroup.ItemLabel;
|
|
86
|
+
},
|
|
87
|
+
get ["class"]() {
|
|
88
|
+
return cn("flex size-7 cursor-pointer items-center justify-center rounded-full border border-border p-0.5", "peer-focus-visible:border-ring peer-focus-visible:ring-3 peer-focus-visible:ring-primary-subtle", "data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50");
|
|
89
|
+
},
|
|
90
|
+
get children() {
|
|
91
|
+
return [memo(() => props.children), (() => {
|
|
92
|
+
var _el$3 = _tmpl$3();
|
|
93
|
+
insert(_el$3, () => props.label);
|
|
94
|
+
return _el$3;
|
|
95
|
+
})()];
|
|
96
|
+
}
|
|
97
|
+
}), createComponent(TooltipContent, { get children() {
|
|
78
98
|
return props.label;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return cn("flex size-7 cursor-pointer items-center justify-center rounded-full border border-border p-0.5", "peer-focus-visible:border-ring peer-focus-visible:ring-3 peer-focus-visible:ring-primary-subtle", "data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50");
|
|
82
|
-
},
|
|
83
|
-
get children() {
|
|
84
|
-
return [memo(() => props.children), (() => {
|
|
85
|
-
var _el$2 = _tmpl$2();
|
|
86
|
-
insert(_el$2, () => props.label);
|
|
87
|
-
return _el$2;
|
|
88
|
-
})()];
|
|
89
|
-
}
|
|
90
|
-
})];
|
|
99
|
+
} })];
|
|
100
|
+
} })];
|
|
91
101
|
}
|
|
92
102
|
});
|
|
93
103
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accent-picker.js","names":["RadioGroup","Radio","
|
|
1
|
+
{"version":3,"file":"accent-picker.js","names":["RadioGroup","Radio","JSX","For","accents","cn","Tooltip","TooltipContent","TooltipTrigger","AccentPickerProps","label","value","onValueChange","inheritLabel","disabled","class","NONE","AccentPicker","props","_$createComponent","aria-label","onChange","id","children","SwatchItem","InheritSwatch","each","accent","_el$","_tmpl$","_$effect","_p$","_v$","light","_v$2","dark","e","_$setStyleProperty","t","undefined","_tmpl$2","Element","Item","data-accent-option","ItemInput","as","ItemLabel","_$memo","_el$3","_tmpl$3","_$insert","_$createComponent","Radio","_$setStyleProperty","_$memo"],"sources":["../../../../src/components/composites/accent-picker/accent-picker.tsx"],"sourcesContent":["import { RadioGroup as Radio } from '@kobalte/core/radio-group'\nimport type { JSX } from 'solid-js'\nimport { For } from 'solid-js'\nimport { accents } from '#lib/accent-presets'\nimport { cn } from '#lib/utils'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'\n\n/**\n * AccentPicker.\n *\n * A row of accent swatches with a leading \"none\" choice, for a setting that\n * overrides the accent it would otherwise inherit: a project's icon colour, a\n * workspace's colour over the app's. `null` is a real value, not an empty one:\n * it means \"do not override\", so the swatch row never has an unchecked state and\n * the control can say what the inherited colour is through `inheritLabel`.\n *\n * The swatches are the theme's own accent presets, each carrying its light and\n * dark colour and switching on the `dark` class, so a swatch is the colour the\n * setting will produce in whichever appearance is in force and the picker needs\n * no theme provider.\n */\nexport type AccentPickerProps = {\n /** The group's accessible name, e.g. \"Icon colour\". */\n label: string\n /** An accent preset id, or `null` for no override. */\n value: string | null\n onValueChange(value: string | null): void\n /** What `null` means here, e.g. \"Inherit workspace accent\". Also the first swatch's name and tooltip. */\n inheritLabel?: string\n disabled?: boolean\n class?: string\n}\n\nconst NONE = '__none__'\n\nexport function AccentPicker(props: AccentPickerProps) {\n return (\n <Radio\n data-slot=\"swatch-picker\"\n aria-label={props.label}\n value={props.value ?? NONE}\n disabled={props.disabled}\n onChange={(id) => props.onValueChange(id === NONE ? null : id)}\n class={cn('flex flex-wrap items-center gap-2', props.class)}\n >\n <SwatchItem id={NONE} label={props.inheritLabel ?? 'Inherit (default)'}>\n <InheritSwatch />\n </SwatchItem>\n <For each={accents}>\n {(accent) => (\n <SwatchItem id={accent.id} label={accent.label}>\n <span\n aria-hidden=\"true\"\n class=\"block size-full rounded-full bg-(--swatch-light) dark:bg-(--swatch-dark)\"\n style={{\n '--swatch-light': accent.light ?? 'currentColor',\n '--swatch-dark': accent.dark ?? accent.light ?? 'currentColor',\n }}\n />\n </SwatchItem>\n )}\n </For>\n </Radio>\n )\n}\n\n/**\n * The \"inherit\" swatch: a black disc with a red slash, the universal \"no\n * override\" mark. It is drawn rather than painted from a token so it reads the\n * same in every theme and appearance.\n */\nfunction InheritSwatch() {\n return (\n <span\n aria-hidden=\"true\"\n data-slot=\"swatch-inherit\"\n class=\"relative block size-full overflow-hidden rounded-full bg-(--swatch-inherit)\"\n style={{ '--swatch-inherit': '#000' }}\n >\n <span class=\"absolute inset-x-0 top-1/2 h-0.5 -translate-y-1/2 -rotate-45 bg-destructive\" />\n </span>\n )\n}\n\nfunction SwatchItem(props: { id: string; label: string; children: JSX.Element }) {\n return (\n <Radio.Item value={props.id} data-accent-option={props.id} class=\"contents\">\n <Radio.ItemInput class=\"peer\" />\n <Tooltip>\n <TooltipTrigger\n as={Radio.ItemLabel}\n class={cn(\n 'flex size-7 cursor-pointer items-center justify-center rounded-full border border-border p-0.5',\n 'peer-focus-visible:border-ring peer-focus-visible:ring-3 peer-focus-visible:ring-primary-subtle',\n 'data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50'\n )}\n >\n {props.children}\n <span class=\"sr-only\">{props.label}</span>\n </TooltipTrigger>\n <TooltipContent>{props.label}</TooltipContent>\n </Tooltip>\n </Radio.Item>\n )\n}\n"],"mappings":";;;;;;;;;;AAiCA,IAAMgB,OAAO;AAEb,SAAgBC,aAAaC,OAA0B;CACrD,OAAAC,gBACGlB,YAAK;EAAA,aAAA;EAAA,KAAA,gBAAA;GAAA,OAEQiB,MAAMR;EAAK;EAAA,IACvBC,QAAK;GAAA,OAAEO,MAAMP,SAASK;EAAI;EAAA,IAC1BF,WAAQ;GAAA,OAAEI,MAAMJ;EAAQ;EACxBO,WAAWC,OAAOJ,MAAMN,cAAcU,OAAON,OAAO,OAAOM,EAAE;EAAC,KAAA,WAAA;GAAA,OACvDjB,GAAG,qCAAqCa,MAAMH,KAAK;EAAC;EAAA,IAAAQ,WAAA;GAAA,OAAA,CAAAJ,gBAE1DK,YAAU;IAACF,IAAIN;IAAI,IAAEN,QAAK;KAAA,OAAEQ,MAAML,gBAAgB;IAAmB;IAAA,IAAAU,WAAA;KAAA,OAAAJ,gBACnEM,eAAa,CAAA,CAAA;IAAA;GAAA,CAAA,GAAAN,gBAEfhB,KAAG;IAACuB,MAAMtB;IAAOmB,WACdI,WAAMR,gBACLK,YAAU;KAAA,IAACF,KAAE;MAAA,OAAEK,OAAOL;KAAE;KAAA,IAAEZ,QAAK;MAAA,OAAEiB,OAAOjB;KAAK;KAAA,IAAAa,WAAA;MAAA,IAAAK,OAAAC,OAAA;MAAAC,QAAAC,QAAA;OAAA,IAAAC,MAKtBL,OAAOM,SAAS,gBAAcC,OAC/BP,OAAOQ,QAAQR,OAAOM,SAAS;OAAcD,QAAAD,IAAAK,KAAAC,iBAAAT,MAAA,kBAAAG,IAAAK,IAAAJ,GAAA;OAAAE,SAAAH,IAAAO,KAAAD,iBAAAT,MAAA,iBAAAG,IAAAO,IAAAJ,IAAA;OAAA,OAAAH;MAAA,GAAA;OAAAK,GAAAG,KAAAA;OAAAD,GAAAC,KAAAA;MAAA,CAAA;MAAA,OAAAX;KAAA;IAAA,CAAA;GAIrE,CAAA,CAAA;EAAA;CAAA,CAAA;AAIT;;;;;;AAOA,SAASH,gBAAgB;CACvB,OAAAe,QAAA;AAUF;AAEA,SAAShB,WAAWN,OAA6D;CAC/E,OAAAC,gBACGlB,WAAMyC,MAAI;EAAA,IAAC/B,QAAK;GAAA,OAAEO,MAAMI;EAAE;EAAA,KAAA,wBAAA;GAAA,OAAsBJ,MAAMI;EAAE;EAAA,SAAA;EAAA,IAAAC,WAAA;GAAA,OAAA,CAAAJ,gBACtDlB,WAAM2C,WAAS,EAAA,SAAA,OAAA,CAAA,GAAAzB,gBACfb,SAAO,EAAA,IAAAiB,WAAA;IAAA,OAAA,CAAAJ,gBACLX,gBAAc;KAAA,IACbqC,KAAE;MAAA,OAAE5C,WAAM6C;KAAS;KAAA,KAAA,WAAA;MAAA,OACZzC,GACL,kGACA,mGACA,mFACA,gEACF;KAAC;KAAA,IAAAkB,WAAA;MAAA,OAAA,CAAAwB,WAEA7B,MAAMK,QAAQ,UAAA;OAAA,IAAAyB,QAAAC,QAAA;OAAAC,OAAAF,aACQ9B,MAAMR,KAAK;OAAA,OAAAsC;MAAA,EAAA,CAAA,CAAA;KAAA;IAAA,CAAA,GAAA7B,gBAEnCZ,gBAAc,EAAA,IAAAgB,WAAA;KAAA,OAAEL,MAAMR;IAAK,EAAA,CAAA,CAAA;GAAA,EAAA,CAAA,CAAA;EAAA;CAAA,CAAA;AAIpC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
1
2
|
/**
|
|
2
3
|
* IconPicker.
|
|
3
4
|
*
|
|
@@ -33,5 +34,19 @@ export type IconPickerProps = {
|
|
|
33
34
|
disabled?: boolean;
|
|
34
35
|
class?: string;
|
|
35
36
|
};
|
|
36
|
-
export declare function IconPicker(props: IconPickerProps):
|
|
37
|
+
export declare function IconPicker(props: IconPickerProps): JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* IconPickerDropdown.
|
|
40
|
+
*
|
|
41
|
+
* The compact form of the picker for a settings row: one button showing the
|
|
42
|
+
* current mark, opening the full grid (and the upload row, when the host offers
|
|
43
|
+
* it) in a popover. Choosing a glyph applies it and closes the popover; an
|
|
44
|
+
* upload or Remove leaves it open so the result is visible. The host owns the
|
|
45
|
+
* value exactly as it does for `IconPicker`.
|
|
46
|
+
*/
|
|
47
|
+
export type IconPickerDropdownProps = IconPickerProps & {
|
|
48
|
+
/** The mark shown on the trigger; defaults to the checked glyph or the uploaded image. */
|
|
49
|
+
trigger?: JSX.Element;
|
|
50
|
+
};
|
|
51
|
+
export declare function IconPickerDropdown(props: IconPickerDropdownProps): JSX.Element;
|
|
37
52
|
//# sourceMappingURL=icon-picker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon-picker.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/icon-picker/icon-picker.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"icon-picker.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/icon-picker/icon-picker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAcnC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,aAAa,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,eAsIhD;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,uBAAuB,GAAG,eAAe,GAAG;IACtD,0FAA0F;IAC1F,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACtB,CAAA;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,eA2ChE"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
2
|
import { Button } from "../../ui/button/button.js";
|
|
3
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover/popover.js";
|
|
3
4
|
import { MARK_IMAGE_MAX_SOURCE_BYTES, MarkImageError, markImageFromFile, markImageProblemCopy } from "./image-data-url.js";
|
|
4
|
-
import { resolveShellIcon, shellIconEntries } from "./shell-icons.js";
|
|
5
|
-
import { className, createComponent, effect, insert, memo, setAttribute, template, use } from "solid-js/web";
|
|
6
|
-
import { ImageUp, Trash2 } from "lucide-solid";
|
|
7
|
-
import { For, Show, createSignal, createUniqueId } from "solid-js";
|
|
5
|
+
import { resolveShellIcon, shellIconEntries, shellIconLabel } from "./shell-icons.js";
|
|
6
|
+
import { Dynamic, className, createComponent, effect, insert, memo, mergeProps, setAttribute, template, use } from "solid-js/web";
|
|
7
|
+
import { ChevronDown, ImageUp, Trash2 } from "lucide-solid";
|
|
8
|
+
import { For, Show, createSignal, createUniqueId, splitProps } from "solid-js";
|
|
8
9
|
import { RadioGroup } from "@kobalte/core/radio-group";
|
|
9
10
|
//#region src/components/composites/icon-picker/icon-picker.tsx
|
|
10
11
|
var _tmpl$ = /*#__PURE__*/ template(`<div class="flex items-center gap-3"data-slot=icon-picker-upload><span aria-hidden=true class="flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border bg-muted"></span><div class="flex flex-wrap items-center gap-2"><input type=file class=sr-only tabindex=-1 aria-hidden=true accept=image/png,image/jpeg,image/webp,image/gif>`);
|
|
@@ -13,6 +14,8 @@ var _tmpl$3 = /*#__PURE__*/ template(`<div data-slot=icon-picker>`);
|
|
|
13
14
|
var _tmpl$4 = /*#__PURE__*/ template(`<img alt class="size-full object-cover">`);
|
|
14
15
|
var _tmpl$5 = /*#__PURE__*/ template(`<p role=alert class="text-sm text-destructive">`);
|
|
15
16
|
var _tmpl$6 = /*#__PURE__*/ template(`<span class=sr-only>`);
|
|
17
|
+
var _tmpl$7 = /*#__PURE__*/ template(`<span aria-hidden=true class="flex size-5 items-center justify-center overflow-hidden">`);
|
|
18
|
+
var _tmpl$8 = /*#__PURE__*/ template(`<img alt class="size-full rounded-sm object-cover">`);
|
|
16
19
|
function IconPicker(props) {
|
|
17
20
|
const inputId = createUniqueId();
|
|
18
21
|
const [problem, setProblem] = createSignal();
|
|
@@ -181,7 +184,82 @@ function IconPicker(props) {
|
|
|
181
184
|
return _el$;
|
|
182
185
|
})();
|
|
183
186
|
}
|
|
187
|
+
function IconPickerDropdown(props) {
|
|
188
|
+
const [open, setOpen] = createSignal(false);
|
|
189
|
+
const [local, picker] = splitProps(props, ["trigger", "class"]);
|
|
190
|
+
const Current = () => resolveShellIcon(props.value ?? props.defaultKey ?? "");
|
|
191
|
+
return createComponent(Popover, {
|
|
192
|
+
get open() {
|
|
193
|
+
return open();
|
|
194
|
+
},
|
|
195
|
+
onOpenChange: setOpen,
|
|
196
|
+
placement: "bottom-start",
|
|
197
|
+
get children() {
|
|
198
|
+
return [createComponent(PopoverTrigger, {
|
|
199
|
+
as: Button,
|
|
200
|
+
type: "button",
|
|
201
|
+
variant: "outline",
|
|
202
|
+
"data-slot": "icon-picker-trigger",
|
|
203
|
+
get ["aria-label"]() {
|
|
204
|
+
return `${props.label}: ${props.image ? "uploaded image" : shellIconLabel(props.value ?? props.defaultKey ?? "")}`;
|
|
205
|
+
},
|
|
206
|
+
get disabled() {
|
|
207
|
+
return props.disabled;
|
|
208
|
+
},
|
|
209
|
+
get ["class"]() {
|
|
210
|
+
return cn("gap-2", local.class);
|
|
211
|
+
},
|
|
212
|
+
get children() {
|
|
213
|
+
return [(() => {
|
|
214
|
+
var _el$11 = _tmpl$7();
|
|
215
|
+
insert(_el$11, createComponent(Show, {
|
|
216
|
+
get when() {
|
|
217
|
+
return local.trigger;
|
|
218
|
+
},
|
|
219
|
+
get fallback() {
|
|
220
|
+
return createComponent(Show, {
|
|
221
|
+
get when() {
|
|
222
|
+
return props.image;
|
|
223
|
+
},
|
|
224
|
+
get fallback() {
|
|
225
|
+
return createComponent(Dynamic, {
|
|
226
|
+
get component() {
|
|
227
|
+
return Current();
|
|
228
|
+
},
|
|
229
|
+
"class": "size-4",
|
|
230
|
+
"aria-hidden": "true"
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
children: (src) => (() => {
|
|
234
|
+
var _el$12 = _tmpl$8();
|
|
235
|
+
effect(() => setAttribute(_el$12, "src", src()));
|
|
236
|
+
return _el$12;
|
|
237
|
+
})()
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
get children() {
|
|
241
|
+
return local.trigger;
|
|
242
|
+
}
|
|
243
|
+
}));
|
|
244
|
+
return _el$11;
|
|
245
|
+
})(), createComponent(ChevronDown, {
|
|
246
|
+
"class": "size-4 text-muted-foreground",
|
|
247
|
+
"aria-hidden": "true"
|
|
248
|
+
})];
|
|
249
|
+
}
|
|
250
|
+
}), createComponent(PopoverContent, {
|
|
251
|
+
"class": "w-[min(22rem,var(--kb-popper-available-width))]",
|
|
252
|
+
get children() {
|
|
253
|
+
return createComponent(IconPicker, mergeProps(picker, { onValueChange: (key) => {
|
|
254
|
+
picker.onValueChange(key);
|
|
255
|
+
setOpen(false);
|
|
256
|
+
} }));
|
|
257
|
+
}
|
|
258
|
+
})];
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
184
262
|
//#endregion
|
|
185
|
-
export { IconPicker };
|
|
263
|
+
export { IconPicker, IconPickerDropdown };
|
|
186
264
|
|
|
187
265
|
//# sourceMappingURL=icon-picker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon-picker.js","names":["RadioGroup","Radio","ImageUp","Trash2","For","Show","createSignal","createUniqueId","cn","Button","MarkImageError","markImageFromFile","markImageProblemCopy","MARK_IMAGE_MAX_SOURCE_BYTES","resolveShellIcon","shellIconEntries","IconPickerProps","label","value","onValueChange","key","defaultKey","image","onImageChange","disabled","class","IconPicker","props","inputId","problem","setProblem","busy","setBusy","fileInput","setFileInput","HTMLInputElement","upload","file","File","undefined","error","_el$","_tmpl$3","_$insert","_$createComponent","when","children","_el$2","_tmpl$","_el$3","firstChild","_el$4","nextSibling","_el$5","fallback","src","_el$0","_tmpl$4","_$effect","_$setAttribute","addEventListener","event","input","currentTarget","files","_$use","type","variant","size","onClick","click","_$memo","_p$","_v$","_v$2","e","t","message","_el$1","_tmpl$5","_el$6","_tmpl$2","_el$7","_el$9","_el$8","aria-label","onChange","each","entry","Icon","Item","ItemInput","ItemLabel","title","_el$10","_tmpl$6","_$className","_$createComponent","_$setAttribute","_$memo","Radio","_$className"],"sources":["../../../../src/components/composites/icon-picker/icon-picker.tsx"],"sourcesContent":["import { RadioGroup as Radio } from '@kobalte/core/radio-group'\nimport { ImageUp, Trash2 } from 'lucide-solid'\nimport { For, Show, createSignal, createUniqueId } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Button } from '../../ui/button'\nimport {\n MarkImageError,\n markImageFromFile,\n markImageProblemCopy,\n MARK_IMAGE_MAX_SOURCE_BYTES,\n} from './image-data-url'\nimport { resolveShellIcon, shellIconEntries } from './shell-icons'\n\n/**\n * IconPicker.\n *\n * The control behind \"choose an icon\" for a workspace or a project: a grid of\n * curated glyphs, and optionally an uploaded picture in their place.\n *\n * It is a single-choice group. The glyph grid is a Kobalte radio group, so\n * arrow keys move the choice and Tab enters and leaves the grid as one stop,\n * which is the contract a 56-cell grid needs to stay usable from a keyboard. An\n * uploaded image is a separate state, not a 57th cell: while `image` is set no\n * glyph is checked, and choosing a glyph asks the caller to drop the image, so\n * the stored mark is never both.\n *\n * The picker does not persist anything. The caller owns `value` and `image`\n * and saves them with the rest of its form; the image arrives already resized\n * to the stored square (see `markImageFromFile`).\n */\nexport type IconPickerProps = {\n /** The group's accessible name, e.g. \"Workspace icon\". */\n label: string\n /** The checked icon key; ignored while `image` is set. */\n value: string | undefined\n onValueChange(key: string): void\n /** The key the surface treats as unset, labelled \"default\" in the grid. */\n defaultKey?: string\n /** An uploaded mark (a data URL) currently chosen in place of a glyph. */\n image?: string\n /**\n * Called with the resized upload, or `undefined` for Remove. Omit it for a\n * surface that offers glyphs only; the upload row then does not render.\n */\n onImageChange?(image: string | undefined): void\n disabled?: boolean\n class?: string\n}\n\nexport function IconPicker(props: IconPickerProps) {\n const inputId = createUniqueId()\n const [problem, setProblem] = createSignal<string>()\n const [busy, setBusy] = createSignal(false)\n const [fileInput, setFileInput] = createSignal<HTMLInputElement>()\n\n const upload = async (file: File | undefined) => {\n if (!file || !props.onImageChange) return\n setProblem(undefined)\n setBusy(true)\n try {\n props.onImageChange(await markImageFromFile(file))\n } catch (error) {\n setProblem(\n markImageProblemCopy[error instanceof MarkImageError ? error.problem : 'unreadable']\n )\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <div data-slot=\"icon-picker\" class={cn('flex flex-col gap-3', props.class)}>\n <Show when={props.onImageChange}>\n <div class=\"flex items-center gap-3\" data-slot=\"icon-picker-upload\">\n <span\n aria-hidden=\"true\"\n class=\"flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border bg-muted\"\n >\n <Show\n when={props.image}\n fallback={<ImageUp class=\"size-5 text-muted-foreground\" aria-hidden=\"true\" />}\n >\n {(src) => <img src={src()} alt=\"\" class=\"size-full object-cover\" />}\n </Show>\n </span>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n ref={setFileInput}\n id={inputId}\n type=\"file\"\n class=\"sr-only\"\n tabIndex={-1}\n // The visible button drives this input; hide the duplicate from assistive\n // technology but keep a name so the form-label rule is satisfied.\n aria-hidden=\"true\"\n aria-label={props.image ? 'Replace image' : 'Upload image'}\n accept=\"image/png,image/jpeg,image/webp,image/gif\"\n disabled={props.disabled}\n onChange={(event) => {\n const input = event.currentTarget\n void upload(input.files?.[0])\n // Clearing the value lets the same file be chosen again after Remove.\n input.value = ''\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={props.disabled || busy()}\n onClick={() => fileInput()?.click()}\n >\n <ImageUp aria-hidden=\"true\" />\n {props.image ? 'Replace image' : 'Upload image'}\n </Button>\n <Show when={props.image}>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={props.disabled || busy()}\n onClick={() => {\n setProblem(undefined)\n props.onImageChange?.(undefined)\n }}\n >\n <Trash2 aria-hidden=\"true\" />\n Remove image\n </Button>\n </Show>\n </div>\n </div>\n <Show when={problem()}>\n {(message) => (\n <p role=\"alert\" class=\"text-sm text-destructive\">\n {message()}\n </p>\n )}\n </Show>\n <p class=\"text-xs text-muted-foreground\">\n PNG, JPEG, WebP or GIF up to {MARK_IMAGE_MAX_SOURCE_BYTES / 1024 / 1024} MB. It is cropped\n to a square.\n </p>\n </Show>\n <Radio\n aria-label={props.label}\n value={props.image ? '' : (props.value ?? '')}\n disabled={props.disabled}\n onChange={(key) => {\n if (key) props.onValueChange(key)\n }}\n class=\"grid grid-cols-[repeat(auto-fill,minmax(2.5rem,1fr))] gap-1.5\"\n >\n <For each={shellIconEntries}>\n {(entry) => {\n const Icon = resolveShellIcon(entry.key)\n return (\n <Radio.Item value={entry.key} class=\"contents\">\n <Radio.ItemInput class=\"peer\" />\n <Radio.ItemLabel\n title={entry.key === props.defaultKey ? `${entry.label} (default)` : entry.label}\n class={cn(\n 'flex size-10 cursor-pointer items-center justify-center rounded-lg border border-border bg-card text-muted-foreground',\n 'transition-colors ease-out',\n 'hover:bg-surface-hover hover:text-foreground',\n 'peer-focus-visible:border-ring peer-focus-visible:ring-3 peer-focus-visible:ring-primary-subtle',\n 'data-[checked]:border-primary data-[checked]:bg-primary-subtle data-[checked]:text-primary',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50'\n )}\n >\n <Icon class=\"size-5\" aria-hidden=\"true\" />\n <span class=\"sr-only\">\n {entry.label}\n {entry.key === props.defaultKey ? ' (default)' : ''}\n </span>\n </Radio.ItemLabel>\n </Radio.Item>\n )\n }}\n </For>\n </Radio>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"icon-picker.js","names":["RadioGroup","Radio","ChevronDown","ImageUp","Trash2","JSX","For","Show","createSignal","createUniqueId","splitProps","Dynamic","cn","Button","Popover","PopoverContent","PopoverTrigger","MarkImageError","markImageFromFile","markImageProblemCopy","MARK_IMAGE_MAX_SOURCE_BYTES","resolveShellIcon","shellIconEntries","shellIconLabel","IconPickerProps","label","value","onValueChange","key","defaultKey","image","onImageChange","disabled","class","IconPicker","props","inputId","problem","setProblem","busy","setBusy","fileInput","setFileInput","HTMLInputElement","upload","file","File","undefined","error","_el$","_tmpl$3","_$insert","_$createComponent","when","children","_el$2","_tmpl$","_el$3","firstChild","_el$4","nextSibling","_el$5","fallback","src","_el$0","_tmpl$4","_$effect","_$setAttribute","addEventListener","event","input","currentTarget","files","_$use","type","variant","size","onClick","click","_$memo","_p$","_v$","_v$2","e","t","message","_el$1","_tmpl$5","_el$6","_tmpl$2","_el$7","_el$9","_el$8","aria-label","onChange","each","entry","Icon","Item","ItemInput","ItemLabel","title","_el$10","_tmpl$6","_$className","IconPickerDropdownProps","trigger","Element","IconPickerDropdown","open","setOpen","local","picker","Current","onOpenChange","placement","as","_el$11","_tmpl$7","component","_el$12","_tmpl$8","_$mergeProps","_$createComponent","_$setAttribute","_$memo","Radio","_$className","_$mergeProps"],"sources":["../../../../src/components/composites/icon-picker/icon-picker.tsx"],"sourcesContent":["import { RadioGroup as Radio } from '@kobalte/core/radio-group'\nimport { ChevronDown, ImageUp, Trash2 } from 'lucide-solid'\nimport type { JSX } from 'solid-js'\nimport { For, Show, createSignal, createUniqueId, splitProps } from 'solid-js'\nimport { Dynamic } from 'solid-js/web'\nimport { cn } from '#lib/utils'\nimport { Button } from '../../ui/button'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover'\nimport {\n MarkImageError,\n markImageFromFile,\n markImageProblemCopy,\n MARK_IMAGE_MAX_SOURCE_BYTES,\n} from './image-data-url'\nimport { resolveShellIcon, shellIconEntries, shellIconLabel } from './shell-icons'\n\n/**\n * IconPicker.\n *\n * The control behind \"choose an icon\" for a workspace or a project: a grid of\n * curated glyphs, and optionally an uploaded picture in their place.\n *\n * It is a single-choice group. The glyph grid is a Kobalte radio group, so\n * arrow keys move the choice and Tab enters and leaves the grid as one stop,\n * which is the contract a 56-cell grid needs to stay usable from a keyboard. An\n * uploaded image is a separate state, not a 57th cell: while `image` is set no\n * glyph is checked, and choosing a glyph asks the caller to drop the image, so\n * the stored mark is never both.\n *\n * The picker does not persist anything. The caller owns `value` and `image`\n * and saves them with the rest of its form; the image arrives already resized\n * to the stored square (see `markImageFromFile`).\n */\nexport type IconPickerProps = {\n /** The group's accessible name, e.g. \"Workspace icon\". */\n label: string\n /** The checked icon key; ignored while `image` is set. */\n value: string | undefined\n onValueChange(key: string): void\n /** The key the surface treats as unset, labelled \"default\" in the grid. */\n defaultKey?: string\n /** An uploaded mark (a data URL) currently chosen in place of a glyph. */\n image?: string\n /**\n * Called with the resized upload, or `undefined` for Remove. Omit it for a\n * surface that offers glyphs only; the upload row then does not render.\n */\n onImageChange?(image: string | undefined): void\n disabled?: boolean\n class?: string\n}\n\nexport function IconPicker(props: IconPickerProps) {\n const inputId = createUniqueId()\n const [problem, setProblem] = createSignal<string>()\n const [busy, setBusy] = createSignal(false)\n const [fileInput, setFileInput] = createSignal<HTMLInputElement>()\n\n const upload = async (file: File | undefined) => {\n if (!file || !props.onImageChange) return\n setProblem(undefined)\n setBusy(true)\n try {\n props.onImageChange(await markImageFromFile(file))\n } catch (error) {\n setProblem(\n markImageProblemCopy[error instanceof MarkImageError ? error.problem : 'unreadable']\n )\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <div data-slot=\"icon-picker\" class={cn('flex flex-col gap-3', props.class)}>\n <Show when={props.onImageChange}>\n <div class=\"flex items-center gap-3\" data-slot=\"icon-picker-upload\">\n <span\n aria-hidden=\"true\"\n class=\"flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border bg-muted\"\n >\n <Show\n when={props.image}\n fallback={<ImageUp class=\"size-5 text-muted-foreground\" aria-hidden=\"true\" />}\n >\n {(src) => <img src={src()} alt=\"\" class=\"size-full object-cover\" />}\n </Show>\n </span>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n ref={setFileInput}\n id={inputId}\n type=\"file\"\n class=\"sr-only\"\n tabIndex={-1}\n // The visible button drives this input; hide the duplicate from assistive\n // technology but keep a name so the form-label rule is satisfied.\n aria-hidden=\"true\"\n aria-label={props.image ? 'Replace image' : 'Upload image'}\n accept=\"image/png,image/jpeg,image/webp,image/gif\"\n disabled={props.disabled}\n onChange={(event) => {\n const input = event.currentTarget\n void upload(input.files?.[0])\n // Clearing the value lets the same file be chosen again after Remove.\n input.value = ''\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={props.disabled || busy()}\n onClick={() => fileInput()?.click()}\n >\n <ImageUp aria-hidden=\"true\" />\n {props.image ? 'Replace image' : 'Upload image'}\n </Button>\n <Show when={props.image}>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={props.disabled || busy()}\n onClick={() => {\n setProblem(undefined)\n props.onImageChange?.(undefined)\n }}\n >\n <Trash2 aria-hidden=\"true\" />\n Remove image\n </Button>\n </Show>\n </div>\n </div>\n <Show when={problem()}>\n {(message) => (\n <p role=\"alert\" class=\"text-sm text-destructive\">\n {message()}\n </p>\n )}\n </Show>\n <p class=\"text-xs text-muted-foreground\">\n PNG, JPEG, WebP or GIF up to {MARK_IMAGE_MAX_SOURCE_BYTES / 1024 / 1024} MB. It is cropped\n to a square.\n </p>\n </Show>\n <Radio\n aria-label={props.label}\n value={props.image ? '' : (props.value ?? '')}\n disabled={props.disabled}\n onChange={(key) => {\n if (key) props.onValueChange(key)\n }}\n class=\"grid grid-cols-[repeat(auto-fill,minmax(2.5rem,1fr))] gap-1.5\"\n >\n <For each={shellIconEntries}>\n {(entry) => {\n const Icon = resolveShellIcon(entry.key)\n return (\n <Radio.Item value={entry.key} class=\"contents\">\n <Radio.ItemInput class=\"peer\" />\n <Radio.ItemLabel\n title={entry.key === props.defaultKey ? `${entry.label} (default)` : entry.label}\n class={cn(\n 'flex size-10 cursor-pointer items-center justify-center rounded-lg border border-border bg-card text-muted-foreground',\n 'transition-colors ease-out',\n 'hover:bg-surface-hover hover:text-foreground',\n 'peer-focus-visible:border-ring peer-focus-visible:ring-3 peer-focus-visible:ring-primary-subtle',\n 'data-[checked]:border-primary data-[checked]:bg-primary-subtle data-[checked]:text-primary',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50'\n )}\n >\n <Icon class=\"size-5\" aria-hidden=\"true\" />\n <span class=\"sr-only\">\n {entry.label}\n {entry.key === props.defaultKey ? ' (default)' : ''}\n </span>\n </Radio.ItemLabel>\n </Radio.Item>\n )\n }}\n </For>\n </Radio>\n </div>\n )\n}\n\n/**\n * IconPickerDropdown.\n *\n * The compact form of the picker for a settings row: one button showing the\n * current mark, opening the full grid (and the upload row, when the host offers\n * it) in a popover. Choosing a glyph applies it and closes the popover; an\n * upload or Remove leaves it open so the result is visible. The host owns the\n * value exactly as it does for `IconPicker`.\n */\nexport type IconPickerDropdownProps = IconPickerProps & {\n /** The mark shown on the trigger; defaults to the checked glyph or the uploaded image. */\n trigger?: JSX.Element\n}\n\nexport function IconPickerDropdown(props: IconPickerDropdownProps) {\n const [open, setOpen] = createSignal(false)\n const [local, picker] = splitProps(props, ['trigger', 'class'])\n const Current = () => resolveShellIcon(props.value ?? props.defaultKey ?? '')\n return (\n <Popover open={open()} onOpenChange={setOpen} placement=\"bottom-start\">\n <PopoverTrigger\n as={Button}\n type=\"button\"\n variant=\"outline\"\n data-slot=\"icon-picker-trigger\"\n aria-label={`${props.label}: ${props.image ? 'uploaded image' : shellIconLabel(props.value ?? props.defaultKey ?? '')}`}\n disabled={props.disabled}\n class={cn('gap-2', local.class)}\n >\n <span aria-hidden=\"true\" class=\"flex size-5 items-center justify-center overflow-hidden\">\n <Show\n when={local.trigger}\n fallback={\n <Show\n when={props.image}\n fallback={<Dynamic component={Current()} class=\"size-4\" aria-hidden=\"true\" />}\n >\n {(src) => <img src={src()} alt=\"\" class=\"size-full rounded-sm object-cover\" />}\n </Show>\n }\n >\n {local.trigger}\n </Show>\n </span>\n <ChevronDown class=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </PopoverTrigger>\n <PopoverContent class=\"w-[min(22rem,var(--kb-popper-available-width))]\">\n <IconPicker\n {...picker}\n onValueChange={(key) => {\n picker.onValueChange(key)\n setOpen(false)\n }}\n />\n </PopoverContent>\n </Popover>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { IconPicker, type IconPickerProps } from './icon-picker';
|
|
1
|
+
export { IconPicker, IconPickerDropdown, type IconPickerDropdownProps, type IconPickerProps, } from './icon-picker';
|
|
2
2
|
export { MARK_IMAGE_MAX_DATA_URL_LENGTH, MARK_IMAGE_MAX_SOURCE_BYTES, MARK_IMAGE_SIZE, MarkImageError, centredSquare, checkMarkImageFile, isMarkImageDataUrl, markImageFromFile, markImageProblemCopy, type MarkImageProblem, } from './image-data-url';
|
|
3
3
|
export { isShellIconKey, resolveShellIcon, shellIconEntries, shellIconLabel, type ShellIconKey, } from './shell-icons';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/icon-picker/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/icon-picker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACrB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,eAAe,EACf,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,KAAK,YAAY,GAClB,MAAM,eAAe,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { MARK_IMAGE_MAX_DATA_URL_LENGTH, MARK_IMAGE_MAX_SOURCE_BYTES, MARK_IMAGE_SIZE, MarkImageError, centredSquare, checkMarkImageFile, isMarkImageDataUrl, markImageFromFile, markImageProblemCopy } from "./image-data-url.js";
|
|
2
2
|
import { isShellIconKey, resolveShellIcon, shellIconEntries, shellIconLabel } from "./shell-icons.js";
|
|
3
|
-
import { IconPicker } from "./icon-picker.js";
|
|
4
|
-
export { IconPicker, MARK_IMAGE_MAX_DATA_URL_LENGTH, MARK_IMAGE_MAX_SOURCE_BYTES, MARK_IMAGE_SIZE, MarkImageError, centredSquare, checkMarkImageFile, isMarkImageDataUrl, isShellIconKey, markImageFromFile, markImageProblemCopy, resolveShellIcon, shellIconEntries, shellIconLabel };
|
|
3
|
+
import { IconPicker, IconPickerDropdown } from "./icon-picker.js";
|
|
4
|
+
export { IconPicker, IconPickerDropdown, MARK_IMAGE_MAX_DATA_URL_LENGTH, MARK_IMAGE_MAX_SOURCE_BYTES, MARK_IMAGE_SIZE, MarkImageError, centredSquare, checkMarkImageFile, isMarkImageDataUrl, isShellIconKey, markImageFromFile, markImageProblemCopy, resolveShellIcon, shellIconEntries, shellIconLabel };
|
|
@@ -130,7 +130,7 @@ function SettingsNavigation(props) {
|
|
|
130
130
|
if (local.value === item.value) local.onReselect?.(item.value);
|
|
131
131
|
},
|
|
132
132
|
get ["class"]() {
|
|
133
|
-
return cn(sidebarNavItemClass, sidebarNavItemStateClass.idle, "h-row-lg justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle
|
|
133
|
+
return cn(sidebarNavItemClass, sidebarNavItemStateClass.idle, "h-row-lg justify-start border-0 px-3 py-2 text-left text-foreground data-[selected]:bg-primary-subtle", strip() ? "w-auto shrink-0" : "w-full");
|
|
134
134
|
},
|
|
135
135
|
get children() {
|
|
136
136
|
return [memo(() => item.icon), (() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-navigation.js","names":["useTabsContext","JSX","createEffect","createUniqueId","onCleanup","Show","splitProps","cn","sidebarNavItemClass","sidebarNavItemStateClass","TabsList","TabsTrigger","TabsListProps","Text","SettingsNavigationItem","value","label","icon","Element","disabled","SettingsNavigationGroup","items","SettingsNavigationProps","Omit","groups","onReselect","revealSelected","trigger","HTMLButtonElement","scrollContainerNearest","container","HTMLElement","row","style","window","getComputedStyle","box","getBoundingClientRect","rowBox","overflowX","left","scrollLeft","right","overflowY","top","scrollTop","bottom","revealNearest","parentElement","SettingsNavigation","props","local","rest","tabs","strip","orientation","triggers","Map","groupIdPrefix","reveal","revealCurrent","get","addEventListener","removeEventListener","_$createComponent","_$mergeProps","aria-label","class","children","map","group","groupIndex","groupId","_el$","_tmpl$","_el$2","firstChild","_$setAttribute","_$insert","when","variant","tone","item","ref","element","set","delete","onClick","idle","_$memo","_el$3","_tmpl$2","_$effect","_$className","truncate","_$createComponent","_$mergeProps","_$memo","_$className"],"sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"sourcesContent":["import { useTabsContext } from '@kobalte/core/tabs'\nimport type { JSX } from 'solid-js'\nimport { createEffect, createUniqueId, onCleanup, Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { sidebarNavItemClass, sidebarNavItemStateClass } from '../../layout/sidebar-nav/sidebar-nav'\nimport { TabsList, TabsTrigger, type TabsListProps } from '../../ui/tabs'\nimport { Text } from '../../ui/typography'\n\n/** One host-owned destination in a settings tab list. Values must be unique. */\nexport type SettingsNavigationItem = {\n value: string\n label: string\n icon?: JSX.Element\n disabled?: boolean\n}\n\n/** A visible, accessibly named group of settings destinations. */\nexport type SettingsNavigationGroup = {\n label: string\n items: readonly SettingsNavigationItem[]\n}\n\nexport type SettingsNavigationProps = Omit<\n TabsListProps,\n 'appearance' | 'children' | 'orientation'\n> & {\n /** Groups are presentation only; the host owns the tab values and panels. */\n groups: readonly SettingsNavigationGroup[]\n /** The same controlled value supplied to the enclosing `Tabs`. */\n value: string\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scrolling\n * of the ancestors that expose a scroll affordance; hidden-overflow ancestors never move. */\n revealSelected?:\n | boolean\n | ((\n trigger: HTMLButtonElement | undefined\n ) => void) /** Every tab list needs a label independent of its visible group headings. */\n 'aria-label': string\n}\n\n/**\n * SettingsNavigation.\n *\n * A grouped vertical tab list for sibling settings panels. It composes the\n * shared Tabs behaviour with the shared sidebar row recipe; the host supplies\n * the controlled value, panel content, icons and any persistence or routing.\n * Use ordinary tabs for a short, ungrouped row of view choices.\n *\n * The shape follows the enclosing `Tabs` orientation rather than a prop of its\n * own, so the visual axis can never disagree with the arrow keys Kobalte\n * binds: `vertical` is the grouped rail, `horizontal` is a single scrollable\n * strip of tabs (what `SettingsLayout` shows below 48rem). The strip keeps the\n * groups in order, separated by a rule; their visible headings drop out, since\n * an overline between tabs reads as another tab, but each tab still carries its\n * group as its accessible description.\n *\n * Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This\n * component adds no URL, storage or host-specific state. Set\n * `revealSelected={false}` when the surrounding layout does not use an internal\n * scrolling viewport.\n */\n/**\n * Reveal by scrolling only the ancestors that expose a scroll affordance\n * (`auto` or `scroll`), by the exact nearest-edge delta. `scrollIntoView` also\n * pans `overflow: hidden` ancestors, which are programmatically scrollable but\n * give the user no way back, so one over-pan left earlier rows permanently\n * clipped outside the viewport with no scroll to recover them (#182).\n */\nfunction scrollContainerNearest(container: HTMLElement, row: HTMLElement): void {\n const style = window.getComputedStyle(container)\n const box = container.getBoundingClientRect()\n const rowBox = row.getBoundingClientRect()\n if (style.overflowX === 'auto' || style.overflowX === 'scroll') {\n if (rowBox.left < box.left) container.scrollLeft += rowBox.left - box.left\n else if (rowBox.right > box.right) container.scrollLeft += rowBox.right - box.right\n }\n if (style.overflowY === 'auto' || style.overflowY === 'scroll') {\n if (rowBox.top < box.top) container.scrollTop += rowBox.top - box.top\n else if (rowBox.bottom > box.bottom) container.scrollTop += rowBox.bottom - box.bottom\n }\n}\n\nfunction revealNearest(row: HTMLElement): void {\n for (let container = row.parentElement; container; container = container.parentElement) {\n scrollContainerNearest(container, row)\n }\n}\n\nexport function SettingsNavigation(props: SettingsNavigationProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'aria-label',\n 'groups',\n 'value',\n 'onReselect',\n 'revealSelected',\n ])\n const tabs = useTabsContext()\n const strip = () => tabs.orientation() === 'horizontal'\n const triggers = new Map<string, HTMLButtonElement>()\n const groupIdPrefix = `settings-navigation-${createUniqueId()}`\n\n createEffect(() => {\n const value = local.value\n const reveal = local.revealSelected\n // Tracked so a rail that becomes a strip (or back) re-reveals on the new axis.\n strip()\n if (reveal === false) return\n\n const revealCurrent = () => {\n const trigger = triggers.get(value)\n if (typeof reveal === 'function') reveal(trigger)\n else if (trigger) revealNearest(trigger)\n }\n\n revealCurrent()\n window.addEventListener('resize', revealCurrent)\n onCleanup(() => window.removeEventListener('resize', revealCurrent))\n })\n\n return (\n <TabsList\n data-slot=\"settings-navigation\"\n aria-label={local['aria-label']}\n orientation={tabs.orientation()}\n class={cn(\n // Positioned so the visually hidden group names are laid out (and\n // clipped) inside the list: unpositioned, their absolute boxes escape a\n // scrolled strip and widen the page by however far it has scrolled.\n 'relative flex min-w-0 shrink-0 justify-start',\n strip()\n ? // The block padding is the room the focus ring needs: a scroller\n // clips at its padding edge, and this one scrolls sideways only.\n 'scroll-fade w-full flex-row items-center gap-1 overflow-x-auto overflow-y-hidden border-b border-border px-2 py-1.5'\n : 'max-h-full min-h-0 flex-col items-stretch gap-2 overflow-x-hidden overflow-y-auto px-2 py-2',\n local.class\n )}\n {...rest}\n >\n {local.groups.map((group, groupIndex) => {\n const groupId = `${groupIdPrefix}-group-${groupIndex}`\n return (\n <div\n role=\"none\"\n data-slot=\"settings-navigation-group\"\n class={cn(\n 'flex gap-0.5',\n strip()\n ? 'shrink-0 flex-row items-center not-first:ms-1 not-first:border-s not-first:border-border not-first:ps-2'\n : 'min-w-0 flex-col'\n )}\n >\n <span id={groupId} class=\"sr-only\">\n {group.label}\n </span>\n <Show when={!strip()}>\n {/* The overline role on the sidebar's own muted foreground. */}\n <Text\n variant=\"overline\"\n tone=\"inherit\"\n aria-hidden=\"true\"\n class=\"px-2 py-1.5 text-sidebar-muted-foreground\"\n >\n {group.label}\n </Text>\n </Show>\n {group.items.map((item) => (\n <TabsTrigger\n data-slot=\"settings-navigation-trigger\"\n aria-describedby={groupId}\n value={item.value}\n disabled={item.disabled}\n ref={(element: HTMLButtonElement | undefined) => {\n if (element) triggers.set(item.value, element)\n else triggers.delete(item.value)\n }}\n onClick={() => {\n if (local.value === item.value) local.onReselect?.(item.value)\n }}\n class={cn(\n sidebarNavItemClass,\n sidebarNavItemStateClass.idle,\n 'h-row-lg justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground',\n strip() ? 'w-auto shrink-0' : 'w-full'\n )}\n >\n {item.icon}\n <span class={cn('min-w-0', { truncate: !strip() })}>{item.label}</span>\n </TabsTrigger>\n ))}\n </div>\n )\n })}\n </TabsList>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"settings-navigation.js","names":["useTabsContext","JSX","createEffect","createUniqueId","onCleanup","Show","splitProps","cn","sidebarNavItemClass","sidebarNavItemStateClass","TabsList","TabsTrigger","TabsListProps","Text","SettingsNavigationItem","value","label","icon","Element","disabled","SettingsNavigationGroup","items","SettingsNavigationProps","Omit","groups","onReselect","revealSelected","trigger","HTMLButtonElement","scrollContainerNearest","container","HTMLElement","row","style","window","getComputedStyle","box","getBoundingClientRect","rowBox","overflowX","left","scrollLeft","right","overflowY","top","scrollTop","bottom","revealNearest","parentElement","SettingsNavigation","props","local","rest","tabs","strip","orientation","triggers","Map","groupIdPrefix","reveal","revealCurrent","get","addEventListener","removeEventListener","_$createComponent","_$mergeProps","aria-label","class","children","map","group","groupIndex","groupId","_el$","_tmpl$","_el$2","firstChild","_$setAttribute","_$insert","when","variant","tone","item","ref","element","set","delete","onClick","idle","_$memo","_el$3","_tmpl$2","_$effect","_$className","truncate","_$createComponent","_$mergeProps","_$memo","_$className"],"sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"sourcesContent":["import { useTabsContext } from '@kobalte/core/tabs'\nimport type { JSX } from 'solid-js'\nimport { createEffect, createUniqueId, onCleanup, Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { sidebarNavItemClass, sidebarNavItemStateClass } from '../../layout/sidebar-nav/sidebar-nav'\nimport { TabsList, TabsTrigger, type TabsListProps } from '../../ui/tabs'\nimport { Text } from '../../ui/typography'\n\n/** One host-owned destination in a settings tab list. Values must be unique. */\nexport type SettingsNavigationItem = {\n value: string\n label: string\n icon?: JSX.Element\n disabled?: boolean\n}\n\n/** A visible, accessibly named group of settings destinations. */\nexport type SettingsNavigationGroup = {\n label: string\n items: readonly SettingsNavigationItem[]\n}\n\nexport type SettingsNavigationProps = Omit<\n TabsListProps,\n 'appearance' | 'children' | 'orientation'\n> & {\n /** Groups are presentation only; the host owns the tab values and panels. */\n groups: readonly SettingsNavigationGroup[]\n /** The same controlled value supplied to the enclosing `Tabs`. */\n value: string\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scrolling\n * of the ancestors that expose a scroll affordance; hidden-overflow ancestors never move. */\n revealSelected?:\n | boolean\n | ((\n trigger: HTMLButtonElement | undefined\n ) => void) /** Every tab list needs a label independent of its visible group headings. */\n 'aria-label': string\n}\n\n/**\n * SettingsNavigation.\n *\n * A grouped vertical tab list for sibling settings panels. It composes the\n * shared Tabs behaviour with the shared sidebar row recipe; the host supplies\n * the controlled value, panel content, icons and any persistence or routing.\n * Use ordinary tabs for a short, ungrouped row of view choices.\n *\n * The shape follows the enclosing `Tabs` orientation rather than a prop of its\n * own, so the visual axis can never disagree with the arrow keys Kobalte\n * binds: `vertical` is the grouped rail, `horizontal` is a single scrollable\n * strip of tabs (what `SettingsLayout` shows below 48rem). The strip keeps the\n * groups in order, separated by a rule; their visible headings drop out, since\n * an overline between tabs reads as another tab, but each tab still carries its\n * group as its accessible description.\n *\n * Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This\n * component adds no URL, storage or host-specific state. Set\n * `revealSelected={false}` when the surrounding layout does not use an internal\n * scrolling viewport.\n */\n/**\n * Reveal by scrolling only the ancestors that expose a scroll affordance\n * (`auto` or `scroll`), by the exact nearest-edge delta. `scrollIntoView` also\n * pans `overflow: hidden` ancestors, which are programmatically scrollable but\n * give the user no way back, so one over-pan left earlier rows permanently\n * clipped outside the viewport with no scroll to recover them (#182).\n */\nfunction scrollContainerNearest(container: HTMLElement, row: HTMLElement): void {\n const style = window.getComputedStyle(container)\n const box = container.getBoundingClientRect()\n const rowBox = row.getBoundingClientRect()\n if (style.overflowX === 'auto' || style.overflowX === 'scroll') {\n if (rowBox.left < box.left) container.scrollLeft += rowBox.left - box.left\n else if (rowBox.right > box.right) container.scrollLeft += rowBox.right - box.right\n }\n if (style.overflowY === 'auto' || style.overflowY === 'scroll') {\n if (rowBox.top < box.top) container.scrollTop += rowBox.top - box.top\n else if (rowBox.bottom > box.bottom) container.scrollTop += rowBox.bottom - box.bottom\n }\n}\n\nfunction revealNearest(row: HTMLElement): void {\n for (let container = row.parentElement; container; container = container.parentElement) {\n scrollContainerNearest(container, row)\n }\n}\n\nexport function SettingsNavigation(props: SettingsNavigationProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'aria-label',\n 'groups',\n 'value',\n 'onReselect',\n 'revealSelected',\n ])\n const tabs = useTabsContext()\n const strip = () => tabs.orientation() === 'horizontal'\n const triggers = new Map<string, HTMLButtonElement>()\n const groupIdPrefix = `settings-navigation-${createUniqueId()}`\n\n createEffect(() => {\n const value = local.value\n const reveal = local.revealSelected\n // Tracked so a rail that becomes a strip (or back) re-reveals on the new axis.\n strip()\n if (reveal === false) return\n\n const revealCurrent = () => {\n const trigger = triggers.get(value)\n if (typeof reveal === 'function') reveal(trigger)\n else if (trigger) revealNearest(trigger)\n }\n\n revealCurrent()\n window.addEventListener('resize', revealCurrent)\n onCleanup(() => window.removeEventListener('resize', revealCurrent))\n })\n\n return (\n <TabsList\n data-slot=\"settings-navigation\"\n aria-label={local['aria-label']}\n orientation={tabs.orientation()}\n class={cn(\n // Positioned so the visually hidden group names are laid out (and\n // clipped) inside the list: unpositioned, their absolute boxes escape a\n // scrolled strip and widen the page by however far it has scrolled.\n 'relative flex min-w-0 shrink-0 justify-start',\n strip()\n ? // The block padding is the room the focus ring needs: a scroller\n // clips at its padding edge, and this one scrolls sideways only.\n 'scroll-fade w-full flex-row items-center gap-1 overflow-x-auto overflow-y-hidden border-b border-border px-2 py-1.5'\n : 'max-h-full min-h-0 flex-col items-stretch gap-2 overflow-x-hidden overflow-y-auto px-2 py-2',\n local.class\n )}\n {...rest}\n >\n {local.groups.map((group, groupIndex) => {\n const groupId = `${groupIdPrefix}-group-${groupIndex}`\n return (\n <div\n role=\"none\"\n data-slot=\"settings-navigation-group\"\n class={cn(\n 'flex gap-0.5',\n strip()\n ? 'shrink-0 flex-row items-center not-first:ms-1 not-first:border-s not-first:border-border not-first:ps-2'\n : 'min-w-0 flex-col'\n )}\n >\n <span id={groupId} class=\"sr-only\">\n {group.label}\n </span>\n <Show when={!strip()}>\n {/* The overline role on the sidebar's own muted foreground. */}\n <Text\n variant=\"overline\"\n tone=\"inherit\"\n aria-hidden=\"true\"\n class=\"px-2 py-1.5 text-sidebar-muted-foreground\"\n >\n {group.label}\n </Text>\n </Show>\n {group.items.map((item) => (\n <TabsTrigger\n data-slot=\"settings-navigation-trigger\"\n aria-describedby={groupId}\n value={item.value}\n disabled={item.disabled}\n ref={(element: HTMLButtonElement | undefined) => {\n if (element) triggers.set(item.value, element)\n else triggers.delete(item.value)\n }}\n onClick={() => {\n if (local.value === item.value) local.onReselect?.(item.value)\n }}\n class={cn(\n sidebarNavItemClass,\n sidebarNavItemStateClass.idle,\n 'h-row-lg justify-start border-0 px-3 py-2 text-left text-foreground data-[selected]:bg-primary-subtle',\n strip() ? 'w-auto shrink-0' : 'w-full'\n )}\n >\n {item.icon}\n <span class={cn('min-w-0', { truncate: !strip() })}>{item.label}</span>\n </TabsTrigger>\n ))}\n </div>\n )\n })}\n </TabsList>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,SAAS6B,uBAAuBC,WAAwBE,KAAwB;CAC9E,MAAMC,QAAQC,OAAOC,iBAAiBL,SAAS;CAC/C,MAAMM,MAAMN,UAAUO,sBAAsB;CAC5C,MAAMC,SAASN,IAAIK,sBAAsB;CACzC,IAAIJ,MAAMM,cAAc,UAAUN,MAAMM,cAAc,UAAU;EAC9D,IAAID,OAAOE,OAAOJ,IAAII,MAAMV,UAAUW,cAAcH,OAAOE,OAAOJ,IAAII;OACjE,IAAIF,OAAOI,QAAQN,IAAIM,OAAOZ,UAAUW,cAAcH,OAAOI,QAAQN,IAAIM;CAChF;CACA,IAAIT,MAAMU,cAAc,UAAUV,MAAMU,cAAc,UAAU;EAC9D,IAAIL,OAAOM,MAAMR,IAAIQ,KAAKd,UAAUe,aAAaP,OAAOM,MAAMR,IAAIQ;OAC7D,IAAIN,OAAOQ,SAASV,IAAIU,QAAQhB,UAAUe,aAAaP,OAAOQ,SAASV,IAAIU;CAClF;AACF;AAEA,SAASC,cAAcf,KAAwB;CAC7C,KAAK,IAAIF,YAAYE,IAAIgB,eAAelB,WAAWA,YAAYA,UAAUkB,eACvEnB,uBAAuBC,WAAWE,GAAG;AAEzC;AAEA,SAAgBiB,mBAAmBC,OAAgC;CACjE,MAAM,CAACC,OAAOC,QAAQ9C,WAAW4C,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;CAAgB,CACjB;CACD,MAAMG,OAAOrD,eAAe;CAC5B,MAAMsD,cAAcD,KAAKE,YAAY,MAAM;CAC3C,MAAMC,2BAAW,IAAIC,IAA+B;CACpD,MAAMC,gBAAgB,uBAAuBvD,eAAe;CAE5DD,mBAAmB;EACjB,MAAMa,QAAQoC,MAAMpC;EACpB,MAAM4C,SAASR,MAAMzB;EAErB4B,MAAM;EACN,IAAIK,WAAW,OAAO;EAEtB,MAAMC,sBAAsB;GAC1B,MAAMjC,UAAU6B,SAASK,IAAI9C,KAAK;GAClC,IAAI,OAAO4C,WAAW,YAAYA,OAAOhC,OAAO;QAC3C,IAAIA,SAASoB,cAAcpB,OAAO;EACzC;EAEAiC,cAAc;EACd1B,OAAO4B,iBAAiB,UAAUF,aAAa;EAC/CxD,gBAAgB8B,OAAO6B,oBAAoB,UAAUH,aAAa,CAAC;CACrE,CAAC;CAED,OAAAI,gBACGtD,UAAQuD,WAAA;EAAA,aAAA;EAAA,KAAA,gBAAA;GAAA,OAEKd,MAAM;EAAa;EAAA,IAC/BI,cAAW;GAAA,OAAEF,KAAKE,YAAY;EAAC;EAAA,KAAA,WAAA;GAAA,OACxBhD,GAIL,gDACA+C,MAAM,IAGF,wHACA,+FACJH,MAAMgB,KACR;EAAC;CAAA,GACGf,MAAI,EAAA,IAAAgB,WAAA;EAAA,OAEPjB,MAAM3B,OAAO6C,KAAKC,OAAOC,eAAe;GACvC,MAAMC,UAAU,GAAGd,cAAa,SAAUa;GAC1C,cAAA;IAAA,IAAAE,OAAAC,OAAA,GAAAC,QAAAF,KAAAG;IAAAC,aAAAF,OAAA,MAWcH,OAAO;IAAAM,OAAAH,aACdL,MAAMtD,KAAK;IAAA8D,OAAAL,MAAAT,gBAEb3D,MAAI;KAAA,IAAC0E,OAAI;MAAA,OAAE,CAACzB,MAAM;KAAC;KAAA,IAAAc,WAAA;MAAA,OAAAJ,gBAEjBnD,MAAI;OACHmE,SAAO;OACPC,MAAI;OAAA,eAAA;OAAA,SAAA;OAAA,IAAAb,WAAA;QAAA,OAIHE,MAAMtD;OAAK;MAAA,CAAA;KAAA;IAAA,CAAA,GAAA,IAAA;IAAA8D,OAAAL,YAGfH,MAAMjD,MAAMgD,KAAKa,SAAIlB,gBACnBrD,aAAW;KAAA,aAAA;KAAA,oBAEQ6D;KAAO,IACzBzD,QAAK;MAAA,OAAEmE,KAAKnE;KAAK;KAAA,IACjBI,WAAQ;MAAA,OAAE+D,KAAK/D;KAAQ;KAAAgE,MACjBC,YAA2C;MAC/C,IAAIA,SAAS5B,SAAS6B,IAAIH,KAAKnE,OAAOqE,OAAO;WACxC5B,SAAS8B,OAAOJ,KAAKnE,KAAK;KACjC;KACAwE,eAAe;MACb,IAAIpC,MAAMpC,UAAUmE,KAAKnE,OAAOoC,MAAM1B,aAAayD,KAAKnE,KAAK;KAC/D;KAAC,KAAA,WAAA;MAAA,OACMR,GACLC,qBACAC,yBAAyB+E,MACzB,yGACAlC,MAAM,IAAI,oBAAoB,QAChC;KAAC;KAAA,IAAAc,WAAA;MAAA,OAAA,CAAAqB,WAEAP,KAAKjE,IAAI,UAAA;OAAA,IAAAyE,QAAAC,QAAA;OAAAb,OAAAY,aAC2CR,KAAKlE,KAAK;OAAA4E,aAAAC,UAAAH,OAAlDnF,GAAG,WAAW,EAAEuF,UAAU,CAACxC,MAAM,EAAE,CAAC,CAAC,CAAA;OAAA,OAAAoC;MAAA,EAAA,CAAA,CAAA;KAAA;IAAA,CAAA,CAErD,GAAC,IAAA;IAAAE,aAAAC,UAAApB,MA5CKlE,GACL,gBACA+C,MAAM,IACF,4GACA,kBACN,CAAC,CAAA;IAAA,OAAAmB;GAAA,EAAA,CAAA;EA0CP,CAAC;CAAC,EAAA,CAAA,CAAA;AAGR"}
|
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
import type { ComponentProps, JSX } from 'solid-js';
|
|
2
|
-
/**
|
|
3
|
-
* SettingsSection and SettingsRow.
|
|
4
|
-
*
|
|
5
|
-
* A settings page is the most repetitive surface in any application, and the
|
|
6
|
-
* one where drift is most visible: label sizes, control widths and description
|
|
7
|
-
* placement all wander unless something fixes them.
|
|
8
|
-
*
|
|
9
|
-
* The row puts the label and description at the start and the control at the
|
|
10
|
-
* end (stacked on a narrow window), so controls line up down the page's end
|
|
11
|
-
* edge regardless of how long the labels are. `SettingsRow` also
|
|
12
|
-
* exposes its label and description through the shared form-field context to
|
|
13
|
-
* the first control inside it. `SettingsField` is the visual stacked layout;
|
|
14
|
-
* use `FormField` when the control needs label, help, or validation wiring.
|
|
15
|
-
*/
|
|
16
2
|
export declare function SettingsSection(props: ComponentProps<'section'> & {
|
|
17
3
|
title: string;
|
|
18
4
|
description?: string;
|
|
5
|
+
/** A short explanation behind a help icon beside the title; prefer it to a long description. */
|
|
6
|
+
help?: string;
|
|
19
7
|
/** A control for the section as a whole, e.g. a master toggle. */
|
|
20
8
|
action?: JSX.Element;
|
|
21
9
|
/** Use a spaced content stack for fields, alerts, and cards instead of divided rows. */
|
|
@@ -32,6 +20,12 @@ export declare function SettingsSection(props: ComponentProps<'section'> & {
|
|
|
32
20
|
export declare function SettingsRow(props: ComponentProps<'div'> & {
|
|
33
21
|
label: string;
|
|
34
22
|
description?: string;
|
|
23
|
+
/**
|
|
24
|
+
* A short explanation behind a help icon beside the label. Prefer it to a
|
|
25
|
+
* long description: the row stays quiet and the explanation is one hover or
|
|
26
|
+
* focus away.
|
|
27
|
+
*/
|
|
28
|
+
help?: string;
|
|
35
29
|
/** Render a vertical stack when the control needs the full width. */
|
|
36
30
|
orientation?: 'horizontal' | 'vertical';
|
|
37
31
|
/** Hide the divider this row would otherwise contribute. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAsCnD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gGAAgG;IAChG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC,eA8CF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qEAAqE;IACrE,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC,4DAA4D;IAC5D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,eA4DF;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAC3B,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,eAiBzF;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASxD"}
|