@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.
- package/agnostic/colors/index.d.ts +4 -4
- package/agnostic/colors/index.js +4 -4
- package/agnostic/css/index.d.ts +2 -2
- package/agnostic/css/index.js +2 -2
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +11 -11
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +11 -11
- package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +2 -2
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +2 -2
- package/agnostic/html/index.d.ts +3 -3
- package/agnostic/html/index.js +3 -3
- package/agnostic/misc/index.d.ts +5 -5
- package/agnostic/misc/index.js +5 -5
- package/agnostic/numbers/index.d.ts +1 -1
- package/agnostic/numbers/index.js +1 -1
- package/agnostic/objects/index.d.ts +3 -3
- package/agnostic/objects/index.js +3 -3
- package/agnostic/sanitization/index.d.ts +1 -1
- package/agnostic/sanitization/index.js +1 -1
- package/agnostic/strings/index.d.ts +2 -2
- package/agnostic/strings/index.js +2 -2
- package/agnostic/time/index.d.ts +2 -2
- package/agnostic/time/index.js +2 -2
- package/components/Button/index.d.ts +2 -7
- package/components/Button/index.js +2 -2
- package/components/Iframe/index.d.ts +16 -34
- package/components/Iframe/index.js +67 -27
- package/components/Input/index.controlled.d.ts +42 -0
- package/components/Input/index.controlled.js +53 -0
- package/components/Input/index.d.ts +21 -17
- package/components/Input/index.js +27 -18
- package/components/Select/index.controlled.d.ts +43 -0
- package/components/Select/index.controlled.js +53 -0
- package/components/Select/index.d.ts +22 -18
- package/components/Select/index.js +27 -18
- package/components/Textarea/index.controlled.d.ts +50 -0
- package/components/Textarea/index.controlled.js +74 -0
- package/components/Textarea/index.d.ts +22 -17
- package/components/Textarea/index.js +27 -18
- package/components/index.d.ts +1 -1
- package/components/index.js +1 -1
- package/node/@aws-s3/storage/directory/index.d.ts +1 -1
- package/node/@aws-s3/storage/directory/index.js +1 -1
- package/node/@aws-s3/storage/file/index.d.ts +1 -1
- package/node/@aws-s3/storage/file/index.js +1 -1
- package/node/@google-cloud/storage/directory/index.d.ts +1 -1
- package/node/@google-cloud/storage/directory/index.js +1 -1
- package/node/@google-cloud/storage/file/index.d.ts +2 -2
- package/node/@google-cloud/storage/file/index.js +2 -2
- package/node/@google-cloud/storage/index.d.ts +1 -1
- package/node/@google-cloud/storage/index.js +1 -1
- package/node/cloud-storage/operations/index.d.ts +1 -1
- package/node/cloud-storage/operations/index.js +1 -1
- package/node/images/transform/operations/index.d.ts +1 -1
- package/node/images/transform/operations/index.js +1 -1
- package/node/index.d.ts +1 -1
- package/node/index.js +1 -1
- package/node/process/index.d.ts +1 -1
- package/node/process/index.js +1 -1
- package/node/sftp/file/index.d.ts +1 -1
- package/node/sftp/file/index.js +1 -1
- 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
|
|
2
|
-
import type
|
|
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
|
-
*
|
|
7
|
-
* with optional label and error content.
|
|
6
|
+
* Alias of {@link ControlledProps}.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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 =
|
|
14
|
-
label?: ReactNode;
|
|
15
|
-
error?: ReactNode;
|
|
16
|
-
}>;
|
|
12
|
+
export type Props = ControlledProps;
|
|
17
13
|
/**
|
|
18
|
-
* Input field component
|
|
14
|
+
* Input field component supporting controlled and hybrid usage.
|
|
19
15
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
-
*
|
|
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
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import {
|
|
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
|
|
5
|
+
* Input field component supporting controlled and hybrid usage.
|
|
11
6
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
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 = (
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
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
|
|
2
|
-
import type
|
|
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
|
-
*
|
|
7
|
-
* with optional label, error content, and option children.
|
|
6
|
+
* Alias of {@link ControlledProps}.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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 =
|
|
15
|
-
label?: ReactNode;
|
|
16
|
-
error?: ReactNode;
|
|
17
|
-
}>>;
|
|
12
|
+
export type Props = ControlledProps;
|
|
18
13
|
/**
|
|
19
|
-
* Select field component
|
|
14
|
+
* Select field component supporting controlled and hybrid usage.
|
|
20
15
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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
|
-
*
|
|
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
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import {
|
|
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
|
|
5
|
+
* Select field component supporting controlled and hybrid usage.
|
|
11
6
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
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 = ({
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
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
|
|
2
|
-
import type
|
|
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
|
-
*
|
|
7
|
-
* with optional label and error content.
|
|
6
|
+
* Alias of {@link ControlledProps}.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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 =
|
|
14
|
-
label?: ReactNode;
|
|
15
|
-
error?: ReactNode;
|
|
16
|
-
}>;
|
|
12
|
+
export type Props = ControlledProps;
|
|
17
13
|
/**
|
|
18
|
-
* Textarea field component
|
|
14
|
+
* Textarea field component supporting controlled and hybrid usage.
|
|
19
15
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
-
*
|
|
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>;
|