prime-ui-kit 0.20.1 → 0.21.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/README.md +1 -1
- package/dist/chunks/{chunk-4KONMO7Z.js → chunk-6L7VWEY6.js} +1 -1
- package/dist/chunks/{chunk-3PY52JP3.js → chunk-S27VR4ON.js} +5828 -5596
- package/dist/chunks/{chunk-4374EBWV.js → chunk-S4H2ZKWJ.js} +1049 -644
- package/dist/color-picker.js +2 -2
- package/dist/components/badge/Badge.d.ts +19 -2
- package/dist/components/card/Card.d.ts +10 -3
- package/dist/components/composer/Composer.d.ts +49 -1
- package/dist/components/crossfade/Crossfade.d.ts +6 -4
- package/dist/components/data-table/DataTable.d.ts +10 -2
- package/dist/components/data-table/Row.d.ts +12 -1
- package/dist/components/data-table/SelectionMenu.d.ts +20 -0
- package/dist/components/data-table/useRowSelection.d.ts +2 -0
- package/dist/components/empty-page/EmptyPage.d.ts +5 -2
- package/dist/components/hint/Hint.d.ts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/input/Input.d.ts +15 -3
- package/dist/components/message/Message.d.ts +6 -3
- package/dist/components/segmented-control/SegmentedControl.d.ts +5 -1
- package/dist/components/smart-filter/SmartFilter.d.ts +4 -0
- package/dist/components/spinner/Spinner.d.ts +3 -2
- package/dist/components/tabs/Tabs.d.ts +10 -1
- package/dist/hooks/useModalLayer.d.ts +15 -8
- package/dist/index.css +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/RegionSwap.d.ts +18 -10
- package/dist/internal/RollingNumber.d.ts +7 -5
- package/dist/internal/ToolRow.d.ts +6 -0
- package/dist/internal/mediaSlot.d.ts +29 -0
- package/dist/internal/overlay/DialogParts.d.ts +30 -6
- package/dist/internal/overlay/focus.d.ts +18 -3
- package/dist/internal/partPresence.d.ts +19 -2
- package/dist/layout/sidebar/Sidebar.d.ts +3 -2
- package/dist/skill/anti-slop.md +35 -37
- package/dist/skill/api-contract.md +50 -2
- package/dist/skill/cheatsheet.md +15 -10
- package/dist/skill/checklist.md +19 -6
- package/dist/skill/choosing.md +79 -6
- package/dist/skill/components.md +1 -1
- package/dist/skill/composition.md +211 -51
- package/dist/skill/foundations.md +17 -6
- package/dist/skill/layouts.md +64 -14
- package/dist/skill/motion.md +9 -3
- package/dist/skill/reference/components/avatar/COMPONENT.md +1 -0
- package/dist/skill/reference/components/badge/COMPONENT.md +29 -5
- package/dist/skill/reference/components/badge/examples/examples.module.css +14 -0
- package/dist/skill/reference/components/badge/examples/overflow-count.tsx +32 -0
- package/dist/skill/reference/components/banner/COMPONENT.md +1 -1
- package/dist/skill/reference/components/card/COMPONENT.md +8 -4
- package/dist/skill/reference/components/card/examples/person-header.tsx +38 -0
- package/dist/skill/reference/components/composer/COMPONENT.md +35 -5
- package/dist/skill/reference/components/composer/examples/bot-keyboard.tsx +43 -0
- package/dist/skill/reference/components/confirm-dialog/COMPONENT.md +1 -1
- package/dist/skill/reference/components/crossfade/COMPONENT.md +18 -10
- package/dist/skill/reference/components/data-table/COMPONENT.md +17 -11
- package/dist/skill/reference/components/data-table/examples/screen.tsx +6 -9
- package/dist/skill/reference/components/data-table/examples/selection.tsx +60 -18
- package/dist/skill/reference/components/drawer/COMPONENT.md +41 -19
- package/dist/skill/reference/components/drawer/examples/footer-actions.tsx +54 -0
- package/dist/skill/reference/components/drawer/examples/header-actions.tsx +46 -25
- package/dist/skill/reference/components/drawer/examples/link-loading.tsx +57 -0
- package/dist/skill/reference/components/drawer/examples/screen.tsx +3 -5
- package/dist/skill/reference/components/empty-page/COMPONENT.md +2 -2
- package/dist/skill/reference/components/field-list/COMPONENT.md +4 -4
- package/dist/skill/reference/components/field-list/examples/row-menu.tsx +1 -1
- package/dist/skill/reference/components/hint/COMPONENT.md +1 -1
- package/dist/skill/reference/components/input/COMPONENT.md +16 -3
- package/dist/skill/reference/components/input/examples/password-policy.tsx +65 -0
- package/dist/skill/reference/components/list/COMPONENT.md +1 -1
- package/dist/skill/reference/components/login-form/COMPONENT.md +13 -11
- package/dist/skill/reference/components/login-form/examples/overview.tsx +5 -5
- package/dist/skill/reference/components/login-form/examples/states.tsx +7 -6
- package/dist/skill/reference/components/message/COMPONENT.md +5 -5
- package/dist/skill/reference/components/message/examples/bot-actions.tsx +47 -28
- package/dist/skill/reference/components/modal/COMPONENT.md +31 -12
- package/dist/skill/reference/components/modal/examples/structure.tsx +2 -4
- package/dist/skill/reference/components/segmented-control/COMPONENT.md +11 -6
- package/dist/skill/reference/components/segmented-control/examples/router.tsx +22 -0
- package/dist/skill/reference/components/select/COMPONENT.md +4 -1
- package/dist/skill/reference/components/sheet/COMPONENT.md +31 -12
- package/dist/skill/reference/components/sheet/examples/header-actions.tsx +5 -3
- package/dist/skill/reference/components/skeleton/COMPONENT.md +3 -2
- package/dist/skill/reference/components/smart-filter/COMPONENT.md +2 -1
- package/dist/skill/reference/components/smart-filter/examples/ranges.tsx +8 -2
- package/dist/skill/reference/components/spinner/COMPONENT.md +1 -1
- package/dist/skill/reference/components/tabs/COMPONENT.md +7 -4
- package/dist/skill/reference/components/tabs/examples/in-drawer.tsx +18 -2
- package/dist/skill/reference/components/thumbnail/COMPONENT.md +1 -0
- package/dist/skill/reference/components/tooltip/COMPONENT.md +3 -3
- package/dist/skill/reference/foundation.md +15 -9
- package/dist/skill/reference/layout/page/COMPONENT.md +1 -0
- package/dist/skill/reference/layout/sidebar/COMPONENT.md +4 -2
- package/dist/skill/reference/layout/sidebar/examples/account-profile.tsx +66 -0
- package/dist/skill/responsive.md +28 -6
- package/dist/skill/ux.md +97 -2
- package/dist/tokens/semantic.d.ts +2 -1
- package/dist/tokens/themes/dark.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/avatar/COMPONENT.md +1 -0
- package/src/components/badge/COMPONENT.md +29 -5
- package/src/components/badge/examples/examples.module.css +14 -0
- package/src/components/badge/examples/overflow-count.tsx +32 -0
- package/src/components/banner/COMPONENT.md +1 -1
- package/src/components/card/COMPONENT.md +8 -4
- package/src/components/card/examples/person-header.tsx +38 -0
- package/src/components/composer/COMPONENT.md +35 -5
- package/src/components/composer/examples/bot-keyboard.tsx +43 -0
- package/src/components/confirm-dialog/COMPONENT.md +1 -1
- package/src/components/crossfade/COMPONENT.md +18 -10
- package/src/components/data-table/COMPONENT.md +17 -11
- package/src/components/data-table/examples/screen.tsx +6 -9
- package/src/components/data-table/examples/selection.tsx +60 -18
- package/src/components/drawer/COMPONENT.md +41 -19
- package/src/components/drawer/examples/footer-actions.tsx +54 -0
- package/src/components/drawer/examples/header-actions.tsx +46 -25
- package/src/components/drawer/examples/link-loading.tsx +57 -0
- package/src/components/drawer/examples/screen.tsx +3 -5
- package/src/components/empty-page/COMPONENT.md +2 -2
- package/src/components/field-list/COMPONENT.md +4 -4
- package/src/components/field-list/examples/row-menu.tsx +1 -1
- package/src/components/hint/COMPONENT.md +1 -1
- package/src/components/input/COMPONENT.md +16 -3
- package/src/components/input/examples/password-policy.tsx +65 -0
- package/src/components/list/COMPONENT.md +1 -1
- package/src/components/login-form/COMPONENT.md +13 -11
- package/src/components/login-form/examples/overview.tsx +5 -5
- package/src/components/login-form/examples/states.tsx +7 -6
- package/src/components/message/COMPONENT.md +5 -5
- package/src/components/message/examples/bot-actions.tsx +47 -28
- package/src/components/modal/COMPONENT.md +31 -12
- package/src/components/modal/examples/structure.tsx +2 -4
- package/src/components/segmented-control/COMPONENT.md +11 -6
- package/src/components/segmented-control/examples/router.tsx +22 -0
- package/src/components/select/COMPONENT.md +4 -1
- package/src/components/sheet/COMPONENT.md +31 -12
- package/src/components/sheet/examples/header-actions.tsx +5 -3
- package/src/components/skeleton/COMPONENT.md +3 -2
- package/src/components/smart-filter/COMPONENT.md +2 -1
- package/src/components/smart-filter/examples/ranges.tsx +8 -2
- package/src/components/spinner/COMPONENT.md +1 -1
- package/src/components/tabs/COMPONENT.md +7 -4
- package/src/components/tabs/examples/in-drawer.tsx +18 -2
- package/src/components/thumbnail/COMPONENT.md +1 -0
- package/src/components/tooltip/COMPONENT.md +3 -3
- package/src/layout/page/COMPONENT.md +1 -0
- package/src/layout/sidebar/COMPONENT.md +4 -2
- package/src/layout/sidebar/examples/account-profile.tsx +66 -0
- package/src/styles/theme-dark.css +1 -0
- package/src/styles/theme-light.css +1 -0
package/dist/index.js
CHANGED
|
@@ -88,7 +88,7 @@ import {
|
|
|
88
88
|
useSidebar,
|
|
89
89
|
useTokenValue,
|
|
90
90
|
useUploadQueue
|
|
91
|
-
} from "./chunks/chunk-
|
|
91
|
+
} from "./chunks/chunk-S4H2ZKWJ.js";
|
|
92
92
|
import {
|
|
93
93
|
Avatar,
|
|
94
94
|
Badge,
|
|
@@ -123,7 +123,7 @@ import {
|
|
|
123
123
|
tierMappings,
|
|
124
124
|
tones,
|
|
125
125
|
variants
|
|
126
|
-
} from "./chunks/chunk-
|
|
126
|
+
} from "./chunks/chunk-S27VR4ON.js";
|
|
127
127
|
import {
|
|
128
128
|
ControlSizeProvider,
|
|
129
129
|
useOptionalControlSize
|
|
@@ -2,7 +2,8 @@ import * as React from "react";
|
|
|
2
2
|
type RegionSwapProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
3
3
|
/**
|
|
4
4
|
* Key of what the region shows (`"loading"`, `"ready"`, `"empty"`, `"error"`, a record id). A new
|
|
5
|
-
* value swaps the old content for the new one; the same value updates in place.
|
|
5
|
+
* value swaps the old content for the new one; the same value updates in place. `"loading"` is
|
|
6
|
+
* the loading state: a swap into or out of it never glides the height.
|
|
6
7
|
*/
|
|
7
8
|
state: string | number;
|
|
8
9
|
/** The region takes its parent's height and every state fills it; the height never glides. */
|
|
@@ -17,25 +18,32 @@ type RegionSwapProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
17
18
|
/**
|
|
18
19
|
* The region swap of the kit (foundation §7 rule 8): a region that changes what it shows (loading /
|
|
19
20
|
* data / empty / error, another record) cross-fades — the old content stays as a leaving layer over
|
|
20
|
-
* the new one and fades out (`swapMotion.swapOut`, `fast` · exit) while the new one fades in
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* to
|
|
21
|
+
* the new one and fades out (`swapMotion.swapOut`, `fast` · exit) while the new one fades in.
|
|
22
|
+
* Between settled states (data / empty / error, another record) the new one fades in on `base`
|
|
23
|
+
* (`swapMotion.swapIn`) and the region glides from the old height to the new one, the "height
|
|
24
|
+
* glide" exception of foundation §7 rule 5. A swap into or out of `"loading"` is a load: the
|
|
25
|
+
* Skeleton and the data replace each other in place — the height settles in one frame (the old
|
|
26
|
+
* layer clipped to it) and the fade is short (`swapMotion.settleIn`, `fast`). Nothing moves on the
|
|
27
|
+
* first render. Focus in the leaving state moves into the new one (`handoffFocus`). Under reduced
|
|
28
|
+
* motion the old state goes at once and the height jumps; the new one still fades in (rule 12).
|
|
29
|
+
* A region with nothing to show takes no room. `Crossfade` is this region, public.
|
|
26
30
|
*/
|
|
27
31
|
export declare function RegionSwap({ state, fill, onSwap, children, className, ref, ...rest }: RegionSwapProps): import("react/jsx-runtime").JSX.Element;
|
|
28
32
|
type SwapIn = {
|
|
29
33
|
/** Key of the swapped content: it remounts under a new state. `undefined` before the first swap. */
|
|
30
34
|
key: string | undefined;
|
|
31
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* The fade-in of the new state: `swapMotion.settleIn` into or out of `"loading"`, else
|
|
37
|
+
* `swapMotion.swapIn`; `undefined` before the first swap.
|
|
38
|
+
*/
|
|
32
39
|
className: string | undefined;
|
|
33
40
|
};
|
|
34
41
|
/**
|
|
35
42
|
* The region swap in place, for a host whose states cannot be laid over each other: a table body
|
|
36
43
|
* (rows leave the table's layout when positioned) or the phrasing content of a button. The new
|
|
37
|
-
* state remounts under `key` and fades in through `className
|
|
38
|
-
* does not glide. Nothing moves on the first render. Every other
|
|
44
|
+
* state remounts under `key` and fades in through `className` (short into or out of `"loading"`);
|
|
45
|
+
* nothing fades out and the height does not glide. Nothing moves on the first render. Every other
|
|
46
|
+
* region is a `RegionSwap`.
|
|
39
47
|
*/
|
|
40
48
|
export declare function useSwapIn(state: string | number): SwapIn;
|
|
41
49
|
export {};
|
|
@@ -5,11 +5,13 @@ type RollingNumberProps = {
|
|
|
5
5
|
};
|
|
6
6
|
/**
|
|
7
7
|
* A number whose digits roll to their new values column by column, like an odometer (foundation §7
|
|
8
|
-
* rule 8: counters ease, they do not flip). The DOM holds the
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* rule 8: counters ease, they do not flip). The DOM holds the value as one run of text — copy, a
|
|
9
|
+
* page without CSS and assistive tech read it as one word («12», never «1 2»: separate boxes per
|
|
10
|
+
* digit make a browser split the name) — and an aria-hidden row of columns, with no text of its own,
|
|
11
|
+
* paints it: each digit column draws the 0–9 strip in a pseudo-element. Columns are keyed from the
|
|
12
|
+
* right, so an unchanged digit stays still and a new leading digit appears without rolling. A value
|
|
13
|
+
* that changes by itself every frame (a running percentage) stays plain tabular text: rolling there
|
|
14
|
+
* is noise.
|
|
13
15
|
*/
|
|
14
16
|
export declare function RollingNumber({ children, className }: RollingNumberProps): import("react/jsx-runtime").JSX.Element;
|
|
15
17
|
/**
|
|
@@ -8,6 +8,12 @@ type ToolGroupProps = DivProps;
|
|
|
8
8
|
* title before its zoom): they wrap inside it.
|
|
9
9
|
*/
|
|
10
10
|
export declare function ToolGroup({ className, ...rest }: ToolGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
type ToolCellProps = DivProps;
|
|
12
|
+
/**
|
|
13
|
+
* A cell the host draws itself, standing in the row as given: a part that comes and goes takes its
|
|
14
|
+
* presence (`usePartPresence`) on the cell, so the row closes up when it leaves. A fixed cell.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ToolCell({ className, ...rest }: ToolCellProps): import("react/jsx-runtime").JSX.Element;
|
|
11
17
|
type ToolRowProps = DivProps & {
|
|
12
18
|
/** The row has a line of its own: it wraps and its cells fill it by kind. Off — one line, every cell at its natural width. */
|
|
13
19
|
fill?: boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The media slot of a leading mark (`Modal.Icon`, `Drawer.Icon`, `Sheet.Icon`, `Card.Icon`): a box
|
|
3
|
+
* that is a filled glyph tile, unless the record's picture stands in it. A person or an object
|
|
4
|
+
* stands for itself, so an `Avatar.Root` or a `Thumbnail.Root` anywhere inside the slot — a direct
|
|
5
|
+
* child, under a wrapper, or rendered by the consumer's own component — reports itself and takes the
|
|
6
|
+
* slot's size, the size of the tile it replaces (40): an avatar `l`, a thumbnail `m` (its height).
|
|
7
|
+
* The report runs in a layout effect, so the tile never paints around a picture.
|
|
8
|
+
*/
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import type { ControlSize } from "./states";
|
|
11
|
+
/** The sizes a picture takes in the slot: the 40 box of the glyph tile. */
|
|
12
|
+
declare const MEDIA_SLOT_SIZE: {
|
|
13
|
+
readonly avatar: "l";
|
|
14
|
+
readonly thumbnail: "m";
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The host side: wraps the slot's children and tells whether a picture stands in it. Children
|
|
18
|
+
* render inside `provide(…)`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useMediaSlotHost(): {
|
|
21
|
+
media: boolean;
|
|
22
|
+
provide: (children: React.ReactNode) => React.ReactElement;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The picture side (Avatar.Root, Thumbnail.Root): inside a media slot it reports itself and
|
|
26
|
+
* returns the size it must take there; outside one, `undefined`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useMediaSlotSize(kind: keyof typeof MEDIA_SLOT_SIZE): ControlSize | undefined;
|
|
29
|
+
export {};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
import { type SlotPartProps } from "../slot";
|
|
9
9
|
import type { ControlSize, Tone } from "../states";
|
|
10
|
+
import type { FocusStart } from "./focus";
|
|
10
11
|
/** System strings of a dialog: its close button and the built-in «discard changes?» confirm. */
|
|
11
12
|
export type DialogLabels = {
|
|
12
13
|
/** `aria-label` of the header close button. */
|
|
@@ -116,6 +117,8 @@ type DialogPanelProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
116
117
|
enterConfirm?: boolean;
|
|
117
118
|
/** The tier of the controls inside (Modal: `m`); without it they keep the surrounding tier. */
|
|
118
119
|
controlSize?: ControlSize;
|
|
120
|
+
/** Where focus lands on open (`useModalLayer`): the first tab stop, or the panel (Drawer). */
|
|
121
|
+
focusStart?: FocusStart;
|
|
119
122
|
ref?: React.Ref<HTMLDivElement>;
|
|
120
123
|
};
|
|
121
124
|
/**
|
|
@@ -131,19 +134,32 @@ export type DialogIconProps = Omit<React.HTMLAttributes<HTMLSpanElement>, "child
|
|
|
131
134
|
*/
|
|
132
135
|
tone?: Tone;
|
|
133
136
|
/**
|
|
134
|
-
* An `Icon` (sized to the m icon), or the record's picture: an `Avatar.Root` (
|
|
137
|
+
* An `Icon` (sized to the m icon), or the record's picture: an `Avatar.Root` (size `l`) or a `Thumbnail.Root` (size `m`), set by the slot.
|
|
135
138
|
*/
|
|
136
139
|
children: React.ReactNode;
|
|
137
140
|
ref?: React.Ref<HTMLSpanElement>;
|
|
138
141
|
};
|
|
142
|
+
/**
|
|
143
|
+
* The leading mark: a glyph on the tone tile, or the record's picture (an `Avatar.Root` or a
|
|
144
|
+
* `Thumbnail.Root` anywhere inside, internal/mediaSlot) with no tile, at the tile's 40 box.
|
|
145
|
+
*/
|
|
139
146
|
export declare function DialogIcon({ tone, children, className, ...rest }: DialogIconProps): import("react/jsx-runtime").JSX.Element;
|
|
140
147
|
export declare namespace DialogIcon {
|
|
141
148
|
var displayName: string;
|
|
142
149
|
}
|
|
143
150
|
export type DialogTitleProps = Omit<React.HTMLAttributes<HTMLHeadingElement>, "id"> & {
|
|
151
|
+
/**
|
|
152
|
+
* The name loads (a record opened by a link): a `Skeleton` holds the title line and `aria-busy` is set, so the header never moves; the name fades in.
|
|
153
|
+
*/
|
|
154
|
+
loading?: boolean;
|
|
144
155
|
ref?: React.Ref<HTMLHeadingElement>;
|
|
145
156
|
};
|
|
146
|
-
|
|
157
|
+
/**
|
|
158
|
+
* The dialog's name. While it loads (`loading`: a record opened by a link knows only its id) a
|
|
159
|
+
* Skeleton holds the title line, so the header never moves when the name arrives; the name then
|
|
160
|
+
* fades in under the same element.
|
|
161
|
+
*/
|
|
162
|
+
export declare function DialogTitle({ loading, className, children, ...rest }: DialogTitleProps): import("react/jsx-runtime").JSX.Element;
|
|
147
163
|
export declare namespace DialogTitle {
|
|
148
164
|
var displayName: string;
|
|
149
165
|
}
|
|
@@ -157,6 +173,9 @@ export type DialogDescriptionProps = Omit<React.HTMLAttributes<HTMLParagraphElem
|
|
|
157
173
|
/**
|
|
158
174
|
* The line under the title. While its data loads (`loading`) it keeps its place with a Skeleton, so
|
|
159
175
|
* the header never moves when the data arrives; the data then fades in under the same element.
|
|
176
|
+
* The record's status badges lead it: a Badge inside takes chip tier `s` (one step under the `m`
|
|
177
|
+
* it provides), the height of the line, and the badges stand before the text whatever the order
|
|
178
|
+
* they are written in.
|
|
160
179
|
*/
|
|
161
180
|
export declare function DialogDescription({ loading, className, children, ...rest }: DialogDescriptionProps): import("react/jsx-runtime").JSX.Element;
|
|
162
181
|
export declare namespace DialogDescription {
|
|
@@ -182,8 +201,11 @@ export type DialogActionsProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
182
201
|
ref?: React.Ref<HTMLDivElement>;
|
|
183
202
|
};
|
|
184
203
|
/**
|
|
185
|
-
*
|
|
186
|
-
* Dropdown
|
|
204
|
+
* The buttons at the end of the header, before the close button: ghost icon buttons (each named,
|
|
205
|
+
* with a Tooltip) and a ⋯ Dropdown — never a status, which leads the `Description`. They take tier
|
|
206
|
+
* `s`, the close button's, and sit on the title's line. In the footer
|
|
207
|
+
* it is the start group — an action apart from the main pair («Удалить», «Выйти») — and its
|
|
208
|
+
* buttons keep the footer's tier.
|
|
187
209
|
*/
|
|
188
210
|
export declare function DialogActions({ className, children, ...rest }: DialogActionsProps): import("react/jsx-runtime").JSX.Element;
|
|
189
211
|
export declare namespace DialogActions {
|
|
@@ -227,8 +249,10 @@ export type DialogFooterProps = React.HTMLAttributes<HTMLElement> & {
|
|
|
227
249
|
};
|
|
228
250
|
/**
|
|
229
251
|
* Actions in DOM order, primary last. Stacks full width when the dialog is narrower than 360px.
|
|
230
|
-
*
|
|
231
|
-
*
|
|
252
|
+
* An `Actions` holds the start group (an action apart from the main pair) and a `Meta` the status
|
|
253
|
+
* text, both at the start; the buttons then move as one row at the end: when the line runs short
|
|
254
|
+
* the start goes above them first, so the buttons never split over two lines while there is room
|
|
255
|
+
* for them in one. Stacked, the start comes first and the primary stays last.
|
|
232
256
|
*/
|
|
233
257
|
export declare function DialogFooter({ layout, className, children, ...rest }: DialogFooterProps): import("react/jsx-runtime").JSX.Element;
|
|
234
258
|
export declare namespace DialogFooter {
|
|
@@ -5,13 +5,20 @@ export declare function getFocusable(container: HTMLElement): HTMLElement[];
|
|
|
5
5
|
* reaches it without an added `tabindex`.
|
|
6
6
|
*/
|
|
7
7
|
export declare function hasNativeTabStop(el: HTMLElement): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Where focus lands in a layer that just opened: `tabStop` — its first tab stop; `container` — the
|
|
10
|
+
* layer itself (a record panel: the person hears its name and description, and Tab goes on to the
|
|
11
|
+
* first control).
|
|
12
|
+
*/
|
|
13
|
+
export type FocusStart = "tabStop" | "container";
|
|
8
14
|
/**
|
|
9
15
|
* Moves focus into a layer that just opened, unless focus is already inside (a child `autoFocus`):
|
|
10
16
|
* the first match of `first` (a menu's first enabled item, which is no tab stop of its own), else
|
|
11
|
-
* `[data-autofocus]`, else the first tab stop, else the container itself. `quiet` — a
|
|
12
|
-
* opened the layer: focus lands without the focus ring until the person turns to the
|
|
17
|
+
* `[data-autofocus]`, else per `start` the first tab stop, else the container itself. `quiet` — a
|
|
18
|
+
* pointer opened the layer: focus lands without the focus ring until the person turns to the
|
|
19
|
+
* keyboard.
|
|
13
20
|
*/
|
|
14
|
-
export declare function focusInto(container: HTMLElement, first?: string, quiet?: boolean): void;
|
|
21
|
+
export declare function focusInto(container: HTMLElement, first?: string, quiet?: boolean, start?: FocusStart): void;
|
|
15
22
|
/**
|
|
16
23
|
* Focuses `el` when it is still in the document. `quiet` — the layer was opened with a pointer:
|
|
17
24
|
* focus returns without the focus ring, also when a key (Escape) closed the layer; the next key
|
|
@@ -35,6 +42,14 @@ export declare function handoffFocus(leaving: Element | null | undefined, next:
|
|
|
35
42
|
* `el` do not count.
|
|
36
43
|
*/
|
|
37
44
|
export declare function tabStopBeside(el: Element): HTMLElement | null;
|
|
45
|
+
/**
|
|
46
|
+
* Whether focus on `el` came from the keyboard, so a part that shows on focus (a tooltip) may
|
|
47
|
+
* show: the last input was a key (or nothing yet, as `:focus-visible` has it), and `el` is not
|
|
48
|
+
* focused quietly (a layer a pointer opened or closed). Focus a click causes, or a layer moves after
|
|
49
|
+
* a click, is no keyboard focus — read from the input itself, since `:focus-visible` is not
|
|
50
|
+
* reported everywhere (jsdom) and a quiet focus keeps it until the next key.
|
|
51
|
+
*/
|
|
52
|
+
export declare function focusFromKeyboard(el: Element): boolean;
|
|
38
53
|
/**
|
|
39
54
|
* Who opened a layer, to return focus to on close: `element` — the control (or the focused
|
|
40
55
|
* element); `target` — the exact element a press landed on (a picture inside a link, for a layer
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
type EnterOnAppear<E extends Element> = {
|
|
3
|
+
/** On the node that appears (the one its host inserts); it may differ from the one that moves. */
|
|
4
|
+
ref: React.RefCallback<E>;
|
|
5
|
+
/** Join `enterMotion.*` while true. */
|
|
6
|
+
entering: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Whether a part plays its enter motion (foundation §1 rule 7, §7): only when it appears after its
|
|
10
|
+
* host is on screen — `active` turns on after the first render (an error, a tab opened again), or
|
|
11
|
+
* its node is inserted on its own into a host already shown (a banner after an event, a bar that
|
|
12
|
+
* selection brings, a keyed excerpt that replaces the last). A part that mounts together with its
|
|
13
|
+
* page, region or overlay is just there. Spread `ref` on the node the host inserts:
|
|
14
|
+
*
|
|
15
|
+
* const appear = useEnterOnAppear<HTMLDivElement>();
|
|
16
|
+
* <div ref={useMergedRefs(appear.ref, ref)} className={cx(appear.entering && enterMotion.enter)} />
|
|
17
|
+
*/
|
|
18
|
+
export declare function useEnterOnAppear<E extends Element>(active?: boolean): EnterOnAppear<E>;
|
|
2
19
|
type PartPresenceOptions = {
|
|
3
20
|
/**
|
|
4
21
|
* Whether the host shows the part now. It enters when this turns on after the host's first
|
|
@@ -18,8 +35,8 @@ type PartPresence<E extends HTMLElement> = {
|
|
|
18
35
|
* Presence of an optional part that comes and goes while its host stays (a clear button, a chip
|
|
19
36
|
* row, a remove segment, a change badge): it rises in when it appears (`enterMotion`; the host
|
|
20
37
|
* tunes it with `--enter-*`) and closes up in place when it goes (`leaveInPlace`), so the parts
|
|
21
|
-
* around it glide instead of jumping. Called by the host that owns the condition; spread the
|
|
22
|
-
*
|
|
38
|
+
* around it glide instead of jumping. Called by the host that owns the condition; spread the result
|
|
39
|
+
* on the part:
|
|
23
40
|
*
|
|
24
41
|
* const clear = usePartPresence<HTMLButtonElement>({ present: hasValue, axis: "inline" });
|
|
25
42
|
* {hasValue ? <button ref={clear.ref} className={cx(styles.clear, clear.className)} /> : null}
|
|
@@ -374,8 +374,9 @@ export type SidebarAccountProps = Omit<React.ComponentPropsWithoutRef<"button">,
|
|
|
374
374
|
ref?: React.Ref<HTMLButtonElement>;
|
|
375
375
|
};
|
|
376
376
|
/**
|
|
377
|
-
* The signed-in person at the bottom of the rail: avatar, name, a muted line and a
|
|
378
|
-
* button, so it can be a `Dropdown.Trigger` child; in compact mode only the
|
|
377
|
+
* The signed-in person at the bottom of the rail: avatar, name, a muted line and, as a menu
|
|
378
|
+
* trigger, a chevron. A button, so it can be a `Dropdown.Trigger` child; in compact mode only the
|
|
379
|
+
* avatar stays.
|
|
379
380
|
*/
|
|
380
381
|
declare function SidebarAccount({ description, className, children, type, ...rest }: SidebarAccountProps): import("react/jsx-runtime").JSX.Element;
|
|
381
382
|
declare namespace SidebarAccount {
|
package/dist/skill/anti-slop.md
CHANGED
|
@@ -242,8 +242,8 @@ The same for one value in several units — one heading, the fields in a row, th
|
|
|
242
242
|
| `<Button.Root onClick={() => location.assign(url)}>` | `<Button.Root asChild><a href={url}>…</a></Button.Root>` or `LinkButton` |
|
|
243
243
|
| a hand-built spinner, shimmer, grey placeholder box or «Loading…» text | `loading` on Button / Select / DataTable; a region: `Skeleton` of its content inside `Crossfade`; no shape to hold: `Spinner` |
|
|
244
244
|
| `{loading ? <Spinner /> : <List />}` — a state that flips | `<Crossfade state={status}>` around the region, `Skeleton` while loading |
|
|
245
|
-
| a search Input + a row of filter Selects above a table | `SmartFilter`
|
|
246
|
-
| the table's search, filter or «Колонки» in the page's bar
|
|
245
|
+
| a search Input + a row of filter Selects above a table | `SmartFilter` with the table it filters: its toolbar and chips in the table's `toolbar` / `toolbarChips` on every page ([where the filters go](composition.md#where-the-filters-go)) |
|
|
246
|
+
| the table's search, filter or «Колонки» in the page's bar, the page that is only the table included | the bar keeps the title, the sections, the view, a period that drives every block and the buttons; the table's tools go in its own `toolbar` ([where the filters go](composition.md#where-the-filters-go)) |
|
|
247
247
|
| a grid of «label: value» text pairs for one record | `DescriptionList.Root` + `DescriptionList.Item` |
|
|
248
248
|
| a Modal hand-built as «Удалить?» with your own pending flag | `ConfirmDialog.Root` with an async `onConfirm` |
|
|
249
249
|
| a `<fieldset>` reset by hand, or `disabled` on every field while saving | `<Fieldset label disabled={saving}>` |
|
|
@@ -310,40 +310,38 @@ cell in every row:
|
|
|
310
310
|
```
|
|
311
311
|
|
|
312
312
|
```tsx good
|
|
313
|
-
// good —
|
|
313
|
+
// good — no row actions: the chooser stands beside the table's search, on the state the table reads
|
|
314
|
+
// declare: const FIELDS: Parameters<typeof SmartFilter.Root>[0]["fields"]
|
|
314
315
|
// declare: const orders: { id: string; client: string; total: number }[]
|
|
315
316
|
// declare: const hidden: string[]
|
|
316
317
|
// declare: const setHidden: (hidden: string[]) => void
|
|
317
|
-
<
|
|
318
|
-
<
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
/>
|
|
345
|
-
</Page.Body>
|
|
346
|
-
</Page.Root>
|
|
318
|
+
<SmartFilter.Root fields={FIELDS}>
|
|
319
|
+
<DataTable
|
|
320
|
+
columns={[
|
|
321
|
+
{ id: "id", header: "Заказ", accessor: "id", hideable: false },
|
|
322
|
+
{ id: "client", header: "Клиент", accessor: "client" },
|
|
323
|
+
{ id: "total", header: "Сумма", accessor: "total", numeric: true },
|
|
324
|
+
]}
|
|
325
|
+
hiddenColumns={hidden}
|
|
326
|
+
rows={orders}
|
|
327
|
+
getRowKey={(row) => row.id}
|
|
328
|
+
toolbar={
|
|
329
|
+
<>
|
|
330
|
+
<SmartFilter.Toolbar />
|
|
331
|
+
<DataTableColumnsMenu
|
|
332
|
+
columns={[
|
|
333
|
+
{ id: "id", header: "Заказ", accessor: "id", hideable: false },
|
|
334
|
+
{ id: "client", header: "Клиент", accessor: "client" },
|
|
335
|
+
{ id: "total", header: "Сумма", accessor: "total", numeric: true },
|
|
336
|
+
]}
|
|
337
|
+
hiddenColumns={hidden}
|
|
338
|
+
onHiddenColumnsChange={setHidden}
|
|
339
|
+
/>
|
|
340
|
+
</>
|
|
341
|
+
}
|
|
342
|
+
toolbarChips={<SmartFilter.Chips />}
|
|
343
|
+
/>
|
|
344
|
+
</SmartFilter.Root>
|
|
347
345
|
```
|
|
348
346
|
|
|
349
347
|
## 7b. A top bar out of its groups
|
|
@@ -409,13 +407,13 @@ Buttons in two places, buttons with only text:
|
|
|
409
407
|
</Page.Actions>
|
|
410
408
|
```
|
|
411
409
|
|
|
412
|
-
`SmartFilter.Root` inside the bar:
|
|
410
|
+
`SmartFilter.Root` inside the bar of a page whose filter narrows several views (a board and its list):
|
|
413
411
|
|
|
414
412
|
```tsx partial
|
|
415
413
|
// bad — the filter's state wraps one group, its chips land outside it
|
|
416
414
|
<Page.TopBar>
|
|
417
415
|
<SmartFilter.Root fields={FIELDS}>
|
|
418
|
-
<Page.Tools aria-label="
|
|
416
|
+
<Page.Tools aria-label="Сделки">
|
|
419
417
|
<SmartFilter.Toolbar />
|
|
420
418
|
</Page.Tools>
|
|
421
419
|
</SmartFilter.Root>
|
|
@@ -429,9 +427,9 @@ Buttons in two places, buttons with only text:
|
|
|
429
427
|
<Page.Root>
|
|
430
428
|
<Page.TopBar>
|
|
431
429
|
<Page.Heading>
|
|
432
|
-
<Page.Title
|
|
430
|
+
<Page.Title>Сделки</Page.Title>
|
|
433
431
|
</Page.Heading>
|
|
434
|
-
<Page.Tools aria-label="
|
|
432
|
+
<Page.Tools aria-label="Сделки">
|
|
435
433
|
<SmartFilter.Toolbar />
|
|
436
434
|
</Page.Tools>
|
|
437
435
|
<Page.Chips>
|
|
@@ -25,7 +25,7 @@ import type { ControlSize, PaletteColor, TextTone, Tone, Variant } from "prime-u
|
|
|
25
25
|
| Text value | native `value` / `onChange` + `onValueChange(string)` | Input (`Input.Field`), Textarea. |
|
|
26
26
|
| Other state | `x` / `defaultX` / `onXChange(x)` | Badge `pressed`; Button `pressed`; ChartFrame `hiddenSeries`; Chat `selected`, `search`; ChatList `search`; ColorPicker `format`; CommandMenu `search`; Composer `recordMode`; DataTable `hiddenColumns`, `columnOrder`, `page`, `sort`, `selected`, `expanded`, `collapsedGroups`; ExampleFrame `viewport`, `colorScheme`; Gallery `index`; Gantt `scale`; Kanban `columnOrder`; MediaViewer `index`; Message.Media `index`; Select `search`; SmartFilter `search`; VideoNote `playing`, `transcriptExpanded`; VoicePlayer `rate`, `playing`, `transcriptExpanded`; Sidebar `mode`. |
|
|
27
27
|
| Checked | `checked` / `defaultChecked` / `onCheckedChange(checked)` | Checkbox, Dropdown.CheckboxItem, Switch, Thumbnail. |
|
|
28
|
-
| Open | `open` / `defaultOpen` / `onOpenChange(open)` | ColorPresets, CommandMenu, ConfirmDialog, Datepicker, Drawer, Dropdown, Gallery, MediaViewer, Message.Media, Modal, Popover, Select, Sheet, TagSelect, Tooltip, Page.Split, Sidebar, Sidebar.Group, Sidebar.Sub. |
|
|
28
|
+
| Open | `open` / `defaultOpen` / `onOpenChange(open)` | ColorPresets, CommandMenu, Composer.Keyboard, ConfirmDialog, Datepicker, Drawer, Dropdown, Gallery, MediaViewer, Message.Media, Modal, Popover, Select, Sheet, TagSelect, Tooltip, Page.Split, Sidebar, Sidebar.Group, Sidebar.Sub. |
|
|
29
29
|
| Dismiss | `closeOnOutsideClick` (default `true`), `closeOnEscape` (default `true`) | Both on every overlay that opens a layer: the dialogs, the floating panels, the panels of Select, TagSelect, Datepicker and ColorPresets, the `suggestions` list of Input, the off-canvas Sidebar and the reaction bar of a message. `ConfirmDialog` takes both with `closeOnOutsideClick` default `false` (Escape is held while `onConfirm` runs). Tooltip and Notification take neither. A form holds closing with `Modal.Guard` / `Drawer.Guard` / `Sheet.Guard`. |
|
|
30
30
|
| Selection mode | `multiple` | One vs many (Select, Accordion) — never `type`. `mode` only for structurally different values (Datepicker `single \| range`). |
|
|
31
31
|
| Status words | `selected`, `current`, `pressed` | `current` marks a navigation location (`Sidebar.Item`, `Breadcrumb.Item`) and sets `aria-current`; `pressed` a toggle button. |
|
|
@@ -168,6 +168,54 @@ export function InviteForm() {
|
|
|
168
168
|
(`.form { display: grid; gap: var(--prime-space-5); }`.) A full create form with groups, validation,
|
|
169
169
|
loading and a toast: [the Drawer screen](reference/components/drawer/examples/screen.tsx).
|
|
170
170
|
|
|
171
|
+
### Field outcomes: a mark for success, words for a problem
|
|
172
|
+
|
|
173
|
+
A field answers a check in one of two ways. Success is quiet; a problem is said.
|
|
174
|
+
|
|
175
|
+
- **A confirmed value is a mark.** A repeat that matches, a code that passed, a free address: `valid`
|
|
176
|
+
on the root draws a check at the field's end and says it to a screen reader — no words under the
|
|
177
|
+
field. Never a `hint` with `hintTone="success"` that writes «Пароли совпадают»: the person reads a
|
|
178
|
+
sentence to learn that nothing is wrong. `hintTone` stays for the status of a value that is not a
|
|
179
|
+
check (a deadline that nears).
|
|
180
|
+
- **A problem is words.** `error` says what is wrong and how to fix it, under the field it concerns.
|
|
181
|
+
Red rings and a shake alone leave the person guessing.
|
|
182
|
+
- **A policy is enforced, not shown.** A new password is `strength` with `minStrength`: below the
|
|
183
|
+
level the form refuses to submit and says why, the `hint` names the requirement in words («От 10
|
|
184
|
+
символов, буквы и цифры»), and the server checks the same rule. A meter alone lets the weak
|
|
185
|
+
password through ([password-policy.tsx](reference/components/input/examples/password-policy.tsx)).
|
|
186
|
+
- **A refused sign-in** marks the e-mail `invalid` and puts the reason on the password as its `error`
|
|
187
|
+
(«Неверная почта или пароль») — one sentence that names neither field as the culprit, no Banner
|
|
188
|
+
over the form ([LoginForm](reference/components/login-form/COMPONENT.md)).
|
|
189
|
+
|
|
190
|
+
```tsx partial
|
|
191
|
+
// bad — a meter that lets anything through and a green sentence for a match
|
|
192
|
+
<Input.Root label="Новый пароль" strength>…</Input.Root>
|
|
193
|
+
<Input.Root label="Повторите пароль" hint="Пароли совпадают" hintTone="success">…</Input.Root>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
```tsx good
|
|
197
|
+
// good — the policy stops the submit, the match is a check, a mismatch is words
|
|
198
|
+
// declare: const matches: boolean
|
|
199
|
+
// declare: const mismatch: boolean
|
|
200
|
+
<>
|
|
201
|
+
<Input.Root label="Новый пароль" strength minStrength={3} hint="От 10 символов, буквы и цифры">
|
|
202
|
+
<Input.Wrapper>
|
|
203
|
+
<Input.Field type="password" autoComplete="new-password" />
|
|
204
|
+
<Input.RevealButton />
|
|
205
|
+
</Input.Wrapper>
|
|
206
|
+
</Input.Root>
|
|
207
|
+
<Input.Root
|
|
208
|
+
label="Повторите пароль"
|
|
209
|
+
valid={matches}
|
|
210
|
+
error={mismatch ? "Пароли не совпадают" : undefined}
|
|
211
|
+
>
|
|
212
|
+
<Input.Wrapper>
|
|
213
|
+
<Input.Field type="password" autoComplete="new-password" />
|
|
214
|
+
</Input.Wrapper>
|
|
215
|
+
</Input.Root>
|
|
216
|
+
</>
|
|
217
|
+
```
|
|
218
|
+
|
|
171
219
|
### Choice controls
|
|
172
220
|
|
|
173
221
|
Checkbox, Switch and Radio: the root renders the `<label>` with the input and the visible text is
|
|
@@ -263,7 +311,7 @@ A standalone destructive action that is easy to redo by hand is `Button.Root hol
|
|
|
263
311
|
action in `onConfirm` ([the recipe](motion.md#5-recipes)).
|
|
264
312
|
|
|
265
313
|
`tone="danger"`. A standalone trigger that opens a confirm (danger zone) is `variant="outline"`; inside
|
|
266
|
-
a bar of other actions (
|
|
314
|
+
a bar of other actions (card actions) it is `soft`; in a Dropdown it is the last item
|
|
267
315
|
with `tone="danger"`; an inline remove in a media or file row is `ghost`. The confirm itself is
|
|
268
316
|
[ConfirmDialog](reference/components/confirm-dialog/COMPONENT.md) — never a hand-built Modal: it is a
|
|
269
317
|
Modal `s` with a solid danger confirm (`tone="accent"` for an outward action that destroys nothing, like
|