@mdbase-dev/ui 0.1.0-beta.126 → 0.1.0-beta.148

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.
Files changed (74) hide show
  1. package/README.md +168 -0
  2. package/css/collections.css +978 -0
  3. package/css/controls.css +33 -1
  4. package/css/screens.css +44 -2
  5. package/dist/AppSwitcher.js +187 -33
  6. package/dist/Checkbox.d.ts +14 -0
  7. package/dist/Checkbox.js +32 -0
  8. package/dist/CollectionModeControl.d.ts +11 -0
  9. package/dist/CollectionModeControl.js +290 -0
  10. package/dist/CommandPalette.d.ts +4 -2
  11. package/dist/CommandPalette.js +275 -103
  12. package/dist/Dialog.js +72 -25
  13. package/dist/Feedback.js +763 -229
  14. package/dist/FeedbackMarkup.d.ts +1 -1
  15. package/dist/FeedbackMarkup.js +326 -125
  16. package/dist/FeedbackVerification.js +115 -68
  17. package/dist/SaveNotice.js +71 -28
  18. package/dist/Select.js +183 -135
  19. package/dist/SelectList.js +59 -14
  20. package/dist/ThemeSelect.js +55 -4
  21. package/dist/apps.d.ts +1 -0
  22. package/dist/apps.js +50 -29
  23. package/dist/brand.js +251 -68
  24. package/dist/codemirror.js +116 -87
  25. package/dist/collections/CollectionGate.d.ts +52 -0
  26. package/dist/collections/CollectionGate.js +456 -0
  27. package/dist/collections/CollectionSaveWarning.d.ts +10 -0
  28. package/dist/collections/CollectionSaveWarning.js +28 -0
  29. package/dist/collections/CollectionSwitcher.d.ts +51 -0
  30. package/dist/collections/CollectionSwitcher.js +598 -0
  31. package/dist/collections/SetupReview.d.ts +9 -0
  32. package/dist/collections/SetupReview.js +216 -0
  33. package/dist/collections/copy.d.ts +100 -0
  34. package/dist/collections/copy.js +937 -0
  35. package/dist/collections/dialogs.d.ts +27 -0
  36. package/dist/collections/dialogs.js +169 -0
  37. package/dist/collections/model.d.ts +146 -0
  38. package/dist/collections/model.js +337 -0
  39. package/dist/collections/mount.d.ts +19 -0
  40. package/dist/collections/mount.js +56 -0
  41. package/dist/collections/parts.d.ts +86 -0
  42. package/dist/collections/parts.js +439 -0
  43. package/dist/collections/testing.d.ts +19 -0
  44. package/dist/collections/testing.js +39 -0
  45. package/dist/collections/use-collection-ui.d.ts +140 -0
  46. package/dist/collections/use-collection-ui.js +236 -0
  47. package/dist/collections.d.ts +19 -0
  48. package/dist/collections.js +13 -0
  49. package/dist/feedback-data.d.ts +13 -12
  50. package/dist/feedback-data.js +365 -168
  51. package/dist/flock.d.ts +63 -0
  52. package/dist/flock.js +140 -0
  53. package/dist/i18n.d.ts +6 -0
  54. package/dist/i18n.js +71 -0
  55. package/dist/locales/de/messages.json +1 -0
  56. package/dist/locales/en/messages.json +1 -0
  57. package/dist/locales/es/messages.json +1 -0
  58. package/dist/locales/fr/messages.json +1 -0
  59. package/dist/locales/it/messages.json +1 -0
  60. package/dist/locales/ja/messages.json +1 -0
  61. package/dist/locales/ko/messages.json +1 -0
  62. package/dist/locales/pt/messages.json +1 -0
  63. package/dist/locales/ru/messages.json +1 -0
  64. package/dist/locales/zh/messages.json +1 -0
  65. package/dist/mark-activity.js +82 -75
  66. package/dist/permission-selection.js +27 -17
  67. package/dist/popover.js +138 -130
  68. package/dist/screens.d.ts +20 -3
  69. package/dist/screens.js +275 -6
  70. package/dist/select-model.js +34 -28
  71. package/dist/text-diff.d.ts +1 -0
  72. package/dist/text-diff.js +106 -74
  73. package/dist/theme.js +34 -40
  74. package/package.json +56 -7
@@ -0,0 +1,456 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId, useLayoutEffect, useRef, useState } from "react";
3
+ import { describeError } from "@mdbase-dev/sdk/describe";
4
+ import { OpeningScreen } from "../screens.js";
5
+ import { CollectionMdbaseContext } from "./CollectionSaveWarning.js";
6
+ import { SwitcherContent } from "./CollectionSwitcher.js";
7
+ import { RowDialogs, SignOutDialog } from "./dialogs.js";
8
+ import { problemControl, actionNotice } from "./model.js";
9
+ import { ActionButton, CollectionMark, GateFooter, ItemCopy, ItemMore, NoticeBlock, RowCopy, RowMore } from "./parts.js";
10
+ import { SetupReview } from "./SetupReview.js";
11
+ import { useAutoStart, useCollectionUi } from "./use-collection-ui.js";
12
+ /**
13
+ * Everything before a collection is open: sign-in, choosing, approval, setup
14
+ * review and problems. In phase `ready` it renders the app, with a banner
15
+ * above it for a live problem (migrating, update required, access removed).
16
+ */
17
+ export function CollectionGate({
18
+ children,
19
+ renderAppCollection,
20
+ extraActions,
21
+ layout = "page",
22
+ autoStart = true,
23
+ ...options
24
+ }) {
25
+ const ui = useCollectionUi(options);
26
+ useAutoStart(options.mdbase, autoStart);
27
+ if (ui.appOpen && renderAppCollection) {
28
+ return _jsxs(CollectionMdbaseContext, {
29
+ value: options.mdbase,
30
+ children: [renderAppCollection(ui.appOpen.item, {
31
+ section: ui.appOpen.section,
32
+ chooseAnother: ui.actions.chooseAnother
33
+ }), _jsx(SignOutDialog, {
34
+ ui: ui
35
+ })]
36
+ });
37
+ }
38
+ if (ui.view.kind === "ready") {
39
+ return _jsxs(_Fragment, {
40
+ children: [ui.view.notice && _jsx("div", {
41
+ className: "mdbase-collections",
42
+ children: _jsx(NoticeBlock, {
43
+ notice: ui.view.notice,
44
+ ui: ui,
45
+ variant: "banner"
46
+ })
47
+ }), _jsx(CollectionMdbaseContext, {
48
+ value: options.mdbase,
49
+ children: children(ui.view.phase.connection)
50
+ }), _jsx(SignOutDialog, {
51
+ ui: ui
52
+ })]
53
+ });
54
+ }
55
+ return _jsx(GateScreen, {
56
+ ui: ui,
57
+ layout: layout,
58
+ extraActions: extraActions
59
+ });
60
+ }
61
+ /**
62
+ * For write-first apps and settings dialogs: the gate's screens in a panel and,
63
+ * once a collection is open, the switcher's list inline.
64
+ */
65
+ export function CollectionPanel({
66
+ extraActions,
67
+ autoStart = true,
68
+ ...options
69
+ }) {
70
+ const ui = useCollectionUi(options);
71
+ useAutoStart(options.mdbase, autoStart);
72
+ const [dialog, setDialog] = useState(null);
73
+ if (ui.view.kind !== "ready") return _jsx(GateScreen, {
74
+ ui: ui,
75
+ layout: "panel",
76
+ extraActions: extraActions
77
+ });
78
+ return _jsxs("div", {
79
+ className: "mdbase-collections mdbase-collections-panel",
80
+ children: [ui.view.notice && _jsx(NoticeBlock, {
81
+ notice: ui.view.notice,
82
+ ui: ui,
83
+ variant: "inline"
84
+ }), _jsx(SwitcherContent, {
85
+ ui: ui,
86
+ onDone: () => {},
87
+ onDialog: setDialog
88
+ }), _jsx(RowDialogs, {
89
+ ui: ui,
90
+ dialog: dialog,
91
+ onClose: () => setDialog(null)
92
+ }), _jsx(SignOutDialog, {
93
+ ui: ui
94
+ })]
95
+ });
96
+ }
97
+ /** One problem with its fix, for banners and toasts inside the app. Renders nothing when there is none. */
98
+ export function CollectionProblem({
99
+ problem,
100
+ variant = "banner",
101
+ ...options
102
+ }) {
103
+ const ui = useCollectionUi(options);
104
+ const {
105
+ view
106
+ } = ui;
107
+ let notice = view.kind === "ready" || view.kind === "problem" || view.kind === "unavailable" ? view.notice : null;
108
+ if (problem !== undefined) {
109
+ const described = describeError(problem);
110
+ const quiet = actionNotice(described);
111
+ notice = described && quiet ? {
112
+ ...quiet,
113
+ control: problemControl(described, {
114
+ collectionId: view.current?.id ?? null,
115
+ signedIn: view.signedIn,
116
+ open: view.kind === "ready"
117
+ })
118
+ } : null;
119
+ }
120
+ if (!notice) return null;
121
+ return _jsx("div", {
122
+ className: "mdbase-collections",
123
+ children: _jsx(NoticeBlock, {
124
+ notice: notice,
125
+ ui: ui,
126
+ variant: variant
127
+ })
128
+ });
129
+ }
130
+ function GateScreen({
131
+ ui,
132
+ layout,
133
+ extraActions
134
+ }) {
135
+ const {
136
+ view,
137
+ options
138
+ } = ui;
139
+ const extra = typeof extraActions === "function" ? extraActions(view) : extraActions;
140
+ // An empty SDK list is not "no collections" when the app lists its own.
141
+ const appListed = view.kind === "choosing" && view.collections.length === 0 && (options.sections ?? []).some(section => section.items.length > 0);
142
+ const headingText = appListed ? ui.say("chooseHeading") : view.heading;
143
+ const lead = appListed ? null : view.lead;
144
+ const heading = useRef(null);
145
+ const root = useRef(null);
146
+ const [dialog, setDialog] = useState(null);
147
+ const previousKind = useRef(null);
148
+ // Move focus to the new heading when the phase changes, unless the person is busy elsewhere on the page.
149
+ useLayoutEffect(() => {
150
+ const before = previousKind.current;
151
+ previousKind.current = view.kind;
152
+ if (before === null || before === view.kind) return;
153
+ const active = document.activeElement;
154
+ if (!active || active === document.body || root.current?.contains(active)) heading.current?.focus({
155
+ preventScroll: true
156
+ });
157
+ }, [view.kind]);
158
+ const Heading = layout === "page" ? "h1" : "h2";
159
+ const opening = view.kind === "starting" || view.kind === "opening";
160
+ const announce = view.kind === "authorizing" ? view.heading : "";
161
+ return _jsxs("div", {
162
+ ref: root,
163
+ className: "mdbase-collections mdbase-collections-gate",
164
+ "data-layout": layout,
165
+ "data-phase": view.kind,
166
+ "aria-busy": ui.busy !== null || undefined,
167
+ children: [_jsxs("div", {
168
+ className: "mdbase-collections-gate-body",
169
+ children: [!opening && _jsxs("p", {
170
+ className: "mdbase-collections-brand",
171
+ children: [options.app.mark && _jsx("span", {
172
+ className: "mdbase-collections-brand-mark",
173
+ "aria-hidden": "true",
174
+ children: options.app.mark
175
+ }), _jsx("span", {
176
+ children: options.app.name
177
+ })]
178
+ }), _jsx(Heading, {
179
+ ref: heading,
180
+ tabIndex: -1,
181
+ className: opening ? "mdbase-collections-visually-hidden" : "mdbase-collections-heading",
182
+ children: headingText
183
+ }), opening ? _jsx(OpeningScreen, {
184
+ app: options.app,
185
+ layout: layout,
186
+ title: headingText,
187
+ detail: lead ?? undefined
188
+ }) : lead && _jsx("p", {
189
+ className: "mdbase-collections-lead",
190
+ children: lead
191
+ }), _jsx(GateBody, {
192
+ ui: ui,
193
+ extraActions: view.kind === "signed_out" ? extra : undefined,
194
+ onDialog: setDialog
195
+ }), ui.error && _jsx(NoticeBlock, {
196
+ notice: ui.error,
197
+ ui: ui,
198
+ variant: "inline"
199
+ }), _jsx(GateFooter, {
200
+ ui: ui,
201
+ chooseAnother: showsChooseAnother(view),
202
+ extra: view.kind === "signed_out" ? undefined : extra
203
+ })]
204
+ }), _jsx("p", {
205
+ className: "mdbase-collections-visually-hidden",
206
+ role: "status",
207
+ children: announce
208
+ }), _jsx(RowDialogs, {
209
+ ui: ui,
210
+ dialog: dialog,
211
+ onClose: () => setDialog(null)
212
+ }), _jsx(SignOutDialog, {
213
+ ui: ui
214
+ })]
215
+ });
216
+ }
217
+ function showsChooseAnother(view) {
218
+ if (!view.canChooseAnother) return false;
219
+ switch (view.kind) {
220
+ case "opening":
221
+ return view.slow;
222
+ case "needs_approval":
223
+ case "needs_setup":
224
+ return true;
225
+ case "problem":
226
+ return view.notice.control?.kind !== "choose_another";
227
+ default:
228
+ return false;
229
+ }
230
+ }
231
+ function GateBody({
232
+ ui,
233
+ extraActions,
234
+ onDialog
235
+ }) {
236
+ const {
237
+ view,
238
+ copy,
239
+ busy,
240
+ actions
241
+ } = ui;
242
+ const inert = busy !== null;
243
+ switch (view.kind) {
244
+ case "starting":
245
+ case "opening":
246
+ return null;
247
+ case "signed_out":
248
+ return _jsxs(_Fragment, {
249
+ children: [view.notice && _jsx(NoticeBlock, {
250
+ notice: view.notice,
251
+ ui: ui,
252
+ variant: "inline"
253
+ }), _jsxs("div", {
254
+ className: "mdbase-collections-actions",
255
+ children: [_jsx(ActionButton, {
256
+ tone: "primary",
257
+ inert: inert,
258
+ progress: busy === "sign_out" ? copy.signingOut : busy === "sign_in" ? copy.addingCollection : null,
259
+ onClick: actions.signIn,
260
+ children: copy.signIn
261
+ }), extraActions]
262
+ }), _jsx(AppSections, {
263
+ ui: ui,
264
+ onDialog: onDialog
265
+ })]
266
+ });
267
+ case "authorizing":
268
+ return _jsxs("div", {
269
+ className: "mdbase-collections-actions",
270
+ children: [_jsx(ActionButton, {
271
+ tone: "secondary",
272
+ onClick: actions.reopen,
273
+ children: copy.openWindowAgain
274
+ }), busy === "sign_in" || busy === "add" || busy === "approve" ? _jsx(ActionButton, {
275
+ tone: "quiet",
276
+ onClick: actions.cancel,
277
+ children: copy.cancel
278
+ }) : null]
279
+ });
280
+ case "choosing":
281
+ return _jsxs(_Fragment, {
282
+ children: [view.notice && _jsx(NoticeBlock, {
283
+ notice: view.notice,
284
+ ui: ui,
285
+ variant: "inline"
286
+ }), view.collections.length > 0 && _jsx(CollectionList, {
287
+ ui: ui,
288
+ onDialog: onDialog
289
+ }), _jsx(AppSections, {
290
+ ui: ui,
291
+ onDialog: onDialog
292
+ }), _jsx("div", {
293
+ className: "mdbase-collections-actions",
294
+ children: _jsx(ActionButton, {
295
+ tone: view.collections.length > 0 ? "secondary" : "primary",
296
+ inert: inert,
297
+ progress: busy === "add" ? copy.addingCollection : null,
298
+ onClick: actions.addCollection,
299
+ children: copy.addCollection
300
+ })
301
+ })]
302
+ });
303
+ case "needs_approval":
304
+ return _jsx("div", {
305
+ className: "mdbase-collections-actions",
306
+ children: _jsx(ActionButton, {
307
+ tone: "primary",
308
+ inert: inert,
309
+ progress: busy === "approve" ? copy.addingCollection : null,
310
+ onClick: () => actions.approve(view.collectionId),
311
+ children: copy.reviewAccess
312
+ })
313
+ });
314
+ case "needs_setup":
315
+ return _jsx(SetupReview, {
316
+ ui: ui
317
+ }, view.collectionId);
318
+ case "problem":
319
+ return _jsx(NoticeBlock, {
320
+ notice: view.notice,
321
+ ui: ui
322
+ });
323
+ case "unavailable":
324
+ case "closed":
325
+ return _jsxs(_Fragment, {
326
+ children: [view.kind === "unavailable" && _jsx(NoticeBlock, {
327
+ notice: view.notice,
328
+ ui: ui
329
+ }), _jsx("div", {
330
+ className: "mdbase-collections-actions",
331
+ children: _jsx(ActionButton, {
332
+ tone: "primary",
333
+ onClick: () => globalThis.location?.reload(),
334
+ children: copy.reload
335
+ })
336
+ })]
337
+ });
338
+ case "ready":
339
+ return null;
340
+ }
341
+ }
342
+ /** The approved collections as a labelled list: one row per collection, never mixed with actions. */
343
+ function CollectionList({
344
+ ui,
345
+ onDialog
346
+ }) {
347
+ const id = useId();
348
+ const {
349
+ view,
350
+ copy,
351
+ busy,
352
+ actions
353
+ } = ui;
354
+ return _jsxs("section", {
355
+ className: "mdbase-collections-section",
356
+ "aria-labelledby": id,
357
+ children: [_jsx("h2", {
358
+ id: id,
359
+ className: "mdbase-collections-section-title",
360
+ children: copy.yourCollections
361
+ }), _jsx("ul", {
362
+ className: "mdbase-collections-list",
363
+ children: view.collections.map(row => {
364
+ const detailId = row.statusDetail ? `${id}-${row.id}` : undefined;
365
+ return _jsxs("li", {
366
+ className: "mdbase-collections-item",
367
+ children: [_jsxs("button", {
368
+ type: "button",
369
+ className: "mdbase-collections-row",
370
+ "aria-disabled": !row.available || busy !== null || undefined,
371
+ "aria-describedby": detailId,
372
+ onClick: () => {
373
+ if (row.available && busy === null) actions.select(row.id);
374
+ },
375
+ children: [_jsx(CollectionMark, {
376
+ row: row
377
+ }), _jsx(RowCopy, {
378
+ row: row,
379
+ ui: ui,
380
+ ...(detailId ? {
381
+ detailId
382
+ } : {})
383
+ })]
384
+ }), _jsx(RowMore, {
385
+ row: row,
386
+ ui: ui,
387
+ onDialog: onDialog
388
+ })]
389
+ }, row.id);
390
+ })
391
+ })]
392
+ });
393
+ }
394
+ /** The app's own sections ("Private collections"), after the SDK's list. */
395
+ function AppSections({
396
+ ui,
397
+ onDialog
398
+ }) {
399
+ const sections = (ui.options.sections ?? []).filter(section => section.items.length > 0 || section.empty);
400
+ if (sections.length === 0) return null;
401
+ return _jsx(_Fragment, {
402
+ children: sections.map(section => _jsx(AppSection, {
403
+ ui: ui,
404
+ section: section,
405
+ onDialog: onDialog
406
+ }, section.id))
407
+ });
408
+ }
409
+ function AppSection({
410
+ ui,
411
+ section,
412
+ onDialog
413
+ }) {
414
+ const id = useId();
415
+ const {
416
+ busy,
417
+ actions
418
+ } = ui;
419
+ return _jsxs("section", {
420
+ className: "mdbase-collections-section",
421
+ "aria-labelledby": id,
422
+ "data-section": section.id,
423
+ children: [_jsx("h2", {
424
+ id: id,
425
+ className: "mdbase-collections-section-title",
426
+ children: section.title
427
+ }), section.items.length === 0 ? _jsx("p", {
428
+ className: "mdbase-collections-empty",
429
+ children: section.empty
430
+ }) : _jsx("ul", {
431
+ className: "mdbase-collections-list",
432
+ children: section.items.map(item => _jsxs("li", {
433
+ className: "mdbase-collections-item",
434
+ children: [_jsxs("button", {
435
+ type: "button",
436
+ className: "mdbase-collections-row",
437
+ "aria-disabled": item.disabled || busy !== null || undefined,
438
+ "aria-current": item.current || undefined,
439
+ onClick: () => {
440
+ if (busy === null) actions.selectItem(section, item);
441
+ },
442
+ children: [_jsx(CollectionMark, {
443
+ row: item
444
+ }), _jsx(ItemCopy, {
445
+ item: item,
446
+ ui: ui
447
+ })]
448
+ }), _jsx(ItemMore, {
449
+ item: item,
450
+ ui: ui,
451
+ onDialog: onDialog
452
+ })]
453
+ }, item.id))
454
+ })]
455
+ });
456
+ }
@@ -0,0 +1,10 @@
1
+ import type { Mdbase } from "@mdbase-dev/sdk";
2
+ import { type JSX } from "react";
3
+ /** The gate supplies the entry to status controls inside the open workspace. */
4
+ export declare const CollectionMdbaseContext: import("react").Context<Mdbase | null>;
5
+ /** Quiet storage-risk copy beside an app's save status. Never shown without
6
+ * the SDK's warning: persistence/installation policy stays SDK-owned. */
7
+ export declare function useCollectionSaveWarning(supplied?: Mdbase): import("@mdbase-dev/sdk").MdbaseErrorDescription | null;
8
+ export declare function CollectionSaveWarning({ mdbase }: {
9
+ readonly mdbase?: Mdbase;
10
+ }): JSX.Element | null;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { describeError } from "@mdbase-dev/sdk/describe";
3
+ import { createContext, useContext, useSyncExternalStore } from "react";
4
+ /** The gate supplies the entry to status controls inside the open workspace. */
5
+ export const CollectionMdbaseContext = createContext(null);
6
+ const emptySubscribe = () => () => {};
7
+ const emptySnapshot = () => null;
8
+ /** Quiet storage-risk copy beside an app's save status. Never shown without
9
+ * the SDK's warning: persistence/installation policy stays SDK-owned. */
10
+ export function useCollectionSaveWarning(supplied) {
11
+ const inherited = useContext(CollectionMdbaseContext);
12
+ const mdbase = supplied ?? inherited;
13
+ const snapshot = useSyncExternalStore(mdbase?.subscribe ?? emptySubscribe, mdbase?.getSnapshot ?? emptySnapshot, emptySnapshot);
14
+ const warning = snapshot?.saveWarning;
15
+ return warning ? describeError(warning) : null;
16
+ }
17
+ export function CollectionSaveWarning({
18
+ mdbase
19
+ }) {
20
+ const copy = useCollectionSaveWarning(mdbase);
21
+ if (!copy) return null;
22
+ return _jsx("span", {
23
+ className: "mdbase-collections-save-warning",
24
+ role: "status",
25
+ title: copy.message,
26
+ children: copy.message
27
+ });
28
+ }
@@ -0,0 +1,51 @@
1
+ import { type JSX, type ReactNode, type RefObject } from "react";
2
+ import { type RowDialog } from "./parts.js";
3
+ import { type CollectionUi, type CollectionUiOptions } from "./use-collection-ui.js";
4
+ export type CollectionSwitcherProps = CollectionUiOptions & {
5
+ readonly className?: string;
6
+ /**
7
+ * Leading content in the trigger: the app's icon, or "mark" for the open
8
+ * collection's initials. In the "icon" variant it replaces the folder glyph.
9
+ */
10
+ readonly icon?: ReactNode | "mark";
11
+ /**
12
+ * Whether the switcher is open. Leave it out and the switcher keeps its own
13
+ * state; pass it and the switcher only asks through `onOpenChange`.
14
+ */
15
+ readonly open?: boolean;
16
+ /** Called whenever the switcher opens or closes itself, controlled or not. */
17
+ readonly onOpenChange?: (open: boolean) => void;
18
+ } & ({
19
+ /** "full": name and role; "compact": name only; "icon": a glyph for toolbars; "none": no trigger. */
20
+ readonly variant?: "full" | "compact" | "icon";
21
+ readonly anchorRef?: never;
22
+ } | {
23
+ /**
24
+ * No trigger of its own: the app opens the switcher with `open` and
25
+ * `onOpenChange`, for instance from an item in its own menu.
26
+ */
27
+ readonly variant: "none";
28
+ /**
29
+ * The app's element the switcher belongs to, such as the button of the menu
30
+ * that offered it. The wide-screen popover is placed against it and focus
31
+ * returns to it on close. It must stay mounted while the switcher is open.
32
+ */
33
+ readonly anchorRef: RefObject<HTMLElement | null>;
34
+ });
35
+ /**
36
+ * The in-app collection switcher: a trigger that opens the approved collections
37
+ * (one click switches, no Connect window), "Add a collection…", "Manage access"
38
+ * and the account. A popover on wide screens, a bottom sheet on narrow ones.
39
+ *
40
+ * Pass `open` and `onOpenChange` to control it. With `variant="none"` it has no
41
+ * trigger, so an app can open it from its own control and pass that control's
42
+ * element (or the menu button that held it) as `anchorRef`.
43
+ */
44
+ export declare function CollectionSwitcher({ variant, className, icon, open: openProp, onOpenChange, anchorRef, ...options }: CollectionSwitcherProps): JSX.Element;
45
+ /** The switcher's list and actions, also shown inline by `CollectionPanel`. */
46
+ export declare function SwitcherContent({ ui, onDone, onDialog }: {
47
+ readonly ui: CollectionUi;
48
+ readonly onDone: () => void;
49
+ /** Open a row's dialog (remove from list, delete), for an SDK collection or an app section item. */
50
+ readonly onDialog: (dialog: NonNullable<RowDialog>) => void;
51
+ }): JSX.Element;