@kennycorrea/ui 0.10.0 → 0.11.1
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/CHANGELOG.md +117 -16
- package/README.md +92 -1
- package/dist/atoms/ActionSheet/ActionSheet.d.ts +30 -0
- package/dist/atoms/ActionSheet/ActionSheet.js +42 -0
- package/dist/atoms/ActionSheet/confirm.d.ts +21 -0
- package/dist/atoms/ActionSheet/confirm.js +65 -0
- package/dist/atoms/ActionSheet/index.d.ts +2 -0
- package/dist/atoms/ActionSheet/index.js +2 -0
- package/dist/atoms/Button/Button.js +3 -3
- package/dist/atoms/Button/Button.types.d.ts +7 -1
- package/dist/atoms/Card/Card.d.ts +28 -7
- package/dist/atoms/Card/Card.js +45 -9
- package/dist/atoms/ChipGroup/ChipGroup.d.ts +31 -0
- package/dist/atoms/ChipGroup/ChipGroup.js +69 -0
- package/dist/atoms/ChipGroup/index.d.ts +1 -0
- package/dist/atoms/ChipGroup/index.js +1 -0
- package/dist/atoms/Input/Input.js +7 -1
- package/dist/atoms/List/List.d.ts +73 -0
- package/dist/atoms/List/List.js +88 -0
- package/dist/atoms/List/index.d.ts +1 -0
- package/dist/atoms/List/index.js +1 -0
- package/dist/atoms/List/swipe.d.ts +68 -0
- package/dist/atoms/List/swipe.js +207 -0
- package/dist/atoms/NavBar/BackButton.d.ts +10 -0
- package/dist/atoms/NavBar/BackButton.js +11 -0
- package/dist/atoms/NavBar/NavBar.d.ts +22 -0
- package/dist/atoms/NavBar/NavBar.js +29 -0
- package/dist/atoms/NavBar/index.d.ts +2 -0
- package/dist/atoms/NavBar/index.js +2 -0
- package/dist/atoms/PullToRefresh/PullToRefresh.d.ts +31 -0
- package/dist/atoms/PullToRefresh/PullToRefresh.js +110 -0
- package/dist/atoms/PullToRefresh/index.d.ts +1 -0
- package/dist/atoms/PullToRefresh/index.js +1 -0
- package/dist/atoms/ResponsiveDialog/ResponsiveDialog.d.ts +13 -0
- package/dist/atoms/ResponsiveDialog/ResponsiveDialog.js +27 -0
- package/dist/atoms/ResponsiveDialog/index.d.ts +1 -0
- package/dist/atoms/ResponsiveDialog/index.js +1 -0
- package/dist/atoms/ScrollStrip/ScrollStrip.d.ts +21 -0
- package/dist/atoms/ScrollStrip/ScrollStrip.js +62 -0
- package/dist/atoms/ScrollStrip/index.d.ts +1 -0
- package/dist/atoms/ScrollStrip/index.js +1 -0
- package/dist/atoms/SegmentedControl/SegmentedControl.d.ts +26 -0
- package/dist/atoms/SegmentedControl/SegmentedControl.js +44 -0
- package/dist/atoms/SegmentedControl/index.d.ts +1 -0
- package/dist/atoms/SegmentedControl/index.js +1 -0
- package/dist/atoms/Select/Select.js +1 -1
- package/dist/atoms/Sheet/Sheet.d.ts +63 -0
- package/dist/atoms/Sheet/Sheet.js +261 -0
- package/dist/atoms/Sheet/index.d.ts +1 -0
- package/dist/atoms/Sheet/index.js +1 -0
- package/dist/atoms/StatTile/StatTile.d.ts +36 -0
- package/dist/atoms/StatTile/StatTile.js +17 -0
- package/dist/atoms/StatTile/index.d.ts +1 -0
- package/dist/atoms/StatTile/index.js +1 -0
- package/dist/atoms/TabBar/TabBar.d.ts +42 -0
- package/dist/atoms/TabBar/TabBar.js +36 -0
- package/dist/atoms/TabBar/index.d.ts +1 -0
- package/dist/atoms/TabBar/index.js +1 -0
- package/dist/atoms/Textarea/Textarea.d.ts +4 -0
- package/dist/atoms/Textarea/Textarea.js +1 -1
- package/dist/atoms/Toast/Toast.d.ts +8 -2
- package/dist/atoms/Toast/Toast.js +19 -4
- package/dist/atoms/index.d.ts +11 -0
- package/dist/atoms/index.js +11 -0
- package/dist/primitives/index.d.ts +1 -1
- package/dist/primitives/index.js +1 -1
- package/dist/strings.d.ts +12 -0
- package/dist/strings.js +6 -0
- package/package.json +4 -1
- package/tokens.css +401 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as RadixDialog from '@radix-ui/react-dialog';
|
|
3
|
+
import { Theme } from '@radix-ui/themes';
|
|
4
|
+
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
5
|
+
import { useUiStrings } from '../../strings.js';
|
|
6
|
+
import { cn } from '../../utils/cn.js';
|
|
7
|
+
import { useThemePortalTarget } from '../../utils/themePortal.js';
|
|
8
|
+
const RANK = { fit: 0, medium: 1, large: 2 };
|
|
9
|
+
/** Share of the visible viewport each detent may take. */
|
|
10
|
+
const SHARE = { fit: 0.9, medium: 0.5, large: 0.92 };
|
|
11
|
+
/** Pixels per millisecond that commit a flick regardless of distance. */
|
|
12
|
+
const VELOCITY = 0.5;
|
|
13
|
+
/** Pixels of upward travel that commit a drag regardless of speed. */
|
|
14
|
+
const RAISE_DISTANCE = 60;
|
|
15
|
+
/** Fraction of the sheet's height a downward drag must cover to commit. */
|
|
16
|
+
const LOWER_FRACTION = 0.25;
|
|
17
|
+
function useControllable(value, initial, onChange) {
|
|
18
|
+
const [inner, setInner] = useState(initial);
|
|
19
|
+
const controlled = value !== undefined;
|
|
20
|
+
const set = useCallback((next) => {
|
|
21
|
+
if (!controlled)
|
|
22
|
+
setInner(next);
|
|
23
|
+
onChange?.(next);
|
|
24
|
+
}, [controlled, onChange]);
|
|
25
|
+
return [controlled ? value : inner, set];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The visible viewport: its height, and how far the on-screen keyboard covers the layout viewport.
|
|
29
|
+
*
|
|
30
|
+
* On Android Chrome the keyboard resizes the layout viewport, so `kb` stays 0 and the sheet's
|
|
31
|
+
* `bottom: 0` is already above it. On iOS Safari it only shrinks the *visual* viewport, which is
|
|
32
|
+
* where `innerHeight - height - offsetTop` comes from. Either way `height` is what is left.
|
|
33
|
+
*/
|
|
34
|
+
function useVisibleViewport(active, contentRef) {
|
|
35
|
+
const [vv, setVv] = useState(null);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const viewport = window.visualViewport;
|
|
38
|
+
if (!active || !viewport)
|
|
39
|
+
return;
|
|
40
|
+
const read = () => {
|
|
41
|
+
const kb = Math.max(0, Math.round(window.innerHeight - viewport.height - viewport.offsetTop));
|
|
42
|
+
setVv({ height: Math.round(viewport.height), kb });
|
|
43
|
+
// Keep the focused field on screen once the layout has caught up with the keyboard.
|
|
44
|
+
const focused = document.activeElement;
|
|
45
|
+
if (focused && contentRef.current?.contains(focused) && focused !== contentRef.current) {
|
|
46
|
+
requestAnimationFrame(() => focused.scrollIntoView?.({ block: 'nearest' }));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
read();
|
|
50
|
+
viewport.addEventListener('resize', read);
|
|
51
|
+
viewport.addEventListener('scroll', read);
|
|
52
|
+
return () => {
|
|
53
|
+
viewport.removeEventListener('resize', read);
|
|
54
|
+
viewport.removeEventListener('scroll', read);
|
|
55
|
+
setVv(null);
|
|
56
|
+
};
|
|
57
|
+
}, [active, contentRef]);
|
|
58
|
+
return vv;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A native-feeling bottom sheet on Radix Dialog.
|
|
62
|
+
*
|
|
63
|
+
* Radix owns what must not be reimplemented: the focus trap and its return on close, Escape,
|
|
64
|
+
* the scrim, `aria-modal`, and the page behind being hidden from assistive technology. This adds
|
|
65
|
+
* the sheet: a drag handle, detents, velocity-based dismissal, a body that scrolls until it is at
|
|
66
|
+
* its top and only then hands the gesture to the sheet, a footer that stays above the safe area
|
|
67
|
+
* and the keyboard, and an optional iOS-style scaled page behind the `large` detent.
|
|
68
|
+
*
|
|
69
|
+
* Like every floating atom here it portals into the nearest `<Theme>` rather than `<body>`.
|
|
70
|
+
*/
|
|
71
|
+
export function Sheet({ open, onOpenChange, title, description, children, footer, detent: detentProp, defaultDetent = 'fit', onDetentChange, detents, scaleBackground = false, dismissible = true, showCloseButton = false, closeLabel, expandLabel, collapseLabel, maxWidthClassName = 'max-w-lg', className, container, }) {
|
|
72
|
+
const strings = useUiStrings();
|
|
73
|
+
const [detent, setDetent] = useControllable(detentProp, defaultDetent, onDetentChange);
|
|
74
|
+
const [offset, setOffset] = useState(0);
|
|
75
|
+
const [dragging, setDragging] = useState(false);
|
|
76
|
+
const drag = useRef(null);
|
|
77
|
+
const suppressClick = useRef(false);
|
|
78
|
+
const contentRef = useRef(null);
|
|
79
|
+
const bodyRef = useRef(null);
|
|
80
|
+
const rootRef = useRef(null);
|
|
81
|
+
const { anchorRef, target, resolved } = useThemePortalTarget(container);
|
|
82
|
+
const vv = useVisibleViewport(open, contentRef);
|
|
83
|
+
const ladder = [...new Set([...(detents ?? []), detent])].sort((a, b) => RANK[a] - RANK[b]);
|
|
84
|
+
const index = ladder.indexOf(detent);
|
|
85
|
+
const higher = ladder[index + 1];
|
|
86
|
+
const lower = ladder[index - 1];
|
|
87
|
+
const requestClose = useCallback(() => onOpenChange(false), [onOpenChange]);
|
|
88
|
+
// The page behind scales while the sheet is at `large`; everything is put back on the way out.
|
|
89
|
+
const scaled = open && scaleBackground && detent === 'large';
|
|
90
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: `resolved` is when the content first mounts.
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!scaled || !rootRef.current)
|
|
93
|
+
return;
|
|
94
|
+
// rootRef sits inside the nested Theme; its parent is the element the portal appended.
|
|
95
|
+
const mine = rootRef.current.parentElement;
|
|
96
|
+
const host = mine?.parentElement;
|
|
97
|
+
if (!mine || !host)
|
|
98
|
+
return;
|
|
99
|
+
const reduce = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
|
|
100
|
+
const pages = Array.from(host.children).filter((el) => el instanceof HTMLElement && el !== mine && !el.hasAttribute('data-sheet-root'));
|
|
101
|
+
const saved = pages.map((el) => el.getAttribute('style'));
|
|
102
|
+
for (const el of pages) {
|
|
103
|
+
el.style.transformOrigin = 'top center';
|
|
104
|
+
el.style.transform = 'translateY(calc(var(--safe-top, 0px) + 8px)) scale(0.94)';
|
|
105
|
+
el.style.borderRadius = '12px';
|
|
106
|
+
el.style.overflow = 'hidden';
|
|
107
|
+
el.style.filter = 'brightness(0.85)';
|
|
108
|
+
if (!reduce)
|
|
109
|
+
el.style.transition = 'transform 250ms ease-out, filter 250ms ease-out';
|
|
110
|
+
}
|
|
111
|
+
return () => {
|
|
112
|
+
pages.forEach((el, i) => {
|
|
113
|
+
if (saved[i] === null)
|
|
114
|
+
el.removeAttribute('style');
|
|
115
|
+
else
|
|
116
|
+
el.setAttribute('style', saved[i]);
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
}, [scaled, resolved]);
|
|
120
|
+
// A body that is scrolled to its top and pulled down would normally start a native overscroll
|
|
121
|
+
// and cancel the pointer stream; cancelling the touch lets the sheet take the gesture instead.
|
|
122
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: `resolved` is when the body first mounts.
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
const body = bodyRef.current;
|
|
125
|
+
if (!open || !body)
|
|
126
|
+
return;
|
|
127
|
+
let startY = 0;
|
|
128
|
+
const onStart = (e) => {
|
|
129
|
+
startY = e.touches[0]?.clientY ?? 0;
|
|
130
|
+
};
|
|
131
|
+
const onMove = (e) => {
|
|
132
|
+
const y = e.touches[0]?.clientY ?? 0;
|
|
133
|
+
if (e.cancelable && body.scrollTop <= 0 && y > startY)
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
};
|
|
136
|
+
body.addEventListener('touchstart', onStart, { passive: true });
|
|
137
|
+
body.addEventListener('touchmove', onMove, { passive: false });
|
|
138
|
+
return () => {
|
|
139
|
+
body.removeEventListener('touchstart', onStart);
|
|
140
|
+
body.removeEventListener('touchmove', onMove);
|
|
141
|
+
};
|
|
142
|
+
}, [open, resolved]);
|
|
143
|
+
const begin = (e, fromBody) => {
|
|
144
|
+
if (e.button !== 0)
|
|
145
|
+
return;
|
|
146
|
+
if (fromBody && (bodyRef.current?.scrollTop ?? 0) > 0)
|
|
147
|
+
return;
|
|
148
|
+
drag.current = {
|
|
149
|
+
pointerId: e.pointerId,
|
|
150
|
+
startY: e.clientY,
|
|
151
|
+
lastY: e.clientY,
|
|
152
|
+
lastT: e.timeStamp,
|
|
153
|
+
velocity: 0,
|
|
154
|
+
moved: false,
|
|
155
|
+
fromBody,
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
const move = (e) => {
|
|
159
|
+
const d = drag.current;
|
|
160
|
+
if (!d || d.pointerId !== e.pointerId)
|
|
161
|
+
return;
|
|
162
|
+
const dy = e.clientY - d.startY;
|
|
163
|
+
if (!d.moved) {
|
|
164
|
+
if (Math.abs(dy) < 4)
|
|
165
|
+
return;
|
|
166
|
+
// Scrolling the content up is the content's business, not the sheet's.
|
|
167
|
+
if (d.fromBody && dy < 0) {
|
|
168
|
+
drag.current = null;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
d.moved = true;
|
|
172
|
+
setDragging(true);
|
|
173
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
174
|
+
}
|
|
175
|
+
// At least one frame, so two events in the same tick do not read as an infinite flick.
|
|
176
|
+
const dt = Math.max(16, e.timeStamp - d.lastT);
|
|
177
|
+
d.velocity = (e.clientY - d.lastY) / dt;
|
|
178
|
+
d.lastY = e.clientY;
|
|
179
|
+
d.lastT = e.timeStamp;
|
|
180
|
+
// Pulling up resists; pulling down past a sheet that cannot close resists too.
|
|
181
|
+
const resists = dy < 0 || (!lower && !dismissible);
|
|
182
|
+
setOffset(resists ? Math.max(dy / 4, -40) : dy);
|
|
183
|
+
};
|
|
184
|
+
const end = (e) => {
|
|
185
|
+
const d = drag.current;
|
|
186
|
+
if (!d || d.pointerId !== e.pointerId)
|
|
187
|
+
return;
|
|
188
|
+
drag.current = null;
|
|
189
|
+
if (!d.moved)
|
|
190
|
+
return;
|
|
191
|
+
suppressClick.current = true;
|
|
192
|
+
// The click that follows a drag is swallowed; if none arrives, do not eat the next real one.
|
|
193
|
+
setTimeout(() => {
|
|
194
|
+
suppressClick.current = false;
|
|
195
|
+
}, 0);
|
|
196
|
+
const dy = e.clientY - d.startY;
|
|
197
|
+
const height = contentRef.current?.getBoundingClientRect().height || 0;
|
|
198
|
+
const up = dy < -RAISE_DISTANCE || d.velocity < -VELOCITY;
|
|
199
|
+
const down = dy > height * LOWER_FRACTION || d.velocity > VELOCITY;
|
|
200
|
+
setDragging(false);
|
|
201
|
+
if (down && dy > 0 && lower) {
|
|
202
|
+
setOffset(0);
|
|
203
|
+
setDetent(lower);
|
|
204
|
+
}
|
|
205
|
+
else if (down && dy > 0 && dismissible) {
|
|
206
|
+
// Leave the offset where the finger let go: the exit animation starts from it.
|
|
207
|
+
requestClose();
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
setOffset(0);
|
|
211
|
+
if (up && higher)
|
|
212
|
+
setDetent(higher);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const cancel = () => {
|
|
216
|
+
drag.current = null;
|
|
217
|
+
setDragging(false);
|
|
218
|
+
setOffset(0);
|
|
219
|
+
};
|
|
220
|
+
const onHandleClick = () => {
|
|
221
|
+
if (suppressClick.current) {
|
|
222
|
+
suppressClick.current = false;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Keyboard and pointer alike: raise until the top, then lower back down.
|
|
226
|
+
if (higher)
|
|
227
|
+
setDetent(higher);
|
|
228
|
+
else if (lower)
|
|
229
|
+
setDetent(lower);
|
|
230
|
+
};
|
|
231
|
+
const dragHandlers = { onPointerMove: move, onPointerUp: end, onPointerCancel: cancel };
|
|
232
|
+
const cycles = ladder.length > 1;
|
|
233
|
+
const raising = !!higher;
|
|
234
|
+
const keyboard = (vv?.kb ?? 0) > 0;
|
|
235
|
+
const viewport = vv ? `${vv.height}px` : '100dvh';
|
|
236
|
+
const style = {
|
|
237
|
+
'--sheet-vh': viewport,
|
|
238
|
+
'--sheet-drag': `${offset}px`,
|
|
239
|
+
bottom: vv?.kb ? `${vv.kb}px` : undefined,
|
|
240
|
+
height: detent === 'fit' ? undefined : `calc(var(--sheet-vh) * ${SHARE[detent]})`,
|
|
241
|
+
maxHeight: `calc(var(--sheet-vh) * ${SHARE[detent]})`,
|
|
242
|
+
};
|
|
243
|
+
const dismissGuard = dismissible
|
|
244
|
+
? undefined
|
|
245
|
+
: (e) => {
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
};
|
|
248
|
+
const content = (_jsxs("div", { ref: rootRef, "data-sheet-root": "", className: "contents", children: [_jsx(RadixDialog.Overlay, { "data-testid": "sheet-scrim", className: "ui-sheet-scrim fixed inset-0 z-40 bg-scrim" }), _jsxs(RadixDialog.Content, { ref: contentRef, "data-detent": detent, "data-dragging": dragging ? '' : undefined, ...(description ? {} : { 'aria-describedby': undefined }), onEscapeKeyDown: dismissGuard, onPointerDownOutside: dismissGuard, onInteractOutside: dismissGuard,
|
|
249
|
+
// The sheet moves focus to its first control; for a sheet that is the handle or close
|
|
250
|
+
// button, which is correct, so Radix's default is kept.
|
|
251
|
+
style: style, className: cn('ui-sheet fixed inset-x-0 bottom-0 z-50 mx-auto flex w-full flex-col overflow-hidden', 'rounded-t-2xl border border-b-0 border-hairline bg-sheet text-ink shadow-2xl outline-none', maxWidthClassName, className), children: [_jsxs("div", { className: "touch-none select-none px-5 pt-2", onPointerDown: (e) => begin(e, false), ...dragHandlers, children: [cycles ? (_jsx("button", { type: "button", "aria-label": raising ? (expandLabel ?? strings.expand) : (collapseLabel ?? strings.collapse), onClick: onHandleClick, className: "mx-auto flex h-6 w-16 cursor-grab items-center justify-center rounded-full focus-visible:outline-2 focus-visible:outline-brand-700 active:cursor-grabbing", children: _jsx("span", { "aria-hidden": "true", className: "h-1.5 w-10 rounded-full bg-hairline" }) })) : (_jsx("div", { "aria-hidden": "true", className: "mx-auto flex h-6 w-16 items-center justify-center", children: _jsx("span", { className: "h-1.5 w-10 rounded-full bg-hairline" }) })), _jsxs("div", { className: "flex items-start gap-3 pb-3 pt-1", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(RadixDialog.Title, { className: "m-0 text-base font-medium text-brand-900", children: title }), description ? (_jsx(RadixDialog.Description, { className: "m-0 mt-1 text-sm text-ink-muted", children: description })) : null] }), showCloseButton && dismissible ? (_jsx(RadixDialog.Close, { "aria-label": closeLabel ?? strings.close, className: "-mr-1 flex size-8 shrink-0 items-center justify-center rounded-full text-ink-muted hover:bg-brand-700/8 focus-visible:outline-2 focus-visible:outline-brand-700", children: _jsx("svg", { "aria-hidden": "true", width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: _jsx("path", { d: "M3.5 3.5l8 8m0-8l-8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })) : null] })] }), _jsx("div", { ref: bodyRef, "data-sheet-body": "", className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 pb-4", onPointerDown: (e) => begin(e, true), ...dragHandlers, children: children }), footer ? (_jsx("div", { "data-sheet-footer": "", className: "flex shrink-0 justify-end gap-2 border-t border-hairline px-5 pt-3",
|
|
252
|
+
// Over the keyboard there is no home indicator to clear, only a gap to leave.
|
|
253
|
+
style: {
|
|
254
|
+
paddingBottom: keyboard ? '0.75rem' : 'max(0.75rem, var(--safe-bottom, 0px))',
|
|
255
|
+
}, children: footer })) : (_jsx("div", { "aria-hidden": "true", className: "shrink-0", style: { height: keyboard ? 0 : 'var(--safe-bottom, 0px)' } }))] })] }));
|
|
256
|
+
// A hidden marker keeps the component's DOM position so the portal can find its Theme.
|
|
257
|
+
const anchor = _jsx("span", { ref: anchorRef, hidden: true, "data-sheet-anchor": "" });
|
|
258
|
+
if (!resolved)
|
|
259
|
+
return anchor;
|
|
260
|
+
return (_jsxs(RadixDialog.Root, { open: open, onOpenChange: onOpenChange, children: [anchor, target ? (_jsx(RadixDialog.Portal, { container: target, children: _jsx(Theme, { hasBackground: false, children: content }) })) : (content)] }));
|
|
261
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Sheet.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Sheet.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, MouseEventHandler, ReactNode } from 'react';
|
|
2
|
+
import { type CardDensity, type CardVariant, type Responsive } from '../Card/index.js';
|
|
3
|
+
export type StatTone = 'positive' | 'negative' | 'neutral';
|
|
4
|
+
export type StatDirection = 'up' | 'down' | 'flat';
|
|
5
|
+
export interface StatDelta {
|
|
6
|
+
/** The change as the reader should see it, already formatted: "+12%", "−€40". */
|
|
7
|
+
value: ReactNode;
|
|
8
|
+
/** Which way the number moved — picks the arrow. `flat` shows none. */
|
|
9
|
+
direction?: StatDirection;
|
|
10
|
+
/** Whether the move is good news. Independent of direction: fewer no-shows is down and positive. */
|
|
11
|
+
tone?: StatTone;
|
|
12
|
+
/** Spoken context for the change, e.g. "versus last month". Visually hidden. */
|
|
13
|
+
label?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface StatTileProps {
|
|
16
|
+
label: ReactNode;
|
|
17
|
+
/** Already formatted; rendered with tabular numerals so a row of tiles lines up. */
|
|
18
|
+
value: ReactNode;
|
|
19
|
+
delta?: StatDelta;
|
|
20
|
+
/** A small chart, rendered under the value in a fixed-height slot. */
|
|
21
|
+
sparkline?: ReactNode;
|
|
22
|
+
/** Makes the tile a link, with a chevron. */
|
|
23
|
+
href?: string;
|
|
24
|
+
linkProps?: Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'className' | 'children'>;
|
|
25
|
+
/** Makes the tile a button (or, with `href`, also fires on the link), with a chevron. */
|
|
26
|
+
onClick?: MouseEventHandler<HTMLElement>;
|
|
27
|
+
variant?: Responsive<CardVariant>;
|
|
28
|
+
density?: Responsive<CardDensity>;
|
|
29
|
+
className?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A compact KPI tile: label, value, optional delta and sparkline. Built on `Card`, so `variant`
|
|
33
|
+
* and `density` mean what they mean there. With `href` or `onClick` the whole tile is the target
|
|
34
|
+
* and a chevron says so.
|
|
35
|
+
*/
|
|
36
|
+
export declare function StatTile({ label, value, delta, sparkline, href, linkProps, onClick, variant, density, className, }: StatTileProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowDownIcon, ArrowUpIcon, ChevronRightIcon } from '@radix-ui/react-icons';
|
|
3
|
+
import { cn } from '../../utils/cn.js';
|
|
4
|
+
import { Card } from '../Card/index.js';
|
|
5
|
+
const ARROWS = { up: ArrowUpIcon, down: ArrowDownIcon };
|
|
6
|
+
/**
|
|
7
|
+
* A compact KPI tile: label, value, optional delta and sparkline. Built on `Card`, so `variant`
|
|
8
|
+
* and `density` mean what they mean there. With `href` or `onClick` the whole tile is the target
|
|
9
|
+
* and a chevron says so.
|
|
10
|
+
*/
|
|
11
|
+
export function StatTile({ label, value, delta, sparkline, href, linkProps, onClick, variant, density, className, }) {
|
|
12
|
+
const interactive = href !== undefined || onClick !== undefined;
|
|
13
|
+
const direction = delta?.direction ?? 'flat';
|
|
14
|
+
const Arrow = direction === 'flat' ? null : ARROWS[direction];
|
|
15
|
+
const body = (_jsxs(_Fragment, { children: [_jsx("span", { className: "ui-stat-label", children: label }), _jsxs("span", { className: "ui-stat-row", children: [_jsx("span", { className: "ui-stat-value", children: value }), delta && (_jsxs("span", { className: "ui-stat-delta", "data-tone": delta.tone ?? 'neutral', children: [Arrow && _jsx(Arrow, { "aria-hidden": true }), delta.value, delta.label && _jsxs("span", { className: "sr-only", children: [" ", delta.label] })] }))] }), sparkline && (_jsx("span", { className: "ui-stat-spark", "aria-hidden": true, children: sparkline })), interactive && _jsx(ChevronRightIcon, { className: "ui-stat-chevron", "aria-hidden": true })] }));
|
|
16
|
+
return (_jsx(Card, { variant: variant, density: density, className: cn('ui-stat-card', className), children: href !== undefined ? (_jsx("a", { ...linkProps, href: href, className: "ui-stat", "data-interactive": true, onClick: onClick, children: body })) : onClick ? (_jsx("button", { type: "button", className: "ui-stat", "data-interactive": true, onClick: onClick, children: body })) : (_jsx("div", { className: "ui-stat", children: body })) }));
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StatTile.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StatTile.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
+
export type TabBarBreakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
3
|
+
/** What a `render` function must spread onto the element it returns (a router link, say). */
|
|
4
|
+
export interface TabBarItemRenderProps {
|
|
5
|
+
className: string;
|
|
6
|
+
'aria-current'?: 'page';
|
|
7
|
+
'aria-label'?: string;
|
|
8
|
+
'data-active': 'true' | 'false';
|
|
9
|
+
onClick: () => void;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
export interface TabBarItem {
|
|
13
|
+
value: string;
|
|
14
|
+
label: string;
|
|
15
|
+
icon: ReactNode;
|
|
16
|
+
/** Shown instead of `icon` while active (a filled glyph, usually). */
|
|
17
|
+
activeIcon?: ReactNode;
|
|
18
|
+
/** A number shows a count (99+ caps); `true` shows a dot. */
|
|
19
|
+
badge?: number | boolean;
|
|
20
|
+
/** Wrap a router link: spread the props onto the element you return. */
|
|
21
|
+
render?: (props: TabBarItemRenderProps) => ReactElement;
|
|
22
|
+
}
|
|
23
|
+
export interface TabBarProps {
|
|
24
|
+
items: TabBarItem[];
|
|
25
|
+
value: string;
|
|
26
|
+
onValueChange?: (value: string) => void;
|
|
27
|
+
/** Breakpoint (Radix Themes' scale) from which the bar is hidden. Default `md`. */
|
|
28
|
+
hideFrom?: TabBarBreakpoint;
|
|
29
|
+
'aria-label'?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A fixed bottom navigation bar for small screens. 3–5 items; the active one is in the brand
|
|
34
|
+
* colour and carries `aria-current="page"`. Height is `--tabbar-height` (49px + `--safe-bottom`);
|
|
35
|
+
* render a {@link TabBarSpacer} (or pad by that variable) so content is not covered.
|
|
36
|
+
* Press feedback is `:active`; nothing depends on hover.
|
|
37
|
+
*/
|
|
38
|
+
export declare function TabBar({ items, value, onValueChange, hideFrom, className, 'aria-label': ariaLabel, }: TabBarProps): import("react").JSX.Element;
|
|
39
|
+
/** Reserves the bar's height in the flow, hidden at the same breakpoint as the bar. */
|
|
40
|
+
export declare function TabBarSpacer({ hideFrom }: {
|
|
41
|
+
hideFrom?: TabBarBreakpoint;
|
|
42
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
function badgeText(badge) {
|
|
3
|
+
if (badge === undefined || badge === false || badge === 0)
|
|
4
|
+
return null;
|
|
5
|
+
if (badge === true)
|
|
6
|
+
return '';
|
|
7
|
+
return badge > 99 ? '99+' : String(badge);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A fixed bottom navigation bar for small screens. 3–5 items; the active one is in the brand
|
|
11
|
+
* colour and carries `aria-current="page"`. Height is `--tabbar-height` (49px + `--safe-bottom`);
|
|
12
|
+
* render a {@link TabBarSpacer} (or pad by that variable) so content is not covered.
|
|
13
|
+
* Press feedback is `:active`; nothing depends on hover.
|
|
14
|
+
*/
|
|
15
|
+
export function TabBar({ items, value, onValueChange, hideFrom = 'md', className, 'aria-label': ariaLabel, }) {
|
|
16
|
+
return (_jsx("nav", { className: ['ui-tabbar', className].filter(Boolean).join(' '), "data-hide-from": hideFrom, "aria-label": ariaLabel, children: items.map((item) => {
|
|
17
|
+
const active = item.value === value;
|
|
18
|
+
const text = badgeText(item.badge);
|
|
19
|
+
const children = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "ui-tabbar__icon", "aria-hidden": "true", children: [active && item.activeIcon ? item.activeIcon : item.icon, text !== null && (_jsx("span", { className: "ui-tabbar__badge", "data-dot": text === '' ? 'true' : undefined, children: text }))] }), _jsx("span", { className: "ui-tabbar__label", children: item.label })] }));
|
|
20
|
+
const props = {
|
|
21
|
+
className: 'ui-tabbar__item',
|
|
22
|
+
'aria-current': active ? 'page' : undefined,
|
|
23
|
+
'aria-label': typeof item.badge === 'number' && item.badge > 0
|
|
24
|
+
? `${item.label}, ${item.badge}`
|
|
25
|
+
: undefined,
|
|
26
|
+
'data-active': active ? 'true' : 'false',
|
|
27
|
+
onClick: () => onValueChange?.(item.value),
|
|
28
|
+
children,
|
|
29
|
+
};
|
|
30
|
+
return (_jsx("span", { className: "ui-tabbar__cell", children: item.render ? item.render(props) : _jsx("button", { type: "button", ...props }) }, item.value));
|
|
31
|
+
}) }));
|
|
32
|
+
}
|
|
33
|
+
/** Reserves the bar's height in the flow, hidden at the same breakpoint as the bar. */
|
|
34
|
+
export function TabBarSpacer({ hideFrom = 'md' }) {
|
|
35
|
+
return _jsx("div", { className: "ui-tabbar-spacer", "data-hide-from": hideFrom, "aria-hidden": "true" });
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TabBar.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TabBar.js';
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { TextArea } from '@radix-ui/themes';
|
|
2
2
|
import { type ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* `inputMode` and `enterKeyHint` pass through to the `<textarea>`. On coarse pointers the text is
|
|
5
|
+
* never below 16px, or iOS zooms the page on focus.
|
|
6
|
+
*/
|
|
3
7
|
export interface TextareaProps extends Omit<ComponentPropsWithoutRef<typeof TextArea>, 'size' | 'color'> {
|
|
4
8
|
invalid?: boolean;
|
|
5
9
|
}
|
|
@@ -3,5 +3,5 @@ import { TextArea } from '@radix-ui/themes';
|
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
4
|
import { cn } from '../../utils/cn.js';
|
|
5
5
|
export const Textarea = forwardRef(function Textarea({ invalid = false, className, rows = 3, ...props }, ref) {
|
|
6
|
-
return (_jsx(TextArea, { ref: ref, rows: rows, size: "2", color: invalid ? 'red' : undefined, "aria-invalid": invalid || undefined, className: cn('w-full', className), ...props }));
|
|
6
|
+
return (_jsx(TextArea, { ref: ref, rows: rows, size: "2", color: invalid ? 'red' : undefined, "aria-invalid": invalid || undefined, className: cn('ui-textarea w-full', className), ...props }));
|
|
7
7
|
});
|
|
@@ -44,7 +44,13 @@ export declare const toast: typeof show & {
|
|
|
44
44
|
export interface ToasterProps {
|
|
45
45
|
/** Milliseconds a toast stays when it was not given its own `duration`. */
|
|
46
46
|
duration?: number;
|
|
47
|
-
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
47
|
+
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center';
|
|
48
|
+
/**
|
|
49
|
+
* Distance from the anchored edge (top for `top-*`, bottom for `bottom-*`). A number is px; a
|
|
50
|
+
* string is any CSS length, so a toast can sit under a nav bar or above a tab bar:
|
|
51
|
+
* `offset="var(--navbar-height)"`, `offset="calc(var(--tabbar-height) + 8px)"`.
|
|
52
|
+
*/
|
|
53
|
+
offset?: number | string;
|
|
48
54
|
/** The landmark name screen readers announce, e.g. "Notificações ({hotkey})". */
|
|
49
55
|
label?: string;
|
|
50
56
|
}
|
|
@@ -55,5 +61,5 @@ export interface ToasterProps {
|
|
|
55
61
|
* stack its live-region announcements, the F8 hotkey to reach it, pause-on-hover and swipe to
|
|
56
62
|
* dismiss.
|
|
57
63
|
*/
|
|
58
|
-
export declare function Toaster({ duration, position, label }: ToasterProps): import("react").JSX.Element;
|
|
64
|
+
export declare function Toaster({ duration, position, offset, label, }: ToasterProps): import("react").JSX.Element;
|
|
59
65
|
export {};
|
|
@@ -68,6 +68,8 @@ const POSITIONS = {
|
|
|
68
68
|
'top-left': 'top-0 left-0',
|
|
69
69
|
'bottom-right': 'bottom-0 right-0 flex-col-reverse',
|
|
70
70
|
'bottom-left': 'bottom-0 left-0 flex-col-reverse',
|
|
71
|
+
'top-center': 'top-0 left-1/2 -translate-x-1/2',
|
|
72
|
+
'bottom-center': 'bottom-0 left-1/2 -translate-x-1/2 flex-col-reverse',
|
|
71
73
|
};
|
|
72
74
|
/**
|
|
73
75
|
* Renders the stack. Mount once, inside `<Theme>` so the toasts take the app's palette.
|
|
@@ -76,10 +78,21 @@ const POSITIONS = {
|
|
|
76
78
|
* stack its live-region announcements, the F8 hotkey to reach it, pause-on-hover and swipe to
|
|
77
79
|
* dismiss.
|
|
78
80
|
*/
|
|
79
|
-
export function Toaster({ duration = 5000, position = 'bottom-right', label }) {
|
|
81
|
+
export function Toaster({ duration = 5000, position = 'bottom-right', offset, label, }) {
|
|
80
82
|
const items = useSyncExternalStore(subscribe, () => records, () => records);
|
|
81
83
|
const strings = useUiStrings();
|
|
82
|
-
|
|
84
|
+
// Centred stacks dismiss vertically, towards the edge they are anchored to: that is the way a
|
|
85
|
+
// thumb flicks a banner away, and sideways would fight the browser's back-swipe.
|
|
86
|
+
const swipeDirection = position.endsWith('center')
|
|
87
|
+
? position.startsWith('top')
|
|
88
|
+
? 'up'
|
|
89
|
+
: 'down'
|
|
90
|
+
: position.endsWith('left')
|
|
91
|
+
? 'left'
|
|
92
|
+
: 'right';
|
|
93
|
+
const vertical = swipeDirection === 'up' || swipeDirection === 'down';
|
|
94
|
+
const edge = position.startsWith('top') ? 'top' : 'bottom';
|
|
95
|
+
const edgeOffset = typeof offset === 'number' ? `${offset}px` : offset;
|
|
83
96
|
return (_jsxs(RadixToast.Provider, { duration: duration, swipeDirection: swipeDirection, label: label, children: [items.map((item) => {
|
|
84
97
|
const { color, icon } = TYPES[item.type];
|
|
85
98
|
return (_jsxs(RadixToast.Root, { open: item.open, duration: item.duration, type: item.type === 'error' ? 'foreground' : 'background', onOpenChange: (open) => {
|
|
@@ -88,10 +101,12 @@ export function Toaster({ duration = 5000, position = 'bottom-right', label }) {
|
|
|
88
101
|
}, onAnimationEnd: () => {
|
|
89
102
|
if (!item.open)
|
|
90
103
|
remove(item.id);
|
|
91
|
-
}, "data-accent-color": color,
|
|
104
|
+
}, "data-accent-color": color, "data-swipe-axis": vertical ? 'y' : undefined, style: vertical
|
|
105
|
+
? { '--ui-toast-swipe-sign': swipeDirection === 'up' ? -1 : 1 }
|
|
106
|
+
: undefined, className: "ui-toast panel pointer-events-auto relative flex w-full items-start gap-3 overflow-hidden py-3 pr-3 pl-4", children: [_jsx("span", { "aria-hidden": true, className: "absolute inset-y-0 left-0 w-1 bg-[var(--accent-9)]" }), _jsx(Icon, { name: icon, className: "mt-0.5 text-[var(--accent-11)]" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(RadixToast.Title, { className: "text-sm font-medium text-ink", children: item.title }), item.description ? (_jsx(RadixToast.Description, { className: "mt-0.5 text-sm text-ink-muted", children: item.description })) : null] }), item.action ? (_jsx(RadixToast.Action, { altText: item.action.label, onClick: (event) => {
|
|
92
107
|
item.action?.onClick();
|
|
93
108
|
if (item.action?.keepOpen)
|
|
94
109
|
event.preventDefault();
|
|
95
110
|
}, className: cn('shrink-0 rounded-md px-2 py-1 text-sm font-medium', 'text-[var(--accent-11)] hover:bg-[var(--accent-a3)]'), children: item.action.label })) : null, _jsx(RadixToast.Close, { "aria-label": strings.dismiss, className: "shrink-0 rounded-md p-1 text-ink-muted hover:bg-[var(--gray-a3)]", children: _jsx(Icon, { name: "close", className: "size-4" }) })] }, item.id));
|
|
96
|
-
}), _jsx(RadixToast.Viewport, { ref: warnMissingScales, className: cn('fixed z-50 m-0 flex w-full max-w-sm list-none flex-col gap-2 p-4 outline-none', POSITIONS[position]) })] }));
|
|
111
|
+
}), _jsx(RadixToast.Viewport, { ref: warnMissingScales, style: edgeOffset === undefined ? undefined : { [edge]: edgeOffset }, className: cn('fixed z-50 m-0 flex w-full max-w-sm list-none flex-col gap-2 p-4 outline-none', POSITIONS[position]) })] }));
|
|
97
112
|
}
|
package/dist/atoms/index.d.ts
CHANGED
|
@@ -1,25 +1,36 @@
|
|
|
1
|
+
export * from './ActionSheet/index.js';
|
|
1
2
|
export * from './Alert/index.js';
|
|
2
3
|
export * from './Avatar/index.js';
|
|
3
4
|
export * from './Badge/index.js';
|
|
4
5
|
export * from './Button/index.js';
|
|
5
6
|
export * from './Card/index.js';
|
|
6
7
|
export * from './Checkbox/index.js';
|
|
8
|
+
export * from './ChipGroup/index.js';
|
|
7
9
|
export * from './ContextMenu/index.js';
|
|
8
10
|
export * from './Dialog/index.js';
|
|
9
11
|
export * from './Drawer/index.js';
|
|
10
12
|
export * from './Icon/index.js';
|
|
11
13
|
export * from './Input/index.js';
|
|
12
14
|
export * from './Label/index.js';
|
|
15
|
+
export * from './List/index.js';
|
|
16
|
+
export * from './NavBar/index.js';
|
|
13
17
|
export * from './NotificationCard/index.js';
|
|
14
18
|
export * from './Popover/index.js';
|
|
15
19
|
export * from './Progress/index.js';
|
|
20
|
+
export * from './PullToRefresh/index.js';
|
|
21
|
+
export * from './ResponsiveDialog/index.js';
|
|
16
22
|
export * from './RichText/index.js';
|
|
23
|
+
export * from './ScrollStrip/index.js';
|
|
24
|
+
export * from './SegmentedControl/index.js';
|
|
17
25
|
export * from './Select/index.js';
|
|
18
26
|
export * from './Separator/index.js';
|
|
27
|
+
export * from './Sheet/index.js';
|
|
19
28
|
export * from './Skeleton/index.js';
|
|
20
29
|
export * from './Slider/index.js';
|
|
21
30
|
export * from './Spinner/index.js';
|
|
31
|
+
export * from './StatTile/index.js';
|
|
22
32
|
export * from './Switch/index.js';
|
|
33
|
+
export * from './TabBar/index.js';
|
|
23
34
|
export * from './Textarea/index.js';
|
|
24
35
|
export * from './Toast/index.js';
|
|
25
36
|
export * from './Tooltip/index.js';
|
package/dist/atoms/index.js
CHANGED
|
@@ -1,25 +1,36 @@
|
|
|
1
|
+
export * from './ActionSheet/index.js';
|
|
1
2
|
export * from './Alert/index.js';
|
|
2
3
|
export * from './Avatar/index.js';
|
|
3
4
|
export * from './Badge/index.js';
|
|
4
5
|
export * from './Button/index.js';
|
|
5
6
|
export * from './Card/index.js';
|
|
6
7
|
export * from './Checkbox/index.js';
|
|
8
|
+
export * from './ChipGroup/index.js';
|
|
7
9
|
export * from './ContextMenu/index.js';
|
|
8
10
|
export * from './Dialog/index.js';
|
|
9
11
|
export * from './Drawer/index.js';
|
|
10
12
|
export * from './Icon/index.js';
|
|
11
13
|
export * from './Input/index.js';
|
|
12
14
|
export * from './Label/index.js';
|
|
15
|
+
export * from './List/index.js';
|
|
16
|
+
export * from './NavBar/index.js';
|
|
13
17
|
export * from './NotificationCard/index.js';
|
|
14
18
|
export * from './Popover/index.js';
|
|
15
19
|
export * from './Progress/index.js';
|
|
20
|
+
export * from './PullToRefresh/index.js';
|
|
21
|
+
export * from './ResponsiveDialog/index.js';
|
|
16
22
|
export * from './RichText/index.js';
|
|
23
|
+
export * from './ScrollStrip/index.js';
|
|
24
|
+
export * from './SegmentedControl/index.js';
|
|
17
25
|
export * from './Select/index.js';
|
|
18
26
|
export * from './Separator/index.js';
|
|
27
|
+
export * from './Sheet/index.js';
|
|
19
28
|
export * from './Skeleton/index.js';
|
|
20
29
|
export * from './Slider/index.js';
|
|
21
30
|
export * from './Spinner/index.js';
|
|
31
|
+
export * from './StatTile/index.js';
|
|
22
32
|
export * from './Switch/index.js';
|
|
33
|
+
export * from './TabBar/index.js';
|
|
23
34
|
export * from './Textarea/index.js';
|
|
24
35
|
export * from './Toast/index.js';
|
|
25
36
|
export * from './Tooltip/index.js';
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* reaches for a `Table` or `Tabs` through this package rather than importing Themes beside it
|
|
7
7
|
* and drifting from the version the atoms were built against.
|
|
8
8
|
*/
|
|
9
|
-
export { AccessibleIcon, AlertDialog, AspectRatio, Blockquote, Box, Callout, CheckboxCards, CheckboxGroup, Code, Container, DataList, DropdownMenu, Em, Flex, Grid, Heading, HoverCard, IconButton, Inset, Kbd, Link, Portal, Quote, Radio, RadioCards, RadioGroup, Reset, ScrollArea, Section, SegmentedControl, Slot, Slottable, Strong, Table, TabNav, Tabs, Text, TextField, Theme as ThemeRoot, ThemePanel, VisuallyHidden, } from '@radix-ui/themes';
|
|
9
|
+
export { AccessibleIcon, AlertDialog, AspectRatio, Blockquote, Box, Callout, CheckboxCards, CheckboxGroup, Code, Container, DataList, DropdownMenu, Em, Flex, Grid, Heading, HoverCard, IconButton, Inset, Kbd, Link, Portal, Quote, Radio, RadioCards, RadioGroup, Reset, ScrollArea, Section, SegmentedControl as RadixSegmentedControl, Slot, Slottable, Strong, Table, TabNav, Tabs, Text, TextField, Theme as ThemeRoot, ThemePanel, VisuallyHidden, } from '@radix-ui/themes';
|
package/dist/primitives/index.js
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* reaches for a `Table` or `Tabs` through this package rather than importing Themes beside it
|
|
7
7
|
* and drifting from the version the atoms were built against.
|
|
8
8
|
*/
|
|
9
|
-
export { AccessibleIcon, AlertDialog, AspectRatio, Blockquote, Box, Callout, CheckboxCards, CheckboxGroup, Code, Container, DataList, DropdownMenu, Em, Flex, Grid, Heading, HoverCard, IconButton, Inset, Kbd, Link, Portal, Quote, Radio, RadioCards, RadioGroup, Reset, ScrollArea, Section, SegmentedControl, Slot, Slottable, Strong, Table, TabNav, Tabs, Text, TextField, Theme as ThemeRoot, ThemePanel, VisuallyHidden, } from '@radix-ui/themes';
|
|
9
|
+
export { AccessibleIcon, AlertDialog, AspectRatio, Blockquote, Box, Callout, CheckboxCards, CheckboxGroup, Code, Container, DataList, DropdownMenu, Em, Flex, Grid, Heading, HoverCard, IconButton, Inset, Kbd, Link, Portal, Quote, Radio, RadioCards, RadioGroup, Reset, ScrollArea, Section, SegmentedControl as RadixSegmentedControl, Slot, Slottable, Strong, Table, TabNav, Tabs, Text, TextField, Theme as ThemeRoot, ThemePanel, VisuallyHidden, } from '@radix-ui/themes';
|
package/dist/strings.d.ts
CHANGED
|
@@ -34,6 +34,18 @@ export interface UiStrings {
|
|
|
34
34
|
expand: string;
|
|
35
35
|
/** Drawer handle / NotificationCard toggle, while expanded. */
|
|
36
36
|
collapse: string;
|
|
37
|
+
/** ListRow: the accessible name of the overflow button holding a row's actions. */
|
|
38
|
+
moreActions: string;
|
|
39
|
+
/** ActionSheet's separated button, and confirm()'s dismiss button. */
|
|
40
|
+
cancel: string;
|
|
41
|
+
/** confirm()'s default accept button. */
|
|
42
|
+
confirm: string;
|
|
43
|
+
/** BackButton's accessible name when it has no visible label. */
|
|
44
|
+
back: string;
|
|
45
|
+
/** PullToRefresh: announced while a refresh is in progress. */
|
|
46
|
+
refreshing: string;
|
|
47
|
+
/** ScrollStrip: the accessible name of the scrollable region, when none is passed. */
|
|
48
|
+
scrollableRegion: string;
|
|
37
49
|
}
|
|
38
50
|
/**
|
|
39
51
|
* Feeds the atoms their strings, once, near the root.
|
package/dist/strings.js
CHANGED
|
@@ -14,6 +14,12 @@ const DEFAULTS = {
|
|
|
14
14
|
close: 'Close',
|
|
15
15
|
expand: 'Expand',
|
|
16
16
|
collapse: 'Collapse',
|
|
17
|
+
moreActions: 'More actions',
|
|
18
|
+
cancel: 'Cancel',
|
|
19
|
+
confirm: 'Confirm',
|
|
20
|
+
back: 'Back',
|
|
21
|
+
refreshing: 'Refreshing',
|
|
22
|
+
scrollableRegion: 'Scrollable content',
|
|
17
23
|
};
|
|
18
24
|
const StringsContext = createContext(DEFAULTS);
|
|
19
25
|
/**
|