@gridui/input 1.1.1 → 1.1.2

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 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](../../docs/component-ownership.md) and [release history](.changeset/history/).
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](../dropdown-menu/docs/context-menu.md) for controlled viewport-point menus.
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-11"
12
+ "gridui-u-p-gridui-8 gridui-u-min-h-11"
13
13
  ));
14
14
  var controlSmallStyles = cva(cn(
15
15
  "gridui-control-small",
16
- "py-gridui-4 px-gridui-8 min-h-9"
16
+ "gridui-u-py-gridui-4 gridui-u-px-gridui-8 gridui-u-min-h-9"
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-11\",\n));\n\nexport const controlSmallStyles = cva(cn(\n \"gridui-control-small\",\n \"py-gridui-4 px-gridui-8 min-h-9\",\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 \"inline-flex items-center gap-gridui-4 type-ui\",\n));\n\nexport const labelIconStyles = cva(\"inline-flex shrink-0 text-foreground [&>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 ? \"resize-none\" : \"resize-y\", className)}\n />);\n};\n"],
5
- "mappings": "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",
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-11\",\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-9\",\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": ";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,iGAAiG;;;ACPpI,SAAS,MAAAC,WAAU;AASP,SAIM,OAAAC,MAJN;AAFL,IAAM,QAAwB,CAAC,UAAsB;AACxD,QAAM,EAAE,MAAM,UAAU,WAAW,GAAG,UAAU,IAAI;AACpD,SAAQ;AAAA,IAAC;AAAA;AAAA,MACJ,GAAG;AAAA,MACJ,WAAWD,IAAG,YAAY,GAAG,SAAS;AAAA,MAErC;AAAA,gBAAS,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACP,eAAY;AAAA,YACZ,WAAW,gBAAgB;AAAA,YAE1B;AAAA;AAAA,QACL;AAAA,QACC;AAAA;AAAA;AAAA,EACL;AACJ;;;ACtBA,SAAyB,aAAa,WAAW,uBAAuB;AAEjE,IAAM,sBAAsB,CAAC,KAA4C,SAAkB,cAAuB;AACrH,MAAI,cAAc,WAAc,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,IAAI;AAC5E,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACrE;AACA,QAAM,SAAS,YAAY,MAAM;AAC7B,UAAM,UAAU,IAAI;AACpB,QAAI,CAAC,WAAW,CAAC,WAAW,QAAQ,gBAAgB,GAAG;AACnD;AAAA,IACJ;AACA,UAAM,QAAQ,iBAAiB,OAAO;AACtC,UAAM,UAAU,WAAW,MAAM,cAAc,IAAI,WAAW,MAAM,iBAAiB;AACrF,UAAM,UAAU,WAAW,MAAM,UAAU,IAAI,WAAW,MAAM,aAAa;AAC7E,YAAQ,MAAM,SAAS;AACvB,UAAM,gBAAgB,QAAQ;AAC9B,UAAM,SAAS,MAAM,cAAc,eAAe,gBAAgB,UAAU,gBAAgB;AAC5F,UAAM,gBAAgB,KAAK,IAAI,QAAQ,aAAa,OAAO,iBAAiB;AAC5E,YAAQ,MAAM,SAAS,GAAG,aAAa;AACvC,YAAQ,MAAM,YAAY,SAAS,gBAAgB,SAAS;AAAA,EAChE,GAAG;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACD,kBAAgB,MAAM;AAClB,UAAM,UAAU,IAAI;AACpB,QAAI,CAAC,WAAW,CAAC,SAAS;AACtB;AAAA,IACJ;AACA,UAAM,iBAAiB,QAAQ,MAAM;AACrC,UAAM,mBAAmB,QAAQ,MAAM;AACvC,WAAO,MAAM;AACT,cAAQ,MAAM,SAAS;AACvB,cAAQ,MAAM,YAAY;AAAA,IAC9B;AAAA,EACJ,GAAG;AAAA,IACC;AAAA,IACA;AAAA,EACJ,CAAC;AACD,kBAAgB,MAAM;AAClB,WAAO;AAAA,EACX,CAAC;AACD,YAAU,MAAM;AACZ,UAAM,UAAU,IAAI;AACpB,QAAI,CAAC,WAAW,CAAC,SAAS;AACtB;AAAA,IACJ;AACA,QAAI,QAAQ;AACZ,QAAI,QAAQ,QAAQ;AACpB,UAAM,WAAW,MAAM;AACnB,2BAAqB,KAAK;AAC1B,cAAQ,sBAAsB,MAAM;AAAA,IACxC;AACA,UAAM,WAAW,OAAO,mBAAmB,cAAc,SAAY,IAAI,eAAe,MAAM;AAC1F,UAAI,QAAQ,gBAAgB,OAAO;AAC/B,gBAAQ,QAAQ;AAChB,iBAAS;AAAA,MACb;AAAA,IACJ,CAAC;AACD,cAAU,QAAQ,OAAO;AAEzB,UAAM,QAAQ,CAAC,UAAuB;AAClC,UAAI,MAAM,WAAW,QAAQ,MAAM;AAC/B,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,UAAM,WAAW,QAAQ;AACzB,aAAS,iBAAiB,SAAS,OAAO,IAAI;AAC9C,WAAO,MAAM;AACT,2BAAqB,KAAK;AAC1B,gBAAU,WAAW;AACrB,eAAS,oBAAoB,SAAS,OAAO,IAAI;AAAA,IACrD;AAAA,EACJ,GAAG;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACD,SAAO;AACX;;;AC9EA,SAAS,MAAAC,WAAU;AACnB,SAA8C,qBAAqB,cAAc;AA4BrE,gBAAAC,YAAA;AAnBL,IAAM,WAA8B,CAAC,UAAyB;AACjE,QAAM;AAAA,IACF,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACP,IAAI;AACJ,QAAM,UAAU,OAA4B,IAAI;AAEhD,sBAAoB,KAAK,MAAM;AAC3B,WAAO,QAAQ;AAAA,EACnB,CAAC;AACD,QAAM,SAAS,oBAAoB,SAAS,UAAU,SAAS;AAC/D,SAAQ,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACJ,GAAG;AAAA,MACJ,KAAK;AAAA,MACL;AAAA,MACA,SAAS,CAAC,UAAU;AAChB,eAAO;AACP,kBAAU,KAAK;AAAA,MACnB;AAAA,MACA,UAAU,CAAC,UAAU;AACjB,mBAAW,KAAK;AAChB,wBAAgB,MAAM,cAAc,KAAK;AAAA,MAC7C;AAAA,MACA,WAAWD,IAAG,gBAAgB,EAAE,MAAM,YAAY,CAAC,GAAG,WAAW,yBAAyB,qBAAqB,SAAS;AAAA;AAAA,EAC5H;AACJ;",
6
6
  "names": ["cn", "cn", "cva", "cn", "jsx", "cn", "jsx"]
7
7
  }
package/dist/styles.css CHANGED
@@ -7,92 +7,86 @@
7
7
  }
8
8
  }
9
9
  @layer utilities {
10
- .box-border {
10
+ .gridui-u-box-border {
11
11
  box-sizing: border-box;
12
12
  }
13
- .hidden {
14
- display: none;
15
- }
16
- .inline-flex {
13
+ .gridui-u-inline-flex {
17
14
  display: inline-flex;
18
15
  }
19
- .min-h-9 {
16
+ .gridui-u-min-h-9 {
20
17
  min-height: calc(var(--spacing) * 9);
21
18
  }
22
- .min-h-11 {
19
+ .gridui-u-min-h-11 {
23
20
  min-height: calc(var(--spacing) * 11);
24
21
  }
25
- .w-full {
22
+ .gridui-u-w-full {
26
23
  width: 100%;
27
24
  }
28
- .min-w-0 {
25
+ .gridui-u-min-w-0 {
29
26
  min-width: 0px;
30
27
  }
31
- .shrink-0 {
28
+ .gridui-u-shrink-0 {
32
29
  flex-shrink: 0;
33
30
  }
34
- .resize {
35
- resize: both;
36
- }
37
- .resize-none {
31
+ .gridui-u-resize-none {
38
32
  resize: none;
39
33
  }
40
- .resize-y {
34
+ .gridui-u-resize-y {
41
35
  resize: vertical;
42
36
  }
43
- .items-center {
37
+ .gridui-u-items-center {
44
38
  align-items: center;
45
39
  }
46
- .gap-gridui-4 {
40
+ .gridui-u-gap-gridui-4 {
47
41
  gap: var(--gridui-space-4);
48
42
  }
49
- .rounded-control {
43
+ .gridui-u-rounded-control {
50
44
  border-radius: var(--gridui-control-radius);
51
45
  }
52
- .border {
46
+ .gridui-u-border {
53
47
  border-style: var(--tw-border-style);
54
48
  border-width: 1px;
55
49
  }
56
- .border-solid {
50
+ .gridui-u-border-solid {
57
51
  --tw-border-style: solid;
58
52
  border-style: solid;
59
53
  }
60
- .border-border {
54
+ .gridui-u-border-border {
61
55
  border-color: var(--gridui-border);
62
56
  }
63
- .bg-background {
57
+ .gridui-u-bg-background {
64
58
  background-color: var(--gridui-background);
65
59
  }
66
- .p-gridui-8 {
60
+ .gridui-u-p-gridui-8 {
67
61
  padding: var(--gridui-space-8);
68
62
  }
69
- .px-gridui-8 {
63
+ .gridui-u-px-gridui-8 {
70
64
  padding-inline: var(--gridui-space-8);
71
65
  }
72
- .py-gridui-4 {
66
+ .gridui-u-py-gridui-4 {
73
67
  padding-block: var(--gridui-space-4);
74
68
  }
75
- .type-input {
69
+ .gridui-u-type-input {
76
70
  font: var(--gridui-type-input);
77
71
  letter-spacing: normal;
78
72
  }
79
- .type-ui {
73
+ .gridui-u-type-ui {
80
74
  font: var(--gridui-type-ui);
81
75
  letter-spacing: normal;
82
76
  }
83
- .text-foreground {
77
+ .gridui-u-text-foreground {
84
78
  color: var(--gridui-foreground);
85
79
  }
86
- .disabled\:cursor-not-allowed:disabled {
80
+ .gridui-u-disabled\:cursor-not-allowed:disabled {
87
81
  cursor: not-allowed;
88
82
  }
89
- .disabled\:opacity-50:disabled {
83
+ .gridui-u-disabled\:opacity-50:disabled {
90
84
  opacity: 50%;
91
85
  }
92
- .aria-invalid\:border-danger-foreground[aria-invalid="true"] {
86
+ .gridui-u-aria-invalid\:border-danger-foreground[aria-invalid="true"] {
93
87
  border-color: var(--gridui-danger-foreground);
94
88
  }
95
- .\[\&\>svg\]\:size-gridui-16 > svg {
89
+ .gridui-u-\[\&\>svg\]\:size-gridui-16 > svg {
96
90
  width: var(--gridui-space-16);
97
91
  height: var(--gridui-space-16);
98
92
  }
@@ -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.
@@ -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.1",
3
+ "version": "1.1.2",
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.1.3",
30
- "@gridui/theme": "^2.2.1",
30
+ "@gridui/style-utils": "^2.1.4",
31
+ "@gridui/theme": "^2.3.2",
31
32
  "class-variance-authority": "0.7.1"
32
33
  },
33
34
  "devDependencies": {