@design-edito/tools 0.4.22 → 0.4.23

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.
Files changed (61) hide show
  1. package/agnostic/colors/index.d.ts +4 -4
  2. package/agnostic/colors/index.js +4 -4
  3. package/agnostic/css/index.d.ts +2 -2
  4. package/agnostic/css/index.js +2 -2
  5. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +11 -11
  6. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +11 -11
  7. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +2 -2
  8. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +2 -2
  9. package/agnostic/html/index.d.ts +3 -3
  10. package/agnostic/html/index.js +3 -3
  11. package/agnostic/misc/index.d.ts +5 -5
  12. package/agnostic/misc/index.js +5 -5
  13. package/agnostic/numbers/index.d.ts +1 -1
  14. package/agnostic/numbers/index.js +1 -1
  15. package/agnostic/objects/index.d.ts +3 -3
  16. package/agnostic/objects/index.js +3 -3
  17. package/agnostic/sanitization/index.d.ts +1 -1
  18. package/agnostic/sanitization/index.js +1 -1
  19. package/agnostic/strings/index.d.ts +2 -2
  20. package/agnostic/strings/index.js +2 -2
  21. package/agnostic/time/index.d.ts +2 -2
  22. package/agnostic/time/index.js +2 -2
  23. package/components/Button/index.d.ts +2 -7
  24. package/components/Button/index.js +2 -2
  25. package/components/Iframe/index.d.ts +16 -34
  26. package/components/Iframe/index.js +67 -27
  27. package/components/Input/index.controlled.d.ts +42 -0
  28. package/components/Input/index.controlled.js +53 -0
  29. package/components/Input/index.d.ts +21 -17
  30. package/components/Input/index.js +27 -18
  31. package/components/Select/index.controlled.d.ts +43 -0
  32. package/components/Select/index.controlled.js +53 -0
  33. package/components/Select/index.d.ts +22 -18
  34. package/components/Select/index.js +27 -18
  35. package/components/Textarea/index.controlled.d.ts +50 -0
  36. package/components/Textarea/index.controlled.js +74 -0
  37. package/components/Textarea/index.d.ts +22 -17
  38. package/components/Textarea/index.js +27 -18
  39. package/components/index.d.ts +1 -1
  40. package/components/index.js +1 -1
  41. package/node/@aws-s3/storage/directory/index.d.ts +1 -1
  42. package/node/@aws-s3/storage/directory/index.js +1 -1
  43. package/node/@aws-s3/storage/file/index.d.ts +1 -1
  44. package/node/@aws-s3/storage/file/index.js +1 -1
  45. package/node/@google-cloud/storage/directory/index.d.ts +1 -1
  46. package/node/@google-cloud/storage/directory/index.js +1 -1
  47. package/node/@google-cloud/storage/file/index.d.ts +2 -2
  48. package/node/@google-cloud/storage/file/index.js +2 -2
  49. package/node/@google-cloud/storage/index.d.ts +1 -1
  50. package/node/@google-cloud/storage/index.js +1 -1
  51. package/node/cloud-storage/operations/index.d.ts +1 -1
  52. package/node/cloud-storage/operations/index.js +1 -1
  53. package/node/images/transform/operations/index.d.ts +1 -1
  54. package/node/images/transform/operations/index.js +1 -1
  55. package/node/index.d.ts +1 -1
  56. package/node/index.js +1 -1
  57. package/node/process/index.d.ts +1 -1
  58. package/node/process/index.js +1 -1
  59. package/node/sftp/file/index.d.ts +1 -1
  60. package/node/sftp/file/index.js +1 -1
  61. package/package.json +1 -1
@@ -0,0 +1,42 @@
1
+ import { type FunctionComponent, type InputHTMLAttributes, type ReactNode } from 'react';
2
+ import type { WithClassName } from '../utils/types.js';
3
+ /**
4
+ * Props for the {@link Input} component.
5
+ *
6
+ * Extends all native {@link InputHTMLAttributes} and {@link WithClassName}
7
+ * with optional label and error content.
8
+ *
9
+ * @property label - Content rendered as an associated `<label>`. When omitted, no label is rendered.
10
+ * @property error - Content rendered as an error message below the input. When omitted, no error is rendered.
11
+ * @property className - Additional class name(s) applied to the root element.
12
+ */
13
+ export type Props = InputHTMLAttributes<HTMLInputElement> & WithClassName<{
14
+ label?: ReactNode;
15
+ error?: ReactNode;
16
+ }>;
17
+ /**
18
+ * Controlled input field component.
19
+ *
20
+ * Renders a native `<input>` element with optional label and error feedback.
21
+ * The input value is entirely driven by the provided `value` prop and all
22
+ * native input attributes are forwarded to the underlying element.
23
+ *
24
+ * A stable auto-generated `id` is created on mount and used to associate the
25
+ * rendered label through the `htmlFor` attribute.
26
+ *
27
+ * ### CSS elements
28
+ * - `label`
29
+ * - `error`
30
+ *
31
+ * @param props - Component properties.
32
+ * @see {@link Props}
33
+ *
34
+ * @returns A labelled controlled input with optional error feedback.
35
+ *
36
+ * @remarks
37
+ * - The component does not manage its own value state.
38
+ * - Consumers are responsible for updating `value` in response to `onChange`.
39
+ * - Additional `className` values are merged with the component public class name
40
+ * and applied to the underlying `<input>` element.
41
+ */
42
+ export declare const ControlledInput: FunctionComponent<Props>;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { clss } from '../../agnostic/css/clss/index.js';
4
+ import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
5
+ import { randomHash } from '../../agnostic/random/uuid/index.js';
6
+ import { input as publicClassName } from '../public-classnames.js';
7
+ import { mergeClassNames } from '../utils/index.js';
8
+ import cssModule from './styles.module.css';
9
+ /**
10
+ * Controlled input field component.
11
+ *
12
+ * Renders a native `<input>` element with optional label and error feedback.
13
+ * The input value is entirely driven by the provided `value` prop and all
14
+ * native input attributes are forwarded to the underlying element.
15
+ *
16
+ * A stable auto-generated `id` is created on mount and used to associate the
17
+ * rendered label through the `htmlFor` attribute.
18
+ *
19
+ * ### CSS elements
20
+ * - `label`
21
+ * - `error`
22
+ *
23
+ * @param props - Component properties.
24
+ * @see {@link Props}
25
+ *
26
+ * @returns A labelled controlled input with optional error feedback.
27
+ *
28
+ * @remarks
29
+ * - The component does not manage its own value state.
30
+ * - Consumers are responsible for updating `value` in response to `onChange`.
31
+ * - Additional `className` values are merged with the component public class name
32
+ * and applied to the underlying `<input>` element.
33
+ */
34
+ export const ControlledInput = ({ label, error, value, className, ...rest }) => {
35
+ const [id] = useState(`_${randomHash(12)}`);
36
+ const c = clss(publicClassName, { cssModule });
37
+ const rootClss = mergeClassNames(c(null), className);
38
+ return _jsxs(_Fragment, { children: [isNotFalsy(label) && _jsx("label", { className: c('label'), htmlFor: id, children: label }), _jsx("input", { ...rest, id: id, className: rootClss, value: value ?? '', onChange: e => {
39
+ rest.onChange?.(e);
40
+ if (value === undefined)
41
+ return;
42
+ if (typeof value === 'number') {
43
+ e.target.value = `${value}`;
44
+ }
45
+ else if (typeof value === 'string') {
46
+ e.target.value = value;
47
+ }
48
+ else {
49
+ // value is readonly string[]
50
+ // not supported for <input>; ignore or normalize if needed
51
+ }
52
+ } }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
53
+ };
@@ -1,33 +1,37 @@
1
- import { type FunctionComponent, type InputHTMLAttributes, type ReactNode } from 'react';
2
- import type { WithClassName } from '../utils/types.js';
1
+ import { type FunctionComponent } from 'react';
2
+ import { type Props as ControlledProps } from './index.controlled.js';
3
3
  /**
4
4
  * Props for the {@link Input} component.
5
5
  *
6
- * Extends all native {@link InputHTMLAttributes} and {@link WithClassName}
7
- * with optional label and error content.
6
+ * Alias of {@link ControlledProps}.
8
7
  *
9
- * @property label - Content rendered as an associated `<label>`. When omitted, no label is rendered.
10
- * @property error - Content rendered as an error message below the input. When omitted, no error is rendered.
11
- * @property className - Additional class name(s) applied to the root element.
8
+ * All native input attributes remain available, including `value`
9
+ * and `onChange`, allowing the component to operate in either
10
+ * controlled or hybrid mode.
12
11
  */
13
- export type Props = InputHTMLAttributes<HTMLInputElement> & WithClassName<{
14
- label?: ReactNode;
15
- error?: ReactNode;
16
- }>;
12
+ export type Props = ControlledProps;
17
13
  /**
18
- * Input field component with optional label and error message.
14
+ * Input field component supporting controlled and hybrid usage.
19
15
  *
20
- * Renders a native `<input>` element inside a wrapper, with a stable
21
- * auto-generated `id` used to associate the label via `htmlFor`.
22
- * All standard input attributes are forwarded to the underlying element.
16
+ * Wraps {@link ControlledInput} and automatically manages the input value
17
+ * when no `value` prop is provided.
23
18
  *
24
19
  * ### CSS elements
25
20
  * - `label`
26
- * - `input`
27
21
  * - `error`
28
22
  *
29
23
  * @param props - Component properties.
30
24
  * @see {@link Props}
31
- * @returns A labelled input wrapper with optional error feedback.
25
+ *
26
+ * @returns A labelled input with optional internal value management.
27
+ *
28
+ * @remarks
29
+ * - When `value` is defined, the component behaves as a controlled component:
30
+ * displayed value updates are entirely driven by the parent component.
31
+ * - When `value` is omitted, the component behaves in hybrid mode:
32
+ * it initializes an internal state from the initial `value` prop and
33
+ * subsequently manages value updates itself.
34
+ * - In hybrid mode, the internal value is updated before forwarding
35
+ * the `onChange` callback.
32
36
  */
33
37
  export declare const Input: FunctionComponent<Props>;
@@ -1,30 +1,39 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
- import { clss } from '../../agnostic/css/clss/index.js';
4
- import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
5
- import { randomHash } from '../../agnostic/random/uuid/index.js';
6
- import { input as publicClassName } from '../public-classnames.js';
7
- import { mergeClassNames } from '../utils/index.js';
8
- import cssModule from './styles.module.css';
3
+ import { ControlledInput } from './index.controlled.js';
9
4
  /**
10
- * Input field component with optional label and error message.
5
+ * Input field component supporting controlled and hybrid usage.
11
6
  *
12
- * Renders a native `<input>` element inside a wrapper, with a stable
13
- * auto-generated `id` used to associate the label via `htmlFor`.
14
- * All standard input attributes are forwarded to the underlying element.
7
+ * Wraps {@link ControlledInput} and automatically manages the input value
8
+ * when no `value` prop is provided.
15
9
  *
16
10
  * ### CSS elements
17
11
  * - `label`
18
- * - `input`
19
12
  * - `error`
20
13
  *
21
14
  * @param props - Component properties.
22
15
  * @see {@link Props}
23
- * @returns A labelled input wrapper with optional error feedback.
16
+ *
17
+ * @returns A labelled input with optional internal value management.
18
+ *
19
+ * @remarks
20
+ * - When `value` is defined, the component behaves as a controlled component:
21
+ * displayed value updates are entirely driven by the parent component.
22
+ * - When `value` is omitted, the component behaves in hybrid mode:
23
+ * it initializes an internal state from the initial `value` prop and
24
+ * subsequently manages value updates itself.
25
+ * - In hybrid mode, the internal value is updated before forwarding
26
+ * the `onChange` callback.
24
27
  */
25
- export const Input = ({ label, error, className, ...rest }) => {
26
- const [id] = useState(`_${randomHash(12)}`);
27
- const c = clss(publicClassName, { cssModule });
28
- const rootClss = mergeClassNames(c(null), className);
29
- return _jsxs(_Fragment, { children: [isNotFalsy(label) && _jsx("label", { className: c('label'), htmlFor: id, children: label }), _jsx("input", { ...rest, className: rootClss, id: id }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
28
+ export const Input = (props) => {
29
+ const { value, defaultValue, onChange, ...rest } = props;
30
+ const isControlled = value !== undefined;
31
+ const [internal, setInternal] = useState(defaultValue ?? '');
32
+ const currentValue = isControlled ? value : internal;
33
+ const handleChange = (e) => {
34
+ if (!isControlled)
35
+ setInternal(e.target.value);
36
+ onChange?.(e);
37
+ };
38
+ return _jsx(ControlledInput, { ...rest, value: currentValue, onChange: handleChange });
30
39
  };
@@ -0,0 +1,43 @@
1
+ import { type FunctionComponent, type SelectHTMLAttributes, type ReactNode, type PropsWithChildren } from 'react';
2
+ import type { WithClassName } from '../utils/types.js';
3
+ /**
4
+ * Props for the {@link ControlledSelect} component.
5
+ *
6
+ * Extends all native {@link SelectHTMLAttributes} and {@link WithClassName}
7
+ * with optional label, error content, and option children.
8
+ *
9
+ * @property label - Content rendered as an associated `<label>`. When omitted, no label is rendered.
10
+ * @property error - Content rendered as an error message below the select. When omitted, no error is rendered.
11
+ * @property className - Additional class name(s) applied to the select element.
12
+ * @property children - `<option>` or `<optgroup>` elements rendered inside the select.
13
+ */
14
+ export type Props = SelectHTMLAttributes<HTMLSelectElement> & PropsWithChildren<WithClassName<{
15
+ label?: ReactNode;
16
+ error?: ReactNode;
17
+ }>>;
18
+ /**
19
+ * Controlled select field component.
20
+ *
21
+ * Renders a native `<select>` element with optional label and error feedback.
22
+ * The selected value is entirely driven by the provided `value` prop and all
23
+ * native select attributes are forwarded to the underlying element.
24
+ *
25
+ * A stable auto-generated `id` is created on mount and used to associate the
26
+ * rendered label through the `htmlFor` attribute.
27
+ *
28
+ * ### CSS elements
29
+ * - `label`
30
+ * - `error`
31
+ *
32
+ * @param props - Component properties.
33
+ * @see {@link Props}
34
+ *
35
+ * @returns A labelled controlled select with optional error feedback.
36
+ *
37
+ * @remarks
38
+ * - The component does not manage its own selection state.
39
+ * - Consumers are responsible for updating `value` in response to `onChange`.
40
+ * - Additional `className` values are merged with the component public class name
41
+ * and applied to the underlying `<select>` element.
42
+ */
43
+ export declare const ControlledSelect: FunctionComponent<Props>;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { clss } from '../../agnostic/css/clss/index.js';
4
+ import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
5
+ import { randomHash } from '../../agnostic/random/uuid/index.js';
6
+ import { select as publicClassName } from '../public-classnames.js';
7
+ import { mergeClassNames } from '../utils/index.js';
8
+ import cssModule from './styles.module.css';
9
+ /**
10
+ * Controlled select field component.
11
+ *
12
+ * Renders a native `<select>` element with optional label and error feedback.
13
+ * The selected value is entirely driven by the provided `value` prop and all
14
+ * native select attributes are forwarded to the underlying element.
15
+ *
16
+ * A stable auto-generated `id` is created on mount and used to associate the
17
+ * rendered label through the `htmlFor` attribute.
18
+ *
19
+ * ### CSS elements
20
+ * - `label`
21
+ * - `error`
22
+ *
23
+ * @param props - Component properties.
24
+ * @see {@link Props}
25
+ *
26
+ * @returns A labelled controlled select with optional error feedback.
27
+ *
28
+ * @remarks
29
+ * - The component does not manage its own selection state.
30
+ * - Consumers are responsible for updating `value` in response to `onChange`.
31
+ * - Additional `className` values are merged with the component public class name
32
+ * and applied to the underlying `<select>` element.
33
+ */
34
+ export const ControlledSelect = ({ label, error, value, className, children, ...rest }) => {
35
+ const [id] = useState(`_${randomHash(12)}`);
36
+ const c = clss(publicClassName, { cssModule });
37
+ const rootClss = mergeClassNames(c(null), className);
38
+ return _jsxs(_Fragment, { children: [isNotFalsy(label) && _jsx("label", { className: c('label'), htmlFor: id, children: label }), _jsx("select", { ...rest, className: rootClss, id: id, value: value, onChange: e => {
39
+ rest.onChange?.(e);
40
+ if (value === undefined)
41
+ return;
42
+ if (typeof value === 'number') {
43
+ e.target.value = `${value}`;
44
+ }
45
+ else if (typeof value === 'string') {
46
+ e.target.value = value;
47
+ }
48
+ else {
49
+ // value is readonly string[]
50
+ // not supported for <select>; ignore or normalize if needed
51
+ }
52
+ }, children: children }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
53
+ };
@@ -1,34 +1,38 @@
1
- import { type FunctionComponent, type SelectHTMLAttributes, type ReactNode, type PropsWithChildren } from 'react';
2
- import type { WithClassName } from '../utils/types.js';
1
+ import { type FunctionComponent } from 'react';
2
+ import { type Props as ControlledProps } from './index.controlled.js';
3
3
  /**
4
4
  * Props for the {@link Select} component.
5
5
  *
6
- * Extends all native {@link SelectHTMLAttributes} and {@link WithClassName}
7
- * with optional label, error content, and option children.
6
+ * Alias of {@link ControlledProps}.
8
7
  *
9
- * @property label - Content rendered as an associated `<label>`. When omitted, no label is rendered.
10
- * @property error - Content rendered as an error message below the select. When omitted, no error is rendered.
11
- * @property className - Additional class name(s) applied to the root element.
12
- * @property children - `<option>` or `<optgroup>` elements rendered inside the select.
8
+ * All native select attributes remain available, including `value`,
9
+ * `defaultValue`, and `onChange`, allowing the component to operate
10
+ * in either controlled or hybrid mode.
13
11
  */
14
- export type Props = SelectHTMLAttributes<HTMLSelectElement> & PropsWithChildren<WithClassName<{
15
- label?: ReactNode;
16
- error?: ReactNode;
17
- }>>;
12
+ export type Props = ControlledProps;
18
13
  /**
19
- * Select field component with optional label and error message.
14
+ * Select field component supporting controlled and hybrid usage.
20
15
  *
21
- * Renders a native `<select>` element inside a wrapper, with a stable
22
- * auto-generated `id` used to associate the label via `htmlFor`.
23
- * All standard select attributes are forwarded to the underlying element.
16
+ * Wraps {@link ControlledSelect} and automatically manages the selected
17
+ * value when no `value` prop is provided.
24
18
  *
25
19
  * ### CSS elements
26
20
  * - `label`
27
- * - `select`
28
21
  * - `error`
29
22
  *
30
23
  * @param props - Component properties.
31
24
  * @see {@link Props}
32
- * @returns A labelled select wrapper with optional error feedback.
25
+ *
26
+ * @returns A labelled select with optional internal selection management.
27
+ *
28
+ * @remarks
29
+ * - In controlled mode (`value` defined), the selected value is fully driven
30
+ * by the parent component and internal state is never updated.
31
+ * - In hybrid mode, internal state is initialized from `defaultValue` and
32
+ * subsequently manages selection updates itself.
33
+ * - In hybrid mode, the internal value is updated before forwarding the
34
+ * `onChange` callback.
35
+ * - `defaultValue` is only used to initialize internal state and is not
36
+ * forwarded to the underlying controlled component.
33
37
  */
34
38
  export declare const Select: FunctionComponent<Props>;
@@ -1,30 +1,39 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
- import { clss } from '../../agnostic/css/clss/index.js';
4
- import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
5
- import { randomHash } from '../../agnostic/random/uuid/index.js';
6
- import { select as publicClassName } from '../public-classnames.js';
7
- import { mergeClassNames } from '../utils/index.js';
8
- import cssModule from './styles.module.css';
3
+ import { ControlledSelect } from './index.controlled.js';
9
4
  /**
10
- * Select field component with optional label and error message.
5
+ * Select field component supporting controlled and hybrid usage.
11
6
  *
12
- * Renders a native `<select>` element inside a wrapper, with a stable
13
- * auto-generated `id` used to associate the label via `htmlFor`.
14
- * All standard select attributes are forwarded to the underlying element.
7
+ * Wraps {@link ControlledSelect} and automatically manages the selected
8
+ * value when no `value` prop is provided.
15
9
  *
16
10
  * ### CSS elements
17
11
  * - `label`
18
- * - `select`
19
12
  * - `error`
20
13
  *
21
14
  * @param props - Component properties.
22
15
  * @see {@link Props}
23
- * @returns A labelled select wrapper with optional error feedback.
16
+ *
17
+ * @returns A labelled select with optional internal selection management.
18
+ *
19
+ * @remarks
20
+ * - In controlled mode (`value` defined), the selected value is fully driven
21
+ * by the parent component and internal state is never updated.
22
+ * - In hybrid mode, internal state is initialized from `defaultValue` and
23
+ * subsequently manages selection updates itself.
24
+ * - In hybrid mode, the internal value is updated before forwarding the
25
+ * `onChange` callback.
26
+ * - `defaultValue` is only used to initialize internal state and is not
27
+ * forwarded to the underlying controlled component.
24
28
  */
25
- export const Select = ({ label, error, className, children, ...rest }) => {
26
- const [id] = useState(`_${randomHash(12)}`);
27
- const c = clss(publicClassName, { cssModule });
28
- const rootClss = mergeClassNames(c(null), className);
29
- return _jsxs(_Fragment, { children: [isNotFalsy(label) && _jsx("label", { className: c('label'), htmlFor: id, children: label }), _jsx("select", { ...rest, className: rootClss, id: id, children: children }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
29
+ export const Select = ({ defaultValue, value, onChange, ...rest }) => {
30
+ const isControlled = value !== undefined;
31
+ const [internal, setInternal] = useState(defaultValue ?? '');
32
+ const currentValue = isControlled ? value : internal;
33
+ const handleChange = (e) => {
34
+ if (!isControlled)
35
+ setInternal(e.target.value);
36
+ onChange?.(e);
37
+ };
38
+ return _jsx(ControlledSelect, { ...rest, value: currentValue, onChange: handleChange });
30
39
  };
@@ -0,0 +1,50 @@
1
+ import { type FunctionComponent, type TextareaHTMLAttributes, type ReactNode } from 'react';
2
+ import type { WithClassName } from '../utils/types.js';
3
+ /**
4
+ * Props for the {@link ControlledTextarea} component.
5
+ *
6
+ * Extends all native {@link TextareaHTMLAttributes} and {@link WithClassName}
7
+ * with optional label, error content, and automatic height adjustment.
8
+ *
9
+ * @property label - Content rendered as an associated `<label>`. When omitted, no label is rendered.
10
+ * @property error - Content rendered as an error message below the textarea. When omitted, no error is rendered.
11
+ * @property autoHeight - When `true`, automatically adjusts the textarea height
12
+ * to fit its content. Defaults to `false`.
13
+ * @property className - Additional class name(s) applied to the textarea element.
14
+ */
15
+ export type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & WithClassName<{
16
+ label?: ReactNode;
17
+ error?: ReactNode;
18
+ autoHeight?: boolean;
19
+ }>;
20
+ /**
21
+ * Controlled textarea field component.
22
+ *
23
+ * Renders a native `<textarea>` element with optional label and error feedback.
24
+ * The textarea value is entirely driven by the provided `value` prop and all
25
+ * native textarea attributes are forwarded to the underlying element.
26
+ *
27
+ * A stable auto-generated `id` is created on mount and used to associate the
28
+ * rendered label through the `htmlFor` attribute.
29
+ *
30
+ * When `autoHeight` is enabled, the textarea height automatically expands or
31
+ * shrinks to match its content.
32
+ *
33
+ * ### CSS elements
34
+ * - `label`
35
+ * - `error`
36
+ *
37
+ * @param props - Component properties.
38
+ * @see {@link Props}
39
+ *
40
+ * @returns A labelled controlled textarea with optional error feedback.
41
+ *
42
+ * @remarks
43
+ * - The component does not manage its own value state.
44
+ * - Consumers are responsible for updating `value` in response to `onChange`.
45
+ * - Additional `className` values are merged with the component public class name
46
+ * and applied to the underlying `<textarea>` element.
47
+ * - When `autoHeight` is enabled, height recalculation occurs whenever the
48
+ * textarea value changes and every 500ms thereafter.
49
+ */
50
+ export declare const ControlledTextarea: FunctionComponent<Props>;
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useLayoutEffect, useRef } from 'react';
3
+ import { clss } from '../../agnostic/css/clss/index.js';
4
+ import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
5
+ import { randomHash } from '../../agnostic/random/uuid/index.js';
6
+ import { textarea as publicClassName } from '../public-classnames.js';
7
+ import { mergeClassNames } from '../utils/index.js';
8
+ import cssModule from './styles.module.css';
9
+ /**
10
+ * Controlled textarea field component.
11
+ *
12
+ * Renders a native `<textarea>` element with optional label and error feedback.
13
+ * The textarea value is entirely driven by the provided `value` prop and all
14
+ * native textarea attributes are forwarded to the underlying element.
15
+ *
16
+ * A stable auto-generated `id` is created on mount and used to associate the
17
+ * rendered label through the `htmlFor` attribute.
18
+ *
19
+ * When `autoHeight` is enabled, the textarea height automatically expands or
20
+ * shrinks to match its content.
21
+ *
22
+ * ### CSS elements
23
+ * - `label`
24
+ * - `error`
25
+ *
26
+ * @param props - Component properties.
27
+ * @see {@link Props}
28
+ *
29
+ * @returns A labelled controlled textarea with optional error feedback.
30
+ *
31
+ * @remarks
32
+ * - The component does not manage its own value state.
33
+ * - Consumers are responsible for updating `value` in response to `onChange`.
34
+ * - Additional `className` values are merged with the component public class name
35
+ * and applied to the underlying `<textarea>` element.
36
+ * - When `autoHeight` is enabled, height recalculation occurs whenever the
37
+ * textarea value changes and every 500ms thereafter.
38
+ */
39
+ export const ControlledTextarea = ({ label, error, autoHeight = false, value, className, ...rest }) => {
40
+ const [id] = useState(`_${randomHash(12)}`);
41
+ const textareaRef = useRef(null);
42
+ // Handlers
43
+ const adjustHeight = () => {
44
+ const textarea = textareaRef.current;
45
+ if (textarea === null)
46
+ return;
47
+ textarea.style.height = 'auto';
48
+ textarea.style.height = `${textarea.scrollHeight}px`;
49
+ };
50
+ // Fx. dep `authHeight`, `value` - Compute content height
51
+ useLayoutEffect(() => {
52
+ if (!autoHeight)
53
+ return;
54
+ adjustHeight();
55
+ }, [autoHeight, value]);
56
+ // Rendering
57
+ const c = clss(publicClassName, { cssModule });
58
+ const rootClss = mergeClassNames(c(null), className);
59
+ return _jsxs(_Fragment, { children: [isNotFalsy(label) && _jsx("label", { className: c('label'), htmlFor: id, children: label }), _jsx("textarea", { ...rest, ref: textareaRef, className: rootClss, id: id, value: value, onChange: e => {
60
+ rest.onChange?.(e);
61
+ if (value === undefined)
62
+ return;
63
+ if (typeof value === 'number') {
64
+ e.target.value = `${value}`;
65
+ }
66
+ else if (typeof value === 'string') {
67
+ e.target.value = value;
68
+ }
69
+ else {
70
+ // value is readonly string[]
71
+ // not supported for <select>; ignore or normalize if needed
72
+ }
73
+ } }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
74
+ };
@@ -1,33 +1,38 @@
1
- import { type FunctionComponent, type TextareaHTMLAttributes, type ReactNode } from 'react';
2
- import type { WithClassName } from '../utils/types.js';
1
+ import { type FunctionComponent } from 'react';
2
+ import { type Props as ControlledProps } from './index.controlled.js';
3
3
  /**
4
4
  * Props for the {@link Textarea} component.
5
5
  *
6
- * Extends all native {@link TextareaHTMLAttributes} and {@link WithClassName}
7
- * with optional label and error content.
6
+ * Alias of {@link ControlledProps}.
8
7
  *
9
- * @property label - Content rendered as an associated `<label>`. When omitted, no label is rendered.
10
- * @property error - Content rendered as an error message below the textarea. When omitted, no error is rendered.
11
- * @property className - Additional class name(s) applied to the root element.
8
+ * All native textarea attributes remain available, including `value`,
9
+ * `defaultValue`, and `onChange`, allowing the component to operate
10
+ * in either controlled or hybrid mode.
12
11
  */
13
- export type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & WithClassName<{
14
- label?: ReactNode;
15
- error?: ReactNode;
16
- }>;
12
+ export type Props = ControlledProps;
17
13
  /**
18
- * Textarea field component with optional label and error message.
14
+ * Textarea field component supporting controlled and hybrid usage.
19
15
  *
20
- * Renders a native `<textarea>` element inside a wrapper, with a stable
21
- * auto-generated `id` used to associate the label via `htmlFor`.
22
- * All standard textarea attributes are forwarded to the underlying element.
16
+ * Wraps {@link ControlledTextarea} and automatically manages the textarea
17
+ * value when no `value` prop is provided.
23
18
  *
24
19
  * ### CSS elements
25
20
  * - `label`
26
- * - `textarea`
27
21
  * - `error`
28
22
  *
29
23
  * @param props - Component properties.
30
24
  * @see {@link Props}
31
- * @returns A labelled textarea wrapper with optional error feedback.
25
+ *
26
+ * @returns A labelled textarea with optional internal value management.
27
+ *
28
+ * @remarks
29
+ * - In controlled mode (`value` defined), the textarea value is fully driven
30
+ * by the parent component and internal state is never updated.
31
+ * - In hybrid mode, internal state is initialized from `defaultValue` and
32
+ * subsequently manages value updates itself.
33
+ * - In hybrid mode, the internal value is updated before forwarding the
34
+ * `onChange` callback.
35
+ * - `defaultValue` is only used to initialize internal state and is not
36
+ * forwarded to the underlying controlled component.
32
37
  */
33
38
  export declare const Textarea: FunctionComponent<Props>;