@lab206/ui 0.1.0
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/LICENSE +114 -0
- package/README.md +6 -0
- package/dist/components/Accordion.d.ts +25 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +123 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/Alert.d.ts +17 -0
- package/dist/components/Alert.d.ts.map +1 -0
- package/dist/components/Alert.js +59 -0
- package/dist/components/Alert.js.map +1 -0
- package/dist/components/AspectRatio.d.ts +13 -0
- package/dist/components/AspectRatio.d.ts.map +1 -0
- package/dist/components/AspectRatio.js +38 -0
- package/dist/components/AspectRatio.js.map +1 -0
- package/dist/components/Avatar.d.ts +17 -0
- package/dist/components/Avatar.d.ts.map +1 -0
- package/dist/components/Avatar.js +57 -0
- package/dist/components/Avatar.js.map +1 -0
- package/dist/components/Badge.d.ts +12 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/Badge.js +62 -0
- package/dist/components/Badge.js.map +1 -0
- package/dist/components/Breadcrumb.d.ts +16 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +76 -0
- package/dist/components/Breadcrumb.js.map +1 -0
- package/dist/components/Button.d.ts +28 -0
- package/dist/components/Button.d.ts.map +1 -0
- package/dist/components/Button.js +110 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.d.ts +18 -0
- package/dist/components/Card.d.ts.map +1 -0
- package/dist/components/Card.js +57 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/Checkbox.d.ts +23 -0
- package/dist/components/Checkbox.d.ts.map +1 -0
- package/dist/components/Checkbox.js +107 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/Chip.d.ts +18 -0
- package/dist/components/Chip.d.ts.map +1 -0
- package/dist/components/Chip.js +103 -0
- package/dist/components/Chip.js.map +1 -0
- package/dist/components/Code.d.ts +12 -0
- package/dist/components/Code.d.ts.map +1 -0
- package/dist/components/Code.js +57 -0
- package/dist/components/Code.js.map +1 -0
- package/dist/components/Collapse.d.ts +15 -0
- package/dist/components/Collapse.d.ts.map +1 -0
- package/dist/components/Collapse.js +49 -0
- package/dist/components/Collapse.js.map +1 -0
- package/dist/components/Combobox.d.ts +47 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +325 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/Command.d.ts +42 -0
- package/dist/components/Command.d.ts.map +1 -0
- package/dist/components/Command.js +263 -0
- package/dist/components/Command.js.map +1 -0
- package/dist/components/Container.d.ts +11 -0
- package/dist/components/Container.d.ts.map +1 -0
- package/dist/components/Container.js +38 -0
- package/dist/components/Container.js.map +1 -0
- package/dist/components/Dialog.d.ts +26 -0
- package/dist/components/Dialog.d.ts.map +1 -0
- package/dist/components/Dialog.js +159 -0
- package/dist/components/Dialog.js.map +1 -0
- package/dist/components/Divider.d.ts +10 -0
- package/dist/components/Divider.d.ts.map +1 -0
- package/dist/components/Divider.js +52 -0
- package/dist/components/Divider.js.map +1 -0
- package/dist/components/Drawer.d.ts +19 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +131 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/Empty.d.ts +17 -0
- package/dist/components/Empty.d.ts.map +1 -0
- package/dist/components/Empty.js +51 -0
- package/dist/components/Empty.js.map +1 -0
- package/dist/components/Field.d.ts +27 -0
- package/dist/components/Field.d.ts.map +1 -0
- package/dist/components/Field.js +153 -0
- package/dist/components/Field.js.map +1 -0
- package/dist/components/Grid.d.ts +13 -0
- package/dist/components/Grid.d.ts.map +1 -0
- package/dist/components/Grid.js +44 -0
- package/dist/components/Grid.js.map +1 -0
- package/dist/components/Input.d.ts +55 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +113 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/Kbd.d.ts +10 -0
- package/dist/components/Kbd.d.ts.map +1 -0
- package/dist/components/Kbd.js +41 -0
- package/dist/components/Kbd.js.map +1 -0
- package/dist/components/Label.d.ts +13 -0
- package/dist/components/Label.d.ts.map +1 -0
- package/dist/components/Label.js +34 -0
- package/dist/components/Label.js.map +1 -0
- package/dist/components/Link.d.ts +18 -0
- package/dist/components/Link.d.ts.map +1 -0
- package/dist/components/Link.js +41 -0
- package/dist/components/Link.js.map +1 -0
- package/dist/components/List.d.ts +22 -0
- package/dist/components/List.d.ts.map +1 -0
- package/dist/components/List.js +157 -0
- package/dist/components/List.js.map +1 -0
- package/dist/components/Menu.d.ts +28 -0
- package/dist/components/Menu.d.ts.map +1 -0
- package/dist/components/Menu.js +126 -0
- package/dist/components/Menu.js.map +1 -0
- package/dist/components/NumberInput.d.ts +27 -0
- package/dist/components/NumberInput.d.ts.map +1 -0
- package/dist/components/NumberInput.js +92 -0
- package/dist/components/NumberInput.js.map +1 -0
- package/dist/components/Pagination.d.ts +13 -0
- package/dist/components/Pagination.d.ts.map +1 -0
- package/dist/components/Pagination.js +104 -0
- package/dist/components/Pagination.js.map +1 -0
- package/dist/components/Popover.d.ts +26 -0
- package/dist/components/Popover.d.ts.map +1 -0
- package/dist/components/Popover.js +165 -0
- package/dist/components/Popover.js.map +1 -0
- package/dist/components/Progress.d.ts +15 -0
- package/dist/components/Progress.d.ts.map +1 -0
- package/dist/components/Progress.js +63 -0
- package/dist/components/Progress.js.map +1 -0
- package/dist/components/Radio.d.ts +25 -0
- package/dist/components/Radio.d.ts.map +1 -0
- package/dist/components/Radio.js +95 -0
- package/dist/components/Radio.js.map +1 -0
- package/dist/components/ScrollArea.d.ts +13 -0
- package/dist/components/ScrollArea.d.ts.map +1 -0
- package/dist/components/ScrollArea.js +33 -0
- package/dist/components/ScrollArea.js.map +1 -0
- package/dist/components/Select.d.ts +34 -0
- package/dist/components/Select.d.ts.map +1 -0
- package/dist/components/Select.js +108 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/Skeleton.d.ts +18 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton.js +86 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Slider.d.ts +26 -0
- package/dist/components/Slider.d.ts.map +1 -0
- package/dist/components/Slider.js +113 -0
- package/dist/components/Slider.js.map +1 -0
- package/dist/components/Spinner.d.ts +12 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +44 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/Stack.d.ts +21 -0
- package/dist/components/Stack.d.ts.map +1 -0
- package/dist/components/Stack.js +48 -0
- package/dist/components/Stack.js.map +1 -0
- package/dist/components/Stat.d.ts +23 -0
- package/dist/components/Stat.d.ts.map +1 -0
- package/dist/components/Stat.js +115 -0
- package/dist/components/Stat.js.map +1 -0
- package/dist/components/Steps.d.ts +20 -0
- package/dist/components/Steps.d.ts.map +1 -0
- package/dist/components/Steps.js +126 -0
- package/dist/components/Steps.js.map +1 -0
- package/dist/components/Switch.d.ts +23 -0
- package/dist/components/Switch.d.ts.map +1 -0
- package/dist/components/Switch.js +108 -0
- package/dist/components/Switch.js.map +1 -0
- package/dist/components/Table.d.ts +27 -0
- package/dist/components/Table.d.ts.map +1 -0
- package/dist/components/Table.js +112 -0
- package/dist/components/Table.js.map +1 -0
- package/dist/components/Tabs.d.ts +27 -0
- package/dist/components/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs.js +171 -0
- package/dist/components/Tabs.js.map +1 -0
- package/dist/components/Text.d.ts +17 -0
- package/dist/components/Text.d.ts.map +1 -0
- package/dist/components/Text.js +48 -0
- package/dist/components/Text.js.map +1 -0
- package/dist/components/Textarea.d.ts +32 -0
- package/dist/components/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea.js +91 -0
- package/dist/components/Textarea.js.map +1 -0
- package/dist/components/Timeline.d.ts +17 -0
- package/dist/components/Timeline.d.ts.map +1 -0
- package/dist/components/Timeline.js +95 -0
- package/dist/components/Timeline.js.map +1 -0
- package/dist/components/Toast.d.ts +36 -0
- package/dist/components/Toast.d.ts.map +1 -0
- package/dist/components/Toast.js +143 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/ToggleGroup.d.ts +25 -0
- package/dist/components/ToggleGroup.d.ts.map +1 -0
- package/dist/components/ToggleGroup.js +103 -0
- package/dist/components/ToggleGroup.js.map +1 -0
- package/dist/components/Tooltip.d.ts +19 -0
- package/dist/components/Tooltip.d.ts.map +1 -0
- package/dist/components/Tooltip.js +141 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/components/Transition.d.ts +22 -0
- package/dist/components/Transition.d.ts.map +1 -0
- package/dist/components/Transition.js +139 -0
- package/dist/components/Transition.js.map +1 -0
- package/dist/density.d.ts +21 -0
- package/dist/density.d.ts.map +1 -0
- package/dist/density.js +31 -0
- package/dist/density.js.map +1 -0
- package/dist/dev.d.ts +27 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +77 -0
- package/dist/dev.js.map +1 -0
- package/dist/focusTrap.d.ts +6 -0
- package/dist/focusTrap.d.ts.map +1 -0
- package/dist/focusTrap.js +55 -0
- package/dist/focusTrap.js.map +1 -0
- package/dist/form.d.ts +125 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +192 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +134 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +85 -0
- package/dist/index.js.map +1 -0
- package/dist/motion.d.ts +29 -0
- package/dist/motion.d.ts.map +1 -0
- package/dist/motion.js +43 -0
- package/dist/motion.js.map +1 -0
- package/dist/overlay.d.ts +63 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +172 -0
- package/dist/overlay.js.map +1 -0
- package/dist/palette.d.ts +20 -0
- package/dist/palette.d.ts.map +1 -0
- package/dist/palette.js +52 -0
- package/dist/palette.js.map +1 -0
- package/dist/reactive.d.ts +25 -0
- package/dist/reactive.d.ts.map +1 -0
- package/dist/reactive.js +43 -0
- package/dist/reactive.js.map +1 -0
- package/dist/rovingFocus.d.ts +36 -0
- package/dist/rovingFocus.d.ts.map +1 -0
- package/dist/rovingFocus.js +127 -0
- package/dist/rovingFocus.js.map +1 -0
- package/dist/size-entries/form-kit.d.ts +12 -0
- package/dist/size-entries/form-kit.d.ts.map +1 -0
- package/dist/size-entries/form-kit.js +12 -0
- package/dist/size-entries/form-kit.js.map +1 -0
- package/dist/styles.d.ts +29 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +53 -0
- package/dist/styles.js.map +1 -0
- package/dist/theme.d.ts +22 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +32 -0
- package/dist/theme.js.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +24 -0
- package/dist/utils.js.map +1 -0
- package/package.json +71 -0
- package/src/components/Accordion.tsx +194 -0
- package/src/components/Alert.tsx +88 -0
- package/src/components/AspectRatio.tsx +64 -0
- package/src/components/Avatar.tsx +89 -0
- package/src/components/Badge.tsx +84 -0
- package/src/components/Breadcrumb.tsx +116 -0
- package/src/components/Button.tsx +148 -0
- package/src/components/Card.tsx +92 -0
- package/src/components/Checkbox.tsx +143 -0
- package/src/components/Chip.tsx +153 -0
- package/src/components/Code.tsx +71 -0
- package/src/components/Collapse.tsx +76 -0
- package/src/components/Combobox.tsx +438 -0
- package/src/components/Command.tsx +380 -0
- package/src/components/Container.tsx +58 -0
- package/src/components/Dialog.tsx +226 -0
- package/src/components/Divider.tsx +72 -0
- package/src/components/Drawer.tsx +191 -0
- package/src/components/Empty.tsx +86 -0
- package/src/components/Field.tsx +184 -0
- package/src/components/Grid.tsx +67 -0
- package/src/components/Input.tsx +169 -0
- package/src/components/Kbd.tsx +57 -0
- package/src/components/Label.tsx +54 -0
- package/src/components/Link.tsx +87 -0
- package/src/components/List.tsx +236 -0
- package/src/components/Menu.tsx +195 -0
- package/src/components/NumberInput.tsx +158 -0
- package/src/components/Pagination.tsx +153 -0
- package/src/components/Popover.tsx +219 -0
- package/src/components/Progress.tsx +108 -0
- package/src/components/Radio.tsx +161 -0
- package/src/components/ScrollArea.tsx +58 -0
- package/src/components/Select.tsx +128 -0
- package/src/components/Skeleton.tsx +117 -0
- package/src/components/Slider.tsx +175 -0
- package/src/components/Spinner.tsx +70 -0
- package/src/components/Stack.tsx +79 -0
- package/src/components/Stat.tsx +169 -0
- package/src/components/Steps.tsx +191 -0
- package/src/components/Switch.tsx +146 -0
- package/src/components/Table.tsx +168 -0
- package/src/components/Tabs.tsx +246 -0
- package/src/components/Text.tsx +69 -0
- package/src/components/Textarea.tsx +128 -0
- package/src/components/Timeline.tsx +150 -0
- package/src/components/Toast.tsx +221 -0
- package/src/components/ToggleGroup.tsx +161 -0
- package/src/components/Tooltip.tsx +187 -0
- package/src/components/Transition.tsx +155 -0
- package/src/density.ts +46 -0
- package/src/dev.ts +87 -0
- package/src/focusTrap.ts +61 -0
- package/src/form.ts +269 -0
- package/src/index.ts +243 -0
- package/src/motion.ts +62 -0
- package/src/overlay.ts +232 -0
- package/src/palette.ts +70 -0
- package/src/reactive.ts +54 -0
- package/src/rovingFocus.ts +171 -0
- package/src/size-entries/form-kit.ts +11 -0
- package/src/styles/base.css +70 -0
- package/src/styles/theme.css +14 -0
- package/src/styles/tokens.css +377 -0
- package/src/styles/utilities.css +216 -0
- package/src/styles.ts +52 -0
- package/src/theme.ts +48 -0
- package/src/utils.ts +25 -0
package/dist/density.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { effect, signal } from "@lab206/core";
|
|
2
|
+
/**
|
|
3
|
+
* Control spacing / control height density via CSS tokens.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const density = createDensity("comfortable");
|
|
8
|
+
* density.bind();
|
|
9
|
+
* density.setDensity("compact");
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export function createDensity(initial = "comfortable") {
|
|
13
|
+
const density = signal(initial);
|
|
14
|
+
function setDensity(next) {
|
|
15
|
+
density.set(next);
|
|
16
|
+
}
|
|
17
|
+
function toggle() {
|
|
18
|
+
density.update((d) => (d === "comfortable" ? "compact" : "comfortable"));
|
|
19
|
+
}
|
|
20
|
+
function bind(target) {
|
|
21
|
+
const el = target ??
|
|
22
|
+
(typeof document !== "undefined" ? document.documentElement : null);
|
|
23
|
+
if (!el)
|
|
24
|
+
return () => { };
|
|
25
|
+
return effect(() => {
|
|
26
|
+
el.setAttribute("data-pu-density", density());
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return { density, setDensity, toggle, bind };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=density.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"density.js","sourceRoot":"","sources":["../src/density.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAe,MAAM,cAAc,CAAC;AAY3D;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,UAAmB,aAAa;IAC5D,MAAM,OAAO,GAAG,MAAM,CAAU,OAAO,CAAC,CAAC;IAEzC,SAAS,UAAU,CAAC,IAAa;QAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,SAAS,MAAM;QACb,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,SAAS,IAAI,CAAC,MAAgB;QAC5B,MAAM,EAAE,GACN,MAAM;YACN,CAAC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtE,IAAI,CAAC,EAAE;YAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;QAEzB,OAAO,MAAM,CAAC,GAAG,EAAE;YACjB,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC/C,CAAC"}
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight development warnings for common Powers mistakes.
|
|
3
|
+
* No-ops when `NODE_ENV === "production"` or when disabled.
|
|
4
|
+
*
|
|
5
|
+
* Call `installDevWarnings()` once from your app bootstrap (demos do this).
|
|
6
|
+
*/
|
|
7
|
+
/** Log a unique warning once per key (console.warn). */
|
|
8
|
+
export declare function devWarnOnce(key: string, message: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* After first paint, check that design tokens exist (theme.css imported).
|
|
11
|
+
* Safe to call multiple times.
|
|
12
|
+
*/
|
|
13
|
+
export declare function warnIfThemeMissing(doc?: Document): void;
|
|
14
|
+
/**
|
|
15
|
+
* Heuristic: plain-string `value` on a named control often means the author
|
|
16
|
+
* wrote `value={email()}` (snapshot) instead of `bind={email}` / `value={email}`.
|
|
17
|
+
* Only warns when `bound` is not used.
|
|
18
|
+
*/
|
|
19
|
+
export declare function warnPossibleSnapshotValue(control: string, opts: {
|
|
20
|
+
hasBind?: boolean;
|
|
21
|
+
valueIsPlainString?: boolean;
|
|
22
|
+
}): void;
|
|
23
|
+
/** Enable/disable all Powers dev warnings. */
|
|
24
|
+
export declare function setDevWarnings(enabled: boolean): void;
|
|
25
|
+
/** Install theme check (call once from demo / template main). */
|
|
26
|
+
export declare function installDevWarnings(): void;
|
|
27
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,wDAAwD;AACxD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAO9D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,QAAmB,GAAG,IAAI,CAgBjE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GACxD,IAAI,CAMN;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAErD;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight development warnings for common Powers mistakes.
|
|
3
|
+
* No-ops when `NODE_ENV === "production"` or when disabled.
|
|
4
|
+
*
|
|
5
|
+
* Call `installDevWarnings()` once from your app bootstrap (demos do this).
|
|
6
|
+
*/
|
|
7
|
+
const g = globalThis;
|
|
8
|
+
function isDev() {
|
|
9
|
+
if (g.__POWER_UI_DEV__ === false)
|
|
10
|
+
return false;
|
|
11
|
+
if (g.__POWER_UI_DEV__ === true)
|
|
12
|
+
return true;
|
|
13
|
+
const env = g.process?.env?.NODE_ENV;
|
|
14
|
+
if (env === "production")
|
|
15
|
+
return false;
|
|
16
|
+
// Browser demos / Vite: treat as dev unless explicitly production
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function warnedSet() {
|
|
20
|
+
if (!g.__POWER_UI_WARNED__)
|
|
21
|
+
g.__POWER_UI_WARNED__ = new Set();
|
|
22
|
+
return g.__POWER_UI_WARNED__;
|
|
23
|
+
}
|
|
24
|
+
/** Log a unique warning once per key (console.warn). */
|
|
25
|
+
export function devWarnOnce(key, message) {
|
|
26
|
+
if (!isDev())
|
|
27
|
+
return;
|
|
28
|
+
const set = warnedSet();
|
|
29
|
+
if (set.has(key))
|
|
30
|
+
return;
|
|
31
|
+
set.add(key);
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.warn(`[Powers] ${message}`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* After first paint, check that design tokens exist (theme.css imported).
|
|
37
|
+
* Safe to call multiple times.
|
|
38
|
+
*/
|
|
39
|
+
export function warnIfThemeMissing(doc = document) {
|
|
40
|
+
if (!isDev() || typeof doc === "undefined")
|
|
41
|
+
return;
|
|
42
|
+
queueMicrotask(() => {
|
|
43
|
+
try {
|
|
44
|
+
const cs = doc.defaultView?.getComputedStyle(doc.documentElement);
|
|
45
|
+
const accent = cs?.getPropertyValue("--pu-color-accent")?.trim();
|
|
46
|
+
if (!accent) {
|
|
47
|
+
devWarnOnce("theme-css", 'Missing design tokens. Import once at the app root: import "@lab206/ui/theme.css"; then createTheme().bind().');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
/* ignore */
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Heuristic: plain-string `value` on a named control often means the author
|
|
57
|
+
* wrote `value={email()}` (snapshot) instead of `bind={email}` / `value={email}`.
|
|
58
|
+
* Only warns when `bound` is not used.
|
|
59
|
+
*/
|
|
60
|
+
export function warnPossibleSnapshotValue(control, opts) {
|
|
61
|
+
if (!isDev() || opts.hasBind || !opts.valueIsPlainString)
|
|
62
|
+
return;
|
|
63
|
+
devWarnOnce(`snapshot-${control}`, `${control}: value is a plain string. If you meant a live signal, pass the signal itself: bind={email} or value={email} — not value={email()}.`);
|
|
64
|
+
}
|
|
65
|
+
/** Enable/disable all Powers dev warnings. */
|
|
66
|
+
export function setDevWarnings(enabled) {
|
|
67
|
+
g.__POWER_UI_DEV__ = enabled;
|
|
68
|
+
}
|
|
69
|
+
/** Install theme check (call once from demo / template main). */
|
|
70
|
+
export function installDevWarnings() {
|
|
71
|
+
if (!isDev())
|
|
72
|
+
return;
|
|
73
|
+
if (typeof document !== "undefined") {
|
|
74
|
+
warnIfThemeMissing(document);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=dev.js.map
|
package/dist/dev.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,GAAG,UAIT,CAAC;AAEF,SAAS,KAAK;IACZ,IAAI,CAAC,CAAC,gBAAgB,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,CAAC,CAAC,gBAAgB,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC;IACrC,IAAI,GAAG,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC;IACvC,kEAAkE;IAClE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,CAAC,CAAC,CAAC,mBAAmB;QAAE,CAAC,CAAC,mBAAmB,GAAG,IAAI,GAAG,EAAE,CAAC;IAC9D,OAAO,CAAC,CAAC,mBAAmB,CAAC;AAC/B,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,OAAe;IACtD,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO;IACrB,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IACzB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACb,sCAAsC;IACtC,OAAO,CAAC,IAAI,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAgB,QAAQ;IACzD,IAAI,CAAC,KAAK,EAAE,IAAI,OAAO,GAAG,KAAK,WAAW;QAAE,OAAO;IACnD,cAAc,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,GAAG,CAAC,WAAW,EAAE,gBAAgB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAClE,MAAM,MAAM,GAAG,EAAE,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,EAAE,IAAI,EAAE,CAAC;YACjE,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,WAAW,CACT,WAAW,EACX,+GAA+G,CAChH,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAAe,EACf,IAAyD;IAEzD,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB;QAAE,OAAO;IACjE,WAAW,CACT,YAAY,OAAO,EAAE,EACrB,GAAG,OAAO,qIAAqI,CAChJ,CAAC;AACJ,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,CAAC,CAAC,gBAAgB,GAAG,OAAO,CAAC;AAC/B,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO;IACrB,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.d.ts","sourceRoot":"","sources":["../src/focusTrap.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,IAAI,CA+CvD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const FOCUSABLE = 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
2
|
+
function listFocusable(root) {
|
|
3
|
+
return [...root.querySelectorAll(FOCUSABLE)].filter((el) => !el.hasAttribute("disabled") && el.offsetParent !== null);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Trap Tab focus inside `root` (for Dialog / Command / Drawer).
|
|
7
|
+
* Returns a dispose that removes the listener and restores prior focus.
|
|
8
|
+
*/
|
|
9
|
+
export function trapFocus(root) {
|
|
10
|
+
const doc = root.ownerDocument;
|
|
11
|
+
const prev = doc.activeElement;
|
|
12
|
+
const focusFirst = () => {
|
|
13
|
+
const items = listFocusable(root);
|
|
14
|
+
(items[0] ?? root).focus?.();
|
|
15
|
+
};
|
|
16
|
+
// Defer so open animation / mount finishes
|
|
17
|
+
const win = doc.defaultView ?? window;
|
|
18
|
+
const t = win.setTimeout(focusFirst, 0);
|
|
19
|
+
const onKey = (e) => {
|
|
20
|
+
if (e.key !== "Tab")
|
|
21
|
+
return;
|
|
22
|
+
const items = listFocusable(root);
|
|
23
|
+
if (items.length === 0) {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const first = items[0];
|
|
28
|
+
const last = items[items.length - 1];
|
|
29
|
+
const active = doc.activeElement;
|
|
30
|
+
if (e.shiftKey) {
|
|
31
|
+
if (active === first || !root.contains(active)) {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
last.focus();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else if (active === last) {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
first.focus();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
doc.addEventListener("keydown", onKey, true);
|
|
42
|
+
return () => {
|
|
43
|
+
win.clearTimeout(t);
|
|
44
|
+
doc.removeEventListener("keydown", onKey, true);
|
|
45
|
+
if (prev && typeof prev.focus === "function") {
|
|
46
|
+
try {
|
|
47
|
+
prev.focus();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
/* ignore */
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=focusTrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.js","sourceRoot":"","sources":["../src/focusTrap.ts"],"names":[],"mappings":"AAAA,MAAM,SAAS,GACb,sIAAsI,CAAC;AAEzI,SAAS,aAAa,CAAC,IAAiB;IACtC,OAAO,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAc,SAAS,CAAC,CAAC,CAAC,MAAM,CAC9D,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,YAAY,KAAK,IAAI,CACjE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAiB;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC;IAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,aAAmC,CAAC;IAErD,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;IAC/B,CAAC,CAAC;IAEF,2CAA2C;IAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC;IACtC,MAAM,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAExC,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,EAAE;QACjC,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK;YAAE,OAAO;QAC5B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACtC,MAAM,MAAM,GAAG,GAAG,CAAC,aAAmC,CAAC;QACvD,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACf,IAAI,MAAM,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC/C,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAC3B,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC;IAEF,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAE7C,OAAO,GAAG,EAAE;QACV,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACpB,GAAG,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAChD,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight form helpers — pure functions for Field `error` props,
|
|
3
|
+
* two-way bind helpers, and a small `createField` pattern.
|
|
4
|
+
*
|
|
5
|
+
* No schema DSL. Keep validation next to signals.
|
|
6
|
+
*
|
|
7
|
+
* @example Preferred control wiring
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const email = signal("");
|
|
10
|
+
* <Input bind={email} />
|
|
11
|
+
* // or spread: <Input {...bindText(email)} />
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @example Validation
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const emailError = () => firstError(required(email()), emailFormat(email()));
|
|
17
|
+
* <Field label="Email" error={emailError}><Input bind={email} /></Field>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import { type Signal } from "@lab206/core";
|
|
21
|
+
export type FieldError = string | undefined | null | false;
|
|
22
|
+
/** Anything that behaves like a writable signal (Powers signal). */
|
|
23
|
+
export type Bindable<T> = {
|
|
24
|
+
(): T;
|
|
25
|
+
set: (value: T) => void;
|
|
26
|
+
};
|
|
27
|
+
/** First truthy message wins (empty string / false / null skipped). */
|
|
28
|
+
export declare function firstError(...msgs: Array<FieldError | (() => FieldError)>): string;
|
|
29
|
+
export declare function required(value: unknown, message?: string): FieldError;
|
|
30
|
+
export declare function minLength(value: string, min: number, message?: string): FieldError;
|
|
31
|
+
export declare function maxLength(value: string, max: number, message?: string): FieldError;
|
|
32
|
+
export declare function emailFormat(value: string, message?: string): FieldError;
|
|
33
|
+
export declare function matches(value: string, other: string, message?: string): FieldError;
|
|
34
|
+
/**
|
|
35
|
+
* Run a map of field → error string. Returns `{ ok, errors }`.
|
|
36
|
+
* Use after submit or whenever you want a full-form snapshot.
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateForm<K extends string>(fields: Record<K, () => FieldError>): {
|
|
39
|
+
ok: boolean;
|
|
40
|
+
errors: Partial<Record<K, string>>;
|
|
41
|
+
};
|
|
42
|
+
/** Read string value from an input / textarea / select event. */
|
|
43
|
+
export declare function eventValue(e: Event): string;
|
|
44
|
+
/** Read checked state from a checkbox / switch-like event. */
|
|
45
|
+
export declare function eventChecked(e: Event): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Props to spread onto `Input` / `Textarea` for two-way signal binding.
|
|
48
|
+
*
|
|
49
|
+
* Prefer the control’s `bind={signal}` prop when available.
|
|
50
|
+
* Named `bindInput` (not `bindText`) so it doesn’t clash with `@lab206/dom`’s
|
|
51
|
+
* DOM `bindText` helper.
|
|
52
|
+
*/
|
|
53
|
+
export declare function bindInput(sig: Bindable<string>): {
|
|
54
|
+
value: Bindable<string>;
|
|
55
|
+
onInput: (e: Event) => void;
|
|
56
|
+
};
|
|
57
|
+
/** Alias of bindInput. */
|
|
58
|
+
export declare const bindString: typeof bindInput;
|
|
59
|
+
/**
|
|
60
|
+
* Props to spread onto `Select` for two-way signal binding.
|
|
61
|
+
* Prefer `bind={signal}` on Select when available.
|
|
62
|
+
*/
|
|
63
|
+
export declare function bindSelect(sig: Bindable<string>): {
|
|
64
|
+
value: Bindable<string>;
|
|
65
|
+
onChange: (e: Event) => void;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Adapt a typed string-union signal (`Signal<"a" | "b">`) for `<Select bind={…} />`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function asSelectBind<T extends string>(sig: {
|
|
71
|
+
(): T;
|
|
72
|
+
set: (value: T) => void;
|
|
73
|
+
}): Bindable<string>;
|
|
74
|
+
/**
|
|
75
|
+
* Props for checkbox / switch patterns that use `checked` + `onChange(boolean)`.
|
|
76
|
+
*/
|
|
77
|
+
export declare function bindChecked(sig: Bindable<boolean>): {
|
|
78
|
+
checked: Bindable<boolean>;
|
|
79
|
+
onChange: (checked: boolean) => void;
|
|
80
|
+
};
|
|
81
|
+
export type CreateFieldOptions = {
|
|
82
|
+
/** Initial string value */
|
|
83
|
+
initial?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Validate when touched (or always if `validateWhen: "always"`).
|
|
86
|
+
* Return a message or falsy.
|
|
87
|
+
*/
|
|
88
|
+
validate?: (value: string) => FieldError;
|
|
89
|
+
/** Default `"touched"` — only show errors after blur/submit. */
|
|
90
|
+
validateWhen?: "touched" | "always";
|
|
91
|
+
};
|
|
92
|
+
export type FieldHandle = {
|
|
93
|
+
/** Writable signal — pass to `bind={field.value}` or `value={field.value}` */
|
|
94
|
+
value: Signal<string>;
|
|
95
|
+
touched: Signal<boolean>;
|
|
96
|
+
/** Accessor for Field `error={field.error}` */
|
|
97
|
+
error: () => string;
|
|
98
|
+
/** Mark touched (call on blur or before submit) */
|
|
99
|
+
touch: () => void;
|
|
100
|
+
/** Reset value + untouched */
|
|
101
|
+
reset: (next?: string) => void;
|
|
102
|
+
/** Current string (sugar for value()) */
|
|
103
|
+
get: () => string;
|
|
104
|
+
set: (v: string) => void;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* One field’s state: value signal + touched + error accessor.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```tsx
|
|
111
|
+
* const name = createField({
|
|
112
|
+
* validate: (v) => required(v, "Name required"),
|
|
113
|
+
* });
|
|
114
|
+
*
|
|
115
|
+
* <Field label="Name" required error={name.error}>
|
|
116
|
+
* <Input bind={name.value} onBlur={name.touch} />
|
|
117
|
+
* </Field>
|
|
118
|
+
*
|
|
119
|
+
* // on submit:
|
|
120
|
+
* name.touch();
|
|
121
|
+
* if (name.error()) return;
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare function createField(options?: CreateFieldOptions): FieldHandle;
|
|
125
|
+
//# sourceMappingURL=form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"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"}
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight form helpers — pure functions for Field `error` props,
|
|
3
|
+
* two-way bind helpers, and a small `createField` pattern.
|
|
4
|
+
*
|
|
5
|
+
* No schema DSL. Keep validation next to signals.
|
|
6
|
+
*
|
|
7
|
+
* @example Preferred control wiring
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const email = signal("");
|
|
10
|
+
* <Input bind={email} />
|
|
11
|
+
* // or spread: <Input {...bindText(email)} />
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @example Validation
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const emailError = () => firstError(required(email()), emailFormat(email()));
|
|
17
|
+
* <Field label="Email" error={emailError}><Input bind={email} /></Field>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import { signal } from "@lab206/core";
|
|
21
|
+
/** First truthy message wins (empty string / false / null skipped). */
|
|
22
|
+
export function firstError(...msgs) {
|
|
23
|
+
for (const m of msgs) {
|
|
24
|
+
const v = typeof m === "function" ? m() : m;
|
|
25
|
+
if (v)
|
|
26
|
+
return String(v);
|
|
27
|
+
}
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
export function required(value, message = "Required") {
|
|
31
|
+
if (value == null)
|
|
32
|
+
return message;
|
|
33
|
+
if (typeof value === "string" && value.trim() === "")
|
|
34
|
+
return message;
|
|
35
|
+
if (Array.isArray(value) && value.length === 0)
|
|
36
|
+
return message;
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
export function minLength(value, min, message) {
|
|
40
|
+
if (!value)
|
|
41
|
+
return "";
|
|
42
|
+
if (value.length < min) {
|
|
43
|
+
return message ?? `At least ${min} characters`;
|
|
44
|
+
}
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
export function maxLength(value, max, message) {
|
|
48
|
+
if (!value)
|
|
49
|
+
return "";
|
|
50
|
+
if (value.length > max) {
|
|
51
|
+
return message ?? `At most ${max} characters`;
|
|
52
|
+
}
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
56
|
+
export function emailFormat(value, message = "Enter a valid email") {
|
|
57
|
+
if (!value || !value.trim())
|
|
58
|
+
return "";
|
|
59
|
+
return EMAIL_RE.test(value.trim()) ? "" : message;
|
|
60
|
+
}
|
|
61
|
+
export function matches(value, other, message = "Does not match") {
|
|
62
|
+
if (!value && !other)
|
|
63
|
+
return "";
|
|
64
|
+
return value === other ? "" : message;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Run a map of field → error string. Returns `{ ok, errors }`.
|
|
68
|
+
* Use after submit or whenever you want a full-form snapshot.
|
|
69
|
+
*/
|
|
70
|
+
export function validateForm(fields) {
|
|
71
|
+
const errors = {};
|
|
72
|
+
let ok = true;
|
|
73
|
+
for (const key of Object.keys(fields)) {
|
|
74
|
+
const msg = fields[key]();
|
|
75
|
+
if (msg) {
|
|
76
|
+
errors[key] = String(msg);
|
|
77
|
+
ok = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { ok, errors };
|
|
81
|
+
}
|
|
82
|
+
/** Read string value from an input / textarea / select event. */
|
|
83
|
+
export function eventValue(e) {
|
|
84
|
+
const t = e.currentTarget ?? e.target;
|
|
85
|
+
// Duck-type (works in happy-dom / SSR stubs without instanceof globals)
|
|
86
|
+
if (t && typeof t === "object" && "value" in t) {
|
|
87
|
+
return String(t.value ?? "");
|
|
88
|
+
}
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
/** Read checked state from a checkbox / switch-like event. */
|
|
92
|
+
export function eventChecked(e) {
|
|
93
|
+
const t = e.currentTarget ?? e.target;
|
|
94
|
+
if (t && typeof t === "object" && "checked" in t) {
|
|
95
|
+
return !!t.checked;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Props to spread onto `Input` / `Textarea` for two-way signal binding.
|
|
101
|
+
*
|
|
102
|
+
* Prefer the control’s `bind={signal}` prop when available.
|
|
103
|
+
* Named `bindInput` (not `bindText`) so it doesn’t clash with `@lab206/dom`’s
|
|
104
|
+
* DOM `bindText` helper.
|
|
105
|
+
*/
|
|
106
|
+
export function bindInput(sig) {
|
|
107
|
+
return {
|
|
108
|
+
value: sig,
|
|
109
|
+
onInput: (e) => {
|
|
110
|
+
sig.set(eventValue(e));
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** Alias of bindInput. */
|
|
115
|
+
export const bindString = bindInput;
|
|
116
|
+
/**
|
|
117
|
+
* Props to spread onto `Select` for two-way signal binding.
|
|
118
|
+
* Prefer `bind={signal}` on Select when available.
|
|
119
|
+
*/
|
|
120
|
+
export function bindSelect(sig) {
|
|
121
|
+
return {
|
|
122
|
+
value: sig,
|
|
123
|
+
onChange: (e) => {
|
|
124
|
+
sig.set(eventValue(e));
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Adapt a typed string-union signal (`Signal<"a" | "b">`) for `<Select bind={…} />`.
|
|
130
|
+
*/
|
|
131
|
+
export function asSelectBind(sig) {
|
|
132
|
+
const bound = (() => sig());
|
|
133
|
+
bound.set = (value) => {
|
|
134
|
+
sig.set(value);
|
|
135
|
+
};
|
|
136
|
+
return bound;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Props for checkbox / switch patterns that use `checked` + `onChange(boolean)`.
|
|
140
|
+
*/
|
|
141
|
+
export function bindChecked(sig) {
|
|
142
|
+
return {
|
|
143
|
+
checked: sig,
|
|
144
|
+
onChange: (checked) => {
|
|
145
|
+
sig.set(checked);
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* One field’s state: value signal + touched + error accessor.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```tsx
|
|
154
|
+
* const name = createField({
|
|
155
|
+
* validate: (v) => required(v, "Name required"),
|
|
156
|
+
* });
|
|
157
|
+
*
|
|
158
|
+
* <Field label="Name" required error={name.error}>
|
|
159
|
+
* <Input bind={name.value} onBlur={name.touch} />
|
|
160
|
+
* </Field>
|
|
161
|
+
*
|
|
162
|
+
* // on submit:
|
|
163
|
+
* name.touch();
|
|
164
|
+
* if (name.error()) return;
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export function createField(options = {}) {
|
|
168
|
+
const initial = options.initial ?? "";
|
|
169
|
+
const value = signal(initial);
|
|
170
|
+
const touched = signal(false);
|
|
171
|
+
const when = options.validateWhen ?? "touched";
|
|
172
|
+
const error = () => {
|
|
173
|
+
if (when === "touched" && !touched())
|
|
174
|
+
return "";
|
|
175
|
+
if (!options.validate)
|
|
176
|
+
return "";
|
|
177
|
+
return firstError(options.validate(value()));
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
value,
|
|
181
|
+
touched,
|
|
182
|
+
error,
|
|
183
|
+
touch: () => touched.set(true),
|
|
184
|
+
reset: (next = initial) => {
|
|
185
|
+
value.set(next);
|
|
186
|
+
touched.set(false);
|
|
187
|
+
},
|
|
188
|
+
get: () => value(),
|
|
189
|
+
set: (v) => value.set(v),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=form.js.map
|
package/dist/form.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.js","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"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"}
|