@gridui/input 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.js +8 -8
- package/dist/index.js.map +2 -2
- package/dist/styles.css +28 -39
- package/docs/common-inputs.md +166 -0
- package/docs/textarea.md +28 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -5,12 +5,12 @@ Core family. Public API: `Input`, `InputProps`, `Label`, `LabelProps`, `Textarea
|
|
|
5
5
|
Import components from `@gridui/input` and load `@gridui/input/styles.css` after Theme.
|
|
6
6
|
Load styles for any composed dependency used by this family. Applications own routes, labels, state persistence, and branding.
|
|
7
7
|
|
|
8
|
-
See [component ownership](
|
|
8
|
+
See [component ownership](https://github.com/sudo-ui/GridUI-React-Monorepo/blob/main/docs/component-ownership.md) and [release history](https://github.com/sudo-ui/GridUI-React-Monorepo/tree/main/packages-core/input/.changeset/history).
|
|
9
9
|
|
|
10
10
|
## Native multiline input and point menus
|
|
11
11
|
|
|
12
12
|
See [Textarea](docs/textarea.md) for multiline input contracts and
|
|
13
|
-
[ContextMenu](
|
|
13
|
+
[ContextMenu](https://github.com/sudo-ui/GridUI-React-Monorepo/blob/main/packages-core/dropdown-menu/docs/context-menu.md) for controlled viewport-point menus.
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
`Label` owns inline label alignment and an optional decorative `icon` slot.
|
package/dist/index.js
CHANGED
|
@@ -3,17 +3,17 @@ import { cn } from "@gridui/style-utils";
|
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
var controlStyles = cva(cn(
|
|
5
5
|
"gridui-control",
|
|
6
|
-
"min-w-0 w-full box-border border border-solid border-border rounded-control text-foreground bg-background type-input",
|
|
7
|
-
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
8
|
-
"aria-invalid:border-danger-foreground"
|
|
6
|
+
"gridui-u-min-w-0 gridui-u-w-full gridui-u-box-border gridui-u-border gridui-u-border-solid gridui-u-border-border gridui-u-rounded-control gridui-u-text-foreground gridui-u-bg-background gridui-u-type-input",
|
|
7
|
+
"gridui-u-disabled:opacity-50 gridui-u-disabled:cursor-not-allowed",
|
|
8
|
+
"gridui-u-aria-invalid:border-danger-foreground"
|
|
9
9
|
));
|
|
10
10
|
var controlStandardStyles = cva(cn(
|
|
11
11
|
"gridui-control-standard",
|
|
12
|
-
"p-gridui-8 min-h-
|
|
12
|
+
"gridui-u-p-gridui-8 gridui-u-min-h-gridui-control"
|
|
13
13
|
));
|
|
14
14
|
var controlSmallStyles = cva(cn(
|
|
15
15
|
"gridui-control-small",
|
|
16
|
-
"py-gridui-4 px-gridui-8 min-h-
|
|
16
|
+
"gridui-u-py-gridui-4 gridui-u-px-gridui-8 gridui-u-min-h-gridui-control-small"
|
|
17
17
|
));
|
|
18
18
|
var controlVariants = cva(controlStyles(), {
|
|
19
19
|
variants: {
|
|
@@ -56,9 +56,9 @@ import { cn as cn3 } from "@gridui/style-utils";
|
|
|
56
56
|
import { cva as cva2 } from "class-variance-authority";
|
|
57
57
|
var labelStyles = cva2(cn3(
|
|
58
58
|
"gridui-label",
|
|
59
|
-
"inline-flex items-center gap-gridui-4 type-ui"
|
|
59
|
+
"gridui-u-inline-flex gridui-u-items-center gridui-u-gap-gridui-4 gridui-u-type-ui"
|
|
60
60
|
));
|
|
61
|
-
var labelIconStyles = cva2("inline-flex shrink-0 text-foreground [&>svg]:size-gridui-16");
|
|
61
|
+
var labelIconStyles = cva2("gridui-u-inline-flex gridui-u-shrink-0 gridui-u-text-foreground gridui-u-[&>svg]:size-gridui-16");
|
|
62
62
|
|
|
63
63
|
// src/label/label.tsx
|
|
64
64
|
import { cn as cn4 } from "@gridui/style-utils";
|
|
@@ -202,7 +202,7 @@ var Textarea = (props) => {
|
|
|
202
202
|
onChange?.(event);
|
|
203
203
|
onValueChange?.(event.currentTarget.value);
|
|
204
204
|
},
|
|
205
|
-
className: cn5(controlVariants({ size: controlSize }), autoSize ? "resize-none" : "resize-y", className)
|
|
205
|
+
className: cn5(controlVariants({ size: controlSize }), autoSize ? "gridui-u-resize-none" : "gridui-u-resize-y", className)
|
|
206
206
|
}
|
|
207
207
|
);
|
|
208
208
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/shared/style.ts", "../src/input/input.tsx", "../src/label/style.ts", "../src/label/label.tsx", "../src/textarea/use-textarea-autosize.ts", "../src/textarea/textarea.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * @author WMXPY\n * @package Input\n * @namespace Shared\n * @description Style\n */\n\nimport { cn } from \"@gridui/style-utils\";\nimport { cva } from \"class-variance-authority\";\n\nexport const controlStyles = cva(cn(\n \"gridui-control\",\n \"min-w-0 w-full box-border border border-solid border-border rounded-control text-foreground bg-background type-input\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"aria-invalid:border-danger-foreground\",\n));\n\nexport const controlStandardStyles = cva(cn(\n \"gridui-control-standard\",\n \"p-gridui-8 min-h-
|
|
5
|
-
"mappings": ";AAOA,SAAS,UAAU;AACnB,SAAS,WAAW;AAEb,IAAM,gBAAgB,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,wBAAwB,IAAI;AAAA,EACrC;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,qBAAqB,IAAI;AAAA,EAClC;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,kBAAkB,IAAI,cAAc,GAAG;AAAA,EAChD,UAAU;AAAA,IACN,MAAM;AAAA,MACF,UAAU,sBAAsB;AAAA,MAChC,OAAO,mBAAmB;AAAA,IAC9B;AAAA,EACJ;AAAA,EACA,iBAAiB,EAAE,MAAM,WAAW;AACxC,CAAC;;;AC3BD,SAAS,MAAAA,WAAU;AAiBP;AATL,IAAM,QAAwB,CAAC,UAAsB;AACxD,QAAM;AAAA,IACF;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACP,IAAI;AACJ,SAAQ;AAAA,IAAC;AAAA;AAAA,MACJ,GAAG;AAAA,MACJ;AAAA,MACA,UAAU,CAAC,UAAU;AACjB,mBAAW,KAAK;AAChB,wBAAgB,MAAM,cAAc,KAAK;AAAA,MAC7C;AAAA,MACA,WAAWA,IAAG,gBAAgB,EAAE,MAAM,YAAY,CAAC,GAAG,SAAS;AAAA;AAAA,EACnE;AACJ;;;AC3BA,SAAS,MAAAC,WAAU;AACnB,SAAS,OAAAC,YAAW;AAEb,IAAM,cAAcA,KAAID;AAAA,EAC3B;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,kBAAkBC,KAAI,
|
|
4
|
+
"sourcesContent": ["/**\n * @author WMXPY\n * @package Input\n * @namespace Shared\n * @description Style\n */\n\nimport { cn } from \"@gridui/style-utils\";\nimport { cva } from \"class-variance-authority\";\n\nexport const controlStyles = cva(cn(\n \"gridui-control\",\n \"gridui-u-min-w-0 gridui-u-w-full gridui-u-box-border gridui-u-border gridui-u-border-solid gridui-u-border-border gridui-u-rounded-control gridui-u-text-foreground gridui-u-bg-background gridui-u-type-input\",\n \"gridui-u-disabled:opacity-50 gridui-u-disabled:cursor-not-allowed\",\n \"gridui-u-aria-invalid:border-danger-foreground\",\n));\n\nexport const controlStandardStyles = cva(cn(\n \"gridui-control-standard\",\n \"gridui-u-p-gridui-8 gridui-u-min-h-gridui-control\",\n));\n\nexport const controlSmallStyles = cva(cn(\n \"gridui-control-small\",\n \"gridui-u-py-gridui-4 gridui-u-px-gridui-8 gridui-u-min-h-gridui-control-small\",\n));\n\nexport const controlVariants = cva(controlStyles(), {\n variants: {\n size: {\n standard: controlStandardStyles(),\n small: controlSmallStyles(),\n },\n },\n defaultVariants: { size: \"standard\" },\n});\n", "/**\n * @author WMXPY\n * @package Input\n * @namespace Input\n * @description Input\n */\n\nimport { controlVariants } from \"../shared/style\";\nimport { cn } from \"@gridui/style-utils\";\nimport type { ComponentPropsWithRef, FC } from \"react\";\n\nexport type InputProps = ComponentPropsWithRef<\"input\"> & {\n readonly controlSize?: \"standard\" | \"small\";\n readonly onValueChange?: (value: string) => void;\n};\n\nexport const Input: FC<InputProps> = (props: InputProps) => {\n const {\n size,\n controlSize = \"standard\",\n className,\n onChange,\n onValueChange,\n ...restProps\n } = props;\n return (<input\n {...restProps}\n size={size}\n onChange={(event) => {\n onChange?.(event);\n onValueChange?.(event.currentTarget.value);\n }}\n className={cn(controlVariants({ size: controlSize }), className)}\n />);\n};\n", "/**\n * @author WMXPY\n * @package Input\n * @namespace Label\n * @description Style\n */\n\nimport { cn } from \"@gridui/style-utils\";\nimport { cva } from \"class-variance-authority\";\n\nexport const labelStyles = cva(cn(\n \"gridui-label\",\n \"gridui-u-inline-flex gridui-u-items-center gridui-u-gap-gridui-4 gridui-u-type-ui\",\n));\n\nexport const labelIconStyles = cva(\"gridui-u-inline-flex gridui-u-shrink-0 gridui-u-text-foreground gridui-u-[&>svg]:size-gridui-16\");\n", "/**\n * @author WMXPY\n * @package Input\n * @namespace Label\n * @description Label\n */\n\nimport { labelIconStyles, labelStyles } from \"./style\";\nimport { cn } from \"@gridui/style-utils\";\nimport type { ComponentPropsWithRef, FC, ReactNode } from \"react\";\n\nexport type LabelProps = ComponentPropsWithRef<\"label\"> & {\n readonly icon?: ReactNode;\n};\n\nexport const Label: FC<LabelProps> = (props: LabelProps) => {\n const { icon, children, className, ...restProps } = props;\n return (<label\n {...restProps}\n className={cn(labelStyles(), className)}\n >\n {icon && (<span\n aria-hidden=\"true\"\n className={labelIconStyles()}\n >\n {icon}\n </span>)}\n {children}\n </label>);\n};\n", "/**\n * @author WMXPY\n * @package Input\n * @namespace Textarea\n * @description Use Textarea Autosize\n */\n\nimport { type RefObject, useCallback, useEffect, useLayoutEffect } from \"react\";\n\nexport const useTextareaAutosize = (ref: RefObject<HTMLTextAreaElement | null>, enabled: boolean, maxHeight?: number) => {\n if (maxHeight !== undefined && (!Number.isFinite(maxHeight) || maxHeight <= 0)) {\n throw new Error(\"Textarea maxHeight must be finite and positive.\");\n }\n const resize = useCallback(() => {\n const element = ref.current;\n if (!enabled || !element || element.clientWidth === 0) {\n return;\n }\n const style = getComputedStyle(element);\n const borders = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);\n const padding = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);\n element.style.height = \"auto\";\n const contentHeight = element.scrollHeight;\n const height = style.boxSizing === \"border-box\" ? contentHeight + borders : contentHeight - padding;\n const boundedHeight = Math.min(height, maxHeight ?? Number.POSITIVE_INFINITY);\n element.style.height = `${boundedHeight}px`;\n element.style.overflowY = height > boundedHeight ? \"auto\" : \"hidden\";\n }, [\n ref,\n enabled,\n maxHeight,\n ]);\n useLayoutEffect(() => {\n const element = ref.current;\n if (!enabled || !element) {\n return;\n }\n const originalHeight = element.style.height;\n const originalOverflow = element.style.overflowY;\n return () => {\n element.style.height = originalHeight;\n element.style.overflowY = originalOverflow;\n };\n }, [\n ref,\n enabled,\n ]);\n useLayoutEffect(() => {\n resize();\n });\n useEffect(() => {\n const element = ref.current;\n if (!enabled || !element) {\n return;\n }\n let frame = 0;\n let width = element.clientWidth;\n const schedule = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(resize);\n };\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(() => {\n if (element.clientWidth !== width) {\n width = element.clientWidth;\n schedule();\n }\n });\n observer?.observe(element);\n // A capture listener follows the live form owner, including DOM reassociation.\n const reset = (event: Event): void => {\n if (event.target === element.form) {\n schedule();\n }\n };\n const document = element.ownerDocument;\n document.addEventListener(\"reset\", reset, true);\n return () => {\n cancelAnimationFrame(frame);\n observer?.disconnect();\n document.removeEventListener(\"reset\", reset, true);\n };\n }, [\n ref,\n enabled,\n resize,\n ]);\n return resize;\n};\n", "/**\n * @author WMXPY\n * @package Input\n * @namespace Textarea\n * @description Textarea\n */\n\nimport { controlVariants } from \"../shared/style\";\nimport { useTextareaAutosize } from \"./use-textarea-autosize\";\nimport { cn } from \"@gridui/style-utils\";\nimport { type ComponentPropsWithRef, type FC, useImperativeHandle, useRef } from \"react\";\n\nexport type TextareaProps = ComponentPropsWithRef<\"textarea\"> & {\n readonly autoSize?: boolean;\n readonly maxHeight?: number;\n readonly controlSize?: \"standard\" | \"small\";\n readonly onValueChange?: (value: string) => void;\n};\n\nexport const Textarea: FC<TextareaProps> = (props: TextareaProps) => {\n const {\n controlSize = \"standard\",\n className,\n onChange,\n onInput,\n onValueChange,\n ref,\n autoSize = false,\n maxHeight,\n style,\n ...restProps\n } = props;\n const element = useRef<HTMLTextAreaElement>(null);\n // React attaches the native element before assigning imperative handles.\n useImperativeHandle(ref, () => {\n return element.current!;\n });\n const resize = useTextareaAutosize(element, autoSize, maxHeight);\n return (<textarea\n {...restProps}\n ref={element}\n style={style}\n onInput={(event) => {\n resize();\n onInput?.(event);\n }}\n onChange={(event) => {\n onChange?.(event);\n onValueChange?.(event.currentTarget.value);\n }}\n className={cn(controlVariants({ size: controlSize }), autoSize ? \"gridui-u-resize-none\" : \"gridui-u-resize-y\", className)}\n />);\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["cn", "cn", "cva", "cn", "jsx", "cn", "jsx"]
|
|
7
7
|
}
|
package/dist/styles.css
CHANGED
|
@@ -1,98 +1,87 @@
|
|
|
1
1
|
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
2
|
@layer properties;
|
|
3
3
|
@layer theme, base, components, utilities;
|
|
4
|
-
@layer theme {
|
|
5
|
-
:root, :host {
|
|
6
|
-
--spacing: 0.25rem;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
4
|
@layer utilities {
|
|
10
|
-
.box-border {
|
|
5
|
+
.gridui-u-box-border {
|
|
11
6
|
box-sizing: border-box;
|
|
12
7
|
}
|
|
13
|
-
.
|
|
14
|
-
display: none;
|
|
15
|
-
}
|
|
16
|
-
.inline-flex {
|
|
8
|
+
.gridui-u-inline-flex {
|
|
17
9
|
display: inline-flex;
|
|
18
10
|
}
|
|
19
|
-
.min-h-
|
|
20
|
-
min-height:
|
|
11
|
+
.gridui-u-min-h-gridui-control {
|
|
12
|
+
min-height: var(--gridui-control-min-height);
|
|
21
13
|
}
|
|
22
|
-
.min-h-
|
|
23
|
-
min-height:
|
|
14
|
+
.gridui-u-min-h-gridui-control-small {
|
|
15
|
+
min-height: var(--gridui-control-min-height-small);
|
|
24
16
|
}
|
|
25
|
-
.w-full {
|
|
17
|
+
.gridui-u-w-full {
|
|
26
18
|
width: 100%;
|
|
27
19
|
}
|
|
28
|
-
.min-w-0 {
|
|
20
|
+
.gridui-u-min-w-0 {
|
|
29
21
|
min-width: 0px;
|
|
30
22
|
}
|
|
31
|
-
.shrink-0 {
|
|
23
|
+
.gridui-u-shrink-0 {
|
|
32
24
|
flex-shrink: 0;
|
|
33
25
|
}
|
|
34
|
-
.resize {
|
|
35
|
-
resize: both;
|
|
36
|
-
}
|
|
37
|
-
.resize-none {
|
|
26
|
+
.gridui-u-resize-none {
|
|
38
27
|
resize: none;
|
|
39
28
|
}
|
|
40
|
-
.resize-y {
|
|
29
|
+
.gridui-u-resize-y {
|
|
41
30
|
resize: vertical;
|
|
42
31
|
}
|
|
43
|
-
.items-center {
|
|
32
|
+
.gridui-u-items-center {
|
|
44
33
|
align-items: center;
|
|
45
34
|
}
|
|
46
|
-
.gap-gridui-4 {
|
|
35
|
+
.gridui-u-gap-gridui-4 {
|
|
47
36
|
gap: var(--gridui-space-4);
|
|
48
37
|
}
|
|
49
|
-
.rounded-control {
|
|
38
|
+
.gridui-u-rounded-control {
|
|
50
39
|
border-radius: var(--gridui-control-radius);
|
|
51
40
|
}
|
|
52
|
-
.border {
|
|
41
|
+
.gridui-u-border {
|
|
53
42
|
border-style: var(--tw-border-style);
|
|
54
43
|
border-width: 1px;
|
|
55
44
|
}
|
|
56
|
-
.border-solid {
|
|
45
|
+
.gridui-u-border-solid {
|
|
57
46
|
--tw-border-style: solid;
|
|
58
47
|
border-style: solid;
|
|
59
48
|
}
|
|
60
|
-
.border-border {
|
|
49
|
+
.gridui-u-border-border {
|
|
61
50
|
border-color: var(--gridui-border);
|
|
62
51
|
}
|
|
63
|
-
.bg-background {
|
|
52
|
+
.gridui-u-bg-background {
|
|
64
53
|
background-color: var(--gridui-background);
|
|
65
54
|
}
|
|
66
|
-
.p-gridui-8 {
|
|
55
|
+
.gridui-u-p-gridui-8 {
|
|
67
56
|
padding: var(--gridui-space-8);
|
|
68
57
|
}
|
|
69
|
-
.px-gridui-8 {
|
|
58
|
+
.gridui-u-px-gridui-8 {
|
|
70
59
|
padding-inline: var(--gridui-space-8);
|
|
71
60
|
}
|
|
72
|
-
.py-gridui-4 {
|
|
61
|
+
.gridui-u-py-gridui-4 {
|
|
73
62
|
padding-block: var(--gridui-space-4);
|
|
74
63
|
}
|
|
75
|
-
.type-input {
|
|
64
|
+
.gridui-u-type-input {
|
|
76
65
|
font: var(--gridui-type-input);
|
|
77
66
|
letter-spacing: normal;
|
|
78
67
|
}
|
|
79
|
-
.type-ui {
|
|
68
|
+
.gridui-u-type-ui {
|
|
80
69
|
font: var(--gridui-type-ui);
|
|
81
70
|
letter-spacing: normal;
|
|
82
71
|
}
|
|
83
|
-
.text-foreground {
|
|
72
|
+
.gridui-u-text-foreground {
|
|
84
73
|
color: var(--gridui-foreground);
|
|
85
74
|
}
|
|
86
|
-
.disabled\:cursor-not-allowed:disabled {
|
|
75
|
+
.gridui-u-disabled\:cursor-not-allowed:disabled {
|
|
87
76
|
cursor: not-allowed;
|
|
88
77
|
}
|
|
89
|
-
.disabled\:opacity-50:disabled {
|
|
78
|
+
.gridui-u-disabled\:opacity-50:disabled {
|
|
90
79
|
opacity: 50%;
|
|
91
80
|
}
|
|
92
|
-
.aria-invalid\:border-danger-foreground[aria-invalid="true"] {
|
|
81
|
+
.gridui-u-aria-invalid\:border-danger-foreground[aria-invalid="true"] {
|
|
93
82
|
border-color: var(--gridui-danger-foreground);
|
|
94
83
|
}
|
|
95
|
-
|
|
84
|
+
.gridui-u-\[\&\>svg\]\:size-gridui-16 > svg {
|
|
96
85
|
width: var(--gridui-space-16);
|
|
97
86
|
height: var(--gridui-space-16);
|
|
98
87
|
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Common inputs
|
|
2
|
+
|
|
3
|
+
Import MultiSelect from `@gridui/select`, NumberField from `@gridui/number-field`, Slider from `@gridui/slider`, and TagInput from `@gridui/tag-input`.
|
|
4
|
+
Load the rendered family styles and Theme CSS using the shared installation instructions. These
|
|
5
|
+
controls use native form elements and Radix primitives; no additional headless
|
|
6
|
+
component library or animation runtime is required.
|
|
7
|
+
|
|
8
|
+
Use a visible associated label or `aria-label` for each control. Localize all
|
|
9
|
+
required labels, including slider thumb names and tag removal actions. Forward
|
|
10
|
+
`aria-describedby` and `aria-invalid` to connect application-owned help and
|
|
11
|
+
validation messages. Standard controls have a 44px minimum target; Slider keeps
|
|
12
|
+
a 44px interaction region around its track and thumbs.
|
|
13
|
+
|
|
14
|
+
## MultiSelect
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
<MultiSelect
|
|
18
|
+
aria-label="Project teams"
|
|
19
|
+
name="teams"
|
|
20
|
+
defaultValue={["design"]}
|
|
21
|
+
options={[
|
|
22
|
+
{ value: "design", label: "Design" },
|
|
23
|
+
{ value: "engineering", label: "Engineering" },
|
|
24
|
+
]}
|
|
25
|
+
placeholder="Choose teams"
|
|
26
|
+
searchLabel="Filter teams"
|
|
27
|
+
emptyLabel="No matching teams"
|
|
28
|
+
clearLabel="Clear teams"
|
|
29
|
+
/>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
MultiSelect uses a Radix Popover and a native search input with an
|
|
33
|
+
active-descendant listbox. `options` accepts the same unique values, groups,
|
|
34
|
+
labels, descriptions, icons, and disabled options as Select/Combobox. Filtering
|
|
35
|
+
uses their normalized, case-insensitive label and description search. Search
|
|
36
|
+
is local; callers own remote loading and option creation.
|
|
37
|
+
|
|
38
|
+
Use `value: readonly string[]` with `onValueChange`, or `defaultValue` for local
|
|
39
|
+
state. Values are deduplicated without changing their order. The closed trigger
|
|
40
|
+
shows selected labels and wraps long content. Unknown selected values remain in
|
|
41
|
+
form submission and display their value until matching options arrive.
|
|
42
|
+
|
|
43
|
+
Enter, Space, or arrow keys open the popup and focus search. Up/Down navigate
|
|
44
|
+
results; Enter or pointer activation toggles an enabled option without closing.
|
|
45
|
+
Disabled options remain discoverable. When options change, a missing active item
|
|
46
|
+
falls back to the first current result. Read-only and disabled transitions close
|
|
47
|
+
the popup and discard the search; enabling it again leaves it closed.
|
|
48
|
+
Native text editing and IME composition
|
|
49
|
+
remain available. Escape closes and restores trigger focus; outside dismissal
|
|
50
|
+
preserves the outside target's focus. `portalContainer` supports placement
|
|
51
|
+
within an enclosing overlay. `dir` also applies to the portalled content.
|
|
52
|
+
|
|
53
|
+
`name`, `form`, `required`, and `disabled` configure a hidden native multiple
|
|
54
|
+
select. Read its values with `FormData.getAll(name)`. A supplied `clearLabel`
|
|
55
|
+
adds a separate clear action, disabled when required or empty. `readOnly`
|
|
56
|
+
prevents editing while retaining submitted values. Availability is checked at
|
|
57
|
+
activation time, including disabled fieldsets and already-open portals.
|
|
58
|
+
|
|
59
|
+
## NumberField
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<NumberField
|
|
63
|
+
aria-label="Quantity"
|
|
64
|
+
name="quantity"
|
|
65
|
+
defaultValue={1.5}
|
|
66
|
+
min={0}
|
|
67
|
+
max={10}
|
|
68
|
+
step={0.25}
|
|
69
|
+
incrementLabel="Increase quantity"
|
|
70
|
+
decrementLabel="Decrease quantity"
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
NumberField wraps a native `input[type=number]` with decrement/increment buttons.
|
|
75
|
+
Its input ref, native attributes, required state, decimal stepping, constraints,
|
|
76
|
+
form association, and keyboard editing remain native. Use `controlSize="small"`
|
|
77
|
+
for the existing compact control size. `dir` applies to the entire control.
|
|
78
|
+
|
|
79
|
+
`value` / `defaultValue` are `number | null`; null means empty. `onValueChange`
|
|
80
|
+
receives numbers or null for both typing and step buttons. Native `onChange`
|
|
81
|
+
receives input changes; buttons do not synthesize input events. A controlled
|
|
82
|
+
consumer can reject a requested value. Buttons return focus to the input and
|
|
83
|
+
stop at bounds. Typed out-of-range values remain visible for native/application
|
|
84
|
+
validation instead of being silently clamped. Bounds and values must be finite,
|
|
85
|
+
`min <= max`, and `step` must be positive and finite.
|
|
86
|
+
|
|
87
|
+
## Slider
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<Slider
|
|
91
|
+
name="hours"
|
|
92
|
+
min={0}
|
|
93
|
+
max={24}
|
|
94
|
+
defaultValue={[9, 17]}
|
|
95
|
+
minStepsBetweenThumbs={1}
|
|
96
|
+
thumbLabels={["Start hour", "End hour"]}
|
|
97
|
+
getValueText={(value) => `${value}:00`}
|
|
98
|
+
/>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Slider wraps Radix Slider. One value renders one thumb; an ordered array renders
|
|
102
|
+
a range. Supply a unique meaningful `thumbLabels` entry per thumb. Optional
|
|
103
|
+
`getValueText(value, index)` announces units and updates with local or controlled
|
|
104
|
+
values. Use `value` / `onValueChange` for controlled state and `onValueCommit` for
|
|
105
|
+
work that should happen after pointer or keyboard interaction.
|
|
106
|
+
|
|
107
|
+
Radix owns pointer capture, arrow/Home/End/Page keys, RTL direction, vertical
|
|
108
|
+
orientation, and thumb separation. GridUI mirrors named form values during SSR. A range named `hours` submits
|
|
109
|
+
repeated `hours[]` fields; a single thumb submits `hours`. Set
|
|
110
|
+
`orientation="vertical"` and optionally override height through `className`.
|
|
111
|
+
The default vertical height is 10rem. Values must be sorted, finite, within the
|
|
112
|
+
finite `min < max` range, and match the label count. Step must be positive.
|
|
113
|
+
Direct manipulation does not add motion that lags behind the pointer.
|
|
114
|
+
|
|
115
|
+
## TagInput
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
<TagInput
|
|
119
|
+
aria-label="Project tags"
|
|
120
|
+
listLabel="Project tag list"
|
|
121
|
+
removeLabel={(tag) => `Remove tag ${tag}`}
|
|
122
|
+
name="tags"
|
|
123
|
+
defaultValue={["planning"]}
|
|
124
|
+
maxTags={8}
|
|
125
|
+
/>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
TagInput is a native text input and a list of removable values. Use `value` /
|
|
129
|
+
`onValueChange` or `defaultValue`. Enter or comma commits a nonempty draft;
|
|
130
|
+
comma/newline-delimited paste replaces the selected draft and commits multiple
|
|
131
|
+
values. IME composition never commits on Enter. Values are trimmed, empty values
|
|
132
|
+
ignored, and duplicates matched case-sensitively. `maxTags` limits new entries;
|
|
133
|
+
`validateTag` optionally accepts each new value. Existing controlled values are
|
|
134
|
+
not rewritten. `onReject` reports `{ value, reason }`, with reason `duplicate`,
|
|
135
|
+
`invalid`, or `limit`. Invalid/limited drafts remain editable. Applications own
|
|
136
|
+
visible validation feedback and live announcements.
|
|
137
|
+
|
|
138
|
+
Backspace on an empty draft focuses the last removal button without deleting.
|
|
139
|
+
Backspace/Delete or activation on that button removes its value and returns
|
|
140
|
+
focus to the input. Long tags wrap. `readOnly` and `disabled` prevent changes.
|
|
141
|
+
The visible field border reflects `aria-invalid`; disabled inputs also dim the
|
|
142
|
+
containing tag field, including when disabled through a fieldset.
|
|
143
|
+
A hidden multiple select owns `name`, `form`, `required`, and submission;
|
|
144
|
+
`FormData.getAll(name)` returns committed tags, excluding the draft. Required
|
|
145
|
+
validation focuses the visible input. Supply an application error message with
|
|
146
|
+
`aria-describedby`/`aria-invalid` when needed.
|
|
147
|
+
|
|
148
|
+
## Forms, reset, and rendering
|
|
149
|
+
|
|
150
|
+
Uncontrolled values render during SSR and restore their initial values on
|
|
151
|
+
native form reset. Cancelled reset events leave values unchanged. MultiSelect
|
|
152
|
+
and TagInput also ask controlled consumers to restore their initial selection
|
|
153
|
+
through `onValueChange`; consumers can reject the request. Controlled NumberField
|
|
154
|
+
and Slider retain the consumer's value, so reset application state explicitly
|
|
155
|
+
when desired. Reset clears an uncontrolled TagInput draft and closes MultiSelect.
|
|
156
|
+
Disabled controls do not submit; readonly controls do. Fieldset disabling is
|
|
157
|
+
checked before committing changes. Keep initial server/client values identical.
|
|
158
|
+
|
|
159
|
+
Storybook includes empty, selected/range, disabled, readonly where supported,
|
|
160
|
+
RTL, and controlled examples. Package tests cover SSR/native values and input
|
|
161
|
+
validation; browser tests cover actual keyboard/pointer interaction, IME,
|
|
162
|
+
submission, reset cancellation, limits, viewport sizes, themes, and reduced motion.
|
|
163
|
+
|
|
164
|
+
The API and visual review reference shadcn's
|
|
165
|
+
[Radix Slider](https://ui.shadcn.com/docs/components/radix/slider), while retaining
|
|
166
|
+
GridUI's geometry, theme tokens, localization, and responsibility boundaries.
|
package/docs/textarea.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Textarea
|
|
2
|
+
|
|
3
|
+
Import `Textarea` and `TextareaProps` from `@gridui/input`. It is a native textarea with the same `controlSize`
|
|
4
|
+
(`standard` or `small`), invalid, disabled, and focus styling as `Input`.
|
|
5
|
+
|
|
6
|
+
All native textarea props and refs are forwarded, including `value`,
|
|
7
|
+
`defaultValue`, `name`, `rows`, `required`, `maxLength`, and ARIA attributes.
|
|
8
|
+
`onChange` runs before the optional `onValueChange(string)` notification, matching
|
|
9
|
+
Input. Use `value` plus `onValueChange` for a controlled field. The default resize
|
|
10
|
+
behavior is vertical; override it with `className` (for example `resize-none`).
|
|
11
|
+
|
|
12
|
+
The caller supplies the accessible label and owns validation, persistence, and
|
|
13
|
+
submission. Enter and IME input retain native multiline behavior; the library
|
|
14
|
+
does not add a chat-specific send shortcut or automatic submission.
|
|
15
|
+
|
|
16
|
+
## Optional automatic height
|
|
17
|
+
|
|
18
|
+
Set `autoSize` to grow and shrink with the content. Native `rows` and CSS minimum
|
|
19
|
+
height establish the baseline. Optional `maxHeight` is a positive finite CSS
|
|
20
|
+
height in pixels; use a value at least as large as the configured CSS minimum
|
|
21
|
+
height. Beyond the cap the field scrolls internally. The height and overflow are
|
|
22
|
+
owned by autosizing while enabled; avoid competing `height` / `overflowY` styles.
|
|
23
|
+
Without autosizing the existing vertical resize behavior remains unchanged.
|
|
24
|
+
|
|
25
|
+
Sizing responds to native input, controlled value renders, element-width changes
|
|
26
|
+
(including becoming visible), and native form reset. Width observation requires
|
|
27
|
+
`ResizeObserver`. Disabling autosizing restores the original inline height and
|
|
28
|
+
overflow. Native refs, onInput, onChange and onValueChange remain available.
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gridui/input",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "GridUI React input",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
+
"docs",
|
|
9
10
|
"README.md"
|
|
10
11
|
],
|
|
11
12
|
"sideEffects": [
|
|
@@ -26,8 +27,8 @@
|
|
|
26
27
|
"react-dom": "^19.2.0"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@gridui/style-utils": "^2.
|
|
30
|
-
"@gridui/theme": "^2.
|
|
30
|
+
"@gridui/style-utils": "^2.2.0",
|
|
31
|
+
"@gridui/theme": "^2.5.0",
|
|
31
32
|
"class-variance-authority": "0.7.1"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|