@adea-ai/ui 0.145.0 → 0.145.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/composites/workspace-switcher/workspace-switcher.d.ts +10 -2
- package/dist/components/composites/workspace-switcher/workspace-switcher.d.ts.map +1 -1
- package/dist/components/composites/workspace-switcher/workspace-switcher.js +13 -2
- package/dist/components/composites/workspace-switcher/workspace-switcher.js.map +1 -1
- package/dist/r/src/components/composites/workspace-switcher/workspace-switcher.tsx +21 -4
- package/package.json +1 -1
- package/src/components/composites/workspace-switcher/workspace-switcher.tsx +21 -4
|
@@ -17,8 +17,16 @@ import type { JSX } from 'solid-js';
|
|
|
17
17
|
export type WorkspaceSwitcherItem = {
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The workspace's mark, drawn in the trigger and in its row. Decorative.
|
|
22
|
+
*
|
|
23
|
+
* A DOM node can sit in one place, so the current workspace's mark would have
|
|
24
|
+
* to be in the trigger and its menu row at once. Pass a function to draw a
|
|
25
|
+
* fresh mark in each place (the preferred form). A node built once is also
|
|
26
|
+
* safe: the trigger keeps it and the current row gets a copy, which carries
|
|
27
|
+
* no event handlers or reactive bindings, so keep marks static.
|
|
28
|
+
*/
|
|
29
|
+
mark: JSX.Element | (() => JSX.Element);
|
|
22
30
|
/** A quiet trailing readout in the row, e.g. an unread count. Optional. */
|
|
23
31
|
detail?: JSX.Element;
|
|
24
32
|
/** Pinned to the top of the order, e.g. Home: it cannot be moved and nothing moves above it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-switcher.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/workspace-switcher/workspace-switcher.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAiBnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ
|
|
1
|
+
{"version":3,"file":"workspace-switcher.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/workspace-switcher/workspace-switcher.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAiBnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;;;OAQG;IACH,IAAI,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAA;IACvC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,gGAAgG;IAChG,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,SAAS,qBAAqB,EAAE,CAAA;IAC5C,kCAAkC;IAClC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,IAAI,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,IAAI,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,SAAS,CAAC,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAWD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,eA2F9D"}
|
|
@@ -9,6 +9,14 @@ import { For, Show, createSignal } from "solid-js";
|
|
|
9
9
|
var _tmpl$ = /*#__PURE__*/ template(`<span aria-hidden=true class="flex shrink-0">`);
|
|
10
10
|
var _tmpl$2 = /*#__PURE__*/ template(`<span class="min-w-0 max-w-40 truncate">`);
|
|
11
11
|
var _tmpl$3 = /*#__PURE__*/ template(`<span class="min-w-0 flex-1 truncate">`);
|
|
12
|
+
/**
|
|
13
|
+
* Draws one placement of a mark. A function draws anew each time; a node that is
|
|
14
|
+
* already placed elsewhere is copied so the original stays where it is.
|
|
15
|
+
*/
|
|
16
|
+
function drawMark(mark, copy) {
|
|
17
|
+
if (typeof mark === "function") return mark();
|
|
18
|
+
return copy && mark instanceof Node ? mark.cloneNode(true) : mark;
|
|
19
|
+
}
|
|
12
20
|
function WorkspaceSwitcher(props) {
|
|
13
21
|
const [open, setOpen] = createSignal(false);
|
|
14
22
|
const [reordering, setReordering] = createSignal(false);
|
|
@@ -45,7 +53,10 @@ function WorkspaceSwitcher(props) {
|
|
|
45
53
|
return [
|
|
46
54
|
(() => {
|
|
47
55
|
var _el$ = _tmpl$();
|
|
48
|
-
insert(_el$, () =>
|
|
56
|
+
insert(_el$, (() => {
|
|
57
|
+
var _c$ = memo(() => !!current());
|
|
58
|
+
return () => _c$() && drawMark(current().mark, false);
|
|
59
|
+
})());
|
|
49
60
|
return _el$;
|
|
50
61
|
})(),
|
|
51
62
|
(() => {
|
|
@@ -93,7 +104,7 @@ function WorkspaceSwitcher(props) {
|
|
|
93
104
|
return [
|
|
94
105
|
(() => {
|
|
95
106
|
var _el$3 = _tmpl$();
|
|
96
|
-
insert(_el$3, () => item.mark);
|
|
107
|
+
insert(_el$3, () => drawMark(item.mark, item.id === props.value));
|
|
97
108
|
return _el$3;
|
|
98
109
|
})(),
|
|
99
110
|
(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-switcher.js","names":["ArrowDownUp","ChevronDown","Plus","Settings","JSX","For","Show","createSignal","cn","DropdownMenu","DropdownMenuContent","DropdownMenuGroup","DropdownMenuItem","DropdownMenuLabel","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuTrigger","ActionButton","WorkspaceReorderDialog","WorkspaceSwitcherItem","id","name","mark","Element","detail","fixed","WorkspaceSwitcherProps","workspaces","value","onValueChange","onCreate","createLabel","onManage","manageLabel","onReorder","ids","Promise","reorderLabel","label","placeholder","disabled","class","WorkspaceSwitcher","props","open","setOpen","reordering","setReordering","trigger","HTMLElement","current","find","item","canReorder","undefined","filter","length","_$createComponent","modal","onOpenChange","children","as","variant","size","ref","el","tooltip","aria-label","_el$","_tmpl$","_$insert","_el$2","_tmpl$2","align","sideOffset","onChange","String","each","closeOnSelect","data-workspace-id","_el$3","_el$4","_tmpl$3","when","onSelect","_$memo","restoreFocusRef","_$createComponent","_$memo"],"sources":["../../../../src/components/composites/workspace-switcher/workspace-switcher.tsx"],"sourcesContent":["import { ArrowDownUp, ChevronDown, Plus, Settings } from 'lucide-solid'\nimport type { JSX } from 'solid-js'\nimport { For, Show, createSignal } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../../ui/dropdown-menu'\nimport { ActionButton } from '../action-button'\nimport { WorkspaceReorderDialog } from './workspace-reorder-dialog'\n\n/**\n * WorkspaceSwitcher.\n *\n * The workspace picker that sits in the top bar beside the sidebar toggle. One\n * ghost button shows the current workspace's mark and name; pressing it opens a\n * radio list of the others. Choosing one only changes what the window is\n * looking at: the switcher reports the id and the host decides what that means,\n * which is why it carries no status of its own and never closes, restarts or\n * moves anything.\n *\n * It is deliberately the simple interaction (a single menu, not a nested\n * accordion): the sidebar below it already shows what is inside the chosen\n * workspace, so the switcher's only job is to name the workspace and let the\n * user change it.\n */\nexport type WorkspaceSwitcherItem = {\n id: string\n name: string\n /** The workspace's mark, drawn in the trigger and its row. Decorative. */\n mark: JSX.Element\n /** A quiet trailing readout in the row, e.g. an unread count. Optional. */\n detail?: JSX.Element\n /** Pinned to the top of the order, e.g. Home: it cannot be moved and nothing moves above it. */\n fixed?: boolean\n}\n\nexport type WorkspaceSwitcherProps = {\n workspaces: readonly WorkspaceSwitcherItem[]\n /** The current workspace's id. */\n value: string | undefined\n onValueChange(id: string): void\n /** Offered at the foot of the menu when present. */\n onCreate?(): void\n createLabel?: string\n /** Offered beside the create action when present: open the current workspace's settings. */\n onManage?(): void\n manageLabel?: string\n /**\n * Offered when present and at least two workspaces can move: opens a dialog\n * with Move up and Move down on each row. Receives the full id order after\n * each move; the host saves it and passes the workspaces back in that order.\n */\n onReorder?(ids: readonly string[]): void | Promise<void>\n reorderLabel?: string\n /** The menu's heading; also the trigger's accessible-name prefix. */\n label?: string\n /** Shown while no workspace is current. */\n placeholder?: string\n disabled?: boolean\n class?: string\n}\n\nexport function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {\n const [open, setOpen] = createSignal(false)\n const [reordering, setReordering] = createSignal(false)\n let trigger: HTMLElement | undefined\n const current = () => props.workspaces.find((item) => item.id === props.value)\n const label = () => props.label ?? 'Workspaces'\n const canReorder = () =>\n props.onReorder !== undefined && props.workspaces.filter((item) => !item.fixed).length > 1\n\n return (\n <>\n <DropdownMenu modal={false} open={open()} onOpenChange={setOpen}>\n <DropdownMenuTrigger\n as={ActionButton}\n variant=\"ghost\"\n size=\"sm\"\n disabled={props.disabled}\n ref={(el: HTMLElement) => (trigger = el)}\n data-slot=\"workspace-switcher\"\n tooltip={`Workspace: ${current()?.name ?? props.placeholder ?? 'Choose workspace'}`}\n aria-label={`Switch workspace, current: ${current()?.name ?? 'none'}`}\n class={cn('max-w-full min-w-0 shrink', props.class)}\n >\n <span aria-hidden=\"true\" class=\"flex shrink-0\">\n {current()?.mark}\n </span>\n <span class=\"min-w-0 max-w-40 truncate\">\n {current()?.name ?? props.placeholder ?? 'Choose workspace'}\n </span>\n <ChevronDown aria-hidden=\"true\" class=\"size-3.5 shrink-0 text-muted-foreground\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" sideOffset={6} class=\"min-w-56\">\n <DropdownMenuGroup>\n <DropdownMenuLabel>{label()}</DropdownMenuLabel>\n <DropdownMenuRadioGroup\n value={props.value}\n onChange={(value) => {\n setOpen(false)\n const id = String(value)\n if (id !== props.value) props.onValueChange(id)\n }}\n >\n <For each={props.workspaces}>\n {(item) => (\n <DropdownMenuRadioItem value={item.id} closeOnSelect data-workspace-id={item.id}>\n <span aria-hidden=\"true\" class=\"flex shrink-0\">\n {item.mark}\n </span>\n <span class=\"min-w-0 flex-1 truncate\">{item.name}</span>\n <Show when={item.detail}>{item.detail}</Show>\n </DropdownMenuRadioItem>\n )}\n </For>\n </DropdownMenuRadioGroup>\n </DropdownMenuGroup>\n <Show when={props.onCreate || props.onManage || canReorder()}>\n <DropdownMenuSeparator />\n <Show when={props.onCreate}>\n <DropdownMenuItem onSelect={() => props.onCreate?.()}>\n <Plus aria-hidden=\"true\" />\n {props.createLabel ?? 'New workspace'}\n </DropdownMenuItem>\n </Show>\n <Show when={canReorder()}>\n <DropdownMenuItem onSelect={() => setReordering(true)}>\n <ArrowDownUp aria-hidden=\"true\" />\n {props.reorderLabel ?? 'Reorder workspaces…'}\n </DropdownMenuItem>\n </Show>\n <Show when={props.onManage}>\n <DropdownMenuItem onSelect={() => props.onManage?.()}>\n <Settings aria-hidden=\"true\" />\n {props.manageLabel ?? 'Workspace settings…'}\n </DropdownMenuItem>\n </Show>\n </Show>\n </DropdownMenuContent>\n </DropdownMenu>\n <Show when={props.onReorder}>\n {(onReorder) => (\n <WorkspaceReorderDialog\n open={reordering()}\n onOpenChange={setReordering}\n workspaces={props.workspaces}\n onReorder={onReorder()}\n restoreFocusRef={() => trigger}\n />\n )}\n </Show>\n </>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workspace-switcher.js","names":["ArrowDownUp","ChevronDown","Plus","Settings","JSX","For","Show","createSignal","cn","DropdownMenu","DropdownMenuContent","DropdownMenuGroup","DropdownMenuItem","DropdownMenuLabel","DropdownMenuRadioGroup","DropdownMenuRadioItem","DropdownMenuSeparator","DropdownMenuTrigger","ActionButton","WorkspaceReorderDialog","WorkspaceSwitcherItem","id","name","mark","Element","detail","fixed","WorkspaceSwitcherProps","workspaces","value","onValueChange","onCreate","createLabel","onManage","manageLabel","onReorder","ids","Promise","reorderLabel","label","placeholder","disabled","class","drawMark","copy","Node","cloneNode","WorkspaceSwitcher","props","open","setOpen","reordering","setReordering","trigger","HTMLElement","current","find","item","canReorder","undefined","filter","length","_$createComponent","modal","onOpenChange","children","as","variant","size","ref","el","tooltip","aria-label","_el$","_tmpl$","_$insert","_c$","_$memo","_el$2","_tmpl$2","align","sideOffset","onChange","String","each","closeOnSelect","data-workspace-id","_el$3","_el$4","_tmpl$3","when","onSelect","restoreFocusRef","_$createComponent","_$memo"],"sources":["../../../../src/components/composites/workspace-switcher/workspace-switcher.tsx"],"sourcesContent":["import { ArrowDownUp, ChevronDown, Plus, Settings } from 'lucide-solid'\nimport type { JSX } from 'solid-js'\nimport { For, Show, createSignal } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../../ui/dropdown-menu'\nimport { ActionButton } from '../action-button'\nimport { WorkspaceReorderDialog } from './workspace-reorder-dialog'\n\n/**\n * WorkspaceSwitcher.\n *\n * The workspace picker that sits in the top bar beside the sidebar toggle. One\n * ghost button shows the current workspace's mark and name; pressing it opens a\n * radio list of the others. Choosing one only changes what the window is\n * looking at: the switcher reports the id and the host decides what that means,\n * which is why it carries no status of its own and never closes, restarts or\n * moves anything.\n *\n * It is deliberately the simple interaction (a single menu, not a nested\n * accordion): the sidebar below it already shows what is inside the chosen\n * workspace, so the switcher's only job is to name the workspace and let the\n * user change it.\n */\nexport type WorkspaceSwitcherItem = {\n id: string\n name: string\n /**\n * The workspace's mark, drawn in the trigger and in its row. Decorative.\n *\n * A DOM node can sit in one place, so the current workspace's mark would have\n * to be in the trigger and its menu row at once. Pass a function to draw a\n * fresh mark in each place (the preferred form). A node built once is also\n * safe: the trigger keeps it and the current row gets a copy, which carries\n * no event handlers or reactive bindings, so keep marks static.\n */\n mark: JSX.Element | (() => JSX.Element)\n /** A quiet trailing readout in the row, e.g. an unread count. Optional. */\n detail?: JSX.Element\n /** Pinned to the top of the order, e.g. Home: it cannot be moved and nothing moves above it. */\n fixed?: boolean\n}\n\nexport type WorkspaceSwitcherProps = {\n workspaces: readonly WorkspaceSwitcherItem[]\n /** The current workspace's id. */\n value: string | undefined\n onValueChange(id: string): void\n /** Offered at the foot of the menu when present. */\n onCreate?(): void\n createLabel?: string\n /** Offered beside the create action when present: open the current workspace's settings. */\n onManage?(): void\n manageLabel?: string\n /**\n * Offered when present and at least two workspaces can move: opens a dialog\n * with Move up and Move down on each row. Receives the full id order after\n * each move; the host saves it and passes the workspaces back in that order.\n */\n onReorder?(ids: readonly string[]): void | Promise<void>\n reorderLabel?: string\n /** The menu's heading; also the trigger's accessible-name prefix. */\n label?: string\n /** Shown while no workspace is current. */\n placeholder?: string\n disabled?: boolean\n class?: string\n}\n\n/**\n * Draws one placement of a mark. A function draws anew each time; a node that is\n * already placed elsewhere is copied so the original stays where it is.\n */\nfunction drawMark(mark: WorkspaceSwitcherItem['mark'], copy: boolean): JSX.Element {\n if (typeof mark === 'function') return mark()\n return copy && mark instanceof Node ? mark.cloneNode(true) : mark\n}\n\nexport function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {\n const [open, setOpen] = createSignal(false)\n const [reordering, setReordering] = createSignal(false)\n let trigger: HTMLElement | undefined\n const current = () => props.workspaces.find((item) => item.id === props.value)\n const label = () => props.label ?? 'Workspaces'\n const canReorder = () =>\n props.onReorder !== undefined && props.workspaces.filter((item) => !item.fixed).length > 1\n\n return (\n <>\n <DropdownMenu modal={false} open={open()} onOpenChange={setOpen}>\n <DropdownMenuTrigger\n as={ActionButton}\n variant=\"ghost\"\n size=\"sm\"\n disabled={props.disabled}\n ref={(el: HTMLElement) => (trigger = el)}\n data-slot=\"workspace-switcher\"\n tooltip={`Workspace: ${current()?.name ?? props.placeholder ?? 'Choose workspace'}`}\n aria-label={`Switch workspace, current: ${current()?.name ?? 'none'}`}\n class={cn('max-w-full min-w-0 shrink', props.class)}\n >\n <span aria-hidden=\"true\" class=\"flex shrink-0\">\n {current() && drawMark(current()!.mark, false)}\n </span>\n <span class=\"min-w-0 max-w-40 truncate\">\n {current()?.name ?? props.placeholder ?? 'Choose workspace'}\n </span>\n <ChevronDown aria-hidden=\"true\" class=\"size-3.5 shrink-0 text-muted-foreground\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" sideOffset={6} class=\"min-w-56\">\n <DropdownMenuGroup>\n <DropdownMenuLabel>{label()}</DropdownMenuLabel>\n <DropdownMenuRadioGroup\n value={props.value}\n onChange={(value) => {\n setOpen(false)\n const id = String(value)\n if (id !== props.value) props.onValueChange(id)\n }}\n >\n <For each={props.workspaces}>\n {(item) => (\n <DropdownMenuRadioItem value={item.id} closeOnSelect data-workspace-id={item.id}>\n <span aria-hidden=\"true\" class=\"flex shrink-0\">\n {drawMark(item.mark, item.id === props.value)}\n </span>\n <span class=\"min-w-0 flex-1 truncate\">{item.name}</span>\n <Show when={item.detail}>{item.detail}</Show>\n </DropdownMenuRadioItem>\n )}\n </For>\n </DropdownMenuRadioGroup>\n </DropdownMenuGroup>\n <Show when={props.onCreate || props.onManage || canReorder()}>\n <DropdownMenuSeparator />\n <Show when={props.onCreate}>\n <DropdownMenuItem onSelect={() => props.onCreate?.()}>\n <Plus aria-hidden=\"true\" />\n {props.createLabel ?? 'New workspace'}\n </DropdownMenuItem>\n </Show>\n <Show when={canReorder()}>\n <DropdownMenuItem onSelect={() => setReordering(true)}>\n <ArrowDownUp aria-hidden=\"true\" />\n {props.reorderLabel ?? 'Reorder workspaces…'}\n </DropdownMenuItem>\n </Show>\n <Show when={props.onManage}>\n <DropdownMenuItem onSelect={() => props.onManage?.()}>\n <Settings aria-hidden=\"true\" />\n {props.manageLabel ?? 'Workspace settings…'}\n </DropdownMenuItem>\n </Show>\n </Show>\n </DropdownMenuContent>\n </DropdownMenu>\n <Show when={props.onReorder}>\n {(onReorder) => (\n <WorkspaceReorderDialog\n open={reordering()}\n onOpenChange={setReordering}\n workspaces={props.workspaces}\n onReorder={onReorder()}\n restoreFocusRef={() => trigger}\n />\n )}\n </Show>\n </>\n )\n}\n"],"mappings":"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"}
|
|
@@ -34,8 +34,16 @@ import { WorkspaceReorderDialog } from './workspace-reorder-dialog'
|
|
|
34
34
|
export type WorkspaceSwitcherItem = {
|
|
35
35
|
id: string
|
|
36
36
|
name: string
|
|
37
|
-
/**
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The workspace's mark, drawn in the trigger and in its row. Decorative.
|
|
39
|
+
*
|
|
40
|
+
* A DOM node can sit in one place, so the current workspace's mark would have
|
|
41
|
+
* to be in the trigger and its menu row at once. Pass a function to draw a
|
|
42
|
+
* fresh mark in each place (the preferred form). A node built once is also
|
|
43
|
+
* safe: the trigger keeps it and the current row gets a copy, which carries
|
|
44
|
+
* no event handlers or reactive bindings, so keep marks static.
|
|
45
|
+
*/
|
|
46
|
+
mark: JSX.Element | (() => JSX.Element)
|
|
39
47
|
/** A quiet trailing readout in the row, e.g. an unread count. Optional. */
|
|
40
48
|
detail?: JSX.Element
|
|
41
49
|
/** Pinned to the top of the order, e.g. Home: it cannot be moved and nothing moves above it. */
|
|
@@ -68,6 +76,15 @@ export type WorkspaceSwitcherProps = {
|
|
|
68
76
|
class?: string
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Draws one placement of a mark. A function draws anew each time; a node that is
|
|
81
|
+
* already placed elsewhere is copied so the original stays where it is.
|
|
82
|
+
*/
|
|
83
|
+
function drawMark(mark: WorkspaceSwitcherItem['mark'], copy: boolean): JSX.Element {
|
|
84
|
+
if (typeof mark === 'function') return mark()
|
|
85
|
+
return copy && mark instanceof Node ? mark.cloneNode(true) : mark
|
|
86
|
+
}
|
|
87
|
+
|
|
71
88
|
export function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {
|
|
72
89
|
const [open, setOpen] = createSignal(false)
|
|
73
90
|
const [reordering, setReordering] = createSignal(false)
|
|
@@ -92,7 +109,7 @@ export function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {
|
|
|
92
109
|
class={cn('max-w-full min-w-0 shrink', props.class)}
|
|
93
110
|
>
|
|
94
111
|
<span aria-hidden="true" class="flex shrink-0">
|
|
95
|
-
{current()
|
|
112
|
+
{current() && drawMark(current()!.mark, false)}
|
|
96
113
|
</span>
|
|
97
114
|
<span class="min-w-0 max-w-40 truncate">
|
|
98
115
|
{current()?.name ?? props.placeholder ?? 'Choose workspace'}
|
|
@@ -114,7 +131,7 @@ export function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {
|
|
|
114
131
|
{(item) => (
|
|
115
132
|
<DropdownMenuRadioItem value={item.id} closeOnSelect data-workspace-id={item.id}>
|
|
116
133
|
<span aria-hidden="true" class="flex shrink-0">
|
|
117
|
-
{item.mark}
|
|
134
|
+
{drawMark(item.mark, item.id === props.value)}
|
|
118
135
|
</span>
|
|
119
136
|
<span class="min-w-0 flex-1 truncate">{item.name}</span>
|
|
120
137
|
<Show when={item.detail}>{item.detail}</Show>
|
package/package.json
CHANGED
|
@@ -34,8 +34,16 @@ import { WorkspaceReorderDialog } from './workspace-reorder-dialog'
|
|
|
34
34
|
export type WorkspaceSwitcherItem = {
|
|
35
35
|
id: string
|
|
36
36
|
name: string
|
|
37
|
-
/**
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* The workspace's mark, drawn in the trigger and in its row. Decorative.
|
|
39
|
+
*
|
|
40
|
+
* A DOM node can sit in one place, so the current workspace's mark would have
|
|
41
|
+
* to be in the trigger and its menu row at once. Pass a function to draw a
|
|
42
|
+
* fresh mark in each place (the preferred form). A node built once is also
|
|
43
|
+
* safe: the trigger keeps it and the current row gets a copy, which carries
|
|
44
|
+
* no event handlers or reactive bindings, so keep marks static.
|
|
45
|
+
*/
|
|
46
|
+
mark: JSX.Element | (() => JSX.Element)
|
|
39
47
|
/** A quiet trailing readout in the row, e.g. an unread count. Optional. */
|
|
40
48
|
detail?: JSX.Element
|
|
41
49
|
/** Pinned to the top of the order, e.g. Home: it cannot be moved and nothing moves above it. */
|
|
@@ -68,6 +76,15 @@ export type WorkspaceSwitcherProps = {
|
|
|
68
76
|
class?: string
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Draws one placement of a mark. A function draws anew each time; a node that is
|
|
81
|
+
* already placed elsewhere is copied so the original stays where it is.
|
|
82
|
+
*/
|
|
83
|
+
function drawMark(mark: WorkspaceSwitcherItem['mark'], copy: boolean): JSX.Element {
|
|
84
|
+
if (typeof mark === 'function') return mark()
|
|
85
|
+
return copy && mark instanceof Node ? mark.cloneNode(true) : mark
|
|
86
|
+
}
|
|
87
|
+
|
|
71
88
|
export function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {
|
|
72
89
|
const [open, setOpen] = createSignal(false)
|
|
73
90
|
const [reordering, setReordering] = createSignal(false)
|
|
@@ -92,7 +109,7 @@ export function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {
|
|
|
92
109
|
class={cn('max-w-full min-w-0 shrink', props.class)}
|
|
93
110
|
>
|
|
94
111
|
<span aria-hidden="true" class="flex shrink-0">
|
|
95
|
-
{current()
|
|
112
|
+
{current() && drawMark(current()!.mark, false)}
|
|
96
113
|
</span>
|
|
97
114
|
<span class="min-w-0 max-w-40 truncate">
|
|
98
115
|
{current()?.name ?? props.placeholder ?? 'Choose workspace'}
|
|
@@ -114,7 +131,7 @@ export function WorkspaceSwitcher(props: WorkspaceSwitcherProps) {
|
|
|
114
131
|
{(item) => (
|
|
115
132
|
<DropdownMenuRadioItem value={item.id} closeOnSelect data-workspace-id={item.id}>
|
|
116
133
|
<span aria-hidden="true" class="flex shrink-0">
|
|
117
|
-
{item.mark}
|
|
134
|
+
{drawMark(item.mark, item.id === props.value)}
|
|
118
135
|
</span>
|
|
119
136
|
<span class="min-w-0 flex-1 truncate">{item.name}</span>
|
|
120
137
|
<Show when={item.detail}>{item.detail}</Show>
|