@k2b/ui 0.43.0 → 0.44.0
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.
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createMemo, getOwner, runWithOwner } from "solid-js";
|
|
2
|
+
|
|
1
3
|
/** A status icon at a row's end, such as "running". `label` names it for screen readers and as its tooltip. */
|
|
2
4
|
|
|
3
5
|
/** Serializable presentation. Functions stay with the controller's owning island. */
|
|
@@ -13,13 +15,21 @@ export function findNavigationItem(items, id) {
|
|
|
13
15
|
|
|
14
16
|
/** One live source for renderers; no router, registry, fetching or persisted state. */
|
|
15
17
|
export function createNavigation(options) {
|
|
18
|
+
const owner = getOwner();
|
|
19
|
+
let current;
|
|
20
|
+
// One tree per change, however many rows read it, and none for a menu that never opens: rebuilding a mailbox's
|
|
21
|
+
// 350 folders for each read took eight seconds to open the phone menu.
|
|
22
|
+
const items = () => {
|
|
23
|
+
current ??= owner ? runWithOwner(owner, () => createMemo(options.items)) ?? options.items : options.items;
|
|
24
|
+
return current();
|
|
25
|
+
};
|
|
16
26
|
return {
|
|
17
|
-
items
|
|
27
|
+
items,
|
|
18
28
|
switcher: options.switcher,
|
|
19
29
|
onNavigate: options.onNavigate,
|
|
20
30
|
onExpandedChange: options.onExpandedChange,
|
|
21
31
|
async activate(id) {
|
|
22
|
-
const item = findNavigationItem(
|
|
32
|
+
const item = findNavigationItem(items(), id);
|
|
23
33
|
if (item?.action) await options.onAction?.(item.action);
|
|
24
34
|
}
|
|
25
35
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["layout/navigation-model.ts"],"sourcesContent":["import type { LinkNavigateEvent, NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport type
|
|
1
|
+
{"version":3,"names":[],"sources":["layout/navigation-model.ts"],"sourcesContent":["import type { LinkNavigateEvent, NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport { type Accessor, createMemo, getOwner, runWithOwner } from \"solid-js\";\nimport type { IntentTone } from \"../semantics\";\nimport type { AppWorkspaceSidebarSwitcherProps } from \"./SidebarSwitcher\";\n\n/** A status icon at a row's end, such as \"running\". `label` names it for screen readers and as its tooltip. */\nexport type NavigationStatus = { icon: string; label: string; tone?: IntentTone };\n\ntype NavigationEntry = {\n id: string;\n label: string;\n icon?: string;\n /** Names a meaningful icon for screen readers, such as \"Homepage\"; without it the icon is decorative. */\n iconLabel?: string;\n badge?: string | number;\n description?: string;\n /**\n * Status icon in a fixed-size slot at the row's end. Set `null` on a row whose status comes and goes: the empty slot\n * keeps its place, so a status change never rewraps the label or moves the rows below.\n */\n status?: NavigationStatus | null;\n active?: boolean;\n /** Current container, independently of its active destination. `false` quiets this row; omitted keeps the default ink. */\n context?: boolean;\n disabled?: boolean;\n color?: string;\n children?: readonly NavigationItem[];\n /** Initial disclosure state; subsequent toggles remain local to the renderer. */\n defaultExpanded?: boolean;\n /** Owner-held disclosure state. The renderer shows it and reports toggles through `onExpandedChange`. */\n expanded?: boolean;\n actions?: readonly NavigationItem[];\n /**\n * Square icon buttons in the row, before the `actions` menu. They run an `action`; a destination belongs in a row,\n * where its link keeps modified clicks and new tabs.\n */\n inlineActions?: readonly { id: string; label: string; icon: string; action: string; disabled?: boolean }[];\n};\n\n/** Serializable presentation. Functions stay with the controller's owning island. */\nexport type NavigationItem = NavigationEntry &\n (\n | {\n href: string;\n action?: string;\n navigation?: \"document\" | \"enhanced\";\n scroll?: NavigationScrollMode;\n section?: never;\n keepOpen?: never;\n }\n | {\n action: string;\n href?: never;\n navigation?: never;\n scroll?: never;\n section?: never;\n /**\n * Runs the action while the host's menu stays open, without `beforeSelect`. Only for an action that changes\n * what the menu shows, such as a filter or a sort order; an action that opens something lets the menu close.\n */\n keepOpen?: boolean;\n }\n | {\n href?: never;\n action?: never;\n navigation?: never;\n scroll?: never;\n keepOpen?: never;\n children: readonly NavigationItem[];\n /** Shows the group as a plain heading over its rows, always open and not indented, instead of a disclosure. */\n section?: boolean;\n }\n );\n\nexport type NavigationOptions = {\n items: Accessor<readonly NavigationItem[]>;\n /** The container the navigation works in, shown above the items with the same switcher as the sidebar's head. */\n switcher?: Accessor<AppWorkspaceSidebarSwitcherProps>;\n onAction?: (action: string) => void | Promise<void>;\n onNavigate?: (event: LinkNavigateEvent) => void | Promise<void>;\n /** Receives disclosure toggles of items that carry `expanded`; the owner decides the next state. */\n onExpandedChange?: (id: string, expanded: boolean) => void;\n};\n\nexport function findNavigationItem(items: readonly NavigationItem[], id: string): NavigationItem | undefined {\n for (const item of items) {\n if (item.disabled) continue;\n if (item.id === id) return item;\n const found =\n findNavigationItem(item.children ?? [], id) ??\n findNavigationItem(item.inlineActions ?? [], id) ??\n findNavigationItem(item.actions ?? [], id);\n if (found) return found;\n }\n}\n\n/** One live source for renderers; no router, registry, fetching or persisted state. */\nexport function createNavigation(options: NavigationOptions) {\n const owner = getOwner();\n let current: Accessor<readonly NavigationItem[]> | undefined;\n // One tree per change, however many rows read it, and none for a menu that never opens: rebuilding a mailbox's\n // 350 folders for each read took eight seconds to open the phone menu.\n const items: Accessor<readonly NavigationItem[]> = () => {\n current ??= owner ? (runWithOwner(owner, () => createMemo(options.items)) ?? options.items) : options.items;\n return current();\n };\n return {\n items,\n switcher: options.switcher,\n onNavigate: options.onNavigate,\n onExpandedChange: options.onExpandedChange,\n async activate(id: string) {\n const item = findNavigationItem(items(), id);\n if (item?.action) await options.onAction?.(item.action);\n },\n };\n}\n\nexport type NavigationController = ReturnType<typeof createNavigation>;\n"],"mappings":"AACA,SAAwB,UAAU,EAAE,QAAQ,EAAE,YAAY,QAAQ,UAAU;;AAI5E;;AAkCA;;AA6CA,OAAO,SAAS,kBAAkB,CAAC,KAAgC,EAAE,EAAU,EAA8B;EAC3G,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;IACxB,IAAI,IAAI,CAAC,QAAQ,EAAE;IACnB,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,IAAI;IAC/B,MAAM,KAAK,GACT,kBAAkB,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,EAAE,CAAC,IAC3C,kBAAkB,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,EAAE,EAAE,CAAC,IAChD,kBAAkB,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,EAAE,CAAC;IAC5C,IAAI,KAAK,EAAE,OAAO,KAAK;EACzB;AACF;;AAEA;AACA,OAAO,SAAS,gBAAgB,CAAC,OAA0B,EAAE;EAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC;EACxB,IAAI,OAAwD;EAC5D;EACA;EACA,MAAM,KAA0C,GAAG,MAAM;IACvD,OAAO,KAAK,KAAK,GAAI,YAAY,CAAC,KAAK,EAAE,MAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,GAAI,OAAO,CAAC,KAAK;IAC3G,OAAO,OAAO,CAAC,CAAC;EAClB,CAAC;EACD,OAAO;IACL,KAAK;IACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;IAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;IAC9B,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;IAC1C,MAAM,QAAQ,CAAC,EAAU,EAAE;MACzB,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;MAC5C,IAAI,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;IACzD;EACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createMemo, getOwner, runWithOwner } from "solid-js";
|
|
2
|
+
|
|
1
3
|
/** A status icon at a row's end, such as "running". `label` names it for screen readers and as its tooltip. */
|
|
2
4
|
|
|
3
5
|
/** Serializable presentation. Functions stay with the controller's owning island. */
|
|
@@ -13,13 +15,21 @@ export function findNavigationItem(items, id) {
|
|
|
13
15
|
|
|
14
16
|
/** One live source for renderers; no router, registry, fetching or persisted state. */
|
|
15
17
|
export function createNavigation(options) {
|
|
18
|
+
const owner = getOwner();
|
|
19
|
+
let current;
|
|
20
|
+
// One tree per change, however many rows read it, and none for a menu that never opens: rebuilding a mailbox's
|
|
21
|
+
// 350 folders for each read took eight seconds to open the phone menu.
|
|
22
|
+
const items = () => {
|
|
23
|
+
current ??= owner ? runWithOwner(owner, () => createMemo(options.items)) ?? options.items : options.items;
|
|
24
|
+
return current();
|
|
25
|
+
};
|
|
16
26
|
return {
|
|
17
|
-
items
|
|
27
|
+
items,
|
|
18
28
|
switcher: options.switcher,
|
|
19
29
|
onNavigate: options.onNavigate,
|
|
20
30
|
onExpandedChange: options.onExpandedChange,
|
|
21
31
|
async activate(id) {
|
|
22
|
-
const item = findNavigationItem(
|
|
32
|
+
const item = findNavigationItem(items(), id);
|
|
23
33
|
if (item?.action) await options.onAction?.(item.action);
|
|
24
34
|
}
|
|
25
35
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["layout/navigation-model.ts"],"sourcesContent":["import type { LinkNavigateEvent, NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport type
|
|
1
|
+
{"version":3,"names":[],"sources":["layout/navigation-model.ts"],"sourcesContent":["import type { LinkNavigateEvent, NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport { type Accessor, createMemo, getOwner, runWithOwner } from \"solid-js\";\nimport type { IntentTone } from \"../semantics\";\nimport type { AppWorkspaceSidebarSwitcherProps } from \"./SidebarSwitcher\";\n\n/** A status icon at a row's end, such as \"running\". `label` names it for screen readers and as its tooltip. */\nexport type NavigationStatus = { icon: string; label: string; tone?: IntentTone };\n\ntype NavigationEntry = {\n id: string;\n label: string;\n icon?: string;\n /** Names a meaningful icon for screen readers, such as \"Homepage\"; without it the icon is decorative. */\n iconLabel?: string;\n badge?: string | number;\n description?: string;\n /**\n * Status icon in a fixed-size slot at the row's end. Set `null` on a row whose status comes and goes: the empty slot\n * keeps its place, so a status change never rewraps the label or moves the rows below.\n */\n status?: NavigationStatus | null;\n active?: boolean;\n /** Current container, independently of its active destination. `false` quiets this row; omitted keeps the default ink. */\n context?: boolean;\n disabled?: boolean;\n color?: string;\n children?: readonly NavigationItem[];\n /** Initial disclosure state; subsequent toggles remain local to the renderer. */\n defaultExpanded?: boolean;\n /** Owner-held disclosure state. The renderer shows it and reports toggles through `onExpandedChange`. */\n expanded?: boolean;\n actions?: readonly NavigationItem[];\n /**\n * Square icon buttons in the row, before the `actions` menu. They run an `action`; a destination belongs in a row,\n * where its link keeps modified clicks and new tabs.\n */\n inlineActions?: readonly { id: string; label: string; icon: string; action: string; disabled?: boolean }[];\n};\n\n/** Serializable presentation. Functions stay with the controller's owning island. */\nexport type NavigationItem = NavigationEntry &\n (\n | {\n href: string;\n action?: string;\n navigation?: \"document\" | \"enhanced\";\n scroll?: NavigationScrollMode;\n section?: never;\n keepOpen?: never;\n }\n | {\n action: string;\n href?: never;\n navigation?: never;\n scroll?: never;\n section?: never;\n /**\n * Runs the action while the host's menu stays open, without `beforeSelect`. Only for an action that changes\n * what the menu shows, such as a filter or a sort order; an action that opens something lets the menu close.\n */\n keepOpen?: boolean;\n }\n | {\n href?: never;\n action?: never;\n navigation?: never;\n scroll?: never;\n keepOpen?: never;\n children: readonly NavigationItem[];\n /** Shows the group as a plain heading over its rows, always open and not indented, instead of a disclosure. */\n section?: boolean;\n }\n );\n\nexport type NavigationOptions = {\n items: Accessor<readonly NavigationItem[]>;\n /** The container the navigation works in, shown above the items with the same switcher as the sidebar's head. */\n switcher?: Accessor<AppWorkspaceSidebarSwitcherProps>;\n onAction?: (action: string) => void | Promise<void>;\n onNavigate?: (event: LinkNavigateEvent) => void | Promise<void>;\n /** Receives disclosure toggles of items that carry `expanded`; the owner decides the next state. */\n onExpandedChange?: (id: string, expanded: boolean) => void;\n};\n\nexport function findNavigationItem(items: readonly NavigationItem[], id: string): NavigationItem | undefined {\n for (const item of items) {\n if (item.disabled) continue;\n if (item.id === id) return item;\n const found =\n findNavigationItem(item.children ?? [], id) ??\n findNavigationItem(item.inlineActions ?? [], id) ??\n findNavigationItem(item.actions ?? [], id);\n if (found) return found;\n }\n}\n\n/** One live source for renderers; no router, registry, fetching or persisted state. */\nexport function createNavigation(options: NavigationOptions) {\n const owner = getOwner();\n let current: Accessor<readonly NavigationItem[]> | undefined;\n // One tree per change, however many rows read it, and none for a menu that never opens: rebuilding a mailbox's\n // 350 folders for each read took eight seconds to open the phone menu.\n const items: Accessor<readonly NavigationItem[]> = () => {\n current ??= owner ? (runWithOwner(owner, () => createMemo(options.items)) ?? options.items) : options.items;\n return current();\n };\n return {\n items,\n switcher: options.switcher,\n onNavigate: options.onNavigate,\n onExpandedChange: options.onExpandedChange,\n async activate(id: string) {\n const item = findNavigationItem(items(), id);\n if (item?.action) await options.onAction?.(item.action);\n },\n };\n}\n\nexport type NavigationController = ReturnType<typeof createNavigation>;\n"],"mappings":"AACA,SAAwB,UAAU,EAAE,QAAQ,EAAE,YAAY,QAAQ,UAAU;;AAI5E;;AAkCA;;AA6CA,OAAO,SAAS,kBAAkB,CAAC,KAAgC,EAAE,EAAU,EAA8B;EAC3G,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;IACxB,IAAI,IAAI,CAAC,QAAQ,EAAE;IACnB,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,IAAI;IAC/B,MAAM,KAAK,GACT,kBAAkB,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,EAAE,CAAC,IAC3C,kBAAkB,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,EAAE,EAAE,CAAC,IAChD,kBAAkB,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,EAAE,CAAC;IAC5C,IAAI,KAAK,EAAE,OAAO,KAAK;EACzB;AACF;;AAEA;AACA,OAAO,SAAS,gBAAgB,CAAC,OAA0B,EAAE;EAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC;EACxB,IAAI,OAAwD;EAC5D;EACA;EACA,MAAM,KAA0C,GAAG,MAAM;IACvD,OAAO,KAAK,KAAK,GAAI,YAAY,CAAC,KAAK,EAAE,MAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,GAAI,OAAO,CAAC,KAAK;IAC3G,OAAO,OAAO,CAAC,CAAC;EAClB,CAAC;EACD,OAAO;IACL,KAAK;IACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;IAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;IAC9B,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;IAC1C,MAAM,QAAQ,CAAC,EAAU,EAAE;MACzB,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;MAC5C,IAAI,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;IACzD;EACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LinkNavigateEvent, NavigationScrollMode } from "@k2b/ssr/nav";
|
|
2
|
-
import type
|
|
2
|
+
import { type Accessor } from "solid-js";
|
|
3
3
|
import type { IntentTone } from "../semantics";
|
|
4
4
|
import type { AppWorkspaceSidebarSwitcherProps } from "./SidebarSwitcher";
|
|
5
5
|
/** A status icon at a row's end, such as "running". `label` names it for screen readers and as its tooltip. */
|