@salemcobalt/ui 0.2.0-rc.23 → 0.2.0-rc.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +180 -22
- package/dist/index.js.map +1 -1
- package/dist/styles.css +53 -6
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Styles ship as one stylesheet inside `@layer sc`, so an app's unlayered CSS alwa
|
|
|
33
33
|
| `Card` | `variant` outlined · elevated · filled · plain (a quiet row: no surface, a hover wash without border or shadow, selected is `accent.wash` with a 1px `accent.border`); `padding` none · sm · md · lg; `interactive`; `selected` (visual only: pair with `aria-pressed`/`aria-selected`/`aria-current`; also turns a Score inside solid); `as` or `render` to make it an `a`/`button`/`li`. |
|
|
34
34
|
| `Stack`, `Inline` | `gap` from the spacing scale (`none` … `4xl`); `align`; `justify`; `as`. `Inline` wraps unless `wrap={false}`. |
|
|
35
35
|
| `Kbd` | One key per `Kbd`; `Kbd.Group label="Command K"` groups a shortcut with a `+` (or `separator="space"`). |
|
|
36
|
-
| `Dialog` | `Root` (`open`, `defaultOpen`, `onOpenChange`, `dismissible`, `onRequestClose(reason)`: Esc, backdrop and every `Close` ask instead of closing; close by setting `open`), `Trigger`, `Content` (`size` sm · md · lg, every size clamps to the viewport minus a 16px gutter; `variant="sheet"` becomes a bottom sheet below 640px with a scrolling `Body`; `variant="fullscreen"` covers the viewport, inset 16px from 900px, with a scrolling `Body`), `Title`, `Description`, `Body`, `Actions`, `Close`. Menus, selects and tooltips opened inside render above it.
|
|
36
|
+
| `Dialog` | `Root` (`open`, `defaultOpen`, `onOpenChange`, `dismissible`, `onRequestClose(reason)`: Esc, backdrop and every `Close` ask instead of closing; close by setting `open`), `Trigger`, `Content` (`size` sm · md · lg, every size clamps to the viewport minus a 16px gutter; `variant="sheet"` becomes a bottom sheet below 640px with a scrolling `Body` and swipe-down-to-dismiss from its handle or header (top 64px, outside controls and `Body`; closes past 30% of its height or on a flick, otherwise springs back; the handle is a `Close` named "Dismiss"; `dismissOnSwipe={false}` where an accidental close loses work, also off when the Root is not `dismissible`); `variant="fullscreen"` covers the viewport, inset 16px from 900px, with a scrolling `Body`), `Title`, `Description`, `Body`, `Actions`, `Close`. Menus, selects and tooltips opened inside render above it. |
|
|
37
37
|
| `FullScreenFlow` | A multi-step takeover on `Dialog variant="fullscreen"` (docs/adr/0006). `Root` (`open` default `true`, required `onRequestClose(reason)` for Esc and the ×, `onNext`/`onBack` for Enter and the arrow keys outside controls, `decoration`), `Header` (`title` names the dialog, `logo`, `compactTitle` below 760px, `status`, `closeLabel`; children: `Progress`), `Progress` (`chapters` `{ label, steps }[]`, `step` counted from 1, `label`; current chapter `aria-current="step"`), `Body`, `Rail` (from 1000px, `footer`) › `RailGroup` (`label`) › `RailItem` (`current`, `done`, `disabled`, `adornment`), `Viewport` (`direction` forward · back; one keyed `Step`), `Step` (`title` an `<h2>` that takes focus, `eyebrow`, `description`, `announcement` for the live region), `Footer` (`back`, `hint`, children; 44px buttons). The app owns the step state; the flow owns motion, focus and announcements. |
|
|
38
38
|
| `Toolbar` | `Root` (required `aria-label`, `wrap`, `gap`), `Group` (`aria-label`, `gap`), `Separator`, `Spacer`, `Button` (`render={<IconButton … />}` or `render={<Menu.Trigger render={…} />}` so the control joins the roving focus: Tab enters, arrows move, Tab leaves), `Link` (an anchor in the same navigation; `render={<IconButton label render={<a href />} />}` for an icon link). |
|
|
39
39
|
| `Banner` | `tone` info (default) · success · warning · danger · neutral; `icon` (or `false`); `action` slot; `dismissible` + `onDismiss` (`open` to control); warning and danger are `role="alert"`, the rest `role="status"`. |
|
package/dist/index.d.ts
CHANGED
|
@@ -738,13 +738,21 @@ interface DialogContentProps extends Omit<Dialog$1.Popup.Props, 'className'> {
|
|
|
738
738
|
* the Root when leaving needs a confirmation.
|
|
739
739
|
*/
|
|
740
740
|
variant?: DialogVariant | undefined;
|
|
741
|
+
/**
|
|
742
|
+
* `sheet` only. Below 640px a downward drag on the handle or the header (the top 64px, outside controls
|
|
743
|
+
* and `Dialog.Body`) moves the sheet; letting go past 30% of its height, or flicking down, closes it the
|
|
744
|
+
* way a press on the handle does (the handle is a `Dialog.Close` named "Dismiss"). Set `false` where an
|
|
745
|
+
* accidental close loses work; the handle then stays as decoration only. Off whenever the Root is not
|
|
746
|
+
* `dismissible`. Default `true`.
|
|
747
|
+
*/
|
|
748
|
+
dismissOnSwipe?: boolean | undefined;
|
|
741
749
|
className?: string | undefined;
|
|
742
750
|
}
|
|
743
751
|
/**
|
|
744
752
|
* Portal, backdrop and the popup. Focus moves inside on open and returns to the trigger on close.
|
|
745
753
|
* Menus, selects and tooltips opened from inside render above it (`--sc-z-dropdown` > `--sc-z-dialog`).
|
|
746
754
|
*/
|
|
747
|
-
declare function Content$2({ size, variant, className, children, ...rest }: DialogContentProps): react.JSX.Element;
|
|
755
|
+
declare function Content$2({ size, variant, dismissOnSwipe, className, children, ...rest }: DialogContentProps): react.JSX.Element;
|
|
748
756
|
interface DialogTitleProps extends Omit<Dialog$1.Title.Props, 'className'> {
|
|
749
757
|
className?: string | undefined;
|
|
750
758
|
}
|
package/dist/index.js
CHANGED
|
@@ -167,6 +167,141 @@ var Kbd = Object.assign(KbdBase, { Group: KbdGroup });
|
|
|
167
167
|
|
|
168
168
|
// src/components/Dialog/Dialog.tsx
|
|
169
169
|
import { Dialog as BaseDialog } from "@base-ui/react/dialog";
|
|
170
|
+
import { createContext, useCallback, useContext } from "react";
|
|
171
|
+
|
|
172
|
+
// src/components/Dialog/sheetSwipe.ts
|
|
173
|
+
var SHEET_SWIPE = {
|
|
174
|
+
/** Height in px, from the sheet's top edge, where a drag may start: the handle and the header. */
|
|
175
|
+
zone: 64,
|
|
176
|
+
/** Movement in px before a press becomes a drag, so taps stay taps. */
|
|
177
|
+
slop: 6,
|
|
178
|
+
/** Fraction of the sheet's height past which a release dismisses. */
|
|
179
|
+
distance: 0.3,
|
|
180
|
+
/** Release speed in px/ms: this fast downward dismisses at any distance, this fast upward keeps it open. */
|
|
181
|
+
velocity: 0.5,
|
|
182
|
+
/** Only samples this recent (ms) before the release count toward its speed. */
|
|
183
|
+
window: 100
|
|
184
|
+
};
|
|
185
|
+
function dragIntent(dx, dy, slop = SHEET_SWIPE.slop) {
|
|
186
|
+
if (Math.abs(dx) < slop && Math.abs(dy) < slop) return "pending";
|
|
187
|
+
return dy > 0 && dy >= Math.abs(dx) ? "drag" : "cancel";
|
|
188
|
+
}
|
|
189
|
+
function releaseVelocity(samples, window2 = SHEET_SWIPE.window) {
|
|
190
|
+
const last = samples[samples.length - 1];
|
|
191
|
+
if (!last) return 0;
|
|
192
|
+
const first = samples.find((s) => last.t - s.t <= window2) ?? last;
|
|
193
|
+
const dt = last.t - first.t;
|
|
194
|
+
return dt > 0 ? (last.y - first.y) / dt : 0;
|
|
195
|
+
}
|
|
196
|
+
function shouldDismissSheet({ offset, height, velocity }) {
|
|
197
|
+
if (offset <= 0 || height <= 0) return false;
|
|
198
|
+
if (velocity >= SHEET_SWIPE.velocity) return true;
|
|
199
|
+
if (velocity <= -SHEET_SWIPE.velocity) return false;
|
|
200
|
+
return offset >= height * SHEET_SWIPE.distance;
|
|
201
|
+
}
|
|
202
|
+
var IGNORE = 'a,button,input,select,textarea,label,[role="button"],[contenteditable]';
|
|
203
|
+
function attachSheetSwipe(handle) {
|
|
204
|
+
const popup = handle.parentElement;
|
|
205
|
+
if (!popup) return () => {
|
|
206
|
+
};
|
|
207
|
+
let press = null;
|
|
208
|
+
let dragging = false;
|
|
209
|
+
let height = 0;
|
|
210
|
+
let samples = [];
|
|
211
|
+
let suppressClick = false;
|
|
212
|
+
const startsDrag = (e) => {
|
|
213
|
+
if (!e.isPrimary || e.pointerType === "mouse" && e.button !== 0) return false;
|
|
214
|
+
if (handle.getClientRects().length === 0) return false;
|
|
215
|
+
const target = e.target instanceof Element ? e.target : null;
|
|
216
|
+
if (!target || handle.contains(target)) return true;
|
|
217
|
+
if (target.closest(IGNORE) || target.closest(".sc-dialog__body")) return false;
|
|
218
|
+
return e.clientY - popup.getBoundingClientRect().top <= SHEET_SWIPE.zone;
|
|
219
|
+
};
|
|
220
|
+
const settle = () => {
|
|
221
|
+
delete popup.dataset.swiping;
|
|
222
|
+
popup.style.removeProperty("transform");
|
|
223
|
+
};
|
|
224
|
+
const onDown = (e) => {
|
|
225
|
+
if (press || !startsDrag(e)) return;
|
|
226
|
+
press = { id: e.pointerId, x: e.clientX, y: e.clientY };
|
|
227
|
+
samples = [{ t: e.timeStamp, y: e.clientY }];
|
|
228
|
+
};
|
|
229
|
+
const onMove = (e) => {
|
|
230
|
+
if (!press || e.pointerId !== press.id) return;
|
|
231
|
+
const dy = e.clientY - press.y;
|
|
232
|
+
if (!dragging) {
|
|
233
|
+
const intent = dragIntent(e.clientX - press.x, dy);
|
|
234
|
+
if (intent === "pending") return;
|
|
235
|
+
if (intent === "cancel") {
|
|
236
|
+
press = null;
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
dragging = true;
|
|
240
|
+
height = popup.getBoundingClientRect().height;
|
|
241
|
+
popup.dataset.swiping = "";
|
|
242
|
+
popup.setPointerCapture?.(e.pointerId);
|
|
243
|
+
window.getSelection()?.removeAllRanges();
|
|
244
|
+
}
|
|
245
|
+
e.preventDefault();
|
|
246
|
+
samples.push({ t: e.timeStamp, y: e.clientY });
|
|
247
|
+
if (samples.length > 32) samples = samples.slice(-16);
|
|
248
|
+
popup.style.transform = `translateY(${Math.max(0, dy)}px)`;
|
|
249
|
+
};
|
|
250
|
+
const end = (e, cancelled) => {
|
|
251
|
+
if (!press || e.pointerId !== press.id) return;
|
|
252
|
+
const startY = press.y;
|
|
253
|
+
const wasDragging = dragging;
|
|
254
|
+
press = null;
|
|
255
|
+
dragging = false;
|
|
256
|
+
if (!wasDragging) return;
|
|
257
|
+
window.setTimeout(() => {
|
|
258
|
+
suppressClick = false;
|
|
259
|
+
}, 0);
|
|
260
|
+
if (cancelled) {
|
|
261
|
+
suppressClick = true;
|
|
262
|
+
settle();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
samples.push({ t: e.timeStamp, y: e.clientY });
|
|
266
|
+
const release = {
|
|
267
|
+
offset: Math.max(0, e.clientY - startY),
|
|
268
|
+
height,
|
|
269
|
+
velocity: releaseVelocity(samples)
|
|
270
|
+
};
|
|
271
|
+
if (shouldDismissSheet(release)) {
|
|
272
|
+
delete popup.dataset.swiping;
|
|
273
|
+
handle.click();
|
|
274
|
+
suppressClick = true;
|
|
275
|
+
window.requestAnimationFrame(() => {
|
|
276
|
+
if (popup.isConnected && popup.hasAttribute("data-open")) settle();
|
|
277
|
+
});
|
|
278
|
+
} else {
|
|
279
|
+
suppressClick = true;
|
|
280
|
+
settle();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
const onUp = (e) => end(e, false);
|
|
284
|
+
const onCancel = (e) => end(e, true);
|
|
285
|
+
const onClick = (e) => {
|
|
286
|
+
if (!suppressClick) return;
|
|
287
|
+
suppressClick = false;
|
|
288
|
+
e.preventDefault();
|
|
289
|
+
e.stopPropagation();
|
|
290
|
+
};
|
|
291
|
+
popup.addEventListener("pointerdown", onDown);
|
|
292
|
+
popup.addEventListener("pointermove", onMove);
|
|
293
|
+
popup.addEventListener("pointerup", onUp);
|
|
294
|
+
popup.addEventListener("pointercancel", onCancel);
|
|
295
|
+
popup.addEventListener("click", onClick, true);
|
|
296
|
+
return () => {
|
|
297
|
+
popup.removeEventListener("pointerdown", onDown);
|
|
298
|
+
popup.removeEventListener("pointermove", onMove);
|
|
299
|
+
popup.removeEventListener("pointerup", onUp);
|
|
300
|
+
popup.removeEventListener("pointercancel", onCancel);
|
|
301
|
+
popup.removeEventListener("click", onClick, true);
|
|
302
|
+
settle();
|
|
303
|
+
};
|
|
304
|
+
}
|
|
170
305
|
|
|
171
306
|
// src/components/Icon/Icon.tsx
|
|
172
307
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
@@ -447,6 +582,7 @@ var REQUESTABLE = /* @__PURE__ */ new Set([
|
|
|
447
582
|
"outside-press",
|
|
448
583
|
"close-press"
|
|
449
584
|
]);
|
|
585
|
+
var DismissibleContext = createContext(true);
|
|
450
586
|
function Root({
|
|
451
587
|
dismissible = true,
|
|
452
588
|
onOpenChange,
|
|
@@ -473,27 +609,49 @@ function Root({
|
|
|
473
609
|
}
|
|
474
610
|
onOpenChange?.(next);
|
|
475
611
|
},
|
|
476
|
-
children
|
|
612
|
+
children: /* @__PURE__ */ jsx6(DismissibleContext.Provider, { value: dismissible, children })
|
|
477
613
|
}
|
|
478
614
|
);
|
|
479
615
|
}
|
|
480
616
|
var Trigger = BaseDialog.Trigger;
|
|
481
|
-
function Content({
|
|
617
|
+
function Content({
|
|
618
|
+
size = "md",
|
|
619
|
+
variant = "dialog",
|
|
620
|
+
dismissOnSwipe = true,
|
|
621
|
+
className,
|
|
622
|
+
children,
|
|
623
|
+
...rest
|
|
624
|
+
}) {
|
|
625
|
+
const dismissible = useContext(DismissibleContext);
|
|
626
|
+
const isSheet = variant === "sheet";
|
|
627
|
+
const swipe = isSheet && dismissOnSwipe && dismissible;
|
|
482
628
|
return /* @__PURE__ */ jsxs5(BaseDialog.Portal, { children: [
|
|
483
629
|
/* @__PURE__ */ jsx6(BaseDialog.Backdrop, { className: "sc-dialog__backdrop" }),
|
|
484
|
-
/* @__PURE__ */
|
|
630
|
+
/* @__PURE__ */ jsxs5(
|
|
485
631
|
BaseDialog.Popup,
|
|
486
632
|
{
|
|
487
633
|
className: cx("sc-dialog__popup", className),
|
|
488
634
|
"data-size": size,
|
|
489
635
|
"data-variant": variant,
|
|
636
|
+
"data-swipe-dismiss": swipe ? "" : void 0,
|
|
490
637
|
"aria-modal": "true",
|
|
491
638
|
...rest,
|
|
492
|
-
children
|
|
639
|
+
children: [
|
|
640
|
+
isSheet && /* @__PURE__ */ jsx6(SheetHandle, { swipe }),
|
|
641
|
+
children
|
|
642
|
+
]
|
|
493
643
|
}
|
|
494
644
|
)
|
|
495
645
|
] });
|
|
496
646
|
}
|
|
647
|
+
function SheetHandle({ swipe }) {
|
|
648
|
+
const attach = useCallback(
|
|
649
|
+
(node) => node ? attachSheetSwipe(node) : void 0,
|
|
650
|
+
[]
|
|
651
|
+
);
|
|
652
|
+
if (!swipe) return /* @__PURE__ */ jsx6("div", { className: "sc-dialog__handle", "aria-hidden": "true" });
|
|
653
|
+
return /* @__PURE__ */ jsx6(BaseDialog.Close, { ref: attach, className: "sc-dialog__handle", "aria-label": "Dismiss" });
|
|
654
|
+
}
|
|
497
655
|
function Title({ className, ...rest }) {
|
|
498
656
|
return /* @__PURE__ */ jsx6(BaseDialog.Title, { className: cx("sc-dialog__title", className), ...rest });
|
|
499
657
|
}
|
|
@@ -1402,14 +1560,14 @@ function Text({
|
|
|
1402
1560
|
// src/components/Table/Table.tsx
|
|
1403
1561
|
import { Checkbox as BaseCheckbox2 } from "@base-ui/react/checkbox";
|
|
1404
1562
|
import {
|
|
1405
|
-
createContext,
|
|
1406
|
-
useContext
|
|
1563
|
+
createContext as createContext2,
|
|
1564
|
+
useContext as useContext2
|
|
1407
1565
|
} from "react";
|
|
1408
1566
|
import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1409
|
-
var SelectionContext =
|
|
1410
|
-
var RowIdContext =
|
|
1567
|
+
var SelectionContext = createContext2(null);
|
|
1568
|
+
var RowIdContext = createContext2(void 0);
|
|
1411
1569
|
function useSelection(part) {
|
|
1412
|
-
const selection =
|
|
1570
|
+
const selection = useContext2(SelectionContext);
|
|
1413
1571
|
if (!selection) throw new Error(`${part} needs Table.Root selection={useRowSelection(ids)}`);
|
|
1414
1572
|
return selection;
|
|
1415
1573
|
}
|
|
@@ -1498,7 +1656,7 @@ function Row({
|
|
|
1498
1656
|
onMouseDown,
|
|
1499
1657
|
...rest
|
|
1500
1658
|
}) {
|
|
1501
|
-
const selection =
|
|
1659
|
+
const selection = useContext2(SelectionContext);
|
|
1502
1660
|
const selectable = selection !== null && selectionId !== void 0;
|
|
1503
1661
|
const isSelected = selected ?? (selectable && selection.isSelected(selectionId));
|
|
1504
1662
|
const row = /* @__PURE__ */ jsx21(
|
|
@@ -1571,7 +1729,7 @@ function SelectionHeader({
|
|
|
1571
1729
|
}
|
|
1572
1730
|
function SelectionCell({ label, className, onClick, onClickCapture, ...rest }) {
|
|
1573
1731
|
const selection = useSelection("Table.SelectionCell");
|
|
1574
|
-
const id =
|
|
1732
|
+
const id = useContext2(RowIdContext);
|
|
1575
1733
|
if (id === void 0) throw new Error("Table.SelectionCell goes inside a Table.Row with selectionId");
|
|
1576
1734
|
return /* @__PURE__ */ jsx21(
|
|
1577
1735
|
"td",
|
|
@@ -2255,9 +2413,9 @@ function fileTypeLabel(file) {
|
|
|
2255
2413
|
}
|
|
2256
2414
|
|
|
2257
2415
|
// src/utils/useMediaQuery.ts
|
|
2258
|
-
import { useCallback, useSyncExternalStore } from "react";
|
|
2416
|
+
import { useCallback as useCallback2, useSyncExternalStore } from "react";
|
|
2259
2417
|
function useMediaQuery(query) {
|
|
2260
|
-
const subscribe =
|
|
2418
|
+
const subscribe = useCallback2(
|
|
2261
2419
|
(onChange) => {
|
|
2262
2420
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
|
|
2263
2421
|
};
|
|
@@ -3190,9 +3348,9 @@ function BroomIcon({ sweeping = false, size = "lg", title, className, ...rest })
|
|
|
3190
3348
|
// src/components/FullScreenFlow/FullScreenFlow.tsx
|
|
3191
3349
|
import {
|
|
3192
3350
|
Children as Children2,
|
|
3193
|
-
createContext as
|
|
3194
|
-
useCallback as
|
|
3195
|
-
useContext as
|
|
3351
|
+
createContext as createContext3,
|
|
3352
|
+
useCallback as useCallback3,
|
|
3353
|
+
useContext as useContext3,
|
|
3196
3354
|
useEffect as useEffect6,
|
|
3197
3355
|
useId as useId5,
|
|
3198
3356
|
useMemo as useMemo3,
|
|
@@ -3200,9 +3358,9 @@ import {
|
|
|
3200
3358
|
useState as useState8
|
|
3201
3359
|
} from "react";
|
|
3202
3360
|
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3203
|
-
var FlowContext =
|
|
3361
|
+
var FlowContext = createContext3({ announce: () => {
|
|
3204
3362
|
} });
|
|
3205
|
-
var FrameContext =
|
|
3363
|
+
var FrameContext = createContext3(void 0);
|
|
3206
3364
|
var ENTER_IGNORES = 'textarea, button, a[href], summary, select, [contenteditable], [role="combobox"], [data-flow-keys="local"]';
|
|
3207
3365
|
var ARROW_IGNORES = 'input, textarea, select, button, a[href], summary, [contenteditable], [role="radiogroup"], [role="slider"], [role="listbox"], [role="tablist"], [role="menu"], [role="grid"], [role="combobox"], [data-flow-keys="local"]';
|
|
3208
3366
|
function Root7({
|
|
@@ -3217,7 +3375,7 @@ function Root7({
|
|
|
3217
3375
|
const popupRef = useRef6(null);
|
|
3218
3376
|
const [message, setMessage] = useState8("");
|
|
3219
3377
|
const timer = useRef6(void 0);
|
|
3220
|
-
const announce =
|
|
3378
|
+
const announce = useCallback3((next) => {
|
|
3221
3379
|
setMessage("");
|
|
3222
3380
|
clearTimeout(timer.current);
|
|
3223
3381
|
timer.current = setTimeout(() => setMessage(next), 30);
|
|
@@ -3403,7 +3561,7 @@ function Viewport2({ direction = "forward", className, children, ...rest }) {
|
|
|
3403
3561
|
} else if (state.current.element !== element) {
|
|
3404
3562
|
setState((s) => ({ ...s, current: { ...s.current, element } }));
|
|
3405
3563
|
}
|
|
3406
|
-
const finishExit =
|
|
3564
|
+
const finishExit = useCallback3((leftKey) => {
|
|
3407
3565
|
setState((s) => s.leaving?.key === leftKey ? { ...s, leaving: null } : s);
|
|
3408
3566
|
}, []);
|
|
3409
3567
|
const ref = useRef6(null);
|
|
@@ -3472,8 +3630,8 @@ function Step({
|
|
|
3472
3630
|
}) {
|
|
3473
3631
|
const id = useId5();
|
|
3474
3632
|
const titleRef = useRef6(null);
|
|
3475
|
-
const phase =
|
|
3476
|
-
const { announce } =
|
|
3633
|
+
const phase = useContext3(FrameContext);
|
|
3634
|
+
const { announce } = useContext3(FlowContext);
|
|
3477
3635
|
const message = announcement ?? (typeof title === "string" ? title : void 0);
|
|
3478
3636
|
const latest = useRef6({ message, announce });
|
|
3479
3637
|
useEffect6(() => {
|