@burtson-labs/ui 0.8.1 → 0.9.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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/dist/components/composer.d.ts +12 -2
  3. package/dist/components/composer.js +2 -2
  4. package/dist/components/composer.js.map +1 -1
  5. package/dist/components/context-menu.d.ts +30 -0
  6. package/dist/components/context-menu.js +131 -0
  7. package/dist/components/context-menu.js.map +1 -0
  8. package/dist/components/dropdown-menu.js +5 -6
  9. package/dist/components/dropdown-menu.js.map +1 -1
  10. package/dist/components/editor-tabs.d.ts +47 -0
  11. package/dist/components/editor-tabs.js +221 -0
  12. package/dist/components/editor-tabs.js.map +1 -0
  13. package/dist/components/menubar.d.ts +30 -0
  14. package/dist/components/menubar.js +140 -0
  15. package/dist/components/menubar.js.map +1 -0
  16. package/dist/components/onboarding-checklist.d.ts +26 -0
  17. package/dist/components/onboarding-checklist.js +106 -0
  18. package/dist/components/onboarding-checklist.js.map +1 -0
  19. package/dist/components/popover.d.ts +2 -0
  20. package/dist/components/popover.js +3 -1
  21. package/dist/components/popover.js.map +1 -1
  22. package/dist/components/resizable.d.ts +33 -0
  23. package/dist/components/resizable.js +54 -0
  24. package/dist/components/resizable.js.map +1 -0
  25. package/dist/components/tour.d.ts +96 -0
  26. package/dist/components/tour.js +367 -0
  27. package/dist/components/tour.js.map +1 -0
  28. package/dist/components/tree-view.d.ts +76 -0
  29. package/dist/components/tree-view.js +286 -0
  30. package/dist/components/tree-view.js.map +1 -0
  31. package/dist/index.d.ts +7 -0
  32. package/dist/index.js +10 -3
  33. package/dist/styles.css +1 -1
  34. package/package.json +2 -1
@@ -0,0 +1,221 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import * as React from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import X from "@burtson-labs/icons/react/x";
5
+ //#region src/components/editor-tabs.tsx
6
+ /**
7
+ * The strip of open files above an editor. One tab stop; Left/Right/Home/End
8
+ * move focus, Enter or Space opens, Delete asks to close, Ctrl+Shift+PageUp
9
+ * and PageDown move the tab. Middle-click closes. The strip scrolls when the
10
+ * tabs overflow and keeps the active tab in view.
11
+ */
12
+ function EditorTabs({ tabs, activeId, onActiveChange, onClose, onMove, actions, onTabContextMenu, className, "aria-label": ariaLabel, ...props }) {
13
+ const baseId = React.useId();
14
+ const tabRefs = React.useRef(/* @__PURE__ */ new Map());
15
+ const listRef = React.useRef(null);
16
+ const [focusedId, setFocusedId] = React.useState(null);
17
+ const [dragId, setDragId] = React.useState(null);
18
+ const [dropGap, setDropGap] = React.useState(null);
19
+ const dragged = React.useRef(false);
20
+ const dragCleanup = React.useRef(null);
21
+ React.useEffect(() => () => dragCleanup.current?.(), []);
22
+ const lastIds = React.useRef([]);
23
+ const hadFocus = React.useRef(false);
24
+ const ids = tabs.map((t) => t.id);
25
+ const idsKey = ids.join("\0");
26
+ const tabStop = (focusedId && ids.includes(focusedId) ? focusedId : null) ?? (activeId && ids.includes(activeId) ? activeId : null) ?? ids[0] ?? null;
27
+ React.useEffect(() => {
28
+ if (activeId) tabRefs.current.get(activeId)?.scrollIntoView?.({
29
+ block: "nearest",
30
+ inline: "nearest"
31
+ });
32
+ }, [activeId]);
33
+ React.useLayoutEffect(() => {
34
+ if (focusedId && !ids.includes(focusedId)) {
35
+ const next = (activeId && ids.includes(activeId) ? activeId : null) ?? ids[Math.min(Math.max(0, lastIds.current.indexOf(focusedId)), ids.length - 1)] ?? null;
36
+ setFocusedId(next);
37
+ const active = document.activeElement;
38
+ if (next && hadFocus.current && (!active || active === document.body)) tabRefs.current.get(next)?.focus();
39
+ }
40
+ lastIds.current = ids;
41
+ }, [
42
+ focusedId,
43
+ idsKey,
44
+ activeId
45
+ ]);
46
+ /** Which gap the pointer is over, measured from the rendered tabs. */
47
+ const gapAt = (clientX) => {
48
+ const els = Array.from(listRef.current?.querySelectorAll("[data-slot=\"editor-tab\"]") ?? []);
49
+ const rtl = listRef.current ? getComputedStyle(listRef.current).direction === "rtl" : false;
50
+ for (let i = 0; i < els.length; i++) {
51
+ const rect = els[i].getBoundingClientRect();
52
+ const mid = rect.left + rect.width / 2;
53
+ if (rtl ? clientX > mid : clientX < mid) return i;
54
+ }
55
+ return els.length;
56
+ };
57
+ const startDrag = (event, id) => {
58
+ if (!onMove || event.button !== 0) return;
59
+ dragCleanup.current?.();
60
+ dragged.current = false;
61
+ const downX = event.clientX;
62
+ const move = (e) => {
63
+ if (!dragged.current && Math.abs(e.clientX - downX) < 4) return;
64
+ if (!dragged.current) {
65
+ dragged.current = true;
66
+ setDragId(id);
67
+ }
68
+ setDropGap(gapAt(e.clientX));
69
+ };
70
+ const stop = (e) => {
71
+ window.removeEventListener("pointermove", move);
72
+ window.removeEventListener("pointerup", up);
73
+ window.removeEventListener("pointercancel", cancel);
74
+ dragCleanup.current = null;
75
+ if (e && dragged.current) {
76
+ const from = lastIds.current.indexOf(id);
77
+ const gap = gapAt(e.clientX);
78
+ const to = gap > from ? gap - 1 : gap;
79
+ if (from !== -1 && to !== from) onMove(id, to);
80
+ }
81
+ setDragId(null);
82
+ setDropGap(null);
83
+ };
84
+ const up = (e) => stop(e);
85
+ const cancel = () => stop(null);
86
+ window.addEventListener("pointermove", move);
87
+ window.addEventListener("pointerup", up);
88
+ window.addEventListener("pointercancel", cancel);
89
+ dragCleanup.current = () => stop(null);
90
+ };
91
+ const focusTab = (id) => {
92
+ if (!id) return;
93
+ setFocusedId(id);
94
+ tabRefs.current.get(id)?.focus();
95
+ };
96
+ const onKeyDown = (event, tab) => {
97
+ const i = ids.indexOf(tab.id);
98
+ const [prev, next] = (listRef.current ? getComputedStyle(listRef.current).direction === "rtl" : false) ? ["ArrowRight", "ArrowLeft"] : ["ArrowLeft", "ArrowRight"];
99
+ let handled = true;
100
+ if (event.ctrlKey && event.shiftKey && (event.key === "PageUp" || event.key === "PageDown")) {
101
+ const to = event.key === "PageUp" ? i - 1 : i + 1;
102
+ if (onMove && to >= 0 && to < ids.length) onMove(tab.id, to);
103
+ } else if (event.key === prev) focusTab(ids[(i - 1 + ids.length) % ids.length]);
104
+ else if (event.key === next) focusTab(ids[(i + 1) % ids.length]);
105
+ else if (event.key === "Home") focusTab(ids[0]);
106
+ else if (event.key === "End") focusTab(ids[ids.length - 1]);
107
+ else if (event.key === "Delete" && tab.closeable !== false && onClose) onClose(tab.id);
108
+ else handled = false;
109
+ if (handled) event.preventDefault();
110
+ };
111
+ return /* @__PURE__ */ jsxs("div", {
112
+ "data-slot": "editor-tabs",
113
+ className: cn("flex h-9 min-w-0 items-stretch border-b bg-surface-muted", className),
114
+ ...props,
115
+ children: [/* @__PURE__ */ jsx("div", {
116
+ ref: listRef,
117
+ role: "tablist",
118
+ "aria-label": ariaLabel,
119
+ "aria-orientation": "horizontal",
120
+ className: "flex min-w-0 flex-1 items-stretch overflow-x-auto [scrollbar-width:thin]",
121
+ onFocus: () => hadFocus.current = true,
122
+ onBlur: (event) => {
123
+ if (!listRef.current?.contains(event.relatedTarget)) hadFocus.current = false;
124
+ },
125
+ children: tabs.map((tab, i) => {
126
+ const active = tab.id === activeId;
127
+ const closeable = tab.closeable !== false && Boolean(onClose);
128
+ const tabId = `${baseId}-tab-${i}`;
129
+ return /* @__PURE__ */ jsxs("div", {
130
+ "data-slot": "editor-tab",
131
+ "data-state": active ? "active" : "inactive",
132
+ "data-dirty": tab.dirty || void 0,
133
+ "data-dragging": dragId === tab.id || void 0,
134
+ "data-drop": dragId === null ? void 0 : dropGap === i ? "before" : dropGap === tabs.length && i === tabs.length - 1 ? "after" : void 0,
135
+ onPointerDown: (event) => startDrag(event, tab.id),
136
+ onClickCapture: (event) => {
137
+ if (dragged.current) {
138
+ dragged.current = false;
139
+ event.stopPropagation();
140
+ event.preventDefault();
141
+ }
142
+ },
143
+ onAuxClick: (event) => {
144
+ if (event.button === 1 && closeable) {
145
+ event.preventDefault();
146
+ onClose?.(tab.id);
147
+ }
148
+ },
149
+ onContextMenu: onTabContextMenu ? (event) => onTabContextMenu(tab.id, event) : void 0,
150
+ className: cn("group/tab relative flex max-w-60 shrink-0 items-center border-e text-[13px] text-muted-foreground", "hover:bg-muted/60 data-[state=active]:bg-background data-[state=active]:text-foreground", "data-[state=active]:before:absolute data-[state=active]:before:inset-x-0 data-[state=active]:before:top-0 data-[state=active]:before:h-0.5 data-[state=active]:before:bg-brand data-[state=active]:before:content-[\"\"]", "data-[dragging]:opacity-50 data-[dragging]:select-none", "data-[drop=before]:shadow-[inset_2px_0_0_var(--brand)] data-[drop=after]:shadow-[inset_-2px_0_0_var(--brand)]"),
151
+ children: [
152
+ /* @__PURE__ */ jsxs("button", {
153
+ ref: (el) => {
154
+ if (el) tabRefs.current.set(tab.id, el);
155
+ else tabRefs.current.delete(tab.id);
156
+ },
157
+ type: "button",
158
+ role: "tab",
159
+ id: tabId,
160
+ "aria-selected": active,
161
+ "aria-controls": tab.controls,
162
+ "aria-describedby": tab.description ? `${tabId}-desc` : void 0,
163
+ title: tab.description,
164
+ tabIndex: tab.id === tabStop ? 0 : -1,
165
+ onFocus: () => setFocusedId(tab.id),
166
+ onClick: () => onActiveChange(tab.id),
167
+ onKeyDown: (event) => onKeyDown(event, tab),
168
+ className: cn("flex h-full min-w-0 items-center gap-1.5 ps-3 outline-none focus-visible:shadow-[inset_0_0_0_1px_var(--ring)]", closeable ? "pe-1" : "pe-3"),
169
+ children: [
170
+ tab.icon && /* @__PURE__ */ jsx("span", {
171
+ "aria-hidden": true,
172
+ className: "flex shrink-0 [&_svg]:size-4",
173
+ children: tab.icon
174
+ }),
175
+ /* @__PURE__ */ jsx("span", {
176
+ className: cn("truncate", tab.preview && "italic"),
177
+ children: tab.label
178
+ }),
179
+ tab.dirty && /* @__PURE__ */ jsx("span", {
180
+ className: "sr-only",
181
+ children: ", unsaved changes"
182
+ })
183
+ ]
184
+ }),
185
+ tab.description && /* @__PURE__ */ jsx("span", {
186
+ id: `${tabId}-desc`,
187
+ hidden: true,
188
+ children: tab.description
189
+ }),
190
+ closeable ? /* @__PURE__ */ jsxs("button", {
191
+ type: "button",
192
+ tabIndex: -1,
193
+ "aria-label": `Close ${tab.label}`,
194
+ title: "Close (Delete)",
195
+ onClick: () => onClose?.(tab.id),
196
+ className: "me-1 flex size-5 shrink-0 items-center justify-center rounded-xs text-muted-foreground outline-none hover:bg-muted hover:text-foreground",
197
+ children: [tab.dirty && /* @__PURE__ */ jsx("span", {
198
+ "aria-hidden": true,
199
+ className: "size-2 rounded-full bg-foreground/70 group-hover/tab:hidden"
200
+ }), /* @__PURE__ */ jsx(X, {
201
+ "aria-hidden": true,
202
+ className: cn("size-3.5", tab.dirty ? "hidden group-hover/tab:block" : "opacity-0 group-hover/tab:opacity-100 group-data-[state=active]/tab:opacity-100 group-focus-within/tab:opacity-100")
203
+ })]
204
+ }) : tab.dirty ? /* @__PURE__ */ jsx("span", {
205
+ "aria-hidden": true,
206
+ className: "me-3 size-2 shrink-0 rounded-full bg-foreground/70"
207
+ }) : null
208
+ ]
209
+ }, tab.id);
210
+ })
211
+ }), actions && /* @__PURE__ */ jsx("div", {
212
+ "data-slot": "editor-tabs-actions",
213
+ className: "flex shrink-0 items-center gap-0.5 px-1",
214
+ children: actions
215
+ })]
216
+ });
217
+ }
218
+ //#endregion
219
+ export { EditorTabs };
220
+
221
+ //# sourceMappingURL=editor-tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor-tabs.js","names":[],"sources":["../../src/components/editor-tabs.tsx"],"sourcesContent":["import X from '@burtson-labs/icons/react/x';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nexport interface EditorTab {\n /** Stable identity, usually the file path. */\n id: string;\n label: string;\n /** Full path or context; the tooltip and the accessible description. */\n description?: string;\n icon?: React.ReactNode;\n /** Unsaved changes: a dot replaces the close button until hover. */\n dirty?: boolean;\n /** Default true. */\n closeable?: boolean;\n /** A preview tab that the next opened file replaces; shown in italics. */\n preview?: boolean;\n /** The id of the panel this tab shows, for aria-controls. */\n controls?: string;\n}\n\nexport interface EditorTabsProps extends Omit<\n React.ComponentProps<'div'>,\n 'children' | 'aria-label'\n> {\n tabs: EditorTab[];\n activeId: string | null;\n onActiveChange: (id: string) => void;\n /**\n * A request to close. The component never removes a tab itself: for a dirty\n * tab, ask the person (save, discard, cancel) and then update `tabs`.\n */\n onClose?: (id: string) => void;\n /**\n * Reorder by drag or Ctrl+Shift+PageUp/PageDown. `toIndex` is the new position.\n * Dragging uses pointer events, not HTML5 drag and drop, so it also works in\n * desktop webviews (Tauri, Electron) that intercept native drags.\n */\n onMove?: (id: string, toIndex: number) => void;\n /** Required: e.g. \"Open editors\". */\n 'aria-label': string;\n /** Right-hand slot for split, more and similar buttons. */\n actions?: React.ReactNode;\n /** Right-click on a tab. */\n onTabContextMenu?: (id: string, event: React.MouseEvent) => void;\n}\n\n/**\n * The strip of open files above an editor. One tab stop; Left/Right/Home/End\n * move focus, Enter or Space opens, Delete asks to close, Ctrl+Shift+PageUp\n * and PageDown move the tab. Middle-click closes. The strip scrolls when the\n * tabs overflow and keeps the active tab in view.\n */\nfunction EditorTabs({\n tabs,\n activeId,\n onActiveChange,\n onClose,\n onMove,\n actions,\n onTabContextMenu,\n className,\n 'aria-label': ariaLabel,\n ...props\n}: EditorTabsProps) {\n const baseId = React.useId();\n const tabRefs = React.useRef(new Map<string, HTMLButtonElement>());\n const listRef = React.useRef<HTMLDivElement>(null);\n const [focusedId, setFocusedId] = React.useState<string | null>(null);\n const [dragId, setDragId] = React.useState<string | null>(null);\n // The gap a dragged tab would land in: 0 is before the first tab,\n // tabs.length after the last.\n const [dropGap, setDropGap] = React.useState<number | null>(null);\n // Set once a press becomes a drag, so the click that ends it does nothing.\n const dragged = React.useRef(false);\n const dragCleanup = React.useRef<(() => void) | null>(null);\n React.useEffect(() => () => dragCleanup.current?.(), []);\n const lastIds = React.useRef<string[]>([]);\n const hadFocus = React.useRef(false);\n\n const ids = tabs.map((t) => t.id);\n const idsKey = ids.join('\\u0000');\n const tabStop =\n (focusedId && ids.includes(focusedId) ? focusedId : null) ??\n (activeId && ids.includes(activeId) ? activeId : null) ??\n ids[0] ??\n null;\n\n React.useEffect(() => {\n if (activeId)\n tabRefs.current.get(activeId)?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });\n }, [activeId]);\n\n // After a close, keep keyboard focus in the strip: the active tab if the\n // app picked one, else the neighbour at the closed tab's position.\n React.useLayoutEffect(() => {\n if (focusedId && !ids.includes(focusedId)) {\n const next =\n (activeId && ids.includes(activeId) ? activeId : null) ??\n ids[Math.min(Math.max(0, lastIds.current.indexOf(focusedId)), ids.length - 1)] ??\n null;\n setFocusedId(next);\n const active = document.activeElement;\n if (next && hadFocus.current && (!active || active === document.body))\n tabRefs.current.get(next)?.focus();\n }\n lastIds.current = ids;\n // ids is derived from tabs each render; idsKey is its stable form.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [focusedId, idsKey, activeId]);\n\n /** Which gap the pointer is over, measured from the rendered tabs. */\n const gapAt = (clientX: number) => {\n const els = Array.from(\n listRef.current?.querySelectorAll<HTMLElement>('[data-slot=\"editor-tab\"]') ?? [],\n );\n const rtl = listRef.current ? getComputedStyle(listRef.current).direction === 'rtl' : false;\n for (let i = 0; i < els.length; i++) {\n const rect = els[i]!.getBoundingClientRect();\n const mid = rect.left + rect.width / 2;\n if (rtl ? clientX > mid : clientX < mid) return i;\n }\n return els.length;\n };\n\n const startDrag = (event: React.PointerEvent, id: string) => {\n if (!onMove || event.button !== 0) return;\n dragCleanup.current?.();\n dragged.current = false;\n const downX = event.clientX;\n const move = (e: PointerEvent) => {\n // A few pixels of slop so an ordinary click is not read as a drag.\n if (!dragged.current && Math.abs(e.clientX - downX) < 4) return;\n if (!dragged.current) {\n dragged.current = true;\n setDragId(id);\n }\n setDropGap(gapAt(e.clientX));\n };\n const stop = (e: PointerEvent | null) => {\n window.removeEventListener('pointermove', move);\n window.removeEventListener('pointerup', up);\n window.removeEventListener('pointercancel', cancel);\n dragCleanup.current = null;\n if (e && dragged.current) {\n const from = lastIds.current.indexOf(id);\n const gap = gapAt(e.clientX);\n const to = gap > from ? gap - 1 : gap;\n if (from !== -1 && to !== from) onMove(id, to);\n }\n setDragId(null);\n setDropGap(null);\n };\n const up = (e: PointerEvent) => stop(e);\n const cancel = () => stop(null);\n window.addEventListener('pointermove', move);\n window.addEventListener('pointerup', up);\n window.addEventListener('pointercancel', cancel);\n dragCleanup.current = () => stop(null);\n };\n\n const focusTab = (id: string | undefined) => {\n if (!id) return;\n setFocusedId(id);\n tabRefs.current.get(id)?.focus();\n };\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, tab: EditorTab) => {\n const i = ids.indexOf(tab.id);\n const rtl = listRef.current ? getComputedStyle(listRef.current).direction === 'rtl' : false;\n const [prev, next] = rtl ? ['ArrowRight', 'ArrowLeft'] : ['ArrowLeft', 'ArrowRight'];\n let handled = true;\n if (event.ctrlKey && event.shiftKey && (event.key === 'PageUp' || event.key === 'PageDown')) {\n const to = event.key === 'PageUp' ? i - 1 : i + 1;\n if (onMove && to >= 0 && to < ids.length) onMove(tab.id, to);\n } else if (event.key === prev) focusTab(ids[(i - 1 + ids.length) % ids.length]);\n else if (event.key === next) focusTab(ids[(i + 1) % ids.length]);\n else if (event.key === 'Home') focusTab(ids[0]);\n else if (event.key === 'End') focusTab(ids[ids.length - 1]);\n else if (event.key === 'Delete' && tab.closeable !== false && onClose) onClose(tab.id);\n else handled = false;\n if (handled) event.preventDefault();\n };\n\n return (\n <div\n data-slot=\"editor-tabs\"\n className={cn('flex h-9 min-w-0 items-stretch border-b bg-surface-muted', className)}\n {...props}\n >\n <div\n ref={listRef}\n role=\"tablist\"\n aria-label={ariaLabel}\n aria-orientation=\"horizontal\"\n className=\"flex min-w-0 flex-1 items-stretch overflow-x-auto [scrollbar-width:thin]\"\n onFocus={() => (hadFocus.current = true)}\n onBlur={(event) => {\n if (!listRef.current?.contains(event.relatedTarget)) hadFocus.current = false;\n }}\n >\n {tabs.map((tab, i) => {\n const active = tab.id === activeId;\n const closeable = tab.closeable !== false && Boolean(onClose);\n const tabId = `${baseId}-tab-${i}`;\n return (\n <div\n key={tab.id}\n data-slot=\"editor-tab\"\n data-state={active ? 'active' : 'inactive'}\n data-dirty={tab.dirty || undefined}\n data-dragging={dragId === tab.id || undefined}\n data-drop={\n dragId === null\n ? undefined\n : dropGap === i\n ? 'before'\n : dropGap === tabs.length && i === tabs.length - 1\n ? 'after'\n : undefined\n }\n onPointerDown={(event) => startDrag(event, tab.id)}\n onClickCapture={(event) => {\n // Swallow the click that ends a drag.\n if (dragged.current) {\n dragged.current = false;\n event.stopPropagation();\n event.preventDefault();\n }\n }}\n onAuxClick={(event) => {\n if (event.button === 1 && closeable) {\n event.preventDefault();\n onClose?.(tab.id);\n }\n }}\n onContextMenu={\n onTabContextMenu ? (event) => onTabContextMenu(tab.id, event) : undefined\n }\n className={cn(\n 'group/tab relative flex max-w-60 shrink-0 items-center border-e text-[13px] text-muted-foreground',\n 'hover:bg-muted/60 data-[state=active]:bg-background data-[state=active]:text-foreground',\n // The active tab's top edge carries the brand colour.\n 'data-[state=active]:before:absolute data-[state=active]:before:inset-x-0 data-[state=active]:before:top-0 data-[state=active]:before:h-0.5 data-[state=active]:before:bg-brand data-[state=active]:before:content-[\"\"]',\n 'data-[dragging]:opacity-50 data-[dragging]:select-none',\n // Where a dragged tab will land.\n 'data-[drop=before]:shadow-[inset_2px_0_0_var(--brand)] data-[drop=after]:shadow-[inset_-2px_0_0_var(--brand)]',\n )}\n >\n <button\n ref={(el) => {\n if (el) tabRefs.current.set(tab.id, el);\n else tabRefs.current.delete(tab.id);\n }}\n type=\"button\"\n role=\"tab\"\n id={tabId}\n aria-selected={active}\n aria-controls={tab.controls}\n aria-describedby={tab.description ? `${tabId}-desc` : undefined}\n title={tab.description}\n tabIndex={tab.id === tabStop ? 0 : -1}\n onFocus={() => setFocusedId(tab.id)}\n onClick={() => onActiveChange(tab.id)}\n onKeyDown={(event) => onKeyDown(event, tab)}\n className={cn(\n 'flex h-full min-w-0 items-center gap-1.5 ps-3 outline-none focus-visible:shadow-[inset_0_0_0_1px_var(--ring)]',\n closeable ? 'pe-1' : 'pe-3',\n )}\n >\n {tab.icon && (\n <span aria-hidden className=\"flex shrink-0 [&_svg]:size-4\">\n {tab.icon}\n </span>\n )}\n <span className={cn('truncate', tab.preview && 'italic')}>{tab.label}</span>\n {tab.dirty && <span className=\"sr-only\">, unsaved changes</span>}\n </button>\n {tab.description && (\n <span id={`${tabId}-desc`} hidden>\n {tab.description}\n </span>\n )}\n {closeable ? (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={`Close ${tab.label}`}\n title=\"Close (Delete)\"\n onClick={() => onClose?.(tab.id)}\n className=\"me-1 flex size-5 shrink-0 items-center justify-center rounded-xs text-muted-foreground outline-none hover:bg-muted hover:text-foreground\"\n >\n {tab.dirty && (\n <span\n aria-hidden\n className=\"size-2 rounded-full bg-foreground/70 group-hover/tab:hidden\"\n />\n )}\n <X\n aria-hidden\n className={cn(\n 'size-3.5',\n tab.dirty\n ? 'hidden group-hover/tab:block'\n : 'opacity-0 group-hover/tab:opacity-100 group-data-[state=active]/tab:opacity-100 group-focus-within/tab:opacity-100',\n )}\n />\n </button>\n ) : tab.dirty ? (\n <span aria-hidden className=\"me-3 size-2 shrink-0 rounded-full bg-foreground/70\" />\n ) : null}\n </div>\n );\n })}\n </div>\n {actions && (\n <div data-slot=\"editor-tabs-actions\" className=\"flex shrink-0 items-center gap-0.5 px-1\">\n {actions}\n </div>\n )}\n </div>\n );\n}\n\nexport { EditorTabs };\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { Menubar as MenubarPrimitive } from 'radix-ui';
2
+ import * as React from 'react';
3
+ /**
4
+ * An application menu bar (File, Edit, View). Left/Right move between menus,
5
+ * Down opens one, and an open menu follows the pointer across triggers. Rows
6
+ * match DropdownMenu and ContextMenu.
7
+ */
8
+ declare function Menubar({ className, ...props }: React.ComponentProps<typeof MenubarPrimitive.Root>): React.JSX.Element;
9
+ declare function MenubarMenu(props: React.ComponentProps<typeof MenubarPrimitive.Menu>): React.JSX.Element;
10
+ declare function MenubarTrigger({ className, ...props }: React.ComponentProps<typeof MenubarPrimitive.Trigger>): React.JSX.Element;
11
+ declare function MenubarGroup(props: React.ComponentProps<typeof MenubarPrimitive.Group>): React.JSX.Element;
12
+ declare function MenubarContent({ className, align, sideOffset, ...props }: React.ComponentProps<typeof MenubarPrimitive.Content>): React.JSX.Element;
13
+ declare function MenubarItem({ className, inset, variant, ...props }: React.ComponentProps<typeof MenubarPrimitive.Item> & {
14
+ inset?: boolean;
15
+ variant?: 'default' | 'destructive';
16
+ }): React.JSX.Element;
17
+ declare function MenubarCheckboxItem({ className, children, checked, ...props }: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>): React.JSX.Element;
18
+ declare function MenubarRadioGroup(props: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>): React.JSX.Element;
19
+ declare function MenubarRadioItem({ className, children, ...props }: React.ComponentProps<typeof MenubarPrimitive.RadioItem>): React.JSX.Element;
20
+ declare function MenubarLabel({ className, inset, ...props }: React.ComponentProps<typeof MenubarPrimitive.Label> & {
21
+ inset?: boolean;
22
+ }): React.JSX.Element;
23
+ declare function MenubarSeparator({ className, ...props }: React.ComponentProps<typeof MenubarPrimitive.Separator>): React.JSX.Element;
24
+ declare function MenubarShortcut({ className, ...props }: React.ComponentProps<'span'>): React.JSX.Element;
25
+ declare function MenubarSub(props: React.ComponentProps<typeof MenubarPrimitive.Sub>): React.JSX.Element;
26
+ declare function MenubarSubTrigger({ className, inset, children, ...props }: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
27
+ inset?: boolean;
28
+ }): React.JSX.Element;
29
+ declare function MenubarSubContent({ className, ...props }: React.ComponentProps<typeof MenubarPrimitive.SubContent>): React.JSX.Element;
30
+ export { Menubar, MenubarMenu, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, };
@@ -0,0 +1,140 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { menuItemClasses, surfaceClasses } from "./popover.js";
3
+ import { Menubar } from "radix-ui";
4
+ import "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import ChevronRight from "@burtson-labs/icons/react/chevron-right";
7
+ import Check from "@burtson-labs/icons/react/check";
8
+ //#region src/components/menubar.tsx
9
+ /**
10
+ * An application menu bar (File, Edit, View). Left/Right move between menus,
11
+ * Down opens one, and an open menu follows the pointer across triggers. Rows
12
+ * match DropdownMenu and ContextMenu.
13
+ */
14
+ function Menubar$1({ className, ...props }) {
15
+ return /* @__PURE__ */ jsx(Menubar.Root, {
16
+ "data-slot": "menubar",
17
+ className: cn("flex h-8 items-center gap-0.5 rounded-md border bg-surface p-0.5", className),
18
+ ...props
19
+ });
20
+ }
21
+ function MenubarMenu(props) {
22
+ return /* @__PURE__ */ jsx(Menubar.Menu, {
23
+ "data-slot": "menubar-menu",
24
+ ...props
25
+ });
26
+ }
27
+ function MenubarTrigger({ className, ...props }) {
28
+ return /* @__PURE__ */ jsx(Menubar.Trigger, {
29
+ "data-slot": "menubar-trigger",
30
+ className: cn("flex h-7 items-center rounded-sm px-2.5 text-[13px] font-medium outline-none select-none hover:bg-muted focus-visible:shadow-[inset_0_0_0_1px_var(--ring)] data-[state=open]:bg-muted", className),
31
+ ...props
32
+ });
33
+ }
34
+ function MenubarGroup(props) {
35
+ return /* @__PURE__ */ jsx(Menubar.Group, {
36
+ "data-slot": "menubar-group",
37
+ ...props
38
+ });
39
+ }
40
+ function MenubarContent({ className, align = "start", sideOffset = 4, ...props }) {
41
+ return /* @__PURE__ */ jsx(Menubar.Portal, { children: /* @__PURE__ */ jsx(Menubar.Content, {
42
+ "data-slot": "menubar-content",
43
+ align,
44
+ sideOffset,
45
+ className: cn(surfaceClasses, "max-h-(--radix-menubar-content-available-height) min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-x-hidden overflow-y-auto p-1", className),
46
+ ...props
47
+ }) });
48
+ }
49
+ function MenubarItem({ className, inset, variant = "default", ...props }) {
50
+ return /* @__PURE__ */ jsx(Menubar.Item, {
51
+ "data-slot": "menubar-item",
52
+ "data-inset": inset || void 0,
53
+ "data-variant": variant,
54
+ className: cn(menuItemClasses, "data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:*:[svg]:!text-destructive", className),
55
+ ...props
56
+ });
57
+ }
58
+ function MenubarCheckboxItem({ className, children, checked, ...props }) {
59
+ return /* @__PURE__ */ jsxs(Menubar.CheckboxItem, {
60
+ "data-slot": "menubar-checkbox-item",
61
+ className: cn(menuItemClasses, "pl-8", className),
62
+ checked,
63
+ ...props,
64
+ children: [/* @__PURE__ */ jsx("span", {
65
+ className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center",
66
+ children: /* @__PURE__ */ jsx(Menubar.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, {
67
+ className: "size-4 text-foreground",
68
+ "aria-hidden": true
69
+ }) })
70
+ }), children]
71
+ });
72
+ }
73
+ function MenubarRadioGroup(props) {
74
+ return /* @__PURE__ */ jsx(Menubar.RadioGroup, {
75
+ "data-slot": "menubar-radio-group",
76
+ ...props
77
+ });
78
+ }
79
+ function MenubarRadioItem({ className, children, ...props }) {
80
+ return /* @__PURE__ */ jsxs(Menubar.RadioItem, {
81
+ "data-slot": "menubar-radio-item",
82
+ className: cn(menuItemClasses, "pl-8", className),
83
+ ...props,
84
+ children: [/* @__PURE__ */ jsx("span", {
85
+ className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center",
86
+ children: /* @__PURE__ */ jsx(Menubar.ItemIndicator, { children: /* @__PURE__ */ jsx("span", { className: "block size-2 rounded-full bg-foreground" }) })
87
+ }), children]
88
+ });
89
+ }
90
+ function MenubarLabel({ className, inset, ...props }) {
91
+ return /* @__PURE__ */ jsx(Menubar.Label, {
92
+ "data-slot": "menubar-label",
93
+ "data-inset": inset || void 0,
94
+ className: cn("px-2 pt-2 pb-1 text-[11px] font-semibold tracking-[0.08em] text-muted-foreground uppercase data-[inset]:pl-8", className),
95
+ ...props
96
+ });
97
+ }
98
+ function MenubarSeparator({ className, ...props }) {
99
+ return /* @__PURE__ */ jsx(Menubar.Separator, {
100
+ "data-slot": "menubar-separator",
101
+ className: cn("-mx-1 my-1 h-px bg-border", className),
102
+ ...props
103
+ });
104
+ }
105
+ function MenubarShortcut({ className, ...props }) {
106
+ return /* @__PURE__ */ jsx("span", {
107
+ "data-slot": "menubar-shortcut",
108
+ className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className),
109
+ ...props
110
+ });
111
+ }
112
+ function MenubarSub(props) {
113
+ return /* @__PURE__ */ jsx(Menubar.Sub, {
114
+ "data-slot": "menubar-sub",
115
+ ...props
116
+ });
117
+ }
118
+ function MenubarSubTrigger({ className, inset, children, ...props }) {
119
+ return /* @__PURE__ */ jsxs(Menubar.SubTrigger, {
120
+ "data-slot": "menubar-sub-trigger",
121
+ "data-inset": inset || void 0,
122
+ className: cn(menuItemClasses, "data-[state=open]:bg-muted", className),
123
+ ...props,
124
+ children: [children, /* @__PURE__ */ jsx(ChevronRight, {
125
+ className: "ml-auto size-4",
126
+ "aria-hidden": true
127
+ })]
128
+ });
129
+ }
130
+ function MenubarSubContent({ className, ...props }) {
131
+ return /* @__PURE__ */ jsx(Menubar.Portal, { children: /* @__PURE__ */ jsx(Menubar.SubContent, {
132
+ "data-slot": "menubar-sub-content",
133
+ className: cn(surfaceClasses, "min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden p-1", className),
134
+ ...props
135
+ }) });
136
+ }
137
+ //#endregion
138
+ export { Menubar$1 as Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger };
139
+
140
+ //# sourceMappingURL=menubar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menubar.js","names":["Menubar","MenubarPrimitive","itemClasses"],"sources":["../../src/components/menubar.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport ChevronRight from '@burtson-labs/icons/react/chevron-right';\nimport { Menubar as MenubarPrimitive } from 'radix-ui';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { menuItemClasses as itemClasses, surfaceClasses } from './popover';\n\n/**\n * An application menu bar (File, Edit, View). Left/Right move between menus,\n * Down opens one, and an open menu follows the pointer across triggers. Rows\n * match DropdownMenu and ContextMenu.\n */\nfunction Menubar({ className, ...props }: React.ComponentProps<typeof MenubarPrimitive.Root>) {\n return (\n <MenubarPrimitive.Root\n data-slot=\"menubar\"\n className={cn('flex h-8 items-center gap-0.5 rounded-md border bg-surface p-0.5', className)}\n {...props}\n />\n );\n}\n\nfunction MenubarMenu(props: React.ComponentProps<typeof MenubarPrimitive.Menu>) {\n return <MenubarPrimitive.Menu data-slot=\"menubar-menu\" {...props} />;\n}\n\nfunction MenubarTrigger({\n className,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {\n return (\n <MenubarPrimitive.Trigger\n data-slot=\"menubar-trigger\"\n className={cn(\n 'flex h-7 items-center rounded-sm px-2.5 text-[13px] font-medium outline-none select-none hover:bg-muted focus-visible:shadow-[inset_0_0_0_1px_var(--ring)] data-[state=open]:bg-muted',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MenubarGroup(props: React.ComponentProps<typeof MenubarPrimitive.Group>) {\n return <MenubarPrimitive.Group data-slot=\"menubar-group\" {...props} />;\n}\n\nfunction MenubarContent({\n className,\n align = 'start',\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.Content>) {\n return (\n <MenubarPrimitive.Portal>\n <MenubarPrimitive.Content\n data-slot=\"menubar-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n surfaceClasses,\n 'max-h-(--radix-menubar-content-available-height) min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-x-hidden overflow-y-auto p-1',\n className,\n )}\n {...props}\n />\n </MenubarPrimitive.Portal>\n );\n}\n\nfunction MenubarItem({\n className,\n inset,\n variant = 'default',\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.Item> & {\n inset?: boolean;\n variant?: 'default' | 'destructive';\n}) {\n return (\n <MenubarPrimitive.Item\n data-slot=\"menubar-item\"\n data-inset={inset || undefined}\n data-variant={variant}\n className={cn(\n itemClasses,\n 'data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:*:[svg]:!text-destructive',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MenubarCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {\n return (\n <MenubarPrimitive.CheckboxItem\n data-slot=\"menubar-checkbox-item\"\n className={cn(itemClasses, 'pl-8', className)}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenubarPrimitive.ItemIndicator>\n <Check className=\"size-4 text-foreground\" aria-hidden />\n </MenubarPrimitive.ItemIndicator>\n </span>\n {children}\n </MenubarPrimitive.CheckboxItem>\n );\n}\n\nfunction MenubarRadioGroup(props: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {\n return <MenubarPrimitive.RadioGroup data-slot=\"menubar-radio-group\" {...props} />;\n}\n\nfunction MenubarRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) {\n return (\n <MenubarPrimitive.RadioItem\n data-slot=\"menubar-radio-item\"\n className={cn(itemClasses, 'pl-8', className)}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenubarPrimitive.ItemIndicator>\n <span className=\"block size-2 rounded-full bg-foreground\" />\n </MenubarPrimitive.ItemIndicator>\n </span>\n {children}\n </MenubarPrimitive.RadioItem>\n );\n}\n\nfunction MenubarLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.Label> & { inset?: boolean }) {\n return (\n <MenubarPrimitive.Label\n data-slot=\"menubar-label\"\n data-inset={inset || undefined}\n className={cn(\n 'px-2 pt-2 pb-1 text-[11px] font-semibold tracking-[0.08em] text-muted-foreground uppercase data-[inset]:pl-8',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MenubarSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.Separator>) {\n return (\n <MenubarPrimitive.Separator\n data-slot=\"menubar-separator\"\n className={cn('-mx-1 my-1 h-px bg-border', className)}\n {...props}\n />\n );\n}\n\nfunction MenubarShortcut({ className, ...props }: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"menubar-shortcut\"\n className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}\n {...props}\n />\n );\n}\n\nfunction MenubarSub(props: React.ComponentProps<typeof MenubarPrimitive.Sub>) {\n return <MenubarPrimitive.Sub data-slot=\"menubar-sub\" {...props} />;\n}\n\nfunction MenubarSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & { inset?: boolean }) {\n return (\n <MenubarPrimitive.SubTrigger\n data-slot=\"menubar-sub-trigger\"\n data-inset={inset || undefined}\n className={cn(itemClasses, 'data-[state=open]:bg-muted', className)}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto size-4\" aria-hidden />\n </MenubarPrimitive.SubTrigger>\n );\n}\n\nfunction MenubarSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {\n return (\n <MenubarPrimitive.Portal>\n <MenubarPrimitive.SubContent\n data-slot=\"menubar-sub-content\"\n className={cn(\n surfaceClasses,\n 'min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden p-1',\n className,\n )}\n {...props}\n />\n </MenubarPrimitive.Portal>\n );\n}\n\nexport {\n Menubar,\n MenubarMenu,\n MenubarCheckboxItem,\n MenubarContent,\n MenubarGroup,\n MenubarItem,\n MenubarLabel,\n MenubarRadioGroup,\n MenubarRadioItem,\n MenubarSeparator,\n MenubarShortcut,\n MenubarSub,\n MenubarSubContent,\n MenubarSubTrigger,\n MenubarTrigger,\n};\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ export interface ChecklistItem {
3
+ id: string;
4
+ title: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ /** Set from real app events (a repo opened, a provider tested), never from a click on the list. */
7
+ done: boolean;
8
+ /** A button or link that starts the task. Hidden once done. */
9
+ action?: React.ReactNode;
10
+ }
11
+ export interface OnboardingChecklistProps extends Omit<React.ComponentProps<'section'>, 'title'> {
12
+ title: React.ReactNode;
13
+ description?: React.ReactNode;
14
+ items: ChecklistItem[];
15
+ /** Shows a close button. Persist the dismissal yourself. */
16
+ onDismiss?: () => void;
17
+ dismissLabel?: string;
18
+ /** Replaces the list once every item is done. */
19
+ complete?: React.ReactNode;
20
+ }
21
+ /**
22
+ * A short list of first tasks with progress. The app decides when each task
23
+ * is done and whether the list shows at all; this renders it.
24
+ */
25
+ declare function OnboardingChecklist({ title, description, items, onDismiss, dismissLabel, complete, className, ...props }: OnboardingChecklistProps): React.JSX.Element;
26
+ export { OnboardingChecklist };
@@ -0,0 +1,106 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { Progress } from "./progress.js";
3
+ import * as React from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import X from "@burtson-labs/icons/react/x";
6
+ import CheckCircle from "@burtson-labs/icons/react/check-circle";
7
+ import CircleDashed from "@burtson-labs/icons/react/circle-dashed";
8
+ //#region src/components/onboarding-checklist.tsx
9
+ /**
10
+ * A short list of first tasks with progress. The app decides when each task
11
+ * is done and whether the list shows at all; this renders it.
12
+ */
13
+ function OnboardingChecklist({ title, description, items, onDismiss, dismissLabel = "Dismiss checklist", complete, className, ...props }) {
14
+ const titleId = React.useId();
15
+ const done = items.filter((i) => i.done).length;
16
+ const allDone = items.length > 0 && done === items.length;
17
+ return /* @__PURE__ */ jsxs("section", {
18
+ "data-slot": "onboarding-checklist",
19
+ "aria-labelledby": titleId,
20
+ className: cn("rounded-lg border bg-card p-4 text-[13px] text-card-foreground", className),
21
+ ...props,
22
+ children: [
23
+ /* @__PURE__ */ jsxs("div", {
24
+ className: "flex items-start gap-3",
25
+ children: [/* @__PURE__ */ jsxs("div", {
26
+ className: "min-w-0 flex-1",
27
+ children: [/* @__PURE__ */ jsx("h2", {
28
+ id: titleId,
29
+ className: "text-sm font-semibold",
30
+ children: title
31
+ }), description && /* @__PURE__ */ jsx("p", {
32
+ className: "mt-0.5 text-muted-foreground",
33
+ children: description
34
+ })]
35
+ }), onDismiss && /* @__PURE__ */ jsx("button", {
36
+ type: "button",
37
+ "aria-label": dismissLabel,
38
+ onClick: onDismiss,
39
+ className: "-me-1 -mt-1 flex size-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:shadow-[inset_0_0_0_1px_var(--ring)]",
40
+ children: /* @__PURE__ */ jsx(X, {
41
+ "aria-hidden": true,
42
+ className: "size-4"
43
+ })
44
+ })]
45
+ }),
46
+ /* @__PURE__ */ jsxs("div", {
47
+ className: "mt-3 flex items-center gap-3",
48
+ children: [/* @__PURE__ */ jsx(Progress, {
49
+ value: done,
50
+ max: Math.max(items.length, 1),
51
+ "aria-label": `${done} of ${items.length} done`,
52
+ className: "flex-1"
53
+ }), /* @__PURE__ */ jsxs("span", {
54
+ className: "shrink-0 text-xs text-muted-foreground tabular-nums",
55
+ "aria-hidden": true,
56
+ children: [
57
+ done,
58
+ "/",
59
+ items.length
60
+ ]
61
+ })]
62
+ }),
63
+ allDone && complete ? /* @__PURE__ */ jsx("div", {
64
+ className: "mt-3",
65
+ children: complete
66
+ }) : /* @__PURE__ */ jsx("ul", {
67
+ className: "mt-3 flex flex-col gap-1",
68
+ children: items.map((item) => /* @__PURE__ */ jsxs("li", {
69
+ "data-slot": "onboarding-checklist-item",
70
+ "data-state": item.done ? "done" : "todo",
71
+ className: "flex items-start gap-2.5 rounded-md px-1 py-1.5",
72
+ children: [
73
+ item.done ? /* @__PURE__ */ jsx(CheckCircle, {
74
+ "aria-hidden": true,
75
+ className: "mt-px size-4 shrink-0 text-success"
76
+ }) : /* @__PURE__ */ jsx(CircleDashed, {
77
+ "aria-hidden": true,
78
+ className: "mt-px size-4 shrink-0 text-muted-foreground"
79
+ }),
80
+ /* @__PURE__ */ jsxs("div", {
81
+ className: "min-w-0 flex-1",
82
+ children: [/* @__PURE__ */ jsxs("p", {
83
+ className: cn("font-medium", item.done && "text-muted-foreground line-through"),
84
+ children: [item.title, /* @__PURE__ */ jsx("span", {
85
+ className: "sr-only",
86
+ children: item.done ? ", done" : ", not done"
87
+ })]
88
+ }), item.description && !item.done && /* @__PURE__ */ jsx("p", {
89
+ className: "mt-0.5 text-muted-foreground",
90
+ children: item.description
91
+ })]
92
+ }),
93
+ item.action && !item.done && /* @__PURE__ */ jsx("div", {
94
+ className: "shrink-0",
95
+ children: item.action
96
+ })
97
+ ]
98
+ }, item.id))
99
+ })
100
+ ]
101
+ });
102
+ }
103
+ //#endregion
104
+ export { OnboardingChecklist };
105
+
106
+ //# sourceMappingURL=onboarding-checklist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding-checklist.js","names":[],"sources":["../../src/components/onboarding-checklist.tsx"],"sourcesContent":["import CheckCircle from '@burtson-labs/icons/react/check-circle';\nimport CircleDashed from '@burtson-labs/icons/react/circle-dashed';\nimport X from '@burtson-labs/icons/react/x';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { Progress } from './progress';\n\nexport interface ChecklistItem {\n id: string;\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Set from real app events (a repo opened, a provider tested), never from a click on the list. */\n done: boolean;\n /** A button or link that starts the task. Hidden once done. */\n action?: React.ReactNode;\n}\n\nexport interface OnboardingChecklistProps extends Omit<React.ComponentProps<'section'>, 'title'> {\n title: React.ReactNode;\n description?: React.ReactNode;\n items: ChecklistItem[];\n /** Shows a close button. Persist the dismissal yourself. */\n onDismiss?: () => void;\n dismissLabel?: string;\n /** Replaces the list once every item is done. */\n complete?: React.ReactNode;\n}\n\n/**\n * A short list of first tasks with progress. The app decides when each task\n * is done and whether the list shows at all; this renders it.\n */\nfunction OnboardingChecklist({\n title,\n description,\n items,\n onDismiss,\n dismissLabel = 'Dismiss checklist',\n complete,\n className,\n ...props\n}: OnboardingChecklistProps) {\n const titleId = React.useId();\n const done = items.filter((i) => i.done).length;\n const allDone = items.length > 0 && done === items.length;\n return (\n <section\n data-slot=\"onboarding-checklist\"\n aria-labelledby={titleId}\n className={cn('rounded-lg border bg-card p-4 text-[13px] text-card-foreground', className)}\n {...props}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"min-w-0 flex-1\">\n <h2 id={titleId} className=\"text-sm font-semibold\">\n {title}\n </h2>\n {description && <p className=\"mt-0.5 text-muted-foreground\">{description}</p>}\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n aria-label={dismissLabel}\n onClick={onDismiss}\n className=\"-me-1 -mt-1 flex size-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:shadow-[inset_0_0_0_1px_var(--ring)]\"\n >\n <X aria-hidden className=\"size-4\" />\n </button>\n )}\n </div>\n <div className=\"mt-3 flex items-center gap-3\">\n <Progress\n value={done}\n max={Math.max(items.length, 1)}\n aria-label={`${done} of ${items.length} done`}\n className=\"flex-1\"\n />\n <span className=\"shrink-0 text-xs text-muted-foreground tabular-nums\" aria-hidden>\n {done}/{items.length}\n </span>\n </div>\n {allDone && complete ? (\n <div className=\"mt-3\">{complete}</div>\n ) : (\n <ul className=\"mt-3 flex flex-col gap-1\">\n {items.map((item) => (\n <li\n key={item.id}\n data-slot=\"onboarding-checklist-item\"\n data-state={item.done ? 'done' : 'todo'}\n className=\"flex items-start gap-2.5 rounded-md px-1 py-1.5\"\n >\n {item.done ? (\n <CheckCircle aria-hidden className=\"mt-px size-4 shrink-0 text-success\" />\n ) : (\n <CircleDashed aria-hidden className=\"mt-px size-4 shrink-0 text-muted-foreground\" />\n )}\n <div className=\"min-w-0 flex-1\">\n <p className={cn('font-medium', item.done && 'text-muted-foreground line-through')}>\n {item.title}\n <span className=\"sr-only\">{item.done ? ', done' : ', not done'}</span>\n </p>\n {item.description && !item.done && (\n <p className=\"mt-0.5 text-muted-foreground\">{item.description}</p>\n )}\n </div>\n {item.action && !item.done && <div className=\"shrink-0\">{item.action}</div>}\n </li>\n ))}\n </ul>\n )}\n </section>\n );\n}\n\nexport { OnboardingChecklist };\n"],"mappings":";;;;;;;;;;;;AAkCA,SAAS,oBAAoB,EAC3B,OACA,aACA,OACA,WACA,eAAe,qBACf,UACA,WACA,GAAG,SACwB;CAC3B,MAAM,UAAU,MAAM,MAAM;CAC5B,MAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC;CACzC,MAAM,UAAU,MAAM,SAAS,KAAK,SAAS,MAAM;CACnD,OACE,qBAAC,WAAD;EACE,aAAU;EACV,mBAAiB;EACjB,WAAW,GAAG,kEAAkE,SAAS;EACzF,GAAI;EAJN,UAAA;GAME,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,oBAAC,MAAD;MAAI,IAAI;MAAS,WAAU;MACxB,UAAA;KACC,CAAA,GACH,eAAe,oBAAC,KAAD;MAAG,WAAU;MAAgC,UAAA;KAAe,CAAA,CACzE;IACJ,CAAA,GAAA,aACC,oBAAC,UAAD;KACE,MAAK;KACL,cAAY;KACZ,SAAS;KACT,WAAU;KAEV,UAAA,oBAAC,GAAD;MAAG,eAAA;MAAY,WAAU;KAAU,CAAA;IAC7B,CAAA,CAEP;;GACL,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,UAAD;KACE,OAAO;KACP,KAAK,KAAK,IAAI,MAAM,QAAQ,CAAC;KAC7B,cAAY,GAAG,KAAK,MAAM,MAAM,OAAO;KACvC,WAAU;IACX,CAAA,GACD,qBAAC,QAAD;KAAM,WAAU;KAAsD,eAAA;KAAtE,UAAA;MACG;MAAK;MAAE,MAAM;KACV;IACH,CAAA,CAAA;;GACJ,WAAW,WACV,oBAAC,OAAD;IAAK,WAAU;IAAQ,UAAA;GAAc,CAAA,IAErC,oBAAC,MAAD;IAAI,WAAU;IACX,UAAA,MAAM,KAAK,SACV,qBAAC,MAAD;KAEE,aAAU;KACV,cAAY,KAAK,OAAO,SAAS;KACjC,WAAU;KAJZ,UAAA;MAMG,KAAK,OACJ,oBAAC,aAAD;OAAa,eAAA;OAAY,WAAU;MAAsC,CAAA,IAEzE,oBAAC,cAAD;OAAc,eAAA;OAAY,WAAU;MAA+C,CAAA;MAErF,qBAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,qBAAC,KAAD;QAAG,WAAW,GAAG,eAAe,KAAK,QAAQ,oCAAoC;QAAjF,UAAA,CACG,KAAK,OACN,oBAAC,QAAD;SAAM,WAAU;SAAW,UAAA,KAAK,OAAO,WAAW;QAAmB,CAAA,CACpE;OACF,CAAA,GAAA,KAAK,eAAe,CAAC,KAAK,QACzB,oBAAC,KAAD;QAAG,WAAU;QAAgC,UAAA,KAAK;OAAe,CAAA,CAEhE;;MACJ,KAAK,UAAU,CAAC,KAAK,QAAQ,oBAAC,OAAD;OAAK,WAAU;OAAY,UAAA,KAAK;MAAY,CAAA;KACxE;IApBG,GAAA,KAAK,EAoBR,CACL;GACC,CAAA;EAEC;;AAEb"}
@@ -5,6 +5,8 @@ import * as React from 'react';
5
5
  * with a strong border, and the one place a heavy shadow is allowed.
6
6
  */
7
7
  export declare const surfaceClasses = "z-50 animate-in rounded-lg border border-border-strong bg-surface-raised text-popover-foreground shadow-[0_16px_48px_rgb(0_0_0_/_0.18)] data-[state=closed]:animate-out data-[side=bottom]:[--bl-ty:-6px] data-[side=top]:[--bl-ty:6px] data-[side=left]:[--bl-tx:6px] data-[side=right]:[--bl-tx:-6px]";
8
+ /** A menu row: 32px, 8px radius; the focused row is a quiet fill, like an active tab. */
9
+ export declare const menuItemClasses = "relative flex min-h-8 cursor-default items-center gap-2 rounded-sm px-2 text-[13px] outline-hidden select-none focus:bg-muted focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground";
8
10
  declare function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>): React.JSX.Element;
9
11
  declare function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>): React.JSX.Element;
10
12
  declare function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>): React.JSX.Element;
@@ -8,6 +8,8 @@ import { jsx } from "react/jsx-runtime";
8
8
  * with a strong border, and the one place a heavy shadow is allowed.
9
9
  */
10
10
  var surfaceClasses = "z-50 animate-in rounded-lg border border-border-strong bg-surface-raised text-popover-foreground shadow-[0_16px_48px_rgb(0_0_0_/_0.18)] data-[state=closed]:animate-out data-[side=bottom]:[--bl-ty:-6px] data-[side=top]:[--bl-ty:6px] data-[side=left]:[--bl-tx:6px] data-[side=right]:[--bl-tx:-6px]";
11
+ /** A menu row: 32px, 8px radius; the focused row is a quiet fill, like an active tab. */
12
+ var menuItemClasses = "relative flex min-h-8 cursor-default items-center gap-2 rounded-sm px-2 text-[13px] outline-hidden select-none focus:bg-muted focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground";
11
13
  function Popover$1(props) {
12
14
  return /* @__PURE__ */ jsx(Popover.Root, {
13
15
  "data-slot": "popover",
@@ -36,6 +38,6 @@ function PopoverContent({ className, align = "center", sideOffset = 6, ...props
36
38
  }) });
37
39
  }
38
40
  //#endregion
39
- export { Popover$1 as Popover, PopoverAnchor, PopoverContent, PopoverTrigger, surfaceClasses };
41
+ export { Popover$1 as Popover, PopoverAnchor, PopoverContent, PopoverTrigger, menuItemClasses, surfaceClasses };
40
42
 
41
43
  //# sourceMappingURL=popover.js.map