@usableapp/cardds 0.2.2 → 0.4.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 +35 -107
- package/cardds.js +6 -449
- package/css/actions.css +41 -35
- package/css/base.css +9 -9
- package/css/card.css +26 -25
- package/css/choice.css +50 -50
- package/css/forms.css +18 -18
- package/css/journey.css +16 -16
- package/css/layover.css +45 -45
- package/css/lists.css +43 -43
- package/css/media.css +33 -33
- package/css/numbers.css +25 -25
- package/css/people.css +11 -11
- package/css/sheet.css +29 -29
- package/css/step.css +6 -6
- package/css/theme.template.css +6 -3
- package/css/tokens.css +2 -0
- package/dist/actions/Btn.d.ts +109 -108
- package/dist/actions/BtnRow.d.ts +2 -2
- package/dist/actions/Chip.d.ts +102 -101
- package/dist/actions/Dot.d.ts +2 -2
- package/dist/actions/Dropdown.d.ts +36 -19
- package/dist/actions/Dropdown.js +41 -11
- package/dist/actions/Fab.d.ts +2 -2
- package/dist/actions/IconBtn.d.ts +298 -10
- package/dist/actions/IconBtn.js +6 -8
- package/dist/actions/Link.d.ts +105 -104
- package/dist/actions/Segment.d.ts +3 -3
- package/dist/cardds.css +354 -345
- package/dist/cards/Card.d.ts +99 -98
- package/dist/cards/CardFoot.d.ts +2 -2
- package/dist/cards/CardHead.d.ts +2 -2
- package/dist/cards/CardParts.d.ts +7 -7
- package/dist/cards/Placeholder.d.ts +2 -2
- package/dist/cards/Stat.d.ts +7 -5
- package/dist/cards/Stat.js +2 -2
- package/dist/choice/Calendar.d.ts +17 -6
- package/dist/choice/Calendar.js +9 -3
- package/dist/choice/Check.d.ts +3 -3
- package/dist/choice/DayStrip.d.ts +3 -3
- package/dist/choice/Mood.d.ts +2 -2
- package/dist/choice/Slider.d.ts +3 -3
- package/dist/choice/Slider.js +12 -8
- package/dist/forms/AddRow.d.ts +2 -2
- package/dist/forms/Composer.d.ts +2 -2
- package/dist/forms/Field.d.ts +2 -2
- package/dist/forms/FileBtn.d.ts +2 -2
- package/dist/forms/Pin.d.ts +10 -7
- package/dist/forms/Pin.js +47 -18
- package/dist/journey/NoteRow.d.ts +2 -2
- package/dist/journey/Route.d.ts +2 -2
- package/dist/journey/TileBadge.d.ts +2 -2
- package/dist/layover/Banner.d.ts +2 -2
- package/dist/layover/Callout.d.ts +2 -2
- package/dist/layover/Deck.d.ts +3 -3
- package/dist/lists/Bubble.d.ts +3 -3
- package/dist/lists/Kv.d.ts +5 -5
- package/dist/lists/Legend.d.ts +3 -3
- package/dist/lists/Row.d.ts +5 -4
- package/dist/lists/Timeline.d.ts +3 -3
- package/dist/media/MapArea.d.ts +2 -2
- package/dist/media/Mosaic.d.ts +3 -3
- package/dist/media/Postcard.d.ts +9 -5
- package/dist/media/Postcard.js +8 -3
- package/dist/media/Quote.d.ts +2 -2
- package/dist/media/Tile.d.ts +5 -4
- package/dist/numbers/Badge.d.ts +2 -2
- package/dist/numbers/Band.d.ts +3 -3
- package/dist/numbers/Bars.d.ts +3 -3
- package/dist/numbers/DotGrid.d.ts +2 -2
- package/dist/numbers/Picker.d.ts +20 -8
- package/dist/numbers/Picker.js +51 -13
- package/dist/numbers/Ring.d.ts +2 -2
- package/dist/numbers/Track.d.ts +3 -3
- package/dist/people/Avatar.d.ts +100 -99
- package/dist/people/AvatarPick.d.ts +3 -3
- package/dist/scaffold/ActionBar.d.ts +3 -3
- package/dist/scaffold/AppBar.d.ts +2 -2
- package/dist/scaffold/BaseContent.d.ts +7 -7
- package/dist/scaffold/BaseContent.js +4 -4
- package/dist/scaffold/Centre.d.ts +3 -3
- package/dist/scaffold/EmptyState.d.ts +2 -2
- package/dist/scaffold/FilterRow.d.ts +2 -2
- package/dist/scaffold/FloatBar.d.ts +105 -104
- package/dist/scaffold/PagerAt.d.ts +2 -2
- package/dist/scaffold/Screen.d.ts +100 -99
- package/dist/scaffold/TopBar.d.ts +2 -2
- package/dist/sheets/CardBack.d.ts +2 -2
- package/dist/sheets/Drawer.d.ts +19 -8
- package/dist/sheets/Drawer.js +29 -5
- package/dist/sheets/Sheet.d.ts +29 -16
- package/dist/sheets/Sheet.js +157 -11
- package/dist/sheets/SheetBody.d.ts +2 -2
- package/dist/sheets/SheetHead.d.ts +2 -2
- package/dist/sheets/SheetStack.d.ts +15 -3
- package/dist/sheets/SheetStack.js +74 -2
- package/dist/sheets/SheetStage.d.ts +2 -2
- package/dist/sheets/Step.d.ts +2 -2
- package/dist/type/Icon.d.ts +22 -7
- package/dist/type/Icon.js +27 -4
- package/dist/type/Text.d.ts +7 -1
- package/dist/type/Text.js +2 -2
- package/package.json +3 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ComponentProps } from 'react';
|
|
2
2
|
export type Palette = 'clay' | 'shine' | 'field' | 'roast' | 'hydro';
|
|
3
|
-
export interface ScreenProps extends
|
|
3
|
+
export interface ScreenProps extends ComponentProps<'div'> {
|
|
4
4
|
/** exactly one phone height (100dvh), clipped: the box a Step or a SheetStack fills — an app screen, not a scrolling page */
|
|
5
5
|
fill?: boolean;
|
|
6
6
|
/** a colour preset from palette.css; omit for the black-and-white wireframe */
|
|
@@ -14,74 +14,119 @@ export interface ScreenProps extends ComponentPropsWithoutRef<'div'> {
|
|
|
14
14
|
* Put an AppBar/TopBar first, the content next, and an ActionBar AFTER the screen (outside it).
|
|
15
15
|
*/
|
|
16
16
|
export declare function Screen({ fill, palette, as, className, ...rest }: ScreenProps): import("react").DetailedReactHTMLElement<{
|
|
17
|
-
|
|
17
|
+
ref?: import("react").Ref<HTMLDivElement> | undefined;
|
|
18
|
+
key?: import("react").Key | null | undefined;
|
|
19
|
+
defaultChecked?: boolean | undefined;
|
|
20
|
+
defaultValue?: string | number | readonly string[] | undefined;
|
|
21
|
+
suppressContentEditableWarning?: boolean | undefined;
|
|
22
|
+
suppressHydrationWarning?: boolean | undefined;
|
|
23
|
+
accessKey?: string | undefined;
|
|
24
|
+
autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {});
|
|
25
|
+
autoFocus?: boolean | undefined;
|
|
26
|
+
contentEditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
|
|
27
|
+
contextMenu?: string | undefined;
|
|
28
|
+
dir?: string | undefined;
|
|
29
|
+
draggable?: (boolean | "true" | "false") | undefined;
|
|
30
|
+
enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined;
|
|
31
|
+
hidden?: boolean | undefined;
|
|
32
|
+
id?: string | undefined;
|
|
33
|
+
lang?: string | undefined;
|
|
34
|
+
nonce?: string | undefined;
|
|
35
|
+
slot?: string | undefined;
|
|
36
|
+
spellCheck?: (boolean | "true" | "false") | undefined;
|
|
18
37
|
style?: import("react").CSSProperties | undefined;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
lang?: string | undefined | undefined;
|
|
24
|
-
nonce?: string | undefined | undefined;
|
|
25
|
-
part?: string | undefined | undefined;
|
|
38
|
+
tabIndex?: number | undefined;
|
|
39
|
+
title?: string | undefined;
|
|
40
|
+
translate?: "yes" | "no" | undefined;
|
|
41
|
+
radioGroup?: string | undefined;
|
|
26
42
|
role?: import("react").AriaRole | undefined;
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
about?: string | undefined;
|
|
44
|
+
content?: string | undefined;
|
|
45
|
+
datatype?: string | undefined;
|
|
46
|
+
inlist?: any;
|
|
47
|
+
prefix?: string | undefined;
|
|
48
|
+
property?: string | undefined;
|
|
49
|
+
rel?: string | undefined;
|
|
50
|
+
resource?: string | undefined;
|
|
51
|
+
rev?: string | undefined;
|
|
52
|
+
typeof?: string | undefined;
|
|
53
|
+
vocab?: string | undefined;
|
|
54
|
+
autoCorrect?: string | undefined;
|
|
55
|
+
autoSave?: string | undefined;
|
|
56
|
+
color?: string | undefined;
|
|
57
|
+
itemProp?: string | undefined;
|
|
58
|
+
itemScope?: boolean | undefined;
|
|
59
|
+
itemType?: string | undefined;
|
|
60
|
+
itemID?: string | undefined;
|
|
61
|
+
itemRef?: string | undefined;
|
|
62
|
+
results?: number | undefined;
|
|
63
|
+
security?: string | undefined;
|
|
64
|
+
unselectable?: "on" | "off" | undefined;
|
|
65
|
+
popover?: "" | "auto" | "manual" | "hint" | undefined;
|
|
66
|
+
popoverTargetAction?: "toggle" | "show" | "hide" | undefined;
|
|
67
|
+
popoverTarget?: string | undefined;
|
|
68
|
+
inert?: boolean | undefined;
|
|
69
|
+
inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined;
|
|
70
|
+
is?: string | undefined;
|
|
71
|
+
exportparts?: string | undefined;
|
|
72
|
+
part?: string | undefined;
|
|
73
|
+
"aria-activedescendant"?: string | undefined;
|
|
29
74
|
"aria-atomic"?: (boolean | "true" | "false") | undefined;
|
|
30
|
-
"aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined
|
|
31
|
-
"aria-braillelabel"?: string | undefined
|
|
32
|
-
"aria-brailleroledescription"?: string | undefined
|
|
75
|
+
"aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined;
|
|
76
|
+
"aria-braillelabel"?: string | undefined;
|
|
77
|
+
"aria-brailleroledescription"?: string | undefined;
|
|
33
78
|
"aria-busy"?: (boolean | "true" | "false") | undefined;
|
|
34
|
-
"aria-checked"?: boolean | "false" | "mixed" | "true" | undefined
|
|
35
|
-
"aria-colcount"?: number | undefined
|
|
36
|
-
"aria-colindex"?: number | undefined
|
|
37
|
-
"aria-colindextext"?: string | undefined
|
|
38
|
-
"aria-colspan"?: number | undefined
|
|
39
|
-
"aria-controls"?: string | undefined
|
|
40
|
-
"aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined
|
|
41
|
-
"aria-describedby"?: string | undefined
|
|
42
|
-
"aria-description"?: string | undefined
|
|
43
|
-
"aria-details"?: string | undefined
|
|
79
|
+
"aria-checked"?: boolean | "false" | "mixed" | "true" | undefined;
|
|
80
|
+
"aria-colcount"?: number | undefined;
|
|
81
|
+
"aria-colindex"?: number | undefined;
|
|
82
|
+
"aria-colindextext"?: string | undefined;
|
|
83
|
+
"aria-colspan"?: number | undefined;
|
|
84
|
+
"aria-controls"?: string | undefined;
|
|
85
|
+
"aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined;
|
|
86
|
+
"aria-describedby"?: string | undefined;
|
|
87
|
+
"aria-description"?: string | undefined;
|
|
88
|
+
"aria-details"?: string | undefined;
|
|
44
89
|
"aria-disabled"?: (boolean | "true" | "false") | undefined;
|
|
45
|
-
"aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined
|
|
46
|
-
"aria-errormessage"?: string | undefined
|
|
90
|
+
"aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined;
|
|
91
|
+
"aria-errormessage"?: string | undefined;
|
|
47
92
|
"aria-expanded"?: (boolean | "true" | "false") | undefined;
|
|
48
|
-
"aria-flowto"?: string | undefined
|
|
93
|
+
"aria-flowto"?: string | undefined;
|
|
49
94
|
"aria-grabbed"?: (boolean | "true" | "false") | undefined;
|
|
50
|
-
"aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined
|
|
95
|
+
"aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined;
|
|
51
96
|
"aria-hidden"?: (boolean | "true" | "false") | undefined;
|
|
52
|
-
"aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined
|
|
53
|
-
"aria-keyshortcuts"?: string | undefined
|
|
54
|
-
"aria-label"?: string | undefined
|
|
55
|
-
"aria-labelledby"?: string | undefined
|
|
56
|
-
"aria-level"?: number | undefined
|
|
57
|
-
"aria-live"?: "off" | "assertive" | "polite" | undefined
|
|
97
|
+
"aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined;
|
|
98
|
+
"aria-keyshortcuts"?: string | undefined;
|
|
99
|
+
"aria-label"?: string | undefined;
|
|
100
|
+
"aria-labelledby"?: string | undefined;
|
|
101
|
+
"aria-level"?: number | undefined;
|
|
102
|
+
"aria-live"?: "off" | "assertive" | "polite" | undefined;
|
|
58
103
|
"aria-modal"?: (boolean | "true" | "false") | undefined;
|
|
59
104
|
"aria-multiline"?: (boolean | "true" | "false") | undefined;
|
|
60
105
|
"aria-multiselectable"?: (boolean | "true" | "false") | undefined;
|
|
61
|
-
"aria-orientation"?: "horizontal" | "vertical" | undefined
|
|
62
|
-
"aria-owns"?: string | undefined
|
|
63
|
-
"aria-placeholder"?: string | undefined
|
|
64
|
-
"aria-posinset"?: number | undefined
|
|
65
|
-
"aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined
|
|
106
|
+
"aria-orientation"?: "horizontal" | "vertical" | undefined;
|
|
107
|
+
"aria-owns"?: string | undefined;
|
|
108
|
+
"aria-placeholder"?: string | undefined;
|
|
109
|
+
"aria-posinset"?: number | undefined;
|
|
110
|
+
"aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined;
|
|
66
111
|
"aria-readonly"?: (boolean | "true" | "false") | undefined;
|
|
67
|
-
"aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined
|
|
112
|
+
"aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined;
|
|
68
113
|
"aria-required"?: (boolean | "true" | "false") | undefined;
|
|
69
|
-
"aria-roledescription"?: string | undefined
|
|
70
|
-
"aria-rowcount"?: number | undefined
|
|
71
|
-
"aria-rowindex"?: number | undefined
|
|
72
|
-
"aria-rowindextext"?: string | undefined
|
|
73
|
-
"aria-rowspan"?: number | undefined
|
|
114
|
+
"aria-roledescription"?: string | undefined;
|
|
115
|
+
"aria-rowcount"?: number | undefined;
|
|
116
|
+
"aria-rowindex"?: number | undefined;
|
|
117
|
+
"aria-rowindextext"?: string | undefined;
|
|
118
|
+
"aria-rowspan"?: number | undefined;
|
|
74
119
|
"aria-selected"?: (boolean | "true" | "false") | undefined;
|
|
75
|
-
"aria-setsize"?: number | undefined
|
|
76
|
-
"aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined
|
|
77
|
-
"aria-valuemax"?: number | undefined
|
|
78
|
-
"aria-valuemin"?: number | undefined
|
|
79
|
-
"aria-valuenow"?: number | undefined
|
|
80
|
-
"aria-valuetext"?: string | undefined
|
|
81
|
-
children?: import("react").ReactNode;
|
|
120
|
+
"aria-setsize"?: number | undefined;
|
|
121
|
+
"aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined;
|
|
122
|
+
"aria-valuemax"?: number | undefined;
|
|
123
|
+
"aria-valuemin"?: number | undefined;
|
|
124
|
+
"aria-valuenow"?: number | undefined;
|
|
125
|
+
"aria-valuetext"?: string | undefined;
|
|
126
|
+
children?: import("react").ReactNode | undefined;
|
|
82
127
|
dangerouslySetInnerHTML?: {
|
|
83
128
|
__html: string | TrustedHTML;
|
|
84
|
-
} | undefined
|
|
129
|
+
} | undefined;
|
|
85
130
|
onCopy?: import("react").ClipboardEventHandler<HTMLDivElement> | undefined;
|
|
86
131
|
onCopyCapture?: import("react").ClipboardEventHandler<HTMLDivElement> | undefined;
|
|
87
132
|
onCut?: import("react").ClipboardEventHandler<HTMLDivElement> | undefined;
|
|
@@ -250,50 +295,6 @@ export declare function Screen({ fill, palette, as, className, ...rest }: Screen
|
|
|
250
295
|
onTransitionRunCapture?: import("react").TransitionEventHandler<HTMLDivElement> | undefined;
|
|
251
296
|
onTransitionStart?: import("react").TransitionEventHandler<HTMLDivElement> | undefined;
|
|
252
297
|
onTransitionStartCapture?: import("react").TransitionEventHandler<HTMLDivElement> | undefined;
|
|
253
|
-
key?: import("react").Key | null | undefined;
|
|
254
|
-
defaultChecked?: boolean | undefined | undefined;
|
|
255
|
-
defaultValue?: string | number | readonly string[] | undefined;
|
|
256
|
-
suppressContentEditableWarning?: boolean | undefined | undefined;
|
|
257
|
-
accessKey?: string | undefined | undefined;
|
|
258
|
-
autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {}) | undefined;
|
|
259
|
-
autoFocus?: boolean | undefined | undefined;
|
|
260
|
-
contentEditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
|
|
261
|
-
contextMenu?: string | undefined | undefined;
|
|
262
|
-
dir?: string | undefined | undefined;
|
|
263
|
-
draggable?: (boolean | "true" | "false") | undefined;
|
|
264
|
-
enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
|
|
265
|
-
hidden?: boolean | undefined | undefined;
|
|
266
|
-
spellCheck?: (boolean | "true" | "false") | undefined;
|
|
267
|
-
translate?: "yes" | "no" | undefined | undefined;
|
|
268
|
-
radioGroup?: string | undefined | undefined;
|
|
269
|
-
about?: string | undefined | undefined;
|
|
270
|
-
content?: string | undefined | undefined;
|
|
271
|
-
datatype?: string | undefined | undefined;
|
|
272
|
-
inlist?: any;
|
|
273
|
-
prefix?: string | undefined | undefined;
|
|
274
|
-
property?: string | undefined | undefined;
|
|
275
|
-
rel?: string | undefined | undefined;
|
|
276
|
-
resource?: string | undefined | undefined;
|
|
277
|
-
rev?: string | undefined | undefined;
|
|
278
|
-
typeof?: string | undefined | undefined;
|
|
279
|
-
vocab?: string | undefined | undefined;
|
|
280
|
-
autoCorrect?: string | undefined | undefined;
|
|
281
|
-
autoSave?: string | undefined | undefined;
|
|
282
|
-
itemProp?: string | undefined | undefined;
|
|
283
|
-
itemScope?: boolean | undefined | undefined;
|
|
284
|
-
itemType?: string | undefined | undefined;
|
|
285
|
-
itemID?: string | undefined | undefined;
|
|
286
|
-
itemRef?: string | undefined | undefined;
|
|
287
|
-
results?: number | undefined | undefined;
|
|
288
|
-
security?: string | undefined | undefined;
|
|
289
|
-
unselectable?: "on" | "off" | undefined | undefined;
|
|
290
|
-
popover?: "" | "auto" | "manual" | "hint" | undefined | undefined;
|
|
291
|
-
popoverTargetAction?: "toggle" | "show" | "hide" | undefined | undefined;
|
|
292
|
-
popoverTarget?: string | undefined | undefined;
|
|
293
|
-
inert?: boolean | undefined | undefined;
|
|
294
|
-
inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined | undefined;
|
|
295
|
-
is?: string | undefined | undefined;
|
|
296
|
-
exportparts?: string | undefined | undefined;
|
|
297
298
|
className: string;
|
|
298
299
|
'data-palette': Palette | undefined;
|
|
299
|
-
},
|
|
300
|
+
}, HTMLDivElement>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export interface TopBarProps extends Omit<
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
|
+
export interface TopBarProps extends Omit<ComponentProps<'header'>, 'title'> {
|
|
3
3
|
/** the screen title (uppercase, ellipsised on one line) */
|
|
4
4
|
title: ReactNode;
|
|
5
5
|
/** a leading control — usually `<IconBtn sm icon="arrow-left" label="Back" />` */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export interface CardBackProps extends
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
export interface CardBackProps extends ComponentProps<'button'> {
|
|
3
3
|
/** accessible name */
|
|
4
4
|
label?: string;
|
|
5
5
|
}
|
package/dist/sheets/Drawer.d.ts
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export interface ModalProps extends
|
|
1
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
2
|
+
export interface ModalProps extends ComponentProps<'article'> {
|
|
3
|
+
/** CONTROLLED: on the screen or not (default `true`). The app owns it. Set it `false` and the modal leaves the way it came — its
|
|
4
|
+
* opening played backwards — then calls `onClosed`, where the app stops rendering it (and its `SheetStage ask`). */
|
|
5
|
+
open?: boolean;
|
|
6
|
+
/** the leaving has finished (at once, when motion is reduced) — unmount here */
|
|
7
|
+
onClosed?: () => void;
|
|
3
8
|
}
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Modal — the ask: ONE card at the centre of a `SheetStage ask`, two parts — a `Lift` (the thing being asked about, light, scrolls
|
|
11
|
+
* when tall) and a `Drawer` (the dark end that asks). No border. Two beats on appearing: drops in from above, then the drawer comes
|
|
12
|
+
* out below while the lift moves up.
|
|
13
|
+
* A COMPONENT, not a wiring: it does not know its buttons. They are plain `IconBtn`s / `Btn`s with the app's `onClick` — the app
|
|
14
|
+
* records the answer and sets `open={false}`; the modal's one behaviour is how it leaves.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Modal({ open, onClosed, className, ...rest }: ModalProps): import("react").JSX.Element;
|
|
17
|
+
export interface LiftProps extends ComponentProps<'div'> {
|
|
7
18
|
}
|
|
8
19
|
/** Lift — the light part of a `Modal`: the case, request or item the question is about (a CardHead, rows, a timeline); scrolls inside when tall. */
|
|
9
20
|
export declare function Lift({ className, ...rest }: LiftProps): import("react").JSX.Element;
|
|
10
|
-
export interface DrawerProps extends Omit<
|
|
21
|
+
export interface DrawerProps extends Omit<ComponentProps<'div'>, 'title'> {
|
|
11
22
|
/** the question */
|
|
12
23
|
title: ReactNode;
|
|
13
24
|
/** the fine print under it */
|
|
14
25
|
caption?: ReactNode;
|
|
15
26
|
}
|
|
16
|
-
/** Drawer — the dark end of a `Modal`: the question, its fine print, and a `BtnTrio` (or two buttons)
|
|
27
|
+
/** Drawer — the dark end of a `Modal`: the question, its fine print, and a `BtnTrio` (or two buttons) — plain buttons with the app's `onClick`; the Drawer does nothing with them. */
|
|
17
28
|
export declare function Drawer({ title, caption, className, children, ...rest }: DrawerProps): import("react").JSX.Element;
|
|
18
|
-
export interface BtnTrioProps extends
|
|
29
|
+
export interface BtnTrioProps extends ComponentProps<'div'> {
|
|
19
30
|
}
|
|
20
31
|
/** BtnTrio — three round buttons with dotted connectors between them (× · ? · ✓); the middle one inverted. */
|
|
21
32
|
export declare function BtnTrio({ className, children, ...rest }: BtnTrioProps): import("react").JSX.Element;
|
package/dist/sheets/Drawer.js
CHANGED
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Children, Fragment } from 'react';
|
|
2
|
+
import { Children, Fragment, useEffect, useRef } from 'react';
|
|
3
3
|
import { cx } from '../cx.js';
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Modal — the ask: ONE card at the centre of a `SheetStage ask`, two parts — a `Lift` (the thing being asked about, light, scrolls
|
|
6
|
+
* when tall) and a `Drawer` (the dark end that asks). No border. Two beats on appearing: drops in from above, then the drawer comes
|
|
7
|
+
* out below while the lift moves up.
|
|
8
|
+
* A COMPONENT, not a wiring: it does not know its buttons. They are plain `IconBtn`s / `Btn`s with the app's `onClick` — the app
|
|
9
|
+
* records the answer and sets `open={false}`; the modal's one behaviour is how it leaves.
|
|
10
|
+
*/
|
|
11
|
+
export function Modal({ open = true, onClosed, className, ...rest }) {
|
|
12
|
+
const ref = useRef(null);
|
|
13
|
+
const closed = useRef(onClosed);
|
|
14
|
+
closed.current = onClosed;
|
|
15
|
+
const shown = useRef(true); // the animations stand played forwards
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const el = ref.current;
|
|
18
|
+
if (!el || open === shown.current)
|
|
19
|
+
return;
|
|
20
|
+
shown.current = open;
|
|
21
|
+
const anims = el.getAnimations({ subtree: true });
|
|
22
|
+
anims.forEach((a) => a.reverse()); // the same keyframes, backwards to leave, forwards again to return — no closing keyframes to keep in step
|
|
23
|
+
if (open)
|
|
24
|
+
return;
|
|
25
|
+
let live = true;
|
|
26
|
+
void Promise.all(anims.map((a) => a.finished.catch(() => undefined))).then(() => { if (live)
|
|
27
|
+
closed.current?.(); });
|
|
28
|
+
return () => { live = false; };
|
|
29
|
+
}, [open]);
|
|
30
|
+
return _jsx("article", { ref: ref, className: cx('card modal', className), ...rest });
|
|
7
31
|
}
|
|
8
32
|
/** Lift — the light part of a `Modal`: the case, request or item the question is about (a CardHead, rows, a timeline); scrolls inside when tall. */
|
|
9
33
|
export function Lift({ className, ...rest }) {
|
|
10
34
|
return _jsx("div", { className: cx('modal__lift', className), ...rest });
|
|
11
35
|
}
|
|
12
|
-
/** Drawer — the dark end of a `Modal`: the question, its fine print, and a `BtnTrio` (or two buttons)
|
|
36
|
+
/** Drawer — the dark end of a `Modal`: the question, its fine print, and a `BtnTrio` (or two buttons) — plain buttons with the app's `onClick`; the Drawer does nothing with them. */
|
|
13
37
|
export function Drawer({ title, caption, className, children, ...rest }) {
|
|
14
38
|
return (_jsxs("div", { className: cx('modal__drawer', className), ...rest, children: [_jsx("h2", { className: "t-h2", children: title }), caption != null && _jsx("p", { className: "t-caption", children: caption }), children] }));
|
|
15
39
|
}
|
package/dist/sheets/Sheet.d.ts
CHANGED
|
@@ -1,24 +1,37 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
/** how far the sheet slid. `away` = not on the screen (slid out, inert) — a state the app sets, never one a drag or a tap lands on */
|
|
2
3
|
export type SheetState = 'peek' | 'half' | '3q' | 'full' | 'away';
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
/** what asked for the change: the handle's `tap`, a `drag` released, `focus` in a peeked sheet, the `keyboard` (or anything else) moving the cut */
|
|
5
|
+
export type SheetChangeReason = 'tap' | 'drag' | 'focus' | 'keyboard';
|
|
6
|
+
declare const ORDER: readonly ["peek", "half", "3q", "full"];
|
|
7
|
+
type Resting = (typeof ORDER)[number];
|
|
8
|
+
export interface SheetProps extends Omit<ComponentProps<'article'>, 'onChange'> {
|
|
9
|
+
/** CONTROLLED: where the sheet is. The app owns it — pair it with `onStateChange`, or the sheet cannot move (like `value` without `onChange`).
|
|
10
|
+
* `peek` (handle + title row) · `half` · `3q` · `full` (its top edge at the top bar) · `away` (not on the screen). A class, never a height. */
|
|
5
11
|
state?: SheetState;
|
|
12
|
+
/** UNCONTROLLED: where it starts (default `half`); the sheet then keeps its own state. Ignored when `state` is given. */
|
|
13
|
+
defaultState?: SheetState;
|
|
14
|
+
/** the sheet ASKS to move — the handle was tapped or dragged, a field in a peeked sheet took focus, the keyboard moved the cut.
|
|
15
|
+
* Controlled: set your state to it (or don't — the sheet stays). This is where an app ties a sheet to its router / back button. */
|
|
16
|
+
onStateChange?: (state: SheetState, reason: SheetChangeReason) => void;
|
|
17
|
+
/** the states a drag snaps to and a reveal may climb through — "peek half full", or an array (default all four) */
|
|
18
|
+
states?: string | readonly Resting[];
|
|
6
19
|
/** floats above a footnote strip on a dim stage (bottom corners and border back) */
|
|
7
20
|
raised?: boolean;
|
|
8
|
-
/** the grab handle
|
|
21
|
+
/** the grab handle; `false` for a sheet with no handle, a string sets its accessible name */
|
|
9
22
|
handle?: boolean | string;
|
|
10
|
-
/** which states a drag snaps to, e.g. "peek half full" (default all four: peek half 3q full) */
|
|
11
|
-
states?: string;
|
|
12
23
|
}
|
|
13
24
|
/**
|
|
14
|
-
* Sheet — the SINGLE sheet: one card as a bottom sheet holding what the member ACTS on
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
25
|
+
* Sheet — the SINGLE sheet: one card as a bottom sheet holding what the member ACTS on. Always the full height of its
|
|
26
|
+
* box — a Step or a SheetStage — a state is only how far it slid (translate; the material never changes size).
|
|
27
|
+
* ONE SHAPE, written top-down: handle · head (a CardHead / SheetHead — pinned, never scrolls) · SheetBody (scrolls, as far as
|
|
28
|
+
* it needs) · CardFoot (the button that finishes the sheet — at the bottom, above the keyboard; gone at peek).
|
|
29
|
+
*
|
|
30
|
+
* It is a COMPONENT, not a wiring: it knows nothing of what opens it. The app owns `state` and sets it from whatever it
|
|
31
|
+
* likes — a button, a route, a timer: `<Btn onClick={() => setSheet('full')}>` … `<Sheet state={sheet} onStateChange={setSheet}>`.
|
|
32
|
+
* Its own behaviour stays inside it and only ever ASKS (`onStateChange`): a tap on the handle toggles its own state ↔ peek,
|
|
33
|
+
* a drag snaps to the nearest of `states`, focus in a peeked sheet raises it, and when the cut moves (the keyboard) it
|
|
34
|
+
* climbs until the focused control has room above the foot.
|
|
23
35
|
*/
|
|
24
|
-
export declare function Sheet({ state,
|
|
36
|
+
export declare function Sheet({ state, defaultState, onStateChange, states, raised, handle, className, children, onFocus, ...rest }: SheetProps): import("react").JSX.Element;
|
|
37
|
+
export {};
|
package/dist/sheets/Sheet.js
CHANGED
|
@@ -1,16 +1,162 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
3
|
import { cx } from '../cx.js';
|
|
4
|
+
const ORDER = ['peek', 'half', '3q', 'full']; // low → high; `away` is below them all and outside them
|
|
5
|
+
const TAP_REM = 0.5; // = --sp-2: a move shorter than this is a tap, not a drag
|
|
3
6
|
/**
|
|
4
|
-
* Sheet — the SINGLE sheet: one card as a bottom sheet holding what the member ACTS on
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* Sheet — the SINGLE sheet: one card as a bottom sheet holding what the member ACTS on. Always the full height of its
|
|
8
|
+
* box — a Step or a SheetStage — a state is only how far it slid (translate; the material never changes size).
|
|
9
|
+
* ONE SHAPE, written top-down: handle · head (a CardHead / SheetHead — pinned, never scrolls) · SheetBody (scrolls, as far as
|
|
10
|
+
* it needs) · CardFoot (the button that finishes the sheet — at the bottom, above the keyboard; gone at peek).
|
|
11
|
+
*
|
|
12
|
+
* It is a COMPONENT, not a wiring: it knows nothing of what opens it. The app owns `state` and sets it from whatever it
|
|
13
|
+
* likes — a button, a route, a timer: `<Btn onClick={() => setSheet('full')}>` … `<Sheet state={sheet} onStateChange={setSheet}>`.
|
|
14
|
+
* Its own behaviour stays inside it and only ever ASKS (`onStateChange`): a tap on the handle toggles its own state ↔ peek,
|
|
15
|
+
* a drag snaps to the nearest of `states`, focus in a peeked sheet raises it, and when the cut moves (the keyboard) it
|
|
16
|
+
* climbs until the focused control has room above the foot.
|
|
13
17
|
*/
|
|
14
|
-
export function Sheet({ state = 'half', raised, handle = true,
|
|
15
|
-
|
|
18
|
+
export function Sheet({ state, defaultState = 'half', onStateChange, states, raised, handle = true, className, children, onFocus, ...rest }) {
|
|
19
|
+
const [inner, setInner] = useState(defaultState);
|
|
20
|
+
const current = state ?? inner;
|
|
21
|
+
const controlled = state !== undefined;
|
|
22
|
+
const ref = useRef(null);
|
|
23
|
+
const handleRef = useRef(null);
|
|
24
|
+
const home = useRef(current !== 'peek' && current !== 'away' ? current : 'half'); // where a tap from peek returns to
|
|
25
|
+
const was = useRef(current);
|
|
26
|
+
const skipClick = useRef(false);
|
|
27
|
+
const drag = useRef(null);
|
|
28
|
+
const allowed = useCallback(() => {
|
|
29
|
+
const list = (typeof states === 'string' ? states.split(/\s+/) : [...(states ?? [])]).filter((s) => ORDER.includes(s));
|
|
30
|
+
return list.length ? ORDER.filter((s) => list.includes(s)) : [...ORDER];
|
|
31
|
+
}, [states]);
|
|
32
|
+
const request = useCallback((next, reason) => {
|
|
33
|
+
if (next === current)
|
|
34
|
+
return;
|
|
35
|
+
if (!controlled)
|
|
36
|
+
setInner(next);
|
|
37
|
+
onStateChange?.(next, reason);
|
|
38
|
+
}, [current, controlled, onStateChange]);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (current !== 'peek' && current !== 'away')
|
|
41
|
+
home.current = current;
|
|
42
|
+
if (was.current === 'away' && current !== 'away')
|
|
43
|
+
handleRef.current?.focus({ preventScroll: true }); // it just came onto the screen: the keyboard user is on it
|
|
44
|
+
was.current = current;
|
|
45
|
+
}, [current]);
|
|
46
|
+
/* geometry, read once per gesture — never stored: how far it has slid, and where each state rests, in px from its top at full */
|
|
47
|
+
const slid = (el) => {
|
|
48
|
+
const box = (el.offsetParent ?? el.parentElement);
|
|
49
|
+
return el.getBoundingClientRect().top - (box.getBoundingClientRect().top + box.clientTop + el.offsetTop);
|
|
50
|
+
};
|
|
51
|
+
const stopsOf = (el) => {
|
|
52
|
+
const cs = getComputedStyle(el), height = el.getBoundingClientRect().height;
|
|
53
|
+
return {
|
|
54
|
+
full: 0,
|
|
55
|
+
half: height * (parseFloat(cs.getPropertyValue('--sheet-half')) || 0),
|
|
56
|
+
'3q': height * (parseFloat(cs.getPropertyValue('--sheet-3q')) || 0),
|
|
57
|
+
peek: height - (parseFloat(cs.getPropertyValue('--sheet-peek')) || 0), // registered in tokens.css: reads back resolved
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
/* bring a control into the part of the body that shows — between the pinned head and the foot's strip on the cut. The sheet asks
|
|
61
|
+
for the lowest of its states, from the current one (or `atLeast`) upward, that has room; if none has, its highest. */
|
|
62
|
+
const reveal = useCallback((target, reason, atLeast) => {
|
|
63
|
+
const el = ref.current;
|
|
64
|
+
if (!el || !target || !el.contains(target) || target.closest('.sheet__handle') || current === 'away')
|
|
65
|
+
return;
|
|
66
|
+
const control = target.closest('.field') ?? target; // the control's box, not the bare input inside it
|
|
67
|
+
const list = allowed(), stops = stopsOf(el);
|
|
68
|
+
const from = Math.max(list.indexOf(current), atLeast ? list.indexOf(atLeast) : -1, 0);
|
|
69
|
+
const rect = el.getBoundingClientRect();
|
|
70
|
+
const restTop = rect.top - slid(el);
|
|
71
|
+
const bodyTop = (el.querySelector(':scope > .sheet__body') ?? el).getBoundingClientRect().top - rect.top;
|
|
72
|
+
const box = el.parentElement.getBoundingClientRect(); // what clips it: the Step or the SheetStage
|
|
73
|
+
const vv = window.visualViewport;
|
|
74
|
+
const edge = Math.min(box.bottom, restTop + rect.height); // the sheet's own bottom edge at rest — above the box's on a stage that gave the keyboard its room
|
|
75
|
+
const cut = Math.min(edge, vv ? vv.offsetTop + vv.height : Infinity);
|
|
76
|
+
const button = el.querySelector(':scope > .card__foot')?.firstElementChild; // the foot's button stands on that edge at every state: the control has to clear its strip too
|
|
77
|
+
const need = control.getBoundingClientRect().height + (button ? edge - button.getBoundingClientRect().top : 0);
|
|
78
|
+
let to = list[list.length - 1];
|
|
79
|
+
for (let i = from; i < list.length; i++)
|
|
80
|
+
if (cut - (restTop + stops[list[i]] + bodyTop) >= need) {
|
|
81
|
+
to = list[i];
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
request(to, reason);
|
|
85
|
+
requestAnimationFrame(() => control.scrollIntoView({ block: 'nearest' })); // after the state (and its scroll-padding) landed
|
|
86
|
+
}, [allowed, current, request]);
|
|
87
|
+
/* the cut moved — the keyboard came up (the Step shrank by --kb, or the layout viewport did), the phone turned: if a control
|
|
88
|
+
in this sheet has the focus, make sure it still shows */
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const el = ref.current;
|
|
91
|
+
if (!el || typeof ResizeObserver === 'undefined')
|
|
92
|
+
return;
|
|
93
|
+
let first = true;
|
|
94
|
+
const ro = new ResizeObserver(() => {
|
|
95
|
+
if (first) {
|
|
96
|
+
first = false;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const at = document.activeElement;
|
|
100
|
+
if (at && el.contains(at))
|
|
101
|
+
reveal(at, 'keyboard');
|
|
102
|
+
});
|
|
103
|
+
ro.observe(el);
|
|
104
|
+
if (el.parentElement)
|
|
105
|
+
ro.observe(el.parentElement);
|
|
106
|
+
return () => ro.disconnect();
|
|
107
|
+
}, [reveal]);
|
|
108
|
+
const focus = (e) => {
|
|
109
|
+
onFocus?.(e);
|
|
110
|
+
if (current !== 'peek' || e.target.closest('.sheet__handle'))
|
|
111
|
+
return;
|
|
112
|
+
const body = ref.current?.querySelector(':scope > .sheet__body');
|
|
113
|
+
if (body)
|
|
114
|
+
body.scrollTop = 0; // the browser's own reveal scrolled it against the peek strip: start over
|
|
115
|
+
reveal(e.target, 'focus', home.current);
|
|
116
|
+
};
|
|
117
|
+
const tap = () => {
|
|
118
|
+
if (skipClick.current) {
|
|
119
|
+
skipClick.current = false;
|
|
120
|
+
return;
|
|
121
|
+
} // the click that follows a drag is not a tap
|
|
122
|
+
request(current === 'peek' ? (allowed().includes(home.current) ? home.current : allowed().find((s) => s !== 'peek') ?? 'half') : 'peek', 'tap');
|
|
123
|
+
};
|
|
124
|
+
const down = (e) => {
|
|
125
|
+
const el = ref.current;
|
|
126
|
+
if (!el || !e.isPrimary || e.button !== 0)
|
|
127
|
+
return;
|
|
128
|
+
const list = allowed(), stops = stopsOf(el), y = slid(el); // where it is now — mid-slide too, so a grab never jumps
|
|
129
|
+
drag.current = { startY: e.clientY, y0: y, y, stops, allowed: list, moved: false,
|
|
130
|
+
min: Math.min(...list.map((s) => stops[s])), max: Math.max(...list.map((s) => stops[s])),
|
|
131
|
+
tap: TAP_REM * parseFloat(getComputedStyle(document.documentElement).fontSize) };
|
|
132
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
133
|
+
};
|
|
134
|
+
const move = (e) => {
|
|
135
|
+
const d = drag.current, el = ref.current;
|
|
136
|
+
if (!d || !el || !e.isPrimary)
|
|
137
|
+
return;
|
|
138
|
+
const dy = e.clientY - d.startY;
|
|
139
|
+
if (!d.moved) {
|
|
140
|
+
if (Math.abs(dy) < d.tap)
|
|
141
|
+
return;
|
|
142
|
+
d.moved = true;
|
|
143
|
+
el.classList.add('is-dragging');
|
|
144
|
+
}
|
|
145
|
+
d.y = Math.min(Math.max(d.y0 + dy, d.min), d.max); // never above full, never below its lowest state
|
|
146
|
+
el.style.setProperty('--_drag', `${d.y - d.y0}px`); // the one thing written while held — the CSS adds it to the state's translate
|
|
147
|
+
};
|
|
148
|
+
const up = () => {
|
|
149
|
+
const d = drag.current, el = ref.current;
|
|
150
|
+
drag.current = null;
|
|
151
|
+
if (!d || !el)
|
|
152
|
+
return;
|
|
153
|
+
el.classList.remove('is-dragging');
|
|
154
|
+
if (!d.moved)
|
|
155
|
+
return; // a tap: the click that follows toggles
|
|
156
|
+
el.style.removeProperty('--_drag'); // same state, or the app says no: it eases back on its own
|
|
157
|
+
request(d.allowed.reduce((a, b) => (Math.abs(d.stops[b] - d.y) < Math.abs(d.stops[a] - d.y) ? b : a)), 'drag');
|
|
158
|
+
skipClick.current = true;
|
|
159
|
+
requestAnimationFrame(() => { skipClick.current = false; });
|
|
160
|
+
};
|
|
161
|
+
return (_jsxs("article", { ref: ref, className: cx('card sheet', `sheet--${current}`, raised && 'sheet--raised', className), inert: current === 'away' || undefined, onFocus: focus, ...rest, children: [handle !== false && (_jsx("button", { ref: handleRef, className: "sheet__handle", type: "button", "aria-label": typeof handle === 'string' ? handle : 'Resize', onClick: tap, onPointerDown: down, onPointerMove: move, onPointerUp: up, onPointerCancel: up })), children] }));
|
|
16
162
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export interface SheetBodyProps extends
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
export interface SheetBodyProps extends ComponentProps<'div'> {
|
|
3
3
|
}
|
|
4
4
|
/** SheetBody — everything under a Sheet's head: the one thing that scrolls; its scroll padding keeps a focused control above the cut. Content never goes straight into the Sheet. */
|
|
5
5
|
export declare function SheetBody({ className, ...rest }: SheetBodyProps): import("react").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export interface SheetHeadProps extends Omit<
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
|
+
export interface SheetHeadProps extends Omit<ComponentProps<'div'>, 'title'> {
|
|
3
3
|
/** the round button on the left (an IconBtn sm) */
|
|
4
4
|
lead?: ReactNode;
|
|
5
5
|
/** the centred title */
|