@forthtilliath/forth-ui 0.2.3 → 0.2.5
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/color-picker/color-picker.d.ts +3 -1
- package/dist/components/color-picker/color-picker.d.ts.map +1 -1
- package/dist/components/color-picker/color-picker.js +2 -2
- package/dist/components/combobox/multi-select.d.ts.map +1 -1
- package/dist/components/combobox/multi-select.js +3 -2
- package/dist/components/command-palette/command-palette.js +1 -1
- package/dist/components/image-input/image-input.js +1 -1
- package/dist/components/rating/rating.d.ts.map +1 -1
- package/dist/components/rating/rating.js +6 -1
- package/dist/components/relative-time/relative-time.js +1 -1
- package/package.json +21 -6
|
@@ -5,6 +5,8 @@ export interface ColorPickerProps {
|
|
|
5
5
|
onValueChange?: (value: string) => void;
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
className?: string;
|
|
8
|
+
/** Accessible name for the color swatch, which otherwise has no visible text. */
|
|
9
|
+
ariaLabel?: string;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* A color swatch + hex text input, kept in sync — built on the browser's
|
|
@@ -20,5 +22,5 @@ export interface ColorPickerProps {
|
|
|
20
22
|
* @author Forth
|
|
21
23
|
* @copyright 2026 Forth
|
|
22
24
|
*/
|
|
23
|
-
export declare function ColorPicker({ value, defaultValue, onValueChange, disabled, className, }: ColorPickerProps): React.JSX.Element;
|
|
25
|
+
export declare function ColorPicker({ value, defaultValue, onValueChange, disabled, className, ariaLabel, }: ColorPickerProps): React.JSX.Element;
|
|
24
26
|
//# sourceMappingURL=color-picker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-picker.d.ts","sourceRoot":"","sources":["../../../src/components/color-picker/color-picker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,YAAwB,EACxB,aAAa,EACb,QAAgB,EAChB,SAAS,
|
|
1
|
+
{"version":3,"file":"color-picker.d.ts","sourceRoot":"","sources":["../../../src/components/color-picker/color-picker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,YAAwB,EACxB,aAAa,EACb,QAAgB,EAChB,SAAS,EACT,SAA0B,GAC3B,EAAE,gBAAgB,qBAoDlB"}
|
|
@@ -18,7 +18,7 @@ const HEX_PATTERN = /^#[0-9a-f]{6}$/i;
|
|
|
18
18
|
* @author Forth
|
|
19
19
|
* @copyright 2026 Forth
|
|
20
20
|
*/
|
|
21
|
-
export function ColorPicker({ value, defaultValue = "#000000", onValueChange, disabled = false, className, }) {
|
|
21
|
+
export function ColorPicker({ value, defaultValue = "#000000", onValueChange, disabled = false, className, ariaLabel = "Choose color", }) {
|
|
22
22
|
const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
|
|
23
23
|
const [textValue, setTextValue] = React.useState(defaultValue);
|
|
24
24
|
const color = value ?? uncontrolledValue;
|
|
@@ -27,7 +27,7 @@ export function ColorPicker({ value, defaultValue = "#000000", onValueChange, di
|
|
|
27
27
|
setTextValue(next);
|
|
28
28
|
onValueChange?.(next);
|
|
29
29
|
}
|
|
30
|
-
return (_jsxs("div", { "data-slot": "color-picker", className: cn("flex items-center gap-2", className), children: [_jsx("label", { className: cn("border-input size-9 shrink-0 overflow-hidden rounded-md border shadow-xs", disabled && "pointer-events-none opacity-50"), style: { backgroundColor: color }, children: _jsx("input", { type: "color", value: color, disabled: disabled, onChange: (e) => {
|
|
30
|
+
return (_jsxs("div", { "data-slot": "color-picker", className: cn("flex items-center gap-2", className), children: [_jsx("label", { "aria-label": ariaLabel, className: cn("border-input size-9 shrink-0 overflow-hidden rounded-md border shadow-xs", disabled && "pointer-events-none opacity-50"), style: { backgroundColor: color }, children: _jsx("input", { type: "color", value: color, disabled: disabled, onChange: (e) => {
|
|
31
31
|
commit(e.target.value);
|
|
32
32
|
}, className: "size-full cursor-pointer opacity-0" }) }), _jsx(Input, { value: textValue, disabled: disabled, onChange: (e) => {
|
|
33
33
|
setTextValue(e.target.value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-select.d.ts","sourceRoot":"","sources":["../../../src/components/combobox/multi-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqB/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,YAAY,EACZ,aAAa,EACb,WAAuB,EACvB,YAAkC,EAClC,QAAgB,EAChB,SAAS,GACV,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"multi-select.d.ts","sourceRoot":"","sources":["../../../src/components/combobox/multi-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqB/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,YAAY,EACZ,aAAa,EACb,WAAuB,EACvB,YAAkC,EAClC,QAAgB,EAChB,SAAS,GACV,EAAE,gBAAgB,qBAoHlB"}
|
|
@@ -23,6 +23,7 @@ import { Badge } from "../badge";
|
|
|
23
23
|
* @copyright 2026 Forth
|
|
24
24
|
*/
|
|
25
25
|
export function MultiSelect({ options, value, defaultValue, onValueChange, placeholder = "Select…", emptyMessage = "No results found.", disabled = false, className, }) {
|
|
26
|
+
const listboxId = React.useId();
|
|
26
27
|
const [open, setOpen] = React.useState(false);
|
|
27
28
|
const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue ?? []);
|
|
28
29
|
const selected = value ?? uncontrolledValue;
|
|
@@ -36,7 +37,7 @@ export function MultiSelect({ options, value, defaultValue, onValueChange, place
|
|
|
36
37
|
: [...selected, optionValue]);
|
|
37
38
|
}
|
|
38
39
|
const selectedOptions = options.filter((option) => selected.includes(option.value));
|
|
39
|
-
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("div", { role: "combobox", tabIndex: disabled ? -1 : 0, "aria-expanded": open, "aria-disabled": disabled, "data-disabled": disabled, onKeyDown: (e) => {
|
|
40
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("div", { role: "combobox", tabIndex: disabled ? -1 : 0, "aria-expanded": open, "aria-controls": listboxId, "aria-disabled": disabled, "data-disabled": disabled, onKeyDown: (e) => {
|
|
40
41
|
if (!disabled && (e.key === "Enter" || e.key === " ")) {
|
|
41
42
|
e.preventDefault();
|
|
42
43
|
setOpen((prev) => !prev);
|
|
@@ -45,7 +46,7 @@ export function MultiSelect({ options, value, defaultValue, onValueChange, place
|
|
|
45
46
|
e.stopPropagation();
|
|
46
47
|
}, onOpenChange: () => {
|
|
47
48
|
toggle(option.value);
|
|
48
|
-
}, children: option.label }, option.value)))) : (_jsx("span", { className: "text-muted-foreground font-normal", children: placeholder })) }), _jsx(ChevronsUpDownIcon, { className: "shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { className: "w-(--radix-popover-trigger-width) p-0", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: placeholder }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: emptyMessage }), _jsx(CommandGroup, { children: options.map((option) => (_jsxs(CommandItem, { value: option.label, disabled: option.disabled, onSelect: () => {
|
|
49
|
+
}, children: option.label }, option.value)))) : (_jsx("span", { className: "text-muted-foreground font-normal", children: placeholder })) }), _jsx(ChevronsUpDownIcon, { className: "shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { id: listboxId, className: "w-(--radix-popover-trigger-width) p-0", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: placeholder }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: emptyMessage }), _jsx(CommandGroup, { children: options.map((option) => (_jsxs(CommandItem, { value: option.label, disabled: option.disabled, onSelect: () => {
|
|
49
50
|
toggle(option.value);
|
|
50
51
|
}, children: [_jsx(CheckIcon, { className: cn("size-4", selected.includes(option.value)
|
|
51
52
|
? "opacity-100"
|
|
@@ -35,7 +35,7 @@ export function CommandPalette({ groups, open, onOpenChange, placeholder = "Type
|
|
|
35
35
|
return () => {
|
|
36
36
|
document.removeEventListener("keydown", onKeyDown);
|
|
37
37
|
};
|
|
38
|
-
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
38
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps -- `setOpen` is a plain function redefined every render; including it would re-register this listener every render instead of only when `isOpen`/`shortcutKey` actually change, which is what its own re-closure over `isOpen` depends on
|
|
39
39
|
}, [isOpen, shortcutKey]);
|
|
40
40
|
return (_jsxs(CommandDialog, { open: isOpen, onOpenChange: setOpen, title: "Command Palette", description: placeholder, children: [_jsx(CommandInput, { placeholder: placeholder }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: emptyMessage }), groups.map((group, index) => (
|
|
41
41
|
// eslint-disable-next-line @eslint-react/no-array-index-key -- `heading` is optional and not guaranteed unique
|
|
@@ -18,7 +18,7 @@ export function ImageInput({ value, onFileChange, disabled = false, className, a
|
|
|
18
18
|
const inputRef = React.useRef(null);
|
|
19
19
|
React.useEffect(() => {
|
|
20
20
|
if (file === null) {
|
|
21
|
-
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
21
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect -- syncs the object URL lifecycle (create/revoke) with the selected File, an external browser resource that can't be derived during render
|
|
22
22
|
setObjectUrl(null);
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rating.d.ts","sourceRoot":"","sources":["../../../src/components/rating/rating.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,YAAgB,EAChB,aAAa,EACb,GAAO,EACP,QAAgB,EAChB,QAAgB,EAChB,IAAgB,EAChB,SAAS,GACV,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"rating.d.ts","sourceRoot":"","sources":["../../../src/components/rating/rating.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,YAAgB,EAChB,aAAa,EACb,GAAO,EACP,QAAgB,EAChB,QAAgB,EAChB,IAAgB,EAChB,SAAS,GACV,EAAE,WAAW,qBA+Db"}
|
|
@@ -27,7 +27,12 @@ export function Rating({ value, defaultValue = 0, onValueChange, max = 5, disabl
|
|
|
27
27
|
setUncontrolledValue(next);
|
|
28
28
|
onValueChange?.(next);
|
|
29
29
|
}
|
|
30
|
-
return (_jsx("div", { role: "radiogroup",
|
|
30
|
+
return (_jsx("div", { role: "radiogroup",
|
|
31
|
+
// Not a Tab stop itself — focus lives on the roving `role="radio"`
|
|
32
|
+
// children below — but jsx-a11y wants an interactive-role element with
|
|
33
|
+
// a mouse handler (onMouseLeave, used only to clear the hover preview)
|
|
34
|
+
// to declare an explicit tabIndex.
|
|
35
|
+
tabIndex: -1, "aria-disabled": disabled, "data-slot": "rating", className: cn("inline-flex items-center gap-0.5", (disabled || readOnly) && "pointer-events-none", className), onMouseLeave: () => {
|
|
31
36
|
setHoverValue(null);
|
|
32
37
|
}, children: Array.from({ length: max }, (_, i) => i + 1).map((starValue) => (_jsx("button", { type: "button", role: "radio", "aria-checked": current === starValue, "aria-label": `${starValue.toString()} star${starValue > 1 ? "s" : ""}`, disabled: disabled, tabIndex: readOnly ? -1 : 0, onMouseEnter: () => {
|
|
33
38
|
setHoverValue(starValue);
|
|
@@ -39,7 +39,7 @@ export function RelativeTime({ date, timeZone, label, formatOptions, className,
|
|
|
39
39
|
// ones — there's no way to read "now" during render without
|
|
40
40
|
// reintroducing the hydration mismatch this state is starting
|
|
41
41
|
// `undefined` to avoid.
|
|
42
|
-
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
42
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
43
43
|
setLiveNow(new Date());
|
|
44
44
|
const interval = setInterval(() => {
|
|
45
45
|
setLiveNow(new Date());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forthtilliath/forth-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
"react": "^19.2.8",
|
|
25
25
|
"react-dom": "^19.2.8",
|
|
26
26
|
"shiki": "^4.4.3",
|
|
27
|
-
"@forthtilliath/react-kit": "0.
|
|
27
|
+
"@forthtilliath/react-kit": "0.4.0",
|
|
28
28
|
"@forthtilliath/shadcn-ui": "0.2.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
+
"@size-limit/file": "^11.2.0",
|
|
31
32
|
"@tailwindcss/postcss": "4.1.12",
|
|
32
33
|
"@turbo/gen": "^2.10.9",
|
|
33
34
|
"@types/node": "^26.2.0",
|
|
@@ -36,11 +37,12 @@
|
|
|
36
37
|
"@types/react-dom": "^19.2.4",
|
|
37
38
|
"concurrently": "^10.0.4",
|
|
38
39
|
"eslint": "10.7.0",
|
|
40
|
+
"size-limit": "^11.2.0",
|
|
39
41
|
"tailwindcss": "4.1.12",
|
|
40
42
|
"typescript": "^5.9.3",
|
|
41
|
-
"@forthtilliath/eslint-config": "0.
|
|
42
|
-
"@forthtilliath/
|
|
43
|
-
"@forthtilliath/
|
|
43
|
+
"@forthtilliath/eslint-config": "0.7.0",
|
|
44
|
+
"@forthtilliath/ts-types": "0.2.0",
|
|
45
|
+
"@forthtilliath/typescript-config": "0.1.1"
|
|
44
46
|
},
|
|
45
47
|
"files": [
|
|
46
48
|
"dist"
|
|
@@ -49,6 +51,18 @@
|
|
|
49
51
|
"./components/*": "./dist/components/*/index.js",
|
|
50
52
|
"./styles/globals.css": "./dist/globals.css"
|
|
51
53
|
},
|
|
54
|
+
"size-limit": [
|
|
55
|
+
{
|
|
56
|
+
"name": "components (all, brotli)",
|
|
57
|
+
"path": "dist/**/*.js",
|
|
58
|
+
"limit": "70 KB"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "globals.css (brotli)",
|
|
62
|
+
"path": "dist/globals.css",
|
|
63
|
+
"limit": "20 KB"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
52
66
|
"scripts": {
|
|
53
67
|
"lint": "eslint . --max-warnings 0",
|
|
54
68
|
"check-types": "tsc --noEmit",
|
|
@@ -58,6 +72,7 @@
|
|
|
58
72
|
"dev:tw": "pnpm run build:tw --watch",
|
|
59
73
|
"build": "pnpm run clean && pnpm run build:ts && pnpm run build:tw",
|
|
60
74
|
"build:ts": "tsc",
|
|
61
|
-
"build:tw": "npx @tailwindcss/cli -i ./src/styles/globals.css -o ./dist/globals.css"
|
|
75
|
+
"build:tw": "npx @tailwindcss/cli -i ./src/styles/globals.css -o ./dist/globals.css",
|
|
76
|
+
"size": "size-limit"
|
|
62
77
|
}
|
|
63
78
|
}
|