@ttoss/fsl-ui 0.2.4
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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/_virtual/_rolldown/runtime.cjs +30 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs +129 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.cts +61 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.mts +61 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.mjs +126 -0
- package/dist/components/Button/Button.cjs +80 -0
- package/dist/components/Button/Button.d.cts +68 -0
- package/dist/components/Button/Button.d.mts +68 -0
- package/dist/components/Button/Button.mjs +79 -0
- package/dist/components/Checkbox/Checkbox.cjs +183 -0
- package/dist/components/Checkbox/Checkbox.d.cts +45 -0
- package/dist/components/Checkbox/Checkbox.d.mts +45 -0
- package/dist/components/Checkbox/Checkbox.mjs +182 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.cjs +97 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.d.cts +56 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.d.mts +56 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.mjs +96 -0
- package/dist/components/FileTrigger/FileTrigger.cjs +51 -0
- package/dist/components/FileTrigger/FileTrigger.d.cts +52 -0
- package/dist/components/FileTrigger/FileTrigger.d.mts +52 -0
- package/dist/components/FileTrigger/FileTrigger.mjs +50 -0
- package/dist/components/GridList/GridList.cjs +220 -0
- package/dist/components/GridList/GridList.d.cts +72 -0
- package/dist/components/GridList/GridList.d.mts +72 -0
- package/dist/components/GridList/GridList.mjs +217 -0
- package/dist/components/Group/Group.cjs +77 -0
- package/dist/components/Group/Group.d.cts +61 -0
- package/dist/components/Group/Group.d.mts +61 -0
- package/dist/components/Group/Group.mjs +74 -0
- package/dist/components/Icon/Icon.cjs +52 -0
- package/dist/components/Icon/Icon.mjs +52 -0
- package/dist/components/Icon/glyphs.cjs +72 -0
- package/dist/components/Icon/glyphs.mjs +63 -0
- package/dist/components/Link/Link.cjs +62 -0
- package/dist/components/Link/Link.d.cts +38 -0
- package/dist/components/Link/Link.d.mts +38 -0
- package/dist/components/Link/Link.mjs +61 -0
- package/dist/components/ListBox/ListBox.cjs +126 -0
- package/dist/components/ListBox/ListBox.d.cts +63 -0
- package/dist/components/ListBox/ListBox.d.mts +63 -0
- package/dist/components/ListBox/ListBox.mjs +123 -0
- package/dist/components/Meter/Meter.cjs +126 -0
- package/dist/components/Meter/Meter.d.cts +63 -0
- package/dist/components/Meter/Meter.d.mts +63 -0
- package/dist/components/Meter/Meter.mjs +125 -0
- package/dist/components/NumberField/NumberField.cjs +221 -0
- package/dist/components/NumberField/NumberField.d.cts +68 -0
- package/dist/components/NumberField/NumberField.d.mts +68 -0
- package/dist/components/NumberField/NumberField.mjs +220 -0
- package/dist/components/ProgressBar/ProgressBar.cjs +132 -0
- package/dist/components/ProgressBar/ProgressBar.d.cts +56 -0
- package/dist/components/ProgressBar/ProgressBar.d.mts +56 -0
- package/dist/components/ProgressBar/ProgressBar.mjs +129 -0
- package/dist/components/RadioGroup/RadioGroup.cjs +218 -0
- package/dist/components/RadioGroup/RadioGroup.d.cts +82 -0
- package/dist/components/RadioGroup/RadioGroup.d.mts +82 -0
- package/dist/components/RadioGroup/RadioGroup.mjs +215 -0
- package/dist/components/Select/Select.cjs +253 -0
- package/dist/components/Select/Select.d.cts +86 -0
- package/dist/components/Select/Select.d.mts +86 -0
- package/dist/components/Select/Select.mjs +250 -0
- package/dist/components/Separator/Separator.cjs +64 -0
- package/dist/components/Separator/Separator.d.cts +44 -0
- package/dist/components/Separator/Separator.d.mts +44 -0
- package/dist/components/Separator/Separator.mjs +63 -0
- package/dist/components/Slider/Slider.cjs +160 -0
- package/dist/components/Slider/Slider.d.cts +45 -0
- package/dist/components/Slider/Slider.d.mts +45 -0
- package/dist/components/Slider/Slider.mjs +159 -0
- package/dist/components/Switch/Switch.cjs +176 -0
- package/dist/components/Switch/Switch.d.cts +43 -0
- package/dist/components/Switch/Switch.d.mts +43 -0
- package/dist/components/Switch/Switch.mjs +175 -0
- package/dist/components/Tabs/Tabs.cjs +180 -0
- package/dist/components/Tabs/Tabs.d.cts +85 -0
- package/dist/components/Tabs/Tabs.d.mts +85 -0
- package/dist/components/Tabs/Tabs.mjs +173 -0
- package/dist/components/Toast/Toast.cjs +218 -0
- package/dist/components/Toast/Toast.d.cts +96 -0
- package/dist/components/Toast/Toast.d.mts +96 -0
- package/dist/components/Toast/Toast.mjs +214 -0
- package/dist/components/ToggleButton/ToggleButton.cjs +125 -0
- package/dist/components/ToggleButton/ToggleButton.d.cts +43 -0
- package/dist/components/ToggleButton/ToggleButton.d.mts +43 -0
- package/dist/components/ToggleButton/ToggleButton.mjs +124 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.cjs +48 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.d.cts +41 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.d.mts +41 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.mjs +47 -0
- package/dist/components/Toolbar/Toolbar.cjs +67 -0
- package/dist/components/Toolbar/Toolbar.d.cts +60 -0
- package/dist/components/Toolbar/Toolbar.d.mts +60 -0
- package/dist/components/Toolbar/Toolbar.mjs +66 -0
- package/dist/composites/Accordion/Accordion.cjs +279 -0
- package/dist/composites/Accordion/Accordion.d.cts +176 -0
- package/dist/composites/Accordion/Accordion.d.mts +176 -0
- package/dist/composites/Accordion/Accordion.mjs +270 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.cjs +206 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.d.cts +166 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.d.mts +166 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.mjs +203 -0
- package/dist/composites/Dialog/Dialog.cjs +372 -0
- package/dist/composites/Dialog/Dialog.d.cts +234 -0
- package/dist/composites/Dialog/Dialog.d.mts +234 -0
- package/dist/composites/Dialog/Dialog.mjs +360 -0
- package/dist/composites/Disclosure/Disclosure.cjs +214 -0
- package/dist/composites/Disclosure/Disclosure.d.cts +118 -0
- package/dist/composites/Disclosure/Disclosure.d.mts +118 -0
- package/dist/composites/Disclosure/Disclosure.mjs +207 -0
- package/dist/composites/Form/Form.cjs +161 -0
- package/dist/composites/Form/Form.d.cts +176 -0
- package/dist/composites/Form/Form.d.mts +176 -0
- package/dist/composites/Form/Form.mjs +156 -0
- package/dist/composites/Menu/Menu.cjs +190 -0
- package/dist/composites/Menu/Menu.d.cts +158 -0
- package/dist/composites/Menu/Menu.d.mts +158 -0
- package/dist/composites/Menu/Menu.mjs +186 -0
- package/dist/composites/Popover/Popover.cjs +80 -0
- package/dist/composites/Popover/Popover.d.cts +56 -0
- package/dist/composites/Popover/Popover.d.mts +56 -0
- package/dist/composites/Popover/Popover.mjs +78 -0
- package/dist/composites/SearchField/SearchField.cjs +194 -0
- package/dist/composites/SearchField/SearchField.d.cts +81 -0
- package/dist/composites/SearchField/SearchField.d.mts +81 -0
- package/dist/composites/SearchField/SearchField.mjs +189 -0
- package/dist/composites/TagGroup/TagGroup.cjs +212 -0
- package/dist/composites/TagGroup/TagGroup.d.cts +84 -0
- package/dist/composites/TagGroup/TagGroup.d.mts +84 -0
- package/dist/composites/TagGroup/TagGroup.mjs +209 -0
- package/dist/composites/TextArea/TextArea.cjs +190 -0
- package/dist/composites/TextArea/TextArea.d.cts +95 -0
- package/dist/composites/TextArea/TextArea.d.mts +95 -0
- package/dist/composites/TextArea/TextArea.mjs +181 -0
- package/dist/composites/TextField/TextField.cjs +200 -0
- package/dist/composites/TextField/TextField.d.cts +111 -0
- package/dist/composites/TextField/TextField.d.mts +111 -0
- package/dist/composites/TextField/TextField.mjs +191 -0
- package/dist/composites/Tooltip/Tooltip.cjs +79 -0
- package/dist/composites/Tooltip/Tooltip.d.cts +53 -0
- package/dist/composites/Tooltip/Tooltip.d.mts +53 -0
- package/dist/composites/Tooltip/Tooltip.mjs +77 -0
- package/dist/composites/Wizard/Wizard.cjs +333 -0
- package/dist/composites/Wizard/Wizard.d.cts +206 -0
- package/dist/composites/Wizard/Wizard.d.mts +206 -0
- package/dist/composites/Wizard/Wizard.mjs +324 -0
- package/dist/composites/scope.cjs +124 -0
- package/dist/composites/scope.mjs +121 -0
- package/dist/index.cjs +186 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.mts +37 -0
- package/dist/index.mjs +39 -0
- package/dist/semantics/componentMeta.d.cts +37 -0
- package/dist/semantics/componentMeta.d.mts +37 -0
- package/dist/semantics/index.cjs +16 -0
- package/dist/semantics/index.d.cts +4 -0
- package/dist/semantics/index.d.mts +4 -0
- package/dist/semantics/index.mjs +3 -0
- package/dist/semantics/taxonomy.cjs +150 -0
- package/dist/semantics/taxonomy.d.cts +153 -0
- package/dist/semantics/taxonomy.d.mts +153 -0
- package/dist/semantics/taxonomy.mjs +140 -0
- package/dist/tokens/escapeHatch.cjs +13 -0
- package/dist/tokens/escapeHatch.mjs +13 -0
- package/dist/tokens/focusRing.cjs +28 -0
- package/dist/tokens/focusRing.mjs +28 -0
- package/dist/tokens/keyframes.cjs +76 -0
- package/dist/tokens/keyframes.mjs +75 -0
- package/dist/tokens/resolveInteractiveStyle.cjs +41 -0
- package/dist/tokens/resolveInteractiveStyle.mjs +41 -0
- package/llms.txt +142 -0
- package/package.json +70 -0
- package/src/tokens/CONTRACT.md +505 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
|
|
2
|
+
import { ConsequencesFor, EvaluationsFor } from "../../semantics/taxonomy.cjs";
|
|
3
|
+
import { DialogActionsPlatform } from "../Dialog/Dialog.cjs";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
//#region src/composites/ConfirmationDialog/ConfirmationDialog.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Formal semantic identity — ConfirmationDialog root (Overlay entity).
|
|
9
|
+
*/
|
|
10
|
+
declare const confirmationDialogMeta: {
|
|
11
|
+
readonly displayName: "ConfirmationDialog";
|
|
12
|
+
readonly entity: "Overlay";
|
|
13
|
+
readonly structure: "root";
|
|
14
|
+
};
|
|
15
|
+
interface ConfirmationDialogBaseProps {
|
|
16
|
+
/**
|
|
17
|
+
* The element that opens the dialog. Typically a {@link Button}.
|
|
18
|
+
* React Aria's `DialogTrigger` wires open/close state to it.
|
|
19
|
+
*/
|
|
20
|
+
trigger: React.ReactElement;
|
|
21
|
+
/** Heading rendered inside the dialog. */
|
|
22
|
+
title: React.ReactNode;
|
|
23
|
+
/** Body content — usually a short explanation of what will happen. */
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* Label for the confirm button. **Required** — flow-critical labels
|
|
27
|
+
* have no English default (CONTRIBUTING §6 / ADR-001); the caller
|
|
28
|
+
* supplies localized copy.
|
|
29
|
+
*/
|
|
30
|
+
confirmLabel: React.ReactNode;
|
|
31
|
+
/**
|
|
32
|
+
* Label for the cancel / dismiss button. **Required** — see
|
|
33
|
+
* `confirmLabel`.
|
|
34
|
+
*/
|
|
35
|
+
cancelLabel: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Semantic emphasis for the confirm button (authorial voice, orthogonal
|
|
38
|
+
* to `consequence`). `destructive` consequence does not imply a
|
|
39
|
+
* `negative` evaluation — color is the author's call.
|
|
40
|
+
* @default 'primary'
|
|
41
|
+
*/
|
|
42
|
+
evaluation?: EvaluationsFor<'Action'>;
|
|
43
|
+
/** Called only when confirmation is definitive (after arming, if required). */
|
|
44
|
+
onConfirm: () => void;
|
|
45
|
+
/**
|
|
46
|
+
* Time window (ms) during which the *second* click of a destructive
|
|
47
|
+
* confirmation must land. After expiry, arming resets silently.
|
|
48
|
+
* Ignored for non-destructive consequences.
|
|
49
|
+
* @default 2000
|
|
50
|
+
*/
|
|
51
|
+
armWindowMs?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Platform convention forwarded to the underlying {@link DialogActions}.
|
|
54
|
+
* @default 'ios'
|
|
55
|
+
*/
|
|
56
|
+
platform?: DialogActionsPlatform;
|
|
57
|
+
}
|
|
58
|
+
interface ConfirmationDialogDestructiveProps extends ConfirmationDialogBaseProps {
|
|
59
|
+
/**
|
|
60
|
+
* Effect on state the confirm action produces. **Selects the confirm
|
|
61
|
+
* mechanism** (see component-level comment): `destructive` requires a
|
|
62
|
+
* two-click arming protocol; `neutral` / `committing` confirm on the
|
|
63
|
+
* first click.
|
|
64
|
+
*/
|
|
65
|
+
consequence: Extract<ConsequencesFor<'Action'>, 'destructive'>;
|
|
66
|
+
/**
|
|
67
|
+
* Label shown on the confirm button while it is *armed* (first click
|
|
68
|
+
* already received, awaiting second). **Required** for `destructive` —
|
|
69
|
+
* it is flow-critical copy the user must be able to read.
|
|
70
|
+
*/
|
|
71
|
+
armedLabel: React.ReactNode;
|
|
72
|
+
}
|
|
73
|
+
interface ConfirmationDialogNonDestructiveProps extends ConfirmationDialogBaseProps {
|
|
74
|
+
/**
|
|
75
|
+
* Effect on state the confirm action produces. `neutral` / `committing`
|
|
76
|
+
* confirm on the first click — no arming protocol.
|
|
77
|
+
* @default 'neutral'
|
|
78
|
+
*/
|
|
79
|
+
consequence?: Exclude<ConsequencesFor<'Action'>, 'destructive'>;
|
|
80
|
+
/** Only destructive confirmations have an armed state. */
|
|
81
|
+
armedLabel?: never;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Props for {@link ConfirmationDialog}. A discriminated union on
|
|
85
|
+
* `consequence`: `destructive` requires `armedLabel`; other consequences
|
|
86
|
+
* forbid it. The type system enforces what the arming mechanism needs.
|
|
87
|
+
*/
|
|
88
|
+
type ConfirmationDialogProps = ConfirmationDialogDestructiveProps | ConfirmationDialogNonDestructiveProps;
|
|
89
|
+
/**
|
|
90
|
+
* A Dialog composite that demands confirmation before invoking a side effect.
|
|
91
|
+
*
|
|
92
|
+
* Reads its `consequence` prop at runtime to select the confirm mechanism —
|
|
93
|
+
* this is what makes `Consequence` a behavior-driving FSL dimension and not
|
|
94
|
+
* decoration. For `destructive`, the confirm button emits
|
|
95
|
+
* `data-arming="true"` while awaiting the second click. (`data-pending` is
|
|
96
|
+
* reserved by React Aria for `isPending`, which also toggles
|
|
97
|
+
* `aria-disabled` — unsuitable here because the armed button must remain
|
|
98
|
+
* clickable for the second press.)
|
|
99
|
+
*
|
|
100
|
+
* @example Single-click commit
|
|
101
|
+
* ```tsx
|
|
102
|
+
* <ConfirmationDialog
|
|
103
|
+
* trigger={<Button>Publish</Button>}
|
|
104
|
+
* title="Publish post?"
|
|
105
|
+
* confirmLabel="Publish"
|
|
106
|
+
* cancelLabel="Cancel"
|
|
107
|
+
* consequence="committing"
|
|
108
|
+
* onConfirm={publish}
|
|
109
|
+
* >
|
|
110
|
+
* Your post will become visible to everyone.
|
|
111
|
+
* </ConfirmationDialog>
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* @example Two-click destructive confirm
|
|
115
|
+
* ```tsx
|
|
116
|
+
* <ConfirmationDialog
|
|
117
|
+
* trigger={<Button evaluation="negative">Delete account</Button>}
|
|
118
|
+
* title="Delete account?"
|
|
119
|
+
* confirmLabel="Delete"
|
|
120
|
+
* armedLabel="Click again to confirm"
|
|
121
|
+
* cancelLabel="Cancel"
|
|
122
|
+
* consequence="destructive"
|
|
123
|
+
* evaluation="negative"
|
|
124
|
+
* onConfirm={deleteAccount}
|
|
125
|
+
* >
|
|
126
|
+
* This action cannot be undone.
|
|
127
|
+
* </ConfirmationDialog>
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example Visually destructive but single-click (escape-hatch pattern)
|
|
131
|
+
*
|
|
132
|
+
* Use `consequence="committing"` with `evaluation="negative"` when the
|
|
133
|
+
* action is irreversible-looking but does **not** warrant two-click arming
|
|
134
|
+
* (e.g. "Remove from list", "Unsubscribe"). The confirm button renders in
|
|
135
|
+
* the negative color without engaging the arming protocol.
|
|
136
|
+
*
|
|
137
|
+
* ```tsx
|
|
138
|
+
* <ConfirmationDialog
|
|
139
|
+
* trigger={<Button evaluation="negative">Unsubscribe</Button>}
|
|
140
|
+
* title="Unsubscribe?"
|
|
141
|
+
* confirmLabel="Unsubscribe"
|
|
142
|
+
* cancelLabel="Cancel"
|
|
143
|
+
* consequence="committing"
|
|
144
|
+
* evaluation="negative"
|
|
145
|
+
* onConfirm={unsubscribe}
|
|
146
|
+
* />
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare const ConfirmationDialog: {
|
|
150
|
+
({
|
|
151
|
+
trigger,
|
|
152
|
+
title,
|
|
153
|
+
children,
|
|
154
|
+
confirmLabel,
|
|
155
|
+
armedLabel,
|
|
156
|
+
cancelLabel,
|
|
157
|
+
consequence,
|
|
158
|
+
evaluation,
|
|
159
|
+
onConfirm,
|
|
160
|
+
armWindowMs,
|
|
161
|
+
platform
|
|
162
|
+
}: ConfirmationDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
163
|
+
displayName: "ConfirmationDialog";
|
|
164
|
+
};
|
|
165
|
+
//#endregion
|
|
166
|
+
export { ConfirmationDialog, ConfirmationDialogProps, confirmationDialogMeta };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
|
|
2
|
+
import { ConsequencesFor, EvaluationsFor } from "../../semantics/taxonomy.mjs";
|
|
3
|
+
import { DialogActionsPlatform } from "../Dialog/Dialog.mjs";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
//#region src/composites/ConfirmationDialog/ConfirmationDialog.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Formal semantic identity — ConfirmationDialog root (Overlay entity).
|
|
9
|
+
*/
|
|
10
|
+
declare const confirmationDialogMeta: {
|
|
11
|
+
readonly displayName: "ConfirmationDialog";
|
|
12
|
+
readonly entity: "Overlay";
|
|
13
|
+
readonly structure: "root";
|
|
14
|
+
};
|
|
15
|
+
interface ConfirmationDialogBaseProps {
|
|
16
|
+
/**
|
|
17
|
+
* The element that opens the dialog. Typically a {@link Button}.
|
|
18
|
+
* React Aria's `DialogTrigger` wires open/close state to it.
|
|
19
|
+
*/
|
|
20
|
+
trigger: React.ReactElement;
|
|
21
|
+
/** Heading rendered inside the dialog. */
|
|
22
|
+
title: React.ReactNode;
|
|
23
|
+
/** Body content — usually a short explanation of what will happen. */
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* Label for the confirm button. **Required** — flow-critical labels
|
|
27
|
+
* have no English default (CONTRIBUTING §6 / ADR-001); the caller
|
|
28
|
+
* supplies localized copy.
|
|
29
|
+
*/
|
|
30
|
+
confirmLabel: React.ReactNode;
|
|
31
|
+
/**
|
|
32
|
+
* Label for the cancel / dismiss button. **Required** — see
|
|
33
|
+
* `confirmLabel`.
|
|
34
|
+
*/
|
|
35
|
+
cancelLabel: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Semantic emphasis for the confirm button (authorial voice, orthogonal
|
|
38
|
+
* to `consequence`). `destructive` consequence does not imply a
|
|
39
|
+
* `negative` evaluation — color is the author's call.
|
|
40
|
+
* @default 'primary'
|
|
41
|
+
*/
|
|
42
|
+
evaluation?: EvaluationsFor<'Action'>;
|
|
43
|
+
/** Called only when confirmation is definitive (after arming, if required). */
|
|
44
|
+
onConfirm: () => void;
|
|
45
|
+
/**
|
|
46
|
+
* Time window (ms) during which the *second* click of a destructive
|
|
47
|
+
* confirmation must land. After expiry, arming resets silently.
|
|
48
|
+
* Ignored for non-destructive consequences.
|
|
49
|
+
* @default 2000
|
|
50
|
+
*/
|
|
51
|
+
armWindowMs?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Platform convention forwarded to the underlying {@link DialogActions}.
|
|
54
|
+
* @default 'ios'
|
|
55
|
+
*/
|
|
56
|
+
platform?: DialogActionsPlatform;
|
|
57
|
+
}
|
|
58
|
+
interface ConfirmationDialogDestructiveProps extends ConfirmationDialogBaseProps {
|
|
59
|
+
/**
|
|
60
|
+
* Effect on state the confirm action produces. **Selects the confirm
|
|
61
|
+
* mechanism** (see component-level comment): `destructive` requires a
|
|
62
|
+
* two-click arming protocol; `neutral` / `committing` confirm on the
|
|
63
|
+
* first click.
|
|
64
|
+
*/
|
|
65
|
+
consequence: Extract<ConsequencesFor<'Action'>, 'destructive'>;
|
|
66
|
+
/**
|
|
67
|
+
* Label shown on the confirm button while it is *armed* (first click
|
|
68
|
+
* already received, awaiting second). **Required** for `destructive` —
|
|
69
|
+
* it is flow-critical copy the user must be able to read.
|
|
70
|
+
*/
|
|
71
|
+
armedLabel: React.ReactNode;
|
|
72
|
+
}
|
|
73
|
+
interface ConfirmationDialogNonDestructiveProps extends ConfirmationDialogBaseProps {
|
|
74
|
+
/**
|
|
75
|
+
* Effect on state the confirm action produces. `neutral` / `committing`
|
|
76
|
+
* confirm on the first click — no arming protocol.
|
|
77
|
+
* @default 'neutral'
|
|
78
|
+
*/
|
|
79
|
+
consequence?: Exclude<ConsequencesFor<'Action'>, 'destructive'>;
|
|
80
|
+
/** Only destructive confirmations have an armed state. */
|
|
81
|
+
armedLabel?: never;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Props for {@link ConfirmationDialog}. A discriminated union on
|
|
85
|
+
* `consequence`: `destructive` requires `armedLabel`; other consequences
|
|
86
|
+
* forbid it. The type system enforces what the arming mechanism needs.
|
|
87
|
+
*/
|
|
88
|
+
type ConfirmationDialogProps = ConfirmationDialogDestructiveProps | ConfirmationDialogNonDestructiveProps;
|
|
89
|
+
/**
|
|
90
|
+
* A Dialog composite that demands confirmation before invoking a side effect.
|
|
91
|
+
*
|
|
92
|
+
* Reads its `consequence` prop at runtime to select the confirm mechanism —
|
|
93
|
+
* this is what makes `Consequence` a behavior-driving FSL dimension and not
|
|
94
|
+
* decoration. For `destructive`, the confirm button emits
|
|
95
|
+
* `data-arming="true"` while awaiting the second click. (`data-pending` is
|
|
96
|
+
* reserved by React Aria for `isPending`, which also toggles
|
|
97
|
+
* `aria-disabled` — unsuitable here because the armed button must remain
|
|
98
|
+
* clickable for the second press.)
|
|
99
|
+
*
|
|
100
|
+
* @example Single-click commit
|
|
101
|
+
* ```tsx
|
|
102
|
+
* <ConfirmationDialog
|
|
103
|
+
* trigger={<Button>Publish</Button>}
|
|
104
|
+
* title="Publish post?"
|
|
105
|
+
* confirmLabel="Publish"
|
|
106
|
+
* cancelLabel="Cancel"
|
|
107
|
+
* consequence="committing"
|
|
108
|
+
* onConfirm={publish}
|
|
109
|
+
* >
|
|
110
|
+
* Your post will become visible to everyone.
|
|
111
|
+
* </ConfirmationDialog>
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* @example Two-click destructive confirm
|
|
115
|
+
* ```tsx
|
|
116
|
+
* <ConfirmationDialog
|
|
117
|
+
* trigger={<Button evaluation="negative">Delete account</Button>}
|
|
118
|
+
* title="Delete account?"
|
|
119
|
+
* confirmLabel="Delete"
|
|
120
|
+
* armedLabel="Click again to confirm"
|
|
121
|
+
* cancelLabel="Cancel"
|
|
122
|
+
* consequence="destructive"
|
|
123
|
+
* evaluation="negative"
|
|
124
|
+
* onConfirm={deleteAccount}
|
|
125
|
+
* >
|
|
126
|
+
* This action cannot be undone.
|
|
127
|
+
* </ConfirmationDialog>
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example Visually destructive but single-click (escape-hatch pattern)
|
|
131
|
+
*
|
|
132
|
+
* Use `consequence="committing"` with `evaluation="negative"` when the
|
|
133
|
+
* action is irreversible-looking but does **not** warrant two-click arming
|
|
134
|
+
* (e.g. "Remove from list", "Unsubscribe"). The confirm button renders in
|
|
135
|
+
* the negative color without engaging the arming protocol.
|
|
136
|
+
*
|
|
137
|
+
* ```tsx
|
|
138
|
+
* <ConfirmationDialog
|
|
139
|
+
* trigger={<Button evaluation="negative">Unsubscribe</Button>}
|
|
140
|
+
* title="Unsubscribe?"
|
|
141
|
+
* confirmLabel="Unsubscribe"
|
|
142
|
+
* cancelLabel="Cancel"
|
|
143
|
+
* consequence="committing"
|
|
144
|
+
* evaluation="negative"
|
|
145
|
+
* onConfirm={unsubscribe}
|
|
146
|
+
* />
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare const ConfirmationDialog: {
|
|
150
|
+
({
|
|
151
|
+
trigger,
|
|
152
|
+
title,
|
|
153
|
+
children,
|
|
154
|
+
confirmLabel,
|
|
155
|
+
armedLabel,
|
|
156
|
+
cancelLabel,
|
|
157
|
+
consequence,
|
|
158
|
+
evaluation,
|
|
159
|
+
onConfirm,
|
|
160
|
+
armWindowMs,
|
|
161
|
+
platform
|
|
162
|
+
}: ConfirmationDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
163
|
+
displayName: "ConfirmationDialog";
|
|
164
|
+
};
|
|
165
|
+
//#endregion
|
|
166
|
+
export { ConfirmationDialog, ConfirmationDialogProps, confirmationDialogMeta };
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import { Button } from "../../components/Button/Button.mjs";
|
|
3
|
+
import { Dialog, DialogActions, DialogBody, DialogHeading, DialogModal, DialogTrigger } from "../Dialog/Dialog.mjs";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
//#region src/composites/ConfirmationDialog/ConfirmationDialog.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Formal semantic identity — ConfirmationDialog root (Overlay entity).
|
|
10
|
+
*/
|
|
11
|
+
var confirmationDialogMeta = {
|
|
12
|
+
displayName: "ConfirmationDialog",
|
|
13
|
+
entity: "Overlay",
|
|
14
|
+
structure: "root"
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Owns the consequence-driven confirm mechanism so the component body stays
|
|
18
|
+
* declarative. `destructive` requires a two-click arm→confirm within
|
|
19
|
+
* `armWindowMs`; everything else confirms on the first click. Toggling
|
|
20
|
+
* `consequence` mid-lifetime resets arming via the render-phase adjustment
|
|
21
|
+
* pattern (no extra commit).
|
|
22
|
+
*/
|
|
23
|
+
var useArmedConfirm = ({
|
|
24
|
+
consequence,
|
|
25
|
+
onConfirm,
|
|
26
|
+
armWindowMs
|
|
27
|
+
}) => {
|
|
28
|
+
const requiresArming = consequence === "destructive";
|
|
29
|
+
const [isArmed, setIsArmed] = React.useState(false);
|
|
30
|
+
const armTimerRef = React.useRef(null);
|
|
31
|
+
const clearArmTimer = React.useCallback(() => {
|
|
32
|
+
if (armTimerRef.current !== null) {
|
|
33
|
+
clearTimeout(armTimerRef.current);
|
|
34
|
+
armTimerRef.current = null;
|
|
35
|
+
}
|
|
36
|
+
}, []);
|
|
37
|
+
const [prevConsequence, setPrevConsequence] = React.useState(consequence);
|
|
38
|
+
if (prevConsequence !== consequence) {
|
|
39
|
+
setPrevConsequence(consequence);
|
|
40
|
+
setIsArmed(false);
|
|
41
|
+
}
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
return () => {
|
|
44
|
+
clearArmTimer();
|
|
45
|
+
};
|
|
46
|
+
}, [clearArmTimer]);
|
|
47
|
+
const reset = React.useCallback(() => {
|
|
48
|
+
clearArmTimer();
|
|
49
|
+
setIsArmed(false);
|
|
50
|
+
}, [clearArmTimer]);
|
|
51
|
+
const arm = React.useCallback(() => {
|
|
52
|
+
setIsArmed(true);
|
|
53
|
+
clearArmTimer();
|
|
54
|
+
armTimerRef.current = setTimeout(() => {
|
|
55
|
+
armTimerRef.current = null;
|
|
56
|
+
setIsArmed(false);
|
|
57
|
+
}, armWindowMs);
|
|
58
|
+
}, [armWindowMs, clearArmTimer]);
|
|
59
|
+
const confirm = close => {
|
|
60
|
+
if (!(!requiresArming || isArmed)) {
|
|
61
|
+
arm();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
reset();
|
|
65
|
+
onConfirm();
|
|
66
|
+
close();
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
isArmed,
|
|
70
|
+
confirm,
|
|
71
|
+
reset
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* A Dialog composite that demands confirmation before invoking a side effect.
|
|
76
|
+
*
|
|
77
|
+
* Reads its `consequence` prop at runtime to select the confirm mechanism —
|
|
78
|
+
* this is what makes `Consequence` a behavior-driving FSL dimension and not
|
|
79
|
+
* decoration. For `destructive`, the confirm button emits
|
|
80
|
+
* `data-arming="true"` while awaiting the second click. (`data-pending` is
|
|
81
|
+
* reserved by React Aria for `isPending`, which also toggles
|
|
82
|
+
* `aria-disabled` — unsuitable here because the armed button must remain
|
|
83
|
+
* clickable for the second press.)
|
|
84
|
+
*
|
|
85
|
+
* @example Single-click commit
|
|
86
|
+
* ```tsx
|
|
87
|
+
* <ConfirmationDialog
|
|
88
|
+
* trigger={<Button>Publish</Button>}
|
|
89
|
+
* title="Publish post?"
|
|
90
|
+
* confirmLabel="Publish"
|
|
91
|
+
* cancelLabel="Cancel"
|
|
92
|
+
* consequence="committing"
|
|
93
|
+
* onConfirm={publish}
|
|
94
|
+
* >
|
|
95
|
+
* Your post will become visible to everyone.
|
|
96
|
+
* </ConfirmationDialog>
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @example Two-click destructive confirm
|
|
100
|
+
* ```tsx
|
|
101
|
+
* <ConfirmationDialog
|
|
102
|
+
* trigger={<Button evaluation="negative">Delete account</Button>}
|
|
103
|
+
* title="Delete account?"
|
|
104
|
+
* confirmLabel="Delete"
|
|
105
|
+
* armedLabel="Click again to confirm"
|
|
106
|
+
* cancelLabel="Cancel"
|
|
107
|
+
* consequence="destructive"
|
|
108
|
+
* evaluation="negative"
|
|
109
|
+
* onConfirm={deleteAccount}
|
|
110
|
+
* >
|
|
111
|
+
* This action cannot be undone.
|
|
112
|
+
* </ConfirmationDialog>
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @example Visually destructive but single-click (escape-hatch pattern)
|
|
116
|
+
*
|
|
117
|
+
* Use `consequence="committing"` with `evaluation="negative"` when the
|
|
118
|
+
* action is irreversible-looking but does **not** warrant two-click arming
|
|
119
|
+
* (e.g. "Remove from list", "Unsubscribe"). The confirm button renders in
|
|
120
|
+
* the negative color without engaging the arming protocol.
|
|
121
|
+
*
|
|
122
|
+
* ```tsx
|
|
123
|
+
* <ConfirmationDialog
|
|
124
|
+
* trigger={<Button evaluation="negative">Unsubscribe</Button>}
|
|
125
|
+
* title="Unsubscribe?"
|
|
126
|
+
* confirmLabel="Unsubscribe"
|
|
127
|
+
* cancelLabel="Cancel"
|
|
128
|
+
* consequence="committing"
|
|
129
|
+
* evaluation="negative"
|
|
130
|
+
* onConfirm={unsubscribe}
|
|
131
|
+
* />
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
var ConfirmationDialog = ({
|
|
135
|
+
trigger,
|
|
136
|
+
title,
|
|
137
|
+
children,
|
|
138
|
+
confirmLabel,
|
|
139
|
+
armedLabel,
|
|
140
|
+
cancelLabel,
|
|
141
|
+
consequence = "neutral",
|
|
142
|
+
evaluation = "primary",
|
|
143
|
+
onConfirm,
|
|
144
|
+
armWindowMs = 2e3,
|
|
145
|
+
platform = "ios"
|
|
146
|
+
}) => {
|
|
147
|
+
const {
|
|
148
|
+
isArmed,
|
|
149
|
+
confirm,
|
|
150
|
+
reset
|
|
151
|
+
} = useArmedConfirm({
|
|
152
|
+
consequence,
|
|
153
|
+
onConfirm,
|
|
154
|
+
armWindowMs
|
|
155
|
+
});
|
|
156
|
+
const currentConfirmLabel = consequence === "destructive" && isArmed ? armedLabel : confirmLabel;
|
|
157
|
+
return /* @__PURE__ */jsxs(DialogTrigger, {
|
|
158
|
+
onOpenChange: open => {
|
|
159
|
+
if (!open) reset();
|
|
160
|
+
},
|
|
161
|
+
children: [trigger, /* @__PURE__ */jsx(DialogModal, {
|
|
162
|
+
children: /* @__PURE__ */jsx(Dialog, {
|
|
163
|
+
"data-scope": "confirmation-dialog",
|
|
164
|
+
"data-consequence": consequence,
|
|
165
|
+
children: ({
|
|
166
|
+
close
|
|
167
|
+
}) => {
|
|
168
|
+
return /* @__PURE__ */jsxs(Fragment, {
|
|
169
|
+
children: [/* @__PURE__ */jsx(DialogHeading, {
|
|
170
|
+
children: title
|
|
171
|
+
}), children !== void 0 && /* @__PURE__ */jsx(DialogBody, {
|
|
172
|
+
children
|
|
173
|
+
}), /* @__PURE__ */jsxs(DialogActions, {
|
|
174
|
+
platform,
|
|
175
|
+
children: [/* @__PURE__ */jsx(Button, {
|
|
176
|
+
slot: "close",
|
|
177
|
+
composition: "dismissAction",
|
|
178
|
+
evaluation: "muted",
|
|
179
|
+
onPress: () => {
|
|
180
|
+
return reset();
|
|
181
|
+
},
|
|
182
|
+
children: cancelLabel
|
|
183
|
+
}), /* @__PURE__ */jsx(Button, {
|
|
184
|
+
composition: "primaryAction",
|
|
185
|
+
consequence,
|
|
186
|
+
evaluation,
|
|
187
|
+
"data-arming": isArmed ? "true" : void 0,
|
|
188
|
+
onPress: () => {
|
|
189
|
+
return confirm(close);
|
|
190
|
+
},
|
|
191
|
+
children: currentConfirmLabel
|
|
192
|
+
})]
|
|
193
|
+
})]
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
})]
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
ConfirmationDialog.displayName = confirmationDialogMeta.displayName;
|
|
201
|
+
|
|
202
|
+
//#endregion
|
|
203
|
+
export { ConfirmationDialog, confirmationDialogMeta };
|