@orianatech/pire 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/feedback/Skeleton.d.cts +43 -0
- package/dist/components/feedback/Skeleton.d.ts +44 -0
- package/dist/components/feedback/Skeleton.d.ts.map +1 -0
- package/dist/components/forms/MultiComboBox.d.cts +58 -0
- package/dist/components/forms/MultiComboBox.d.ts +59 -0
- package/dist/components/forms/MultiComboBox.d.ts.map +1 -0
- package/dist/components/forms/TextArea.d.cts +42 -0
- package/dist/components/forms/TextArea.d.ts +43 -0
- package/dist/components/forms/TextArea.d.ts.map +1 -0
- package/dist/components.css +51 -1
- package/dist/index.cjs +558 -299
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +561 -293
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
export type SkeletonShape = 'text' | 'block' | 'circle';
|
|
3
|
+
export interface SkeletonProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
4
|
+
/** text — a line of copy; block — a card, chart or image; circle — an avatar. */
|
|
5
|
+
shape?: SkeletonShape;
|
|
6
|
+
/** Overrides the shape's default. A percentage reads better than a pixel count in a
|
|
7
|
+
* fluid layout, and keeps the placeholder honest when the real value is unknown. */
|
|
8
|
+
width?: number | string;
|
|
9
|
+
height?: number | string;
|
|
10
|
+
}
|
|
11
|
+
/** Loading placeholder for content whose shape is known before its value is.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately `aria-hidden`: a screen reader gains nothing from "graphic, graphic, graphic",
|
|
14
|
+
* and announcing placeholders as content is worse than silence. Announce the wait once, on the
|
|
15
|
+
* region that is loading — `aria-busy="true"` on the container, or a live-region message — and
|
|
16
|
+
* let the skeleton be purely visual. Nothing inside is focusable, so keyboard order is
|
|
17
|
+
* unaffected while the data is in flight.
|
|
18
|
+
*
|
|
19
|
+
* The shimmer is suppressed under `prefers-reduced-motion`; the placeholder still reads as one
|
|
20
|
+
* because the shape and the surface contrast carry it, not the animation. */
|
|
21
|
+
export declare function Skeleton({ shape, width, height, className, style, ...rest }: SkeletonProps): React.JSX.Element;
|
|
22
|
+
export interface SkeletonTableColumn {
|
|
23
|
+
align?: 'left' | 'right' | 'center';
|
|
24
|
+
/** Fixed column width. Omit to let the column share the free space, as DataTable does. */
|
|
25
|
+
width?: number | string;
|
|
26
|
+
/** Right-aligns the bar. Mirrors DataTable's column flag so a loading table and a loaded one
|
|
27
|
+
* put their numbers in the same place. */
|
|
28
|
+
numeric?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface SkeletonTableRowProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
31
|
+
/** A column count, or one descriptor per column when alignment or width matters. */
|
|
32
|
+
columns: number | SkeletonTableColumn[];
|
|
33
|
+
/** Must match the DataTable it stands in for, or the rows jump on load. */
|
|
34
|
+
density?: 'condensed' | 'regular' | 'comfortable';
|
|
35
|
+
}
|
|
36
|
+
/** One placeholder row, sized to line up with `DataTable` — same row height per density, same
|
|
37
|
+
* cell padding, same bottom rule. Render a handful inside `.pire-table-wrap` (or above a real
|
|
38
|
+
* table's body) while the first page loads, so the grid does not resize when it arrives.
|
|
39
|
+
*
|
|
40
|
+
* It is not a real `<tr>` and cannot be handed to `DataTable` as a row: DataTable builds its
|
|
41
|
+
* collection from `rows`, and React Aria's table owns everything under it. Use it in place of
|
|
42
|
+
* the table, not inside one. */
|
|
43
|
+
export declare function SkeletonTableRow({ columns, density, className, style, ...rest }: SkeletonTableRowProps): React.JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
export type SkeletonShape = 'text' | 'block' | 'circle';
|
|
3
|
+
export interface SkeletonProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
4
|
+
/** text — a line of copy; block — a card, chart or image; circle — an avatar. */
|
|
5
|
+
shape?: SkeletonShape;
|
|
6
|
+
/** Overrides the shape's default. A percentage reads better than a pixel count in a
|
|
7
|
+
* fluid layout, and keeps the placeholder honest when the real value is unknown. */
|
|
8
|
+
width?: number | string;
|
|
9
|
+
height?: number | string;
|
|
10
|
+
}
|
|
11
|
+
/** Loading placeholder for content whose shape is known before its value is.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately `aria-hidden`: a screen reader gains nothing from "graphic, graphic, graphic",
|
|
14
|
+
* and announcing placeholders as content is worse than silence. Announce the wait once, on the
|
|
15
|
+
* region that is loading — `aria-busy="true"` on the container, or a live-region message — and
|
|
16
|
+
* let the skeleton be purely visual. Nothing inside is focusable, so keyboard order is
|
|
17
|
+
* unaffected while the data is in flight.
|
|
18
|
+
*
|
|
19
|
+
* The shimmer is suppressed under `prefers-reduced-motion`; the placeholder still reads as one
|
|
20
|
+
* because the shape and the surface contrast carry it, not the animation. */
|
|
21
|
+
export declare function Skeleton({ shape, width, height, className, style, ...rest }: SkeletonProps): React.JSX.Element;
|
|
22
|
+
export interface SkeletonTableColumn {
|
|
23
|
+
align?: 'left' | 'right' | 'center';
|
|
24
|
+
/** Fixed column width. Omit to let the column share the free space, as DataTable does. */
|
|
25
|
+
width?: number | string;
|
|
26
|
+
/** Right-aligns the bar. Mirrors DataTable's column flag so a loading table and a loaded one
|
|
27
|
+
* put their numbers in the same place. */
|
|
28
|
+
numeric?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface SkeletonTableRowProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
31
|
+
/** A column count, or one descriptor per column when alignment or width matters. */
|
|
32
|
+
columns: number | SkeletonTableColumn[];
|
|
33
|
+
/** Must match the DataTable it stands in for, or the rows jump on load. */
|
|
34
|
+
density?: 'condensed' | 'regular' | 'comfortable';
|
|
35
|
+
}
|
|
36
|
+
/** One placeholder row, sized to line up with `DataTable` — same row height per density, same
|
|
37
|
+
* cell padding, same bottom rule. Render a handful inside `.pire-table-wrap` (or above a real
|
|
38
|
+
* table's body) while the first page loads, so the grid does not resize when it arrives.
|
|
39
|
+
*
|
|
40
|
+
* It is not a real `<tr>` and cannot be handed to `DataTable` as a row: DataTable builds its
|
|
41
|
+
* collection from `rows`, and React Aria's table owns everything under it. Use it in place of
|
|
42
|
+
* the table, not inside one. */
|
|
43
|
+
export declare function SkeletonTableRow({ columns, density, className, style, ...rest }: SkeletonTableRowProps): React.JSX.Element;
|
|
44
|
+
//# sourceMappingURL=Skeleton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../../src/components/feedback/Skeleton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAGpC,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExD,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC3F,iFAAiF;IACjF,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;yFACqF;IACrF,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;8EAS8E;AAC9E,wBAAgB,QAAQ,CAAC,EAAE,KAAc,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,qBAKnG;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpC,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;+CAC2C;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IACnG,oFAAoF;IACpF,OAAO,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACxC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,aAAa,CAAC;CACnD;AAOD;;;;;;iCAMiC;AACjC,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,OAAmB,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,qBAAqB,qBAmBlH"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ComboBoxOption } from './ComboBox.cjs';
|
|
3
|
+
export interface MultiComboBoxProps {
|
|
4
|
+
label?: React.ReactNode;
|
|
5
|
+
/** Selected values, in the order they were picked. */
|
|
6
|
+
value?: string[];
|
|
7
|
+
defaultValue?: string[];
|
|
8
|
+
onChange?: (values: string[]) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Free-text as typed, on every keystroke.
|
|
11
|
+
*
|
|
12
|
+
* Server-side search is only partly supported, exactly as on ComboBox. `options` is passed
|
|
13
|
+
* to React Aria as `defaultItems`, so React Aria applies its own local "contains" filter on
|
|
14
|
+
* top of whatever you supply: any result whose `label` does not literally contain the typed
|
|
15
|
+
* text is hidden client-side, and when the local filter matches nothing the menu closes.
|
|
16
|
+
* Fuzzy, synonym, code-to-description and server-ranked matches are therefore dropped. Use
|
|
17
|
+
* this to observe input (analytics, a debounced prefetch), or for server search only where
|
|
18
|
+
* the returned labels are guaranteed to contain the query as a substring.
|
|
19
|
+
*/
|
|
20
|
+
onInputChange?: (text: string) => void;
|
|
21
|
+
options?: Array<string | ComboBoxOption>;
|
|
22
|
+
placeholder?: string;
|
|
23
|
+
description?: React.ReactNode;
|
|
24
|
+
errorMessage?: React.ReactNode;
|
|
25
|
+
isInvalid?: boolean;
|
|
26
|
+
isDisabled?: boolean;
|
|
27
|
+
isRequired?: boolean;
|
|
28
|
+
isReadOnly?: boolean;
|
|
29
|
+
/** Let Enter commit text that is not in `options` — how product tags get coined. */
|
|
30
|
+
allowsCustomValue?: boolean;
|
|
31
|
+
size?: 'sm' | 'md' | 'lg';
|
|
32
|
+
fullWidth?: boolean;
|
|
33
|
+
name?: string;
|
|
34
|
+
className?: string;
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
}
|
|
37
|
+
/** Multi-value type-ahead: pick several options, each shown as a removable chip, and — with
|
|
38
|
+
* `allowsCustomValue` — coin new ones by typing. Product tags, cost-centre sets, watchers.
|
|
39
|
+
*
|
|
40
|
+
* A separate component rather than a mode on ComboBox: ComboBox's `value` is a `string | null`
|
|
41
|
+
* and its `onChange` emits one key, while multi-select needs an array on both. Folding the two
|
|
42
|
+
* together would put a discriminated union in front of every existing consumer of
|
|
43
|
+
* `ComboBoxProps` — a breaking change in exchange for a purely additive feature.
|
|
44
|
+
*
|
|
45
|
+
* Three things worth knowing before you use it:
|
|
46
|
+
*
|
|
47
|
+
* - **Free text commits on Enter, never on blur.** React Aria treats the input text as
|
|
48
|
+
* independent of the selection in multi-select mode, so leaving the field discards whatever
|
|
49
|
+
* was typed but not committed. The chips are the only record of what was entered.
|
|
50
|
+
* - **Backspace on an empty input removes the last chip.** The handler runs in the capture
|
|
51
|
+
* phase because React Aria's own key handling sits on the same input and would otherwise act
|
|
52
|
+
* on the same key.
|
|
53
|
+
* - **The chips are plain `Tag`s, not a React Aria TagGroup.** React Aria's ComboBox builds its
|
|
54
|
+
* listbox collection by shallow-rendering its entire subtree, which swallows any collection
|
|
55
|
+
* component nested inside it — a TagGroup there registers its tags as listbox items and then
|
|
56
|
+
* crashes looking for grid state. Each chip's remove button is an ordinary Tab stop instead,
|
|
57
|
+
* and removals are announced through the live region below. */
|
|
58
|
+
export declare function MultiComboBox({ label, value, defaultValue, onChange, onInputChange, options, description, errorMessage, allowsCustomValue, size, fullWidth, className, style, ...rest }: MultiComboBoxProps): React.JSX.Element;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ComboBoxOption } from './ComboBox.js';
|
|
3
|
+
export interface MultiComboBoxProps {
|
|
4
|
+
label?: React.ReactNode;
|
|
5
|
+
/** Selected values, in the order they were picked. */
|
|
6
|
+
value?: string[];
|
|
7
|
+
defaultValue?: string[];
|
|
8
|
+
onChange?: (values: string[]) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Free-text as typed, on every keystroke.
|
|
11
|
+
*
|
|
12
|
+
* Server-side search is only partly supported, exactly as on ComboBox. `options` is passed
|
|
13
|
+
* to React Aria as `defaultItems`, so React Aria applies its own local "contains" filter on
|
|
14
|
+
* top of whatever you supply: any result whose `label` does not literally contain the typed
|
|
15
|
+
* text is hidden client-side, and when the local filter matches nothing the menu closes.
|
|
16
|
+
* Fuzzy, synonym, code-to-description and server-ranked matches are therefore dropped. Use
|
|
17
|
+
* this to observe input (analytics, a debounced prefetch), or for server search only where
|
|
18
|
+
* the returned labels are guaranteed to contain the query as a substring.
|
|
19
|
+
*/
|
|
20
|
+
onInputChange?: (text: string) => void;
|
|
21
|
+
options?: Array<string | ComboBoxOption>;
|
|
22
|
+
placeholder?: string;
|
|
23
|
+
description?: React.ReactNode;
|
|
24
|
+
errorMessage?: React.ReactNode;
|
|
25
|
+
isInvalid?: boolean;
|
|
26
|
+
isDisabled?: boolean;
|
|
27
|
+
isRequired?: boolean;
|
|
28
|
+
isReadOnly?: boolean;
|
|
29
|
+
/** Let Enter commit text that is not in `options` — how product tags get coined. */
|
|
30
|
+
allowsCustomValue?: boolean;
|
|
31
|
+
size?: 'sm' | 'md' | 'lg';
|
|
32
|
+
fullWidth?: boolean;
|
|
33
|
+
name?: string;
|
|
34
|
+
className?: string;
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
}
|
|
37
|
+
/** Multi-value type-ahead: pick several options, each shown as a removable chip, and — with
|
|
38
|
+
* `allowsCustomValue` — coin new ones by typing. Product tags, cost-centre sets, watchers.
|
|
39
|
+
*
|
|
40
|
+
* A separate component rather than a mode on ComboBox: ComboBox's `value` is a `string | null`
|
|
41
|
+
* and its `onChange` emits one key, while multi-select needs an array on both. Folding the two
|
|
42
|
+
* together would put a discriminated union in front of every existing consumer of
|
|
43
|
+
* `ComboBoxProps` — a breaking change in exchange for a purely additive feature.
|
|
44
|
+
*
|
|
45
|
+
* Three things worth knowing before you use it:
|
|
46
|
+
*
|
|
47
|
+
* - **Free text commits on Enter, never on blur.** React Aria treats the input text as
|
|
48
|
+
* independent of the selection in multi-select mode, so leaving the field discards whatever
|
|
49
|
+
* was typed but not committed. The chips are the only record of what was entered.
|
|
50
|
+
* - **Backspace on an empty input removes the last chip.** The handler runs in the capture
|
|
51
|
+
* phase because React Aria's own key handling sits on the same input and would otherwise act
|
|
52
|
+
* on the same key.
|
|
53
|
+
* - **The chips are plain `Tag`s, not a React Aria TagGroup.** React Aria's ComboBox builds its
|
|
54
|
+
* listbox collection by shallow-rendering its entire subtree, which swallows any collection
|
|
55
|
+
* component nested inside it — a TagGroup there registers its tags as listbox items and then
|
|
56
|
+
* crashes looking for grid state. Each chip's remove button is an ordinary Tab stop instead,
|
|
57
|
+
* and removals are announced through the live region below. */
|
|
58
|
+
export declare function MultiComboBox({ label, value, defaultValue, onChange, onInputChange, options, description, errorMessage, allowsCustomValue, size, fullWidth, className, style, ...rest }: MultiComboBoxProps): React.JSX.Element;
|
|
59
|
+
//# sourceMappingURL=MultiComboBox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MultiComboBox.d.ts","sourceRoot":"","sources":["../../../src/components/forms/MultiComboBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAKD;;;;;;;;;;;;;;;;;;;;kEAoBkE;AAClE,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAY,EAC/F,WAAW,EAAE,YAAY,EAAE,iBAAiB,EAAE,IAAW,EACzD,SAAgB,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,kBAAkB,qBAiHlE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface TextAreaProps {
|
|
3
|
+
label?: React.ReactNode;
|
|
4
|
+
value?: string;
|
|
5
|
+
defaultValue?: string;
|
|
6
|
+
/** Receives the raw string value. */
|
|
7
|
+
onChange?: (value: string) => void;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
/** Help text below the field. Replaced by errorMessage when invalid. */
|
|
10
|
+
description?: React.ReactNode;
|
|
11
|
+
errorMessage?: React.ReactNode;
|
|
12
|
+
isInvalid?: boolean;
|
|
13
|
+
isDisabled?: boolean;
|
|
14
|
+
isReadOnly?: boolean;
|
|
15
|
+
isRequired?: boolean;
|
|
16
|
+
size?: 'sm' | 'md' | 'lg';
|
|
17
|
+
/** Visible lines before scrolling. Three is the ERP default: an address or a short note. */
|
|
18
|
+
rows?: number;
|
|
19
|
+
/** Grow with the content instead of scrolling. Off by default — see the component note. */
|
|
20
|
+
autoGrow?: boolean;
|
|
21
|
+
/** Ceiling for `autoGrow`, in lines. Beyond it the field scrolls, so a pasted essay
|
|
22
|
+
* cannot push the form's buttons off screen. */
|
|
23
|
+
maxRows?: number;
|
|
24
|
+
fullWidth?: boolean;
|
|
25
|
+
name?: string;
|
|
26
|
+
autoFocus?: boolean;
|
|
27
|
+
className?: string;
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
textAreaRef?: React.Ref<HTMLTextAreaElement>;
|
|
30
|
+
}
|
|
31
|
+
/** Multi-line text entry — notes, addresses, rejection reasons. The prop API is Input's, so a
|
|
32
|
+
* field can be swapped between the two without relearning; labels, descriptions and errors are
|
|
33
|
+
* wired up by React Aria, so no manual aria-describedby is ever needed.
|
|
34
|
+
*
|
|
35
|
+
* `autoGrow` is opt-in, not always-on, for two reasons. It costs a synchronous measure-and-write
|
|
36
|
+
* on every keystroke, and in a dense form it makes every field below the one being typed in jump
|
|
37
|
+
* — the wrong trade in a screen the user is tabbing through. Turn it on where the content length
|
|
38
|
+
* is genuinely unknown (a long free-text comment) and leave it off everywhere else. When on, the
|
|
39
|
+
* height is only written when it actually changed, so a keystroke that does not wrap costs no
|
|
40
|
+
* layout; and the resize grip is removed, since a manual height would be overwritten on the next
|
|
41
|
+
* keystroke anyway. */
|
|
42
|
+
export declare function TextArea({ label, description, errorMessage, size, rows, autoGrow, maxRows, fullWidth, className, style, textAreaRef, onChange, ...rest }: TextAreaProps): React.JSX.Element;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface TextAreaProps {
|
|
3
|
+
label?: React.ReactNode;
|
|
4
|
+
value?: string;
|
|
5
|
+
defaultValue?: string;
|
|
6
|
+
/** Receives the raw string value. */
|
|
7
|
+
onChange?: (value: string) => void;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
/** Help text below the field. Replaced by errorMessage when invalid. */
|
|
10
|
+
description?: React.ReactNode;
|
|
11
|
+
errorMessage?: React.ReactNode;
|
|
12
|
+
isInvalid?: boolean;
|
|
13
|
+
isDisabled?: boolean;
|
|
14
|
+
isReadOnly?: boolean;
|
|
15
|
+
isRequired?: boolean;
|
|
16
|
+
size?: 'sm' | 'md' | 'lg';
|
|
17
|
+
/** Visible lines before scrolling. Three is the ERP default: an address or a short note. */
|
|
18
|
+
rows?: number;
|
|
19
|
+
/** Grow with the content instead of scrolling. Off by default — see the component note. */
|
|
20
|
+
autoGrow?: boolean;
|
|
21
|
+
/** Ceiling for `autoGrow`, in lines. Beyond it the field scrolls, so a pasted essay
|
|
22
|
+
* cannot push the form's buttons off screen. */
|
|
23
|
+
maxRows?: number;
|
|
24
|
+
fullWidth?: boolean;
|
|
25
|
+
name?: string;
|
|
26
|
+
autoFocus?: boolean;
|
|
27
|
+
className?: string;
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
textAreaRef?: React.Ref<HTMLTextAreaElement>;
|
|
30
|
+
}
|
|
31
|
+
/** Multi-line text entry — notes, addresses, rejection reasons. The prop API is Input's, so a
|
|
32
|
+
* field can be swapped between the two without relearning; labels, descriptions and errors are
|
|
33
|
+
* wired up by React Aria, so no manual aria-describedby is ever needed.
|
|
34
|
+
*
|
|
35
|
+
* `autoGrow` is opt-in, not always-on, for two reasons. It costs a synchronous measure-and-write
|
|
36
|
+
* on every keystroke, and in a dense form it makes every field below the one being typed in jump
|
|
37
|
+
* — the wrong trade in a screen the user is tabbing through. Turn it on where the content length
|
|
38
|
+
* is genuinely unknown (a long free-text comment) and leave it off everywhere else. When on, the
|
|
39
|
+
* height is only written when it actually changed, so a keystroke that does not wrap costs no
|
|
40
|
+
* layout; and the resize grip is removed, since a manual height would be overwritten on the next
|
|
41
|
+
* keystroke anyway. */
|
|
42
|
+
export declare function TextArea({ label, description, errorMessage, size, rows, autoGrow, maxRows, fullWidth, className, style, textAreaRef, onChange, ...rest }: TextAreaProps): React.JSX.Element;
|
|
43
|
+
//# sourceMappingURL=TextArea.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../../src/components/forms/TextArea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,4FAA4F;IAC5F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;qDACiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,WAAW,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;wBAUwB;AACxB,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAW,EAAE,IAAQ,EAAE,QAAQ,EAC1F,OAAO,EAAE,SAAgB,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,qBAsD7F"}
|
package/dist/components.css
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
.pire-card-body{padding:var(--sp-4);flex:1}
|
|
60
60
|
.pire-card-foot{display:flex;justify-content:flex-end;gap:var(--sp-2);padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--border-subtle)}
|
|
61
61
|
|
|
62
|
-
/* ---- Field shell (Input, Select, ComboBox)
|
|
62
|
+
/* ---- Field shell (Input, Select, ComboBox, TextArea, MultiComboBox) --- */
|
|
63
63
|
.pire-field{display:flex;flex-direction:column;gap:var(--sp-1)}
|
|
64
64
|
.pire-field-label{font:var(--type-label);color:var(--text-label)}
|
|
65
65
|
.pire-field-req{color:var(--status-negative-fg);margin-left:var(--sp-0h)}
|
|
@@ -90,6 +90,30 @@
|
|
|
90
90
|
.pire-input::placeholder{color:var(--text-tertiary)}
|
|
91
91
|
.pire-affix{font:var(--type-caption);color:var(--text-secondary);flex:0 0 auto}
|
|
92
92
|
.pire-leading-icon{color:var(--text-tertiary);flex:0 0 auto}
|
|
93
|
+
/* The shell is fixed-height by default because every single-line control is. Controls whose
|
|
94
|
+
content sets the height (TextArea, MultiComboBox's chips) opt out with data-multiline and
|
|
95
|
+
keep everything else — focus ring, invalid, disabled, readonly, sizing — from the rules
|
|
96
|
+
above. This block sits after the [data-size] heights on purpose: equal specificity, so the
|
|
97
|
+
later `height:auto` wins, and the per-size floor is restored as a min-height. */
|
|
98
|
+
.pire-control[data-multiline="true"]{height:auto;min-height:var(--control-h-md);align-items:stretch;padding-top:var(--sp-2);padding-bottom:var(--sp-2)}
|
|
99
|
+
.pire-control[data-multiline="true"][data-size="sm"]{min-height:var(--control-h-sm)}
|
|
100
|
+
.pire-control[data-multiline="true"][data-size="lg"]{min-height:var(--control-h-lg)}
|
|
101
|
+
|
|
102
|
+
/* ---- TextArea -------------------------------------------------------- */
|
|
103
|
+
.pire-textarea{display:block;width:100%;resize:vertical;overflow:auto}
|
|
104
|
+
/* autoGrow writes an inline height on every keystroke, so a height dragged by hand would be
|
|
105
|
+
thrown away on the next one — the grip is removed rather than left lying about it. */
|
|
106
|
+
.pire-textarea[data-auto-grow="true"]{resize:none}
|
|
107
|
+
|
|
108
|
+
/* ---- MultiComboBox --------------------------------------------------- */
|
|
109
|
+
.pire-chipfield{flex:1;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-1)}
|
|
110
|
+
/* Never narrower than a few characters, so the caret stays reachable however many chips are in. */
|
|
111
|
+
.pire-chipinput{flex:1 1 6ch;min-width:6ch}
|
|
112
|
+
/* Each chip's remove button is a Tab stop of its own inside the shell, and the shell's blanket
|
|
113
|
+
`:focus-visible{outline:none}` (see above) would otherwise leave it unmarked. */
|
|
114
|
+
.pire-chipfield .pire-tag-remove[data-focus-visible]{outline:var(--focus-w) solid var(--focus-color);outline-offset:var(--focus-offset)}
|
|
115
|
+
/* One ring at a time: while a chip owns focus it, not the shell, is what moved. */
|
|
116
|
+
.pire-control:has(.pire-tag-remove[data-focus-visible])[data-focus-within]{outline:none}
|
|
93
117
|
|
|
94
118
|
/* ---- Select / ListBox popover ---------------------------------------- */
|
|
95
119
|
.pire-select-value{flex:1;min-width:0;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:var(--type-body)}
|
|
@@ -203,6 +227,28 @@
|
|
|
203
227
|
.pire-progress[data-status="negative"] .pire-progress-fill{background:var(--status-negative-fg)}
|
|
204
228
|
.pire-tooltip{max-width:240px;padding:var(--sp-1) var(--sp-2);border-radius:var(--radius-1);background:var(--gray-90);color:var(--text-inverse);font:var(--type-caption);box-shadow:var(--shadow-2);animation:pire-fade-in var(--dur-fast) var(--ease-entrance)}
|
|
205
229
|
|
|
230
|
+
/* ---- Skeleton ---------------------------------------------------------- */
|
|
231
|
+
/* The sweep is a lightness *difference* between two surface aliases, not a fixed direction:
|
|
232
|
+
--surface-hover is lighter than --surface-active in the light theme and darker in the dark
|
|
233
|
+
one, and either reads as a sweep. That keeps the placeholder on the shared alias layer
|
|
234
|
+
instead of introducing a skeleton-only token pair for both themes to maintain.
|
|
235
|
+
The duration is derived, not literal: a shimmer at --dur-slow (400ms) is agitated rather
|
|
236
|
+
than patient, and 3x is the slowest step the motion scale can express. */
|
|
237
|
+
.pire-skeleton{display:block;width:100%;background-color:var(--surface-active);background-image:linear-gradient(90deg,transparent,var(--surface-hover),transparent);background-size:200% 100%;background-repeat:no-repeat;border-radius:var(--radius-1);animation:pire-skeleton-sweep calc(var(--dur-slow) * 3) var(--ease-standard) infinite}
|
|
238
|
+
.pire-skeleton[data-shape="text"]{height:var(--fs-body)}
|
|
239
|
+
.pire-skeleton[data-shape="block"]{height:var(--sp-16);border-radius:var(--radius-2)}
|
|
240
|
+
.pire-skeleton[data-shape="circle"]{width:var(--sp-8);height:var(--sp-8);border-radius:var(--radius-pill);flex:0 0 auto}
|
|
241
|
+
@keyframes pire-skeleton-sweep{from{background-position:150% 0}to{background-position:-50% 0}}
|
|
242
|
+
/* Geometry copied from .pire-tr / .pire-td, not approximated, so a loading table and a loaded
|
|
243
|
+
one are the same height and the rows do not jump when the data lands. */
|
|
244
|
+
.pire-skeleton-row{display:flex;align-items:center;height:var(--row-h-regular);border-bottom:1px solid var(--border-subtle)}
|
|
245
|
+
.pire-skeleton-row[data-density="condensed"]{height:var(--row-h-condensed)}
|
|
246
|
+
.pire-skeleton-row[data-density="comfortable"]{height:var(--row-h-comfortable)}
|
|
247
|
+
.pire-skeleton-cell{flex:1 1 0;min-width:0;display:flex;padding:0 var(--sp-3)}
|
|
248
|
+
.pire-skeleton-row[data-density="condensed"] .pire-skeleton-cell{padding:0 var(--sp-2)}
|
|
249
|
+
.pire-skeleton-cell[data-align="right"]{justify-content:flex-end}
|
|
250
|
+
.pire-skeleton-cell[data-align="center"]{justify-content:center}
|
|
251
|
+
|
|
206
252
|
/* ---- Data ------------------------------------------------------------ */
|
|
207
253
|
.pire-table-wrap{overflow:auto;background:var(--surface-card);border:1px solid var(--border-subtle);border-radius:var(--radius-2)}
|
|
208
254
|
.pire-table{width:100%;border-collapse:collapse;font:var(--type-body)}
|
|
@@ -348,6 +394,10 @@
|
|
|
348
394
|
|
|
349
395
|
/* ---- Reduced motion --------------------------------------------------- */
|
|
350
396
|
@media (prefers-reduced-motion: reduce){.pire-modal,.pire-toast,.pire-popover,.pire-tooltip,.pire-sidepanel{animation:none}
|
|
397
|
+
/* The skeleton also drops its background sweep, not just the animation: an infinite shimmer is
|
|
398
|
+
exactly the "large, repeated motion" the preference is asking us to stop, and the placeholder
|
|
399
|
+
still reads from its shape and surface contrast alone. */
|
|
400
|
+
.pire-skeleton{animation:none;background-image:none}
|
|
351
401
|
/* !important is the point here: this must beat every component transition, including any a
|
|
352
402
|
consuming app has layered on top. Honouring the OS motion preference outranks the style rule. */
|
|
353
403
|
/* biome-ignore lint/complexity/noImportantStyles: accessibility override must win unconditionally */
|