@kerfjs/ui 5.0.0-beta.16 → 5.0.0-beta.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/ai/public-api-signatures-v1.md +20 -2
- package/ai/skill.md +2 -2
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/catalog.d.ts +2 -0
- package/dist/catalog.js +6 -2
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-LCOBDOCC.js → chunk-6NLRBSVX.js} +32 -3
- package/dist/chunk-6NLRBSVX.js.map +1 -0
- package/dist/{chunk-BLK3UEEI.js → chunk-WRTAQ7BZ.js} +2 -2
- package/dist/{chunk-BLK3UEEI.js.map → chunk-WRTAQ7BZ.js.map} +1 -1
- package/dist/chunk-WXMGNX2Y.js +38 -0
- package/dist/chunk-WXMGNX2Y.js.map +1 -0
- package/dist/{chunk-XE5ZLPCC.js → chunk-ZQQ6P4SV.js} +2 -2
- package/dist/{chunk-XE5ZLPCC.js.map → chunk-ZQQ6P4SV.js.map} +1 -1
- package/dist/index.js +3 -3
- package/dist/nav-stack.js +1 -1
- package/dist/panel-header.js +1 -1
- package/dist/split-view.js +1 -1
- package/dist/styles/catalog.css +22 -1
- package/dist/styles/toolbar-text.css +54 -2
- package/dist/styles/webawesome.css +131 -0
- package/dist/toolbar-text.d.ts +17 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/docs/catalog.md +4 -3
- package/docs/component-selection.md +5 -2
- package/docs/ux-demo.md +2 -2
- package/docs/webawesome-theme.md +65 -0
- package/llms.txt +16 -1
- package/package.json +1 -1
- package/dist/chunk-LCOBDOCC.js.map +0 -1
- package/dist/chunk-RUNWEPR4.js +0 -10
- package/dist/chunk-RUNWEPR4.js.map +0 -1
package/README.md
CHANGED
|
@@ -330,8 +330,9 @@ after browser input to recover text plus token offsets, and
|
|
|
330
330
|
Call `wireTokenSearchFields()` once at a stable root to make Enter submit through
|
|
331
331
|
`onSubmit` without inserting a contenteditable line break and to preserve focus
|
|
332
332
|
plus the text-relative caret when keyboard deletion of a chip causes controlled
|
|
333
|
-
rendering to replace the editor.
|
|
334
|
-
|
|
333
|
+
rendering to replace the editor. A select-all deletion is normalized to a truly
|
|
334
|
+
empty editor even when the browser leaves an atomic chip or line break behind.
|
|
335
|
+
Text still wraps visually when it reaches the field edge.
|
|
335
336
|
Clear actions should empty the editor's `textContent` before clearing app state.
|
|
336
337
|
The leading icon, first text line, clear action, and trailing slot share one
|
|
337
338
|
fixed alignment row; when text wraps, those controls stay pinned to that first
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Public API signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.17` and `kerfjs@5.0.0-beta.17`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
|
|
4
4
|
|
|
5
5
|
## `@kerfjs/ui/disclosure-arrow`
|
|
6
6
|
|
|
@@ -61,8 +61,24 @@ interface ToolbarTextProps {
|
|
|
61
61
|
headingLevel?: HeadingLevel;
|
|
62
62
|
/** Render the text as an unanimated loading skeleton instead of its value. */
|
|
63
63
|
placeholder?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Wrap onto multiple lines when the text does not fit, instead of the default
|
|
66
|
+
* single line. Combine with `maxLines` to cap the number of lines. Default false.
|
|
67
|
+
*/
|
|
68
|
+
wrap?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Show a trailing ellipsis (…) where the text is truncated — on the single line
|
|
71
|
+
* (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip
|
|
72
|
+
* instead. Default true.
|
|
73
|
+
*/
|
|
74
|
+
ellipsis?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Cap wrapped text to this many lines, truncating past it. Only takes effect with
|
|
77
|
+
* `wrap`; ignored on a single line. `null`/omitted wraps without a line cap. Default null.
|
|
78
|
+
*/
|
|
79
|
+
maxLines?: number | null;
|
|
64
80
|
}
|
|
65
|
-
declare function ToolbarText({ text, size, className, id, headingLevel, placeholder }: ToolbarTextProps): kerfjs.SafeHtml;
|
|
81
|
+
declare function ToolbarText({ text, size, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, }: ToolbarTextProps): kerfjs.SafeHtml;
|
|
66
82
|
|
|
67
83
|
export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
|
|
68
84
|
```
|
|
@@ -934,6 +950,8 @@ interface CatalogEntry {
|
|
|
934
950
|
id: string;
|
|
935
951
|
name: string;
|
|
936
952
|
description?: string;
|
|
953
|
+
/** Short metadata tags shown at the trailing edge of the sidebar row. */
|
|
954
|
+
tags?: readonly string[];
|
|
937
955
|
resources?: readonly CatalogResource[];
|
|
938
956
|
related?: readonly CatalogRelated[];
|
|
939
957
|
}
|
package/ai/skill.md
CHANGED
|
@@ -59,8 +59,8 @@ Hard rules:
|
|
|
59
59
|
10. Compose `AppTab` inside controlled `TabBar`; call `wireTabBars()` once and retain its disposer. It owns same-bar drag mechanics, including proximity-based horizontal edge autoscroll, while the app applies `onReorder` and owns order, selection, panels, close policy, routing, and persistence. Arrow/Home/End select on move by default; pass `activation: 'manual'` (or `TabBar activation="manual"` per strip) so arrow keys move focus only and the user selects with Enter/Space/click — use it when selecting a tab is a heavy action. Use runtime-filtered `rootAttributes` for domain metadata and keep an optional `closeIcon` decorative and noninteractive.
|
|
60
60
|
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded, and `wireTokenSearchFields()` manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse). Bind the field's `expanded` to the returned handle's `expanded(id)` signal or adopt your own via `collapsible.signals`; opt a behavior out only when the app must own it. Do not hand-roll the open handler, the focusout collapse, or a clear-button mousedown guard — the helper owns them. For real-app adoption it also provides a focusout keep-open exception (`data-token-search-keep-open` regions or `collapsible.keepOpenOn(target)`, so a sibling suggestions/date/help surface does not collapse an empty field), opt-in atomic-chip keyboard (`keyboard`: Backspace/Delete remove the adjacent chip via `onRemoveToken`, ArrowRight moves the caret past a trailing chip), and an `onEdit({id, editor, event})` input callback (the `InputEvent` lets you gate on `inputType`/`data`) — reach for these instead of re-adding hand-rolled keydown/input handlers around the field.
|
|
61
61
|
12. Demo work uses public production component subpaths and their browser-selected CSS. Give every public visual component its own category-grouped catalog route; list themed third-party components under a clearly labeled collapsible ecosystem section, with a focused route for each. Project its deterministic repository-relative demo source and existing documentation path so the detail can expose `View demo source` and `Read guidance` links without a runtime export; also derive first-party component implementation paths from their canonical browser imports, and label Web Awesome documentation as Kerf integration guidance. Declare direct `uses` relationships so `Used by` stays derivable, and theme shell chrome through the same semantic tokens as the stage instead of drawing a substitute.
|
|
62
|
-
13. The Web Awesome theme makes Tooltip and Popover arrowless by default. Keep that default unless a pointer materially clarifies the anchor; opt back in with `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or a popover's public `--arrow-size`, and use `without-arrow` when local no-arrow intent should survive theme changes.
|
|
63
|
-
14. Treat the complete Web Awesome catalog as support coverage, not a recommendation list. Consider Popup when it replaces custom anchored positioning. Prefer Kerf `Select` over direct Dropdown/Dropdown Item/Select/Option composition, `SegmentedControl` over Button Group, `TabBar` or `SegmentedControl` over Web Awesome Tabs, `LucideIcon` over Web Awesome Icon, and `ResizableRegion` over Split Panel. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
|
|
62
|
+
13. The Web Awesome theme makes Tooltip and Popover arrowless by default. Keep that default unless a pointer materially clarifies the anchor; opt back in with `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or a popover's public `--arrow-size`, and use `without-arrow` when local no-arrow intent should survive theme changes. Its non-field chrome uses `--kui-wa-control-inset` (8px controls/items), `--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside Accordion, Card, Details, Callout, and Include), and `--kui-wa-container-inset` (16px Tab Panel content); override those tiers instead of restyling individual parts. Checkbox and Radio Group option regions, the Color Picker trigger, and Slider's complete interactive region receive the shared 8px logical inline outer inset because those controls have no bordered field shell. Known Date field captions and bordered text-like field hints align with values at the 9px border-plus-padding inset. OTP Input's label uses the same uppercase xs/650 treatment as other field labels, and both its label and hint use that 9px inset.
|
|
63
|
+
14. Treat the complete Web Awesome catalog as support coverage, not a recommendation list. Its UX sidebar marks superseded and exceptional entries `Discouraged`. Consider Popup when it replaces custom anchored positioning. Prefer Kerf `Select` over direct Dropdown/Dropdown Item/Select/Option composition, `SegmentedControl` over Button Group, `TabBar` or `SegmentedControl` over Web Awesome Tabs, `LucideIcon` over Web Awesome Icon, and `ResizableRegion` over Split Panel. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
|
|
64
64
|
15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Add `.kui-content` for 24px major vertical separation and `.kui-content-item` for a child-owned 8px inline margin, 1px transparent-or-visible border, 8px padding, and 12px radius. Use the pill modifier for 22px. Do not pad pane shells or duplicate item geometry in wrappers.
|
|
65
65
|
16. Keep a visible collapsible pane's collapse control in its own toolbar. When hidden, move its restore control into the adjacent main toolbar on the corresponding logical edge: leading for an inline-start sidebar and trailing for an inline-end inspector. Collapse the pane completely; do not preserve an empty icon-only rail.
|
|
66
66
|
16. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a `ToolbarControlGroup`. For an ordinary icon/action control inside a group, use a plain `<button>` (the group styles `> button` fully) — that is the default; reach for `wa-button` only when you need a Web Awesome feature, chiefly a `wa-dropdown` popup trigger. A popup menu in a toolbar is a `single` `ToolbarControlGroup` wrapping a `wa-dropdown` whose `slot="trigger"` `wa-button` is the toolbar button and whose `wa-dropdown-item`s are the menu, with the dropdown kept under `data-morph-skip-children`. `PanelHeader` composes these for you as a panel/dialog/page heading. Wrap every toolbar item, including dormant text, in `ToolbarControlGroup`. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `ListHeader` fills the available inline width and keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end 44px action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; never shrink the target to match it. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `ListItem.trailing` is dormant; use `ListActionRow` when primary and trailing actions need sibling 44px native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web Awesome JSX signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from the emitted `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from the emitted `@kerfjs/ui@5.0.0-beta.17` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { KerfCustomElement } from 'kerfjs/jsx-runtime';
|
package/dist/catalog.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ interface CatalogEntry {
|
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
20
|
description?: string;
|
|
21
|
+
/** Short metadata tags shown at the trailing edge of the sidebar row. */
|
|
22
|
+
tags?: readonly string[];
|
|
21
23
|
resources?: readonly CatalogResource[];
|
|
22
24
|
related?: readonly CatalogRelated[];
|
|
23
25
|
}
|
package/dist/catalog.js
CHANGED
|
@@ -15,6 +15,10 @@ function findEntry(sections, id) {
|
|
|
15
15
|
}
|
|
16
16
|
return void 0;
|
|
17
17
|
}
|
|
18
|
+
function catalogEntryTags(tags) {
|
|
19
|
+
if (!tags?.length) return void 0;
|
|
20
|
+
return /* @__PURE__ */ jsx("span", { class: "kui-catalog__tags", children: tags.map((tag) => /* @__PURE__ */ jsx("span", { class: "kui-catalog__tag", children: tag })) });
|
|
21
|
+
}
|
|
18
22
|
function relatedMenuItems(related, selectAction) {
|
|
19
23
|
const groups = [];
|
|
20
24
|
for (const entry of related) if (!groups.includes(entry.group)) groups.push(entry.group);
|
|
@@ -70,13 +74,13 @@ function Catalog({
|
|
|
70
74
|
/* @__PURE__ */ jsxs("nav", { class: "kui-pane__content kui-content", "aria-label": `${brand.title} components`, children: [
|
|
71
75
|
sections.map((section) => /* @__PURE__ */ jsxs("section", { class: "kui-catalog__group", children: [
|
|
72
76
|
/* @__PURE__ */ jsx(ListHeader, { label: section.category }),
|
|
73
|
-
/* @__PURE__ */ jsx("div", { class: "kui-catalog__items", children: section.entries.map((entry) => /* @__PURE__ */ jsx(ListItem, { action: selectAction, itemId: entry.id, label: entry.name, selected: active === entry.id, title: entry.description, multiline: true })) })
|
|
77
|
+
/* @__PURE__ */ jsx("div", { class: "kui-catalog__items", children: section.entries.map((entry) => /* @__PURE__ */ jsx(ListItem, { action: selectAction, itemId: entry.id, label: entry.name, trailing: catalogEntryTags(entry.tags), selected: active === entry.id, title: entry.description, multiline: true })) })
|
|
74
78
|
] })),
|
|
75
79
|
secondarySections ? /* @__PURE__ */ jsxs("section", { class: "kui-catalog__group kui-catalog__group--secondary", children: [
|
|
76
80
|
/* @__PURE__ */ jsx(ListHeader, { label: secondarySections.label, toggle: secondarySections.collapsible, expanded: secondarySections.collapsible ? Boolean(secondarySections.expanded) : void 0, action: secondarySections.collapsible ? toggleSecondaryAction : void 0 }),
|
|
77
81
|
!secondarySections.collapsible || secondarySections.expanded ? /* @__PURE__ */ jsx("div", { class: "kui-catalog__secondary", "data-catalog-secondary": true, children: secondarySections.sections.map((section) => /* @__PURE__ */ jsxs("section", { class: "kui-catalog__secondary-group", children: [
|
|
78
82
|
/* @__PURE__ */ jsx("h3", { class: "kui-catalog__secondary-heading", children: section.category }),
|
|
79
|
-
/* @__PURE__ */ jsx("div", { class: "kui-catalog__items", children: section.entries.map((entry) => /* @__PURE__ */ jsx(ListItem, { action: selectAction, itemId: entry.id, label: entry.name, selected: active === entry.id, title: entry.description, multiline: true })) })
|
|
83
|
+
/* @__PURE__ */ jsx("div", { class: "kui-catalog__items", children: section.entries.map((entry) => /* @__PURE__ */ jsx(ListItem, { action: selectAction, itemId: entry.id, label: entry.name, trailing: catalogEntryTags(entry.tags), selected: active === entry.id, title: entry.description, multiline: true })) })
|
|
80
84
|
] })) }) : /* @__PURE__ */ jsx(Fragment, {})
|
|
81
85
|
] }) : /* @__PURE__ */ jsx(Fragment, {}),
|
|
82
86
|
sidebarFooter
|
package/dist/catalog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalog.tsx"],"names":[],"mappings":"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","file":"catalog.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\nimport { ExternalLink, Moon, PanelLeftClose, PanelLeftOpen, Sun, Waypoints } from 'lucide';\n\nimport { ListHeader } from './list-header.js';\nimport { ListItem } from './list-item.js';\nimport { LucideIcon } from './lucide-icon.js';\nimport { Toolbar } from './toolbar.js';\nimport { ToolbarControlGroup } from './toolbar-control-group.js';\n\n/** A reference link shown in the detail footer for the active entry. */\nexport interface CatalogResource {\n label: string;\n href: string;\n /** Optional monospace detail (e.g. a file path) shown after the label. */\n detail?: string;\n}\n\n/** A related entry offered in the detail footer's \"Related entries\" popup menu. */\nexport interface CatalogRelated {\n id: string;\n name: string;\n /** Group heading in the menu, e.g. \"Uses\" / \"Used by\". */\n group: string;\n}\n\nexport interface CatalogEntry {\n id: string;\n name: string;\n description?: string;\n resources?: readonly CatalogResource[];\n related?: readonly CatalogRelated[];\n}\n\nexport interface CatalogSection {\n category: string;\n entries: readonly CatalogEntry[];\n}\n\n/**\n * A secondary group of sections shown below the primary sidebar sections with a\n * quieter \"ecosystem\" treatment (e.g. third-party components). Optionally\n * collapsible — the app owns `expanded` and toggles it from `wireCatalog`'s\n * `onToggleSecondary`.\n */\nexport interface CatalogSecondaryGroup {\n label: string;\n sections: readonly CatalogSection[];\n /** When true, the group's label is a disclosure toggle controlling `expanded`. */\n collapsible?: boolean;\n /** Whether the group is expanded (controlled). Ignored unless `collapsible`. */\n expanded?: boolean;\n}\n\nexport interface CatalogBrand {\n title: string;\n subtitle?: string;\n /** Logo image URL (rendered decorative). Omit for a text-only brand. */\n logoUrl?: string;\n}\n\nexport interface CatalogProps {\n brand: CatalogBrand;\n sections: readonly CatalogSection[];\n /** The controlled active entry id — the app owns this signal. */\n active: string;\n /** The rendered preview for the active entry; the app computes it from `active`. */\n content: SafeHtml;\n /** Whether the sidebar is collapsed (controlled). */\n collapsed?: boolean;\n /** Current theme; when set, a theme toggle is shown that switches to the opposite. Omit to hide it. */\n theme?: 'light' | 'dark';\n /** Extra header controls placed before the theme toggle (each a `ToolbarControlGroup`). */\n headerActions?: SafeHtml;\n /** A secondary \"ecosystem\" group of sections below the primary category groups. */\n secondarySections?: CatalogSecondaryGroup;\n /** Extra sidebar content below the category groups (and the secondary group). */\n sidebarFooter?: SafeHtml;\n /** Status line content shown at the start of the detail footer. */\n status?: SafeHtml;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n /** Action fired by the secondary group's disclosure toggle (when collapsible). */\n toggleSecondaryAction?: string;\n className?: string;\n}\n\nfunction findEntry(sections: readonly CatalogSection[], id: string): CatalogEntry | undefined {\n for (const section of sections) {\n for (const entry of section.entries) if (entry.id === id) return entry;\n }\n return undefined;\n}\n\n/**\n * Render the related entries as a `wa-dropdown` popup-menu body: a heading per\n * `group` (first-seen order) followed by that group's entries, each a navigable\n * item carrying the same `selectAction` the sidebar items use, so a chosen entry\n * routes through the one `wireCatalog` select handler.\n */\nfunction relatedMenuItems(related: readonly CatalogRelated[], selectAction: string): SafeHtml[] {\n const groups: string[] = [];\n for (const entry of related) if (!groups.includes(entry.group)) groups.push(entry.group);\n const nodes: SafeHtml[] = [];\n groups.forEach((group, index) => {\n if (index > 0) nodes.push(<wa-divider></wa-divider>);\n nodes.push(<small class=\"kui-catalog__related-heading\">{group}</small>);\n for (const entry of related) {\n if (entry.group === group) nodes.push(<wa-dropdown-item data-action={selectAction} data-item-id={entry.id}>{entry.name}</wa-dropdown-item>);\n }\n });\n return nodes;\n}\n\n/**\n * A reusable component-catalog shell: a collapsible category sidebar, a titled\n * detail stage that renders the active entry's preview, and a footer with\n * reference links and a related-entry popup menu. Built entirely from public\n * `@kerfjs/ui` primitives. Controlled and stateless — the app owns the `active`,\n * `collapsed`, and `theme` signals and computes `content` from `active` in its own\n * render; wire the sidebar/collapse/theme actions with `wireCatalog`.\n */\nexport function Catalog({\n brand,\n sections,\n active,\n content,\n collapsed = false,\n theme,\n headerActions,\n secondarySections,\n sidebarFooter,\n status,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n className = '',\n}: CatalogProps) {\n const selected = findEntry(sections, active) ?? (secondarySections ? findEntry(secondarySections.sections, active) : undefined);\n const name = selected?.name ?? '';\n const nextTheme = theme === 'dark' ? 'light' : 'dark';\n const resources = selected?.resources ?? [];\n const related = selected?.related ?? [];\n\n return <main class={`kui-catalog ${className}`.trim()} data-component=\"catalog\" data-sidebar-collapsed={String(collapsed)}>\n <aside class=\"kui-catalog__sidebar kui-pane\" aria-label={`${brand.title} catalog`}>\n <header class=\"kui-catalog__brand kui-pane__toolbar\">\n <Toolbar\n label={`${brand.title} catalog header`}\n divider={false}\n leading={<ToolbarControlGroup appearance=\"borderless\" className=\"kui-catalog__identity\">{brand.logoUrl ? <img class=\"kui-catalog__mark\" src={brand.logoUrl} alt=\"\" /> : <></>}<h1>{brand.title}</h1></ToolbarControlGroup>}\n trailing={<ToolbarControlGroup appearance=\"borderless\" single><button type=\"button\" data-action={toggleSidebarAction} aria-label={`Collapse ${brand.title} catalog`}><LucideIcon icon={PanelLeftClose} name=\"panel-left-close\" /></button></ToolbarControlGroup>}\n />\n {brand.subtitle ? <p class=\"kui-catalog__subtitle\">{brand.subtitle}</p> : <></>}\n </header>\n <nav class=\"kui-pane__content kui-content\" aria-label={`${brand.title} components`}>\n {sections.map((section) => <section class=\"kui-catalog__group\">\n <ListHeader label={section.category} />\n <div class=\"kui-catalog__items\">\n {section.entries.map((entry) => <ListItem action={selectAction} itemId={entry.id} label={entry.name} selected={active === entry.id} title={entry.description} multiline />)}\n </div>\n </section>)}\n {secondarySections ? <section class=\"kui-catalog__group kui-catalog__group--secondary\">\n <ListHeader label={secondarySections.label} toggle={secondarySections.collapsible} expanded={secondarySections.collapsible ? Boolean(secondarySections.expanded) : undefined} action={secondarySections.collapsible ? toggleSecondaryAction : undefined} />\n {!secondarySections.collapsible || secondarySections.expanded\n ? <div class=\"kui-catalog__secondary\" data-catalog-secondary>\n {secondarySections.sections.map((section) => <section class=\"kui-catalog__secondary-group\">\n <h3 class=\"kui-catalog__secondary-heading\">{section.category}</h3>\n <div class=\"kui-catalog__items\">\n {section.entries.map((entry) => <ListItem action={selectAction} itemId={entry.id} label={entry.name} selected={active === entry.id} title={entry.description} multiline />)}\n </div>\n </section>)}\n </div>\n : <></>}\n </section> : <></>}\n {sidebarFooter}\n </nav>\n </aside>\n <article class=\"kui-catalog__detail kui-pane\">\n <header class=\"kui-catalog__header kui-pane__toolbar\">\n <Toolbar\n label={`${name} header`}\n divider={false}\n leading={<>{collapsed ? <ToolbarControlGroup appearance=\"borderless\" single><button type=\"button\" data-action={toggleSidebarAction} aria-label={`Expand ${brand.title} catalog`}><LucideIcon icon={PanelLeftOpen} name=\"panel-left-open\" /></button></ToolbarControlGroup> : <></>}<ToolbarControlGroup appearance=\"borderless\" className=\"kui-catalog__title\"><h2>{name}</h2></ToolbarControlGroup></>}\n trailing={<div class=\"kui-catalog__header-actions\">{headerActions}{theme ? <ToolbarControlGroup className=\"kui-catalog__settings\" label=\"Catalog display\"><button type=\"button\" data-action={toggleThemeAction} aria-label={`Use ${nextTheme} theme`} data-theme-preview={theme}>{nextTheme === 'dark' ? <LucideIcon icon={Moon} name=\"moon\" /> : <LucideIcon icon={Sun} name=\"sun\" />}<span>{nextTheme === 'dark' ? 'Dark' : 'Light'}</span></button></ToolbarControlGroup> : <></>}</div>}\n />\n {selected?.description ? <p class=\"kui-catalog__description kui-content-item\">{selected.description}</p> : <></>}\n </header>\n <section class=\"kui-catalog__stage kui-pane__content\" aria-label={`${name} preview`}>\n <div class=\"kui-catalog__canvas\">{content}</div>\n </section>\n <footer class=\"kui-catalog__footer kui-pane__footer\">\n {status ? <div class=\"kui-catalog__status\">{status}</div> : <></>}\n <Toolbar\n label={`${name} resources`}\n divider={false}\n leading={resources.length > 0\n ? <nav class=\"kui-catalog__resources\" aria-label={`${name} resources`}><ToolbarControlGroup className=\"kui-catalog__resource-group\" label={`${name} resources`}>{resources.map((resource) => <a class=\"kui-catalog__resource\" href={resource.href} target=\"_blank\" rel=\"noopener noreferrer\" aria-label={`${name}: ${resource.label} (opens in new tab)`}><LucideIcon icon={ExternalLink} name=\"external-link\" /><span>{resource.label}</span>{resource.detail ? <code>{resource.detail}</code> : <></>}</a>)}</ToolbarControlGroup></nav>\n : <></>}\n trailing={related.length > 0\n ? <div class=\"kui-catalog__related\" data-catalog-related><ToolbarControlGroup single className=\"kui-catalog__related-group\" label=\"Related entries\"><wa-dropdown class=\"kui-catalog__related-menu\" placement=\"top-end\" data-key={`kui-catalog-related-${active}`} data-morph-skip-children><wa-button slot=\"trigger\" appearance=\"plain\" with-caret><span class=\"kui-catalog__related-trigger\"><LucideIcon icon={Waypoints} name=\"waypoints\" /><span class=\"kui-catalog__related-label\">Component</span></span></wa-button>{relatedMenuItems(related, selectAction)}</wa-dropdown></ToolbarControlGroup></div>\n : <></>}\n />\n </footer>\n </article>\n </main>;\n}\n\n/**\n * How a {@link CatalogExample}'s content aligns its visible left edge with the\n * example's `ListHeader` label (which sits 16px in — 8px title + 8px label):\n * - `'glyph'` — a bare glyph/text specimen with no inline geometry insets the full 16px.\n * - `'inline-control'` — a control that already carries ~8px of its own inline padding insets 8px so its content lands on the same line.\n * - `'none'` — a content-item / composition that owns its geometry and already aligns; no inset (default).\n */\nexport type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';\n\nexport interface CatalogExampleProps {\n /** The example's label, shown as a `ListHeader` above the specimen. Omit for a bare specimen. */\n label?: string;\n /** Optional explanatory note between the label and the specimen. */\n note?: SafeHtml | string;\n /** Alignment inset for the specimen — see {@link CatalogExampleAlign}. Default `'none'`. */\n align?: CatalogExampleAlign;\n className?: string;\n children?: SafeHtml | readonly SafeHtml[];\n}\n\n/**\n * One labeled example in a catalog preview: a `ListHeader` label, an optional\n * note, and the specimen. `align` insets the specimen so its visible left edge\n * lines up with the label text, encoding the catalog's alignment rules as a\n * first-class prop instead of per-demo CSS. The inset is published as the\n * `--kui-catalog-example-align` custom property so a debug overlay can exclude it\n * from a specimen's measured margin.\n */\nexport function CatalogExample({ label, note, align = 'none', className = '', children }: CatalogExampleProps) {\n return <section class={`kui-catalog-example ${className}`.trim()} data-catalog-example data-align={align}>\n {label !== undefined ? <ListHeader label={label} /> : <></>}\n {note !== undefined ? <p class=\"kui-catalog-example__note\">{note}</p> : <></>}\n {children}\n </section>;\n}\n\nexport interface CatalogExampleStackProps {\n /** Accessible label for the stack region. */\n label?: string;\n className?: string;\n children?: SafeHtml | readonly SafeHtml[];\n}\n\n/** A vertically-stacked group of {@link CatalogExample}s with the catalog's example rhythm. */\nexport function CatalogExampleStack({ label, className = '', children }: CatalogExampleStackProps) {\n return <div class={`kui-catalog-example-stack ${className}`.trim()} data-catalog-example-stack aria-label={label}>{children}</div>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/catalog.tsx"],"names":[],"mappings":"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","file":"catalog.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\nimport { ExternalLink, Moon, PanelLeftClose, PanelLeftOpen, Sun, Waypoints } from 'lucide';\n\nimport { ListHeader } from './list-header.js';\nimport { ListItem } from './list-item.js';\nimport { LucideIcon } from './lucide-icon.js';\nimport { Toolbar } from './toolbar.js';\nimport { ToolbarControlGroup } from './toolbar-control-group.js';\n\n/** A reference link shown in the detail footer for the active entry. */\nexport interface CatalogResource {\n label: string;\n href: string;\n /** Optional monospace detail (e.g. a file path) shown after the label. */\n detail?: string;\n}\n\n/** A related entry offered in the detail footer's \"Related entries\" popup menu. */\nexport interface CatalogRelated {\n id: string;\n name: string;\n /** Group heading in the menu, e.g. \"Uses\" / \"Used by\". */\n group: string;\n}\n\nexport interface CatalogEntry {\n id: string;\n name: string;\n description?: string;\n /** Short metadata tags shown at the trailing edge of the sidebar row. */\n tags?: readonly string[];\n resources?: readonly CatalogResource[];\n related?: readonly CatalogRelated[];\n}\n\nexport interface CatalogSection {\n category: string;\n entries: readonly CatalogEntry[];\n}\n\n/**\n * A secondary group of sections shown below the primary sidebar sections with a\n * quieter \"ecosystem\" treatment (e.g. third-party components). Optionally\n * collapsible — the app owns `expanded` and toggles it from `wireCatalog`'s\n * `onToggleSecondary`.\n */\nexport interface CatalogSecondaryGroup {\n label: string;\n sections: readonly CatalogSection[];\n /** When true, the group's label is a disclosure toggle controlling `expanded`. */\n collapsible?: boolean;\n /** Whether the group is expanded (controlled). Ignored unless `collapsible`. */\n expanded?: boolean;\n}\n\nexport interface CatalogBrand {\n title: string;\n subtitle?: string;\n /** Logo image URL (rendered decorative). Omit for a text-only brand. */\n logoUrl?: string;\n}\n\nexport interface CatalogProps {\n brand: CatalogBrand;\n sections: readonly CatalogSection[];\n /** The controlled active entry id — the app owns this signal. */\n active: string;\n /** The rendered preview for the active entry; the app computes it from `active`. */\n content: SafeHtml;\n /** Whether the sidebar is collapsed (controlled). */\n collapsed?: boolean;\n /** Current theme; when set, a theme toggle is shown that switches to the opposite. Omit to hide it. */\n theme?: 'light' | 'dark';\n /** Extra header controls placed before the theme toggle (each a `ToolbarControlGroup`). */\n headerActions?: SafeHtml;\n /** A secondary \"ecosystem\" group of sections below the primary category groups. */\n secondarySections?: CatalogSecondaryGroup;\n /** Extra sidebar content below the category groups (and the secondary group). */\n sidebarFooter?: SafeHtml;\n /** Status line content shown at the start of the detail footer. */\n status?: SafeHtml;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n /** Action fired by the secondary group's disclosure toggle (when collapsible). */\n toggleSecondaryAction?: string;\n className?: string;\n}\n\nfunction findEntry(sections: readonly CatalogSection[], id: string): CatalogEntry | undefined {\n for (const section of sections) {\n for (const entry of section.entries) if (entry.id === id) return entry;\n }\n return undefined;\n}\n\nfunction catalogEntryTags(tags: readonly string[] | undefined): SafeHtml | undefined {\n if (!tags?.length) return undefined;\n return <span class=\"kui-catalog__tags\">{tags.map((tag) => <span class=\"kui-catalog__tag\">{tag}</span>)}</span>;\n}\n\n/**\n * Render the related entries as a `wa-dropdown` popup-menu body: a heading per\n * `group` (first-seen order) followed by that group's entries, each a navigable\n * item carrying the same `selectAction` the sidebar items use, so a chosen entry\n * routes through the one `wireCatalog` select handler.\n */\nfunction relatedMenuItems(related: readonly CatalogRelated[], selectAction: string): SafeHtml[] {\n const groups: string[] = [];\n for (const entry of related) if (!groups.includes(entry.group)) groups.push(entry.group);\n const nodes: SafeHtml[] = [];\n groups.forEach((group, index) => {\n if (index > 0) nodes.push(<wa-divider></wa-divider>);\n nodes.push(<small class=\"kui-catalog__related-heading\">{group}</small>);\n for (const entry of related) {\n if (entry.group === group) nodes.push(<wa-dropdown-item data-action={selectAction} data-item-id={entry.id}>{entry.name}</wa-dropdown-item>);\n }\n });\n return nodes;\n}\n\n/**\n * A reusable component-catalog shell: a collapsible category sidebar, a titled\n * detail stage that renders the active entry's preview, and a footer with\n * reference links and a related-entry popup menu. Built entirely from public\n * `@kerfjs/ui` primitives. Controlled and stateless — the app owns the `active`,\n * `collapsed`, and `theme` signals and computes `content` from `active` in its own\n * render; wire the sidebar/collapse/theme actions with `wireCatalog`.\n */\nexport function Catalog({\n brand,\n sections,\n active,\n content,\n collapsed = false,\n theme,\n headerActions,\n secondarySections,\n sidebarFooter,\n status,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n className = '',\n}: CatalogProps) {\n const selected = findEntry(sections, active) ?? (secondarySections ? findEntry(secondarySections.sections, active) : undefined);\n const name = selected?.name ?? '';\n const nextTheme = theme === 'dark' ? 'light' : 'dark';\n const resources = selected?.resources ?? [];\n const related = selected?.related ?? [];\n\n return <main class={`kui-catalog ${className}`.trim()} data-component=\"catalog\" data-sidebar-collapsed={String(collapsed)}>\n <aside class=\"kui-catalog__sidebar kui-pane\" aria-label={`${brand.title} catalog`}>\n <header class=\"kui-catalog__brand kui-pane__toolbar\">\n <Toolbar\n label={`${brand.title} catalog header`}\n divider={false}\n leading={<ToolbarControlGroup appearance=\"borderless\" className=\"kui-catalog__identity\">{brand.logoUrl ? <img class=\"kui-catalog__mark\" src={brand.logoUrl} alt=\"\" /> : <></>}<h1>{brand.title}</h1></ToolbarControlGroup>}\n trailing={<ToolbarControlGroup appearance=\"borderless\" single><button type=\"button\" data-action={toggleSidebarAction} aria-label={`Collapse ${brand.title} catalog`}><LucideIcon icon={PanelLeftClose} name=\"panel-left-close\" /></button></ToolbarControlGroup>}\n />\n {brand.subtitle ? <p class=\"kui-catalog__subtitle\">{brand.subtitle}</p> : <></>}\n </header>\n <nav class=\"kui-pane__content kui-content\" aria-label={`${brand.title} components`}>\n {sections.map((section) => <section class=\"kui-catalog__group\">\n <ListHeader label={section.category} />\n <div class=\"kui-catalog__items\">\n {section.entries.map((entry) => <ListItem action={selectAction} itemId={entry.id} label={entry.name} trailing={catalogEntryTags(entry.tags)} selected={active === entry.id} title={entry.description} multiline />)}\n </div>\n </section>)}\n {secondarySections ? <section class=\"kui-catalog__group kui-catalog__group--secondary\">\n <ListHeader label={secondarySections.label} toggle={secondarySections.collapsible} expanded={secondarySections.collapsible ? Boolean(secondarySections.expanded) : undefined} action={secondarySections.collapsible ? toggleSecondaryAction : undefined} />\n {!secondarySections.collapsible || secondarySections.expanded\n ? <div class=\"kui-catalog__secondary\" data-catalog-secondary>\n {secondarySections.sections.map((section) => <section class=\"kui-catalog__secondary-group\">\n <h3 class=\"kui-catalog__secondary-heading\">{section.category}</h3>\n <div class=\"kui-catalog__items\">\n {section.entries.map((entry) => <ListItem action={selectAction} itemId={entry.id} label={entry.name} trailing={catalogEntryTags(entry.tags)} selected={active === entry.id} title={entry.description} multiline />)}\n </div>\n </section>)}\n </div>\n : <></>}\n </section> : <></>}\n {sidebarFooter}\n </nav>\n </aside>\n <article class=\"kui-catalog__detail kui-pane\">\n <header class=\"kui-catalog__header kui-pane__toolbar\">\n <Toolbar\n label={`${name} header`}\n divider={false}\n leading={<>{collapsed ? <ToolbarControlGroup appearance=\"borderless\" single><button type=\"button\" data-action={toggleSidebarAction} aria-label={`Expand ${brand.title} catalog`}><LucideIcon icon={PanelLeftOpen} name=\"panel-left-open\" /></button></ToolbarControlGroup> : <></>}<ToolbarControlGroup appearance=\"borderless\" className=\"kui-catalog__title\"><h2>{name}</h2></ToolbarControlGroup></>}\n trailing={<div class=\"kui-catalog__header-actions\">{headerActions}{theme ? <ToolbarControlGroup className=\"kui-catalog__settings\" label=\"Catalog display\"><button type=\"button\" data-action={toggleThemeAction} aria-label={`Use ${nextTheme} theme`} data-theme-preview={theme}>{nextTheme === 'dark' ? <LucideIcon icon={Moon} name=\"moon\" /> : <LucideIcon icon={Sun} name=\"sun\" />}<span>{nextTheme === 'dark' ? 'Dark' : 'Light'}</span></button></ToolbarControlGroup> : <></>}</div>}\n />\n {selected?.description ? <p class=\"kui-catalog__description kui-content-item\">{selected.description}</p> : <></>}\n </header>\n <section class=\"kui-catalog__stage kui-pane__content\" aria-label={`${name} preview`}>\n <div class=\"kui-catalog__canvas\">{content}</div>\n </section>\n <footer class=\"kui-catalog__footer kui-pane__footer\">\n {status ? <div class=\"kui-catalog__status\">{status}</div> : <></>}\n <Toolbar\n label={`${name} resources`}\n divider={false}\n leading={resources.length > 0\n ? <nav class=\"kui-catalog__resources\" aria-label={`${name} resources`}><ToolbarControlGroup className=\"kui-catalog__resource-group\" label={`${name} resources`}>{resources.map((resource) => <a class=\"kui-catalog__resource\" href={resource.href} target=\"_blank\" rel=\"noopener noreferrer\" aria-label={`${name}: ${resource.label} (opens in new tab)`}><LucideIcon icon={ExternalLink} name=\"external-link\" /><span>{resource.label}</span>{resource.detail ? <code>{resource.detail}</code> : <></>}</a>)}</ToolbarControlGroup></nav>\n : <></>}\n trailing={related.length > 0\n ? <div class=\"kui-catalog__related\" data-catalog-related><ToolbarControlGroup single className=\"kui-catalog__related-group\" label=\"Related entries\"><wa-dropdown class=\"kui-catalog__related-menu\" placement=\"top-end\" data-key={`kui-catalog-related-${active}`} data-morph-skip-children><wa-button slot=\"trigger\" appearance=\"plain\" with-caret><span class=\"kui-catalog__related-trigger\"><LucideIcon icon={Waypoints} name=\"waypoints\" /><span class=\"kui-catalog__related-label\">Component</span></span></wa-button>{relatedMenuItems(related, selectAction)}</wa-dropdown></ToolbarControlGroup></div>\n : <></>}\n />\n </footer>\n </article>\n </main>;\n}\n\n/**\n * How a {@link CatalogExample}'s content aligns its visible left edge with the\n * example's `ListHeader` label (which sits 16px in — 8px title + 8px label):\n * - `'glyph'` — a bare glyph/text specimen with no inline geometry insets the full 16px.\n * - `'inline-control'` — a control that already carries ~8px of its own inline padding insets 8px so its content lands on the same line.\n * - `'none'` — a content-item / composition that owns its geometry and already aligns; no inset (default).\n */\nexport type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';\n\nexport interface CatalogExampleProps {\n /** The example's label, shown as a `ListHeader` above the specimen. Omit for a bare specimen. */\n label?: string;\n /** Optional explanatory note between the label and the specimen. */\n note?: SafeHtml | string;\n /** Alignment inset for the specimen — see {@link CatalogExampleAlign}. Default `'none'`. */\n align?: CatalogExampleAlign;\n className?: string;\n children?: SafeHtml | readonly SafeHtml[];\n}\n\n/**\n * One labeled example in a catalog preview: a `ListHeader` label, an optional\n * note, and the specimen. `align` insets the specimen so its visible left edge\n * lines up with the label text, encoding the catalog's alignment rules as a\n * first-class prop instead of per-demo CSS. The inset is published as the\n * `--kui-catalog-example-align` custom property so a debug overlay can exclude it\n * from a specimen's measured margin.\n */\nexport function CatalogExample({ label, note, align = 'none', className = '', children }: CatalogExampleProps) {\n return <section class={`kui-catalog-example ${className}`.trim()} data-catalog-example data-align={align}>\n {label !== undefined ? <ListHeader label={label} /> : <></>}\n {note !== undefined ? <p class=\"kui-catalog-example__note\">{note}</p> : <></>}\n {children}\n </section>;\n}\n\nexport interface CatalogExampleStackProps {\n /** Accessible label for the stack region. */\n label?: string;\n className?: string;\n children?: SafeHtml | readonly SafeHtml[];\n}\n\n/** A vertically-stacked group of {@link CatalogExample}s with the catalog's example rhythm. */\nexport function CatalogExampleStack({ label, className = '', children }: CatalogExampleStackProps) {\n return <div class={`kui-catalog-example-stack ${className}`.trim()} data-catalog-example-stack aria-label={label}>{children}</div>;\n}\n"]}
|
|
@@ -26,9 +26,36 @@ function editorIsEmpty(editor) {
|
|
|
26
26
|
const value = readTokenSearchField(editor);
|
|
27
27
|
return value.query.length === 0 && value.tokens.length === 0;
|
|
28
28
|
}
|
|
29
|
+
function selectionCoversEditor(editor) {
|
|
30
|
+
const selected = editor.ownerDocument.getSelection().getRangeAt(0);
|
|
31
|
+
if (selected.collapsed || !editor.contains(selected.commonAncestorContainer)) return false;
|
|
32
|
+
const contents = editor.ownerDocument.createRange();
|
|
33
|
+
contents.selectNodeContents(editor);
|
|
34
|
+
const RangeCtor = editor.ownerDocument.defaultView.Range;
|
|
35
|
+
return selected.compareBoundaryPoints(RangeCtor.START_TO_START, contents) <= 0 && selected.compareBoundaryPoints(RangeCtor.END_TO_END, contents) >= 0;
|
|
36
|
+
}
|
|
29
37
|
var TOKEN_SELECTOR = '[data-component="token-search-token"]';
|
|
30
38
|
var TEXT_SELECTOR = "[data-token-search-text]";
|
|
31
39
|
var stripZwsp = (text) => text.replaceAll("\u200B", "");
|
|
40
|
+
function normalizeEmptiedEditor(editor, selectedAll = false) {
|
|
41
|
+
const lineBreaks = editor.querySelectorAll("br");
|
|
42
|
+
if (lineBreaks.length === 0 && !selectedAll) return;
|
|
43
|
+
for (const lineBreak of lineBreaks) lineBreak.remove();
|
|
44
|
+
if (!selectedAll && editor.querySelector(TOKEN_SELECTOR)) return;
|
|
45
|
+
if (!selectedAll && stripZwsp(editor.textContent ?? "") !== "") return;
|
|
46
|
+
const doc = editor.ownerDocument;
|
|
47
|
+
const span = doc.createElement("span");
|
|
48
|
+
span.setAttribute("data-token-search-text", "");
|
|
49
|
+
span.setAttribute("data-empty", "true");
|
|
50
|
+
editor.replaceChildren(span);
|
|
51
|
+
if (doc.activeElement !== editor) return;
|
|
52
|
+
const selection = doc.getSelection();
|
|
53
|
+
const range = doc.createRange();
|
|
54
|
+
range.setStart(span, 0);
|
|
55
|
+
range.collapse(true);
|
|
56
|
+
selection.removeAllRanges();
|
|
57
|
+
selection.addRange(range);
|
|
58
|
+
}
|
|
32
59
|
function collapsedCaret(editor) {
|
|
33
60
|
const selection = editor.ownerDocument.getSelection();
|
|
34
61
|
if (!selection || selection.rangeCount === 0) return;
|
|
@@ -149,18 +176,20 @@ function wireTokenSearchFields(root, { onSubmit, onEdit, collapsible = true, key
|
|
|
149
176
|
pending.set(editor, {
|
|
150
177
|
id,
|
|
151
178
|
offset,
|
|
152
|
-
tokenCount: editor.querySelectorAll('[data-component="token-search-token"]').length
|
|
179
|
+
tokenCount: editor.querySelectorAll('[data-component="token-search-token"]').length,
|
|
180
|
+
selectedAll: selectionCoversEditor(editor)
|
|
153
181
|
});
|
|
154
182
|
};
|
|
155
183
|
const onInput = (event) => {
|
|
156
184
|
const editor = editorFromEvent(root, event);
|
|
157
185
|
if (!editor) return;
|
|
186
|
+
const deletion = pending.get(editor);
|
|
187
|
+
if (event.inputType?.startsWith("delete")) normalizeEmptiedEditor(editor, deletion?.selectedAll);
|
|
158
188
|
if (onEdit) {
|
|
159
189
|
const field = editor.closest('[data-component="token-search-field"]');
|
|
160
190
|
const id = field?.dataset.tokenSearchId;
|
|
161
191
|
if (id && field?.dataset.disabled !== "true") onEdit({ id, editor, event });
|
|
162
192
|
}
|
|
163
|
-
const deletion = pending.get(editor);
|
|
164
193
|
pending.delete(editor);
|
|
165
194
|
if (!deletion || editor.querySelectorAll('[data-component="token-search-token"]').length >= deletion.tokenCount) return;
|
|
166
195
|
editor.ownerDocument.defaultView.requestAnimationFrame(() => {
|
|
@@ -262,4 +291,4 @@ function wireTokenSearchFields(root, { onSubmit, onEdit, collapsible = true, key
|
|
|
262
291
|
|
|
263
292
|
export { wireTokenSearchFields };
|
|
264
293
|
|
|
265
|
-
//# sourceMappingURL=chunk-
|
|
294
|
+
//# sourceMappingURL=chunk-6NLRBSVX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/wire-token-search-fields.ts"],"names":["block"],"mappings":"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","file":"chunk-6NLRBSVX.js","sourcesContent":["import { delegate, type Signal,signal } from 'kerfjs';\n\nimport { placeTokenSearchCaret, readTokenSearchField } from './token-search-field.js';\n\nexport interface TokenSearchSubmit {\n id: string;\n editor: HTMLElement;\n}\n\n/** Reported to {@link WireTokenSearchFieldsOptions.onEdit} on every editor input. */\nexport interface TokenSearchEdit extends TokenSearchSubmit {\n /**\n * The `InputEvent` that mutated the editor — read `event.inputType` / `event.data`\n * to gate commit behavior (e.g. only parse a chip on whitespace-terminated input)\n * without keeping a separate `input` listener.\n */\n event: InputEvent;\n}\n\n/** Reported when adjacent-token keyboard deletion asks the app to drop a chip. */\nexport interface TokenSearchTokenRemoval {\n id: string;\n /** The `data-token-value` of the token the app should remove from its state. */\n value: string;\n editor: HTMLElement;\n /** `'backward'` = the token before the caret (Backspace); `'forward'` = after (Delete). */\n direction: 'backward' | 'forward';\n}\n\n/**\n * Opt-in keyboard behavior for the atomic token chips. Off unless `keyboard` is\n * set; each piece defaults on once opted in. The helper never mutates app state:\n * a removal is reported through {@link TokenSearchKeyboardOptions.onRemoveToken}\n * for the caller to apply, while caret movement past a chip is a pure ephemeral\n * mechanic the helper performs itself.\n */\nexport interface TokenSearchKeyboardOptions {\n /**\n * From a collapsed caret with no selection, Backspace removes the token\n * immediately before it and Delete the token immediately after — reported via\n * `onRemoveToken` — instead of deleting a character. Default: true.\n */\n removeAdjacentToken?: boolean;\n /**\n * ArrowRight moves the caret past a trailing atomic token so text typed next\n * lands after the chip. Default: true.\n */\n moveCaretPastToken?: boolean;\n /** Apply the reported removal to your controlled state, then re-render. */\n onRemoveToken?: (removal: TokenSearchTokenRemoval) => void;\n}\n\n/**\n * Managed collapsible behavior for the iconic TokenSearchField. Every piece is on\n * by default; disable a specific one to own it in the app. Provide `signals` to\n * drive app-owned `expanded` signals per field id instead of helper-created ones.\n */\nexport interface TokenSearchCollapsibleOptions {\n /** Expand the field and focus its editor when the iconic trigger is activated. Default: true. */\n expandOnActivate?: boolean;\n /** Collapse the field when focus leaves it while it is empty. Default: true. */\n collapseOnEmptyBlur?: boolean;\n /** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */\n collapseOnEscape?: boolean;\n /** Focus the editor on expand and the trigger on Escape-collapse. Default: true. */\n manageFocus?: boolean;\n /**\n * Keep an empty field expanded when focus moves to a caller-owned surface\n * rendered outside the field — a suggestions dropdown, date picker, or help\n * popover shown beside it. Return true for any focus target that must NOT\n * trigger collapse-on-empty-blur. An element carrying `data-token-search-keep-open`\n * (or any node inside one) is always exempt, so this predicate is only needed\n * for surfaces you cannot mark declaratively.\n */\n keepOpenOn?: (target: Node | null) => boolean;\n /** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */\n signals?: Readonly<Record<string, Signal<boolean>>>;\n}\n\nexport interface WireTokenSearchFieldsOptions {\n onSubmit?: (submission: TokenSearchSubmit) => void;\n /** Fired on every editor `input`, after the browser mutates it, so a caller can drop its own `input` listener. */\n onEdit?: (edit: TokenSearchEdit) => void;\n /** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */\n collapsible?: boolean | TokenSearchCollapsibleOptions;\n /** Opt-in atomic-chip keyboard behavior (off by default). `true` = on with defaults. */\n keyboard?: boolean | TokenSearchKeyboardOptions;\n}\n\n/**\n * The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to\n * tear down. When collapsible behavior is managed, it also exposes the transient\n * `expanded` state per field id so the app can read it in render, hand in its own\n * signal, or drive it imperatively.\n */\nexport interface TokenSearchFieldsHandle {\n (): void;\n dispose(): void;\n /** The managed `expanded` signal for a field id (adopted or helper-created); undefined when unmanaged. */\n expanded(id: string): Signal<boolean> | undefined;\n /** Expand the field (and, when focus is managed, focus its editor). */\n open(id: string): void;\n /** Collapse the field (and, when focus is managed, restore focus to its trigger). */\n close(id: string): void;\n}\n\ninterface PendingTokenDeletion {\n id: string;\n offset: number;\n tokenCount: number;\n selectedAll: boolean;\n}\n\nfunction editorFromEvent(root: HTMLElement, event: Event): HTMLElement | undefined {\n const target = event.target;\n if (!(target instanceof Element)) return;\n const editor = target.closest<HTMLElement>('[data-token-search-editor]');\n return editor && root.contains(editor) ? editor : undefined;\n}\n\nfunction caretQueryOffset(editor: HTMLElement): number | undefined {\n const selection = editor.ownerDocument.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n const selectionRange = selection.getRangeAt(0);\n if (!editor.contains(selectionRange.startContainer)) return;\n const prefixRange = editor.ownerDocument.createRange();\n prefixRange.selectNodeContents(editor);\n prefixRange.setEnd(selectionRange.startContainer, selectionRange.startOffset);\n const prefix = editor.ownerDocument.createElement('div');\n prefix.append(prefixRange.cloneContents());\n return readTokenSearchField(prefix).query.length;\n}\n\nfunction replacementEditor(root: HTMLElement, id: string): HTMLElement | undefined {\n return [...root.querySelectorAll<HTMLElement>('[data-token-search-editor]')]\n .find((editor) => editor.dataset.tokenSearchEditor === id);\n}\n\nfunction editorIsEmpty(editor: HTMLElement): boolean {\n const value = readTokenSearchField(editor);\n return value.query.length === 0 && value.tokens.length === 0;\n}\n\n/** Whether the active selection covers every child of the editing host. */\nfunction selectionCoversEditor(editor: HTMLElement): boolean {\n // onBeforeInput calls this only after caretQueryOffset verified a live range.\n const selected = editor.ownerDocument.getSelection()!.getRangeAt(0);\n if (selected.collapsed || !editor.contains(selected.commonAncestorContainer)) return false;\n const contents = editor.ownerDocument.createRange();\n contents.selectNodeContents(editor);\n const RangeCtor = editor.ownerDocument.defaultView!.Range;\n return selected.compareBoundaryPoints(RangeCtor.START_TO_START, contents) <= 0\n && selected.compareBoundaryPoints(RangeCtor.END_TO_END, contents) >= 0;\n}\n\nconst TOKEN_SELECTOR = '[data-component=\"token-search-token\"]';\nconst TEXT_SELECTOR = '[data-token-search-text]';\nconst stripZwsp = (text: string): string => text.replaceAll('', '');\n\n/**\n * A `contenteditable` host emptied by select-all + Delete (or Backspace) leaves a\n * bogus `<br>` in every engine: it renders as a stray newline and `readTokenSearchField`\n * reads it back as a space. Strip those line breaks after a delete, and when the field\n * is otherwise empty (no chips, no visible text) restore the canonical empty text span\n * and place the caret in it so typing resumes cleanly. Called only after the browser has\n * applied the delete, so an ordinary partial delete is never touched.\n */\nfunction normalizeEmptiedEditor(editor: HTMLElement, selectedAll = false): void {\n const lineBreaks = editor.querySelectorAll('br');\n if (lineBreaks.length === 0 && !selectedAll) return;\n for (const lineBreak of lineBreaks) lineBreak.remove();\n if (!selectedAll && editor.querySelector(TOKEN_SELECTOR)) return;\n if (!selectedAll && stripZwsp(editor.textContent ?? '') !== '') return;\n const doc = editor.ownerDocument;\n const span = doc.createElement('span');\n span.setAttribute('data-token-search-text', '');\n span.setAttribute('data-empty', 'true');\n editor.replaceChildren(span);\n if (doc.activeElement !== editor) return;\n // The editor is the focused, connected editing host, so a selection exists.\n const selection = doc.getSelection()!;\n const range = doc.createRange();\n range.setStart(span, 0);\n range.collapse(true);\n selection.removeAllRanges();\n selection.addRange(range);\n}\n\n/** The collapsed, in-editor caret range, or undefined when there is a selection or none. */\nfunction collapsedCaret(editor: HTMLElement): Range | undefined {\n const selection = editor.ownerDocument.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n const range = selection.getRangeAt(0);\n if (!range.collapsed || !editor.contains(range.startContainer)) return;\n return range;\n}\n\n/** The direct child of `editor` that contains `node` (or `node` itself), else null. */\nfunction topBlock(editor: HTMLElement, node: Node): Node | null {\n let current: Node = node;\n while (current.parentNode && current.parentNode !== editor) current = current.parentNode;\n return current.parentNode === editor ? current : null;\n}\n\n/** A text node or text span holding no visible character (only zero-width spacers). */\nfunction isBlankText(node: Node): boolean {\n if (node.nodeType === Node.TEXT_NODE) return stripZwsp((node as CharacterData).data) === '';\n return node instanceof Element && node.matches(TEXT_SELECTOR) && stripZwsp(node.textContent ?? '') === '';\n}\n\n/** The nearest sibling block on `direction`, skipping blank text spacers. */\nfunction meaningfulSibling(block: Node, direction: 'backward' | 'forward'): Node | null {\n let sibling = direction === 'backward' ? block.previousSibling : block.nextSibling;\n while (sibling && isBlankText(sibling)) sibling = direction === 'backward' ? sibling.previousSibling : sibling.nextSibling;\n return sibling;\n}\n\n/** The node immediately on `direction` of the caret, only when no visible character separates them. */\nfunction caretSideNode(editor: HTMLElement, range: Range, direction: 'backward' | 'forward'): Node | null {\n const { startContainer: container, startOffset: offset } = range;\n if (container.nodeType === Node.TEXT_NODE) {\n const text = (container as CharacterData).data;\n const side = direction === 'backward' ? text.slice(0, offset) : text.slice(offset);\n if (stripZwsp(side).length > 0) return null;\n // An in-editor text node always has a top block (itself or its wrapping span).\n return meaningfulSibling(topBlock(editor, container)!, direction);\n }\n const child = container.childNodes[direction === 'backward' ? offset - 1 : offset] as Node | undefined;\n if (child) {\n // `child` is a descendant of the in-editor caret container, so it has a top block.\n const block = topBlock(editor, child)!;\n return isBlankText(child) ? meaningfulSibling(block, direction) : block;\n }\n const block = topBlock(editor, container);\n return block ? meaningfulSibling(block, direction) : null;\n}\n\n/** The atomic token chip adjacent to a collapsed caret on `direction`, if any. */\nfunction adjacentToken(editor: HTMLElement, direction: 'backward' | 'forward'): HTMLElement | undefined {\n const range = collapsedCaret(editor);\n if (!range) return;\n const node = caretSideNode(editor, range, direction);\n if (!node) return;\n const base = node instanceof Element ? node : node.parentElement;\n const chip = base?.closest<HTMLElement>(TOKEN_SELECTOR) ?? null;\n return chip && editor.contains(chip) ? chip : undefined;\n}\n\n/** Move the caret to just after an atomic token chip so typed text lands after it.\n * Only reached after {@link collapsedCaret} already found a live selection, and the\n * chip is a direct block of the editor. */\nfunction placeCaretAfterToken(editor: HTMLElement, chip: HTMLElement): void {\n const selection = editor.ownerDocument.getSelection()!;\n const range = editor.ownerDocument.createRange();\n range.setStartAfter(topBlock(editor, chip)!);\n range.collapse(true);\n selection.removeAllRanges();\n selection.addRange(range);\n editor.focus({ preventScroll: true });\n}\n\n/**\n * Wire every TokenSearchField under `root`: submit on Enter, preserve the caret across\n * controlled token deletion, and (by default) manage the collapsible field's transient\n * expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also\n * exposes the managed `expanded` state per field id.\n */\nexport function wireTokenSearchFields(\n root: HTMLElement,\n { onSubmit, onEdit, collapsible = true, keyboard = false }: WireTokenSearchFieldsOptions = {},\n): TokenSearchFieldsHandle {\n const managed = collapsible !== false;\n const config = typeof collapsible === 'object' ? collapsible : {};\n const expandOnActivate = managed && (config.expandOnActivate ?? true);\n const collapseOnEmptyBlur = managed && (config.collapseOnEmptyBlur ?? true);\n const collapseOnEscape = managed && (config.collapseOnEscape ?? true);\n const manageFocus = managed && (config.manageFocus ?? true);\n const keepOpenOn = config.keepOpenOn;\n const keyboardOn = keyboard !== false;\n const keyboardConfig = typeof keyboard === 'object' ? keyboard : {};\n const removeAdjacentToken = keyboardOn && (keyboardConfig.removeAdjacentToken ?? true);\n const moveCaretPastToken = keyboardOn && (keyboardConfig.moveCaretPastToken ?? true);\n const onRemoveToken = keyboardConfig.onRemoveToken;\n const adopted = config.signals ?? {};\n const created = new Map<string, Signal<boolean>>();\n\n /** True when focus moving to `element` should keep an empty field expanded. */\n const isExemptTarget = (element: Element | null): boolean => {\n if (!element) return false;\n if (element.closest('[data-token-search-keep-open]')) return true;\n return keepOpenOn ? keepOpenOn(element) : false;\n };\n\n const signalFor = (id: string): Signal<boolean> => {\n const own = adopted[id];\n if (own) return own;\n let field = created.get(id);\n if (!field) {\n field = signal(false);\n created.set(id, field);\n }\n return field;\n };\n const setExpanded = (id: string, next: boolean) => {\n const state = signalFor(id);\n if (state.value !== next) state.value = next;\n };\n const view = () => root.ownerDocument.defaultView!;\n const focusAfterRender = (id: string, selector: string) => {\n view().requestAnimationFrame(() => {\n const target = root.querySelector<HTMLElement>(\n `[data-component=\"token-search-field\"][data-token-search-id=\"${CSS.escape(id)}\"] ${selector}`,\n );\n if (!target) return;\n if (target.matches('[data-token-search-editor]')) placeTokenSearchCaret(target);\n else target.focus();\n });\n };\n const openField = (id: string) => {\n setExpanded(id, true);\n if (manageFocus) focusAfterRender(id, '[data-token-search-editor]');\n };\n const closeField = (id: string) => {\n setExpanded(id, false);\n if (manageFocus) focusAfterRender(id, '.kui-token-search__expand');\n };\n\n const pending = new WeakMap<HTMLElement, PendingTokenDeletion>();\n const onBeforeInput = (event: Event) => {\n const inputEvent = event as InputEvent;\n const editor = editorFromEvent(root, event);\n if (!editor || !inputEvent.inputType.startsWith('delete') || editor.ownerDocument.activeElement !== editor) return;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n const offset = caretQueryOffset(editor);\n if (!id || offset === undefined || field?.dataset.disabled === 'true') return;\n pending.set(editor, {\n id,\n offset,\n tokenCount: editor.querySelectorAll('[data-component=\"token-search-token\"]').length,\n selectedAll: selectionCoversEditor(editor),\n });\n };\n const onInput = (event: Event) => {\n const editor = editorFromEvent(root, event);\n if (!editor) return;\n const deletion = pending.get(editor);\n if ((event as InputEvent).inputType?.startsWith('delete')) normalizeEmptiedEditor(editor, deletion?.selectedAll);\n if (onEdit) {\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (id && field?.dataset.disabled !== 'true') onEdit({ id, editor, event: event as InputEvent });\n }\n pending.delete(editor);\n if (!deletion || editor.querySelectorAll('[data-component=\"token-search-token\"]').length >= deletion.tokenCount) return;\n editor.ownerDocument.defaultView!.requestAnimationFrame(() => {\n const replacement = replacementEditor(root, deletion.id);\n if (!replacement) return;\n const active = replacement.ownerDocument.activeElement;\n if (active !== editor && active !== replacement && active !== replacement.ownerDocument.body) return;\n placeTokenSearchCaret(replacement, deletion.offset);\n });\n };\n root.addEventListener('beforeinput', onBeforeInput, true);\n root.addEventListener('input', onInput, true);\n\n const disposers: Array<() => void> = [\n () => root.removeEventListener('beforeinput', onBeforeInput, true),\n () => root.removeEventListener('input', onInput, true),\n ];\n\n disposers.push(\n delegate(root, 'keydown', '[data-token-search-editor]', (event, element) => {\n const keyboardEvent = event as KeyboardEvent;\n const editor = element as HTMLElement;\n if (keyboardEvent.isComposing) return;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (!id || field?.dataset.disabled === 'true') return;\n if (keyboardEvent.key === 'Enter') {\n keyboardEvent.preventDefault();\n onSubmit?.({ id, editor });\n return;\n }\n if (removeAdjacentToken && (keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete')) {\n const direction = keyboardEvent.key === 'Backspace' ? 'backward' : 'forward';\n const chip = adjacentToken(editor, direction);\n if (chip) {\n keyboardEvent.preventDefault();\n onRemoveToken?.({ id, value: chip.dataset.tokenValue ?? '', editor, direction });\n return;\n }\n }\n if (moveCaretPastToken && keyboardEvent.key === 'ArrowRight') {\n const chip = adjacentToken(editor, 'forward');\n if (chip) {\n keyboardEvent.preventDefault();\n placeCaretAfterToken(editor, chip);\n return;\n }\n }\n if (collapseOnEscape && keyboardEvent.key === 'Escape' && field?.dataset.collapsible === 'true' && editorIsEmpty(editor)) {\n keyboardEvent.preventDefault();\n closeField(id);\n }\n }),\n );\n\n if (expandOnActivate) {\n disposers.push(\n delegate(root, 'click', '.kui-token-search__expand', (_event, element) => {\n // The selector guarantees a collapsible field ancestor; only skip a disabled one.\n const field = (element as HTMLElement).closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (id && field?.dataset.disabled !== 'true') openField(id);\n }),\n );\n }\n\n if (collapseOnEmptyBlur) {\n // Pressing an in-field control (clear, trailing, token, or the trigger) must not\n // blur the editor — otherwise the transient blur would collapse the field before\n // the control's own handler runs. Keeping focus also keeps the click firing.\n disposers.push(\n delegate(root, 'mousedown', '[data-component=\"token-search-field\"][data-collapsible=\"true\"] button', (event) => {\n event.preventDefault();\n }),\n );\n disposers.push(\n delegate(root, 'focusout', '[data-token-search-editor]', (event, element) => {\n const editor = element as HTMLElement;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (!id || field?.dataset.collapsible !== 'true' || field.dataset.disabled === 'true') return;\n const next = (event as FocusEvent).relatedTarget;\n if (next instanceof Node && field.contains(next)) return;\n if (isExemptTarget(next instanceof Element ? next : null)) return;\n if (!editorIsEmpty(editor)) return;\n view().queueMicrotask(() => {\n const active = field.ownerDocument.activeElement;\n if (!field.contains(active) && !isExemptTarget(active)) setExpanded(id, false);\n });\n }),\n );\n }\n\n const dispose = () => {\n for (const stop of disposers.splice(0)) stop();\n };\n const handle = (() => dispose()) as TokenSearchFieldsHandle;\n handle.dispose = dispose;\n handle.expanded = (id) => (managed ? signalFor(id) : undefined);\n handle.open = (id) => {\n if (managed) openField(id);\n };\n handle.close = (id) => {\n if (managed) closeField(id);\n };\n return handle;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ToolbarText } from './chunk-
|
|
1
|
+
import { ToolbarText } from './chunk-WXMGNX2Y.js';
|
|
2
2
|
import { jsxs, jsx } from 'kerfjs/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
function NavStack({ id, label, views, backLabel = "Back", hideToolbar = false, bottomToolbar, className = "" }) {
|
|
@@ -20,4 +20,4 @@ function NavStack({ id, label, views, backLabel = "Back", hideToolbar = false, b
|
|
|
20
20
|
|
|
21
21
|
export { NavStack };
|
|
22
22
|
|
|
23
|
-
//# sourceMappingURL=chunk-
|
|
23
|
+
//# sourceMappingURL=chunk-WRTAQ7BZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nav-stack.tsx"],"names":[],"mappings":";;;AAuCO,SAAS,QAAA,CAAS,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,SAAA,GAAY,MAAA,EAAQ,WAAA,GAAc,KAAA,EAAO,aAAA,EAAe,SAAA,GAAY,EAAA,EAAG,EAAkB;AACpI,EAAA,MAAM,QAAA,GAAW,MAAM,MAAA,GAAS,CAAA;AAChC,EAAA,MAAM,GAAA,GAAM,MAAM,QAAQ,CAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,GAAS,CAAA;AAC9B,EAAA,4BAAQ,SAAA,EAAA,EAAQ,KAAA,EAAO,iBAAiB,SAAS,CAAA,CAAA,CAAG,MAAK,EAAG,EAAA,EAAQ,kBAAe,WAAA,EAAY,mBAAA,EAAmB,IAAI,YAAA,EAAY,MAAA,CAAO,MAAM,MAAM,CAAA,EAAG,cAAY,KAAA,EACjK,QAAA,EAAA;AAAA,IAAA,CAAC,+BAAe,IAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,uBAAA,EAAwB,yBAAqB,IAAA,EAC1E,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,OAAM,qBAAA,EACR,QAAA,EAAA;AAAA,QAAA,MAAA,wBAAW,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,KAAA,EAAM,uBAAsB,eAAA,EAAa,IAAA,EAAC,YAAA,EAAY,SAAA,EACrF,8BAAC,KAAA,EAAA,EAAI,KAAA,EAAM,0BAAA,EAA2B,OAAA,EAAQ,aAAY,KAAA,EAAM,IAAA,EAAK,MAAA,EAAO,IAAA,EAAK,eAAY,MAAA,EAAO,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,gBAAe,cAAA,EAAa,GAAA,EAAI,gBAAA,EAAe,OAAA,EAAQ,mBAAgB,OAAA,EAAQ,QAAA,kBAAA,GAAA,CAAC,UAAK,CAAA,EAAE,gBAAA,EAAiB,GAAE,CAAA,EACnO,CAAA;AAAA,wBACA,GAAA,CAAC,eAAY,IAAA,EAAM,GAAA,EAAK,SAAS,EAAA,EAAI,IAAA,EAAK,OAAA,EAAQ,SAAA,EAAU,sBAAA,EAAuB;AAAA,OAAA,EACrF,CAAA;AAAA,MACC,KAAK,OAAA,oBAAW,GAAA,CAAC,SAAI,KAAA,EAAM,wBAAA,EAA0B,cAAI,OAAA,EAAQ;AAAA,KAAA,EACpE,CAAA;AAAA,oBACA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,yBAAA,EAA0B,2BAAuB,IAAA,EACzD,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,MAAM,KAAA,qBAAU,GAAA,CAAC,SAAA,EAAA,EAAQ,KAAA,EAAM,uBAAsB,UAAA,EAAU,IAAA,CAAK,GAAA,EAAK,cAAA,EAAc,IAAA,CAAK,GAAA,EAAK,iBAAA,EAAiB,MAAA,CAAO,UAAU,QAAQ,CAAA,EAAG,aAAA,EAAa,MAAA,CAAO,UAAU,QAAQ,CAAA,EAAI,QAAA,EAAA,IAAA,CAAK,OAAA,EAAQ,CAAU,CAAA,EAC7N,CAAA;AAAA,IACC,iCAAiB,GAAA,CAAC,QAAA,EAAA,EAAO,OAAM,uBAAA,EAAwB,uBAAA,EAAqB,MAAE,QAAA,EAAA,aAAA,EAAc;AAAA,GAAA,EAC/F,CAAA;AACF","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/nav-stack.tsx"],"names":[],"mappings":";;;AAuCO,SAAS,QAAA,CAAS,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,SAAA,GAAY,MAAA,EAAQ,WAAA,GAAc,KAAA,EAAO,aAAA,EAAe,SAAA,GAAY,EAAA,EAAG,EAAkB;AACpI,EAAA,MAAM,QAAA,GAAW,MAAM,MAAA,GAAS,CAAA;AAChC,EAAA,MAAM,GAAA,GAAM,MAAM,QAAQ,CAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,GAAS,CAAA;AAC9B,EAAA,4BAAQ,SAAA,EAAA,EAAQ,KAAA,EAAO,iBAAiB,SAAS,CAAA,CAAA,CAAG,MAAK,EAAG,EAAA,EAAQ,kBAAe,WAAA,EAAY,mBAAA,EAAmB,IAAI,YAAA,EAAY,MAAA,CAAO,MAAM,MAAM,CAAA,EAAG,cAAY,KAAA,EACjK,QAAA,EAAA;AAAA,IAAA,CAAC,+BAAe,IAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,uBAAA,EAAwB,yBAAqB,IAAA,EAC1E,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,OAAM,qBAAA,EACR,QAAA,EAAA;AAAA,QAAA,MAAA,wBAAW,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,KAAA,EAAM,uBAAsB,eAAA,EAAa,IAAA,EAAC,YAAA,EAAY,SAAA,EACrF,8BAAC,KAAA,EAAA,EAAI,KAAA,EAAM,0BAAA,EAA2B,OAAA,EAAQ,aAAY,KAAA,EAAM,IAAA,EAAK,MAAA,EAAO,IAAA,EAAK,eAAY,MAAA,EAAO,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,gBAAe,cAAA,EAAa,GAAA,EAAI,gBAAA,EAAe,OAAA,EAAQ,mBAAgB,OAAA,EAAQ,QAAA,kBAAA,GAAA,CAAC,UAAK,CAAA,EAAE,gBAAA,EAAiB,GAAE,CAAA,EACnO,CAAA;AAAA,wBACA,GAAA,CAAC,eAAY,IAAA,EAAM,GAAA,EAAK,SAAS,EAAA,EAAI,IAAA,EAAK,OAAA,EAAQ,SAAA,EAAU,sBAAA,EAAuB;AAAA,OAAA,EACrF,CAAA;AAAA,MACC,KAAK,OAAA,oBAAW,GAAA,CAAC,SAAI,KAAA,EAAM,wBAAA,EAA0B,cAAI,OAAA,EAAQ;AAAA,KAAA,EACpE,CAAA;AAAA,oBACA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,yBAAA,EAA0B,2BAAuB,IAAA,EACzD,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,MAAM,KAAA,qBAAU,GAAA,CAAC,SAAA,EAAA,EAAQ,KAAA,EAAM,uBAAsB,UAAA,EAAU,IAAA,CAAK,GAAA,EAAK,cAAA,EAAc,IAAA,CAAK,GAAA,EAAK,iBAAA,EAAiB,MAAA,CAAO,UAAU,QAAQ,CAAA,EAAG,aAAA,EAAa,MAAA,CAAO,UAAU,QAAQ,CAAA,EAAI,QAAA,EAAA,IAAA,CAAK,OAAA,EAAQ,CAAU,CAAA,EAC7N,CAAA;AAAA,IACC,iCAAiB,GAAA,CAAC,QAAA,EAAA,EAAO,OAAM,uBAAA,EAAwB,uBAAA,EAAqB,MAAE,QAAA,EAAA,aAAA,EAAc;AAAA,GAAA,EAC/F,CAAA;AACF","file":"chunk-WRTAQ7BZ.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { ToolbarText } from './toolbar-text.js';\n\n/**\n * One entry in a {@link NavStack}. The app owns the stack as an array (usually a\n * signal); `NavStack` renders it and `wireNavStack` animates the transitions.\n */\nexport interface NavStackView {\n /** Stable identity for keyed reconcile and transition direction. */\n key: string;\n content: SafeHtml;\n /** Title shown in the top toolbar for this view. */\n title?: string;\n /** Trailing actions for this view's top toolbar. */\n toolbar?: SafeHtml;\n}\n\nexport interface NavStackProps {\n id: string;\n /** Accessible name for the stack region. */\n label: string;\n /** The stack, root first; the last entry is the active top view. */\n views: NavStackView[];\n /** Accessible label for the back control (default \"Back\"). */\n backLabel?: string;\n /** Hide the top toolbar entirely (rare — a fully custom-chrome view). */\n hideToolbar?: boolean;\n /** Optional persistent bottom toolbar. */\n bottomToolbar?: SafeHtml;\n className?: string;\n}\n\n/**\n * A navigation stack (iOS-style push/pop). Renders every entry stacked, the last\n * one active; `@kerfjs/ui/wire-nav-stack`'s `wireNavStack` slides the content and\n * cross-fades the chrome across a change. A single-pane layout is a `NavStack`\n * with one entry. See `docs/23-app-layouts.md` §3.1.\n */\nexport function NavStack({ id, label, views, backLabel = 'Back', hideToolbar = false, bottomToolbar, className = '' }: NavStackProps) {\n const topIndex = views.length - 1;\n const top = views[topIndex];\n const canPop = views.length > 1;\n return <section class={`kui-nav-stack ${className}`.trim()} id={id} data-component=\"nav-stack\" data-nav-stack-id={id} data-depth={String(views.length)} aria-label={label}>\n {!hideToolbar && <header class=\"kui-nav-stack__chrome\" data-nav-stack-chrome>\n <div class=\"kui-nav-stack__lead\">\n {canPop && <button type=\"button\" class=\"kui-nav-stack__back\" data-nav-back aria-label={backLabel}>\n <svg class=\"kui-nav-stack__back-icon\" viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 18-6-6 6-6\" /></svg>\n </button>}\n <ToolbarText text={top?.title ?? ''} size=\"large\" className=\"kui-nav-stack__title\" />\n </div>\n {top?.toolbar && <div class=\"kui-nav-stack__actions\">{top.toolbar}</div>}\n </header>}\n <div class=\"kui-nav-stack__viewport\" data-nav-stack-viewport>\n {views.map((view, index) => <article class=\"kui-nav-stack__view\" data-key={view.key} data-nav-key={view.key} data-nav-active={String(index === topIndex)} aria-hidden={String(index !== topIndex)}>{view.content}</article>)}\n </div>\n {bottomToolbar && <footer class=\"kui-nav-stack__bottom\" data-nav-stack-bottom>{bottomToolbar}</footer>}\n </section>;\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Skeleton } from './chunk-B24C4A6T.js';
|
|
2
|
+
import { jsx } from 'kerfjs/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
function ToolbarText({
|
|
5
|
+
text,
|
|
6
|
+
size = "default",
|
|
7
|
+
className = "",
|
|
8
|
+
id,
|
|
9
|
+
headingLevel,
|
|
10
|
+
placeholder = false,
|
|
11
|
+
wrap = false,
|
|
12
|
+
ellipsis = true,
|
|
13
|
+
maxLines = null
|
|
14
|
+
}) {
|
|
15
|
+
const capped = wrap && maxLines != null && maxLines > 0;
|
|
16
|
+
return /* @__PURE__ */ jsx(
|
|
17
|
+
"span",
|
|
18
|
+
{
|
|
19
|
+
class: `kui-toolbar-text ${className}`.trim(),
|
|
20
|
+
"data-component": "toolbar-text",
|
|
21
|
+
"data-size": size,
|
|
22
|
+
"data-wrap": wrap ? "true" : void 0,
|
|
23
|
+
"data-ellipsis": ellipsis ? void 0 : "false",
|
|
24
|
+
"data-max-lines": capped ? String(maxLines) : void 0,
|
|
25
|
+
style: capped ? `--kui-toolbar-text-max-lines:${maxLines}` : void 0,
|
|
26
|
+
"data-placeholder": placeholder ? "true" : void 0,
|
|
27
|
+
id,
|
|
28
|
+
role: headingLevel ? "heading" : void 0,
|
|
29
|
+
"aria-level": headingLevel ? String(headingLevel) : void 0,
|
|
30
|
+
"aria-busy": placeholder ? "true" : void 0,
|
|
31
|
+
children: placeholder ? /* @__PURE__ */ jsx(Skeleton, { width: "8em" }) : /* @__PURE__ */ jsx("span", { class: "kui-toolbar-text__text", children: text })
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { ToolbarText };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=chunk-WXMGNX2Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/toolbar-text.tsx"],"names":[],"mappings":";;;AAuCO,SAAS,WAAA,CAAY;AAAA,EAC1B,IAAA;AAAA,EACA,IAAA,GAAO,SAAA;AAAA,EACP,SAAA,GAAY,EAAA;AAAA,EACZ,EAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA,GAAc,KAAA;AAAA,EACd,IAAA,GAAO,KAAA;AAAA,EACP,QAAA,GAAW,IAAA;AAAA,EACX,QAAA,GAAW;AACb,CAAA,EAAqB;AACnB,EAAA,MAAM,MAAA,GAAS,IAAA,IAAQ,QAAA,IAAY,IAAA,IAAQ,QAAA,GAAW,CAAA;AACtD,EAAA,uBAAO,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACN,KAAA,EAAO,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MAC5C,gBAAA,EAAe,cAAA;AAAA,MACf,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAW,OAAO,MAAA,GAAS,MAAA;AAAA,MAC3B,eAAA,EAAe,WAAW,MAAA,GAAY,OAAA;AAAA,MACtC,gBAAA,EAAgB,MAAA,GAAS,MAAA,CAAO,QAAQ,CAAA,GAAI,MAAA;AAAA,MAC5C,KAAA,EAAO,MAAA,GAAS,CAAA,6BAAA,EAAgC,QAAQ,CAAA,CAAA,GAAK,MAAA;AAAA,MAC7D,kBAAA,EAAkB,cAAc,MAAA,GAAS,MAAA;AAAA,MACzC,EAAA;AAAA,MACA,IAAA,EAAM,eAAe,SAAA,GAAY,MAAA;AAAA,MACjC,YAAA,EAAY,YAAA,GAAe,MAAA,CAAO,YAAY,CAAA,GAAI,MAAA;AAAA,MAClD,WAAA,EAAW,cAAc,MAAA,GAAS,MAAA;AAAA,MAClC,QAAA,EAAA,WAAA,mBAAc,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,KAAA,EAAM,oBAAK,GAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAM,wBAAA,EAA0B,QAAA,EAAA,IAAA,EAAK;AAAA;AAAA,GAAQ;AAChG","file":"chunk-WXMGNX2Y.js","sourcesContent":["import { Skeleton } from './skeleton.js';\n\nexport type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small';\n\n/** ARIA heading level for a title exposed as a heading landmark. */\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport interface ToolbarTextProps {\n text: string;\n size?: ToolbarTextSize;\n className?: string;\n /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */\n id?: string;\n /**\n * Expose heading semantics (`role=\"heading\"` + `aria-level`) so the text acts as\n * a heading landmark — e.g. a page's primary title. Omit to keep the plain span\n * (the default), which suits a dialog title referenced via `aria-labelledby`.\n */\n headingLevel?: HeadingLevel;\n /** Render the text as an unanimated loading skeleton instead of its value. */\n placeholder?: boolean;\n /**\n * Wrap onto multiple lines when the text does not fit, instead of the default\n * single line. Combine with `maxLines` to cap the number of lines. Default false.\n */\n wrap?: boolean;\n /**\n * Show a trailing ellipsis (…) where the text is truncated — on the single line\n * (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip\n * instead. Default true.\n */\n ellipsis?: boolean;\n /**\n * Cap wrapped text to this many lines, truncating past it. Only takes effect with\n * `wrap`; ignored on a single line. `null`/omitted wraps without a line cap. Default null.\n */\n maxLines?: number | null;\n}\n\nexport function ToolbarText({\n text,\n size = 'default',\n className = '',\n id,\n headingLevel,\n placeholder = false,\n wrap = false,\n ellipsis = true,\n maxLines = null,\n}: ToolbarTextProps) {\n const capped = wrap && maxLines != null && maxLines > 0;\n return <span\n class={`kui-toolbar-text ${className}`.trim()}\n data-component=\"toolbar-text\"\n data-size={size}\n data-wrap={wrap ? 'true' : undefined}\n data-ellipsis={ellipsis ? undefined : 'false'}\n data-max-lines={capped ? String(maxLines) : undefined}\n style={capped ? `--kui-toolbar-text-max-lines:${maxLines}` : undefined}\n data-placeholder={placeholder ? 'true' : undefined}\n id={id}\n role={headingLevel ? 'heading' : undefined}\n aria-level={headingLevel ? String(headingLevel) : undefined}\n aria-busy={placeholder ? 'true' : undefined}\n >{placeholder ? <Skeleton width=\"8em\" /> : <span class=\"kui-toolbar-text__text\">{text}</span>}</span>;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ToolbarText } from './chunk-
|
|
1
|
+
import { ToolbarText } from './chunk-WXMGNX2Y.js';
|
|
2
2
|
import { ToolbarControlGroup } from './chunk-RNPGFQ33.js';
|
|
3
3
|
import { Toolbar } from './chunk-DAFFDO33.js';
|
|
4
4
|
import { Skeleton } from './chunk-B24C4A6T.js';
|
|
@@ -17,4 +17,4 @@ function PanelHeader({ title, titleId, summary, summaryId, icon, iconClassName =
|
|
|
17
17
|
|
|
18
18
|
export { PanelHeader };
|
|
19
19
|
|
|
20
|
-
//# sourceMappingURL=chunk-
|
|
20
|
+
//# sourceMappingURL=chunk-ZQQ6P4SV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/panel-header.tsx"],"names":[],"mappings":";;;;;;AAsCO,SAAS,WAAA,CAAY,EAAE,KAAA,EAAO,OAAA,EAAS,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,aAAA,GAAgB,EAAA,EAAI,OAAA,EAAS,YAAA,EAAc,WAAA,GAAc,OAAM,EAAqB;AAC1J,EAAA,MAAM,2BAAW,IAAA,CAAA,QAAA,EAAA,EACd,QAAA,EAAA;AAAA,IAAA,IAAA,oBAAQ,GAAA,CAAC,mBAAA,EAAA,EAAoB,MAAA,EAAM,IAAA,EAAC,SAAA,EAAW,0BAA0B,aAAa,CAAA,CAAA,CAAG,IAAA,EAAK,EAAI,QAAA,EAAA,IAAA,EAAK,CAAA;AAAA,oBACxG,GAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK,QAAA,EAAS,EAAA,EAAI,OAAA,EAAS,SAAA,EAAU,yBAAA,EAA0B,YAAA,EAA4B,WAAA,EAA0B;AAAA,GAAA,EACjJ,CAAA;AAEA,EAAA,uBAAO,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,kBAAA,EAAmB,gBAAA,EAAe,cAAA,EAAe,eAAA,EAAe,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA,EAAG,kBAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA,EAAG,kBAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA,EAAG,kBAAA,EAAkB,WAAA,GAAc,MAAA,GAAS,MAAA,EACxO,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,WAAQ,OAAA,EAAS,QAAA,EAAU,QAAA,EAAU,OAAA,EAAS,SAAS,KAAA,EAAO,CAAA;AAAA,IAC9D,OAAA,oBAAW,GAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,2BAAA,EAA4B,EAAA,EAAI,SAAA,EAAY,QAAA,EAAA,WAAA,mBAAc,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,MAAA,EAAO,IAAK,OAAA,EAAQ;AAAA,GAAA,EACrH,CAAA;AACF","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/panel-header.tsx"],"names":[],"mappings":";;;;;;AAsCO,SAAS,WAAA,CAAY,EAAE,KAAA,EAAO,OAAA,EAAS,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,aAAA,GAAgB,EAAA,EAAI,OAAA,EAAS,YAAA,EAAc,WAAA,GAAc,OAAM,EAAqB;AAC1J,EAAA,MAAM,2BAAW,IAAA,CAAA,QAAA,EAAA,EACd,QAAA,EAAA;AAAA,IAAA,IAAA,oBAAQ,GAAA,CAAC,mBAAA,EAAA,EAAoB,MAAA,EAAM,IAAA,EAAC,SAAA,EAAW,0BAA0B,aAAa,CAAA,CAAA,CAAG,IAAA,EAAK,EAAI,QAAA,EAAA,IAAA,EAAK,CAAA;AAAA,oBACxG,GAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK,QAAA,EAAS,EAAA,EAAI,OAAA,EAAS,SAAA,EAAU,yBAAA,EAA0B,YAAA,EAA4B,WAAA,EAA0B;AAAA,GAAA,EACjJ,CAAA;AAEA,EAAA,uBAAO,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,kBAAA,EAAmB,gBAAA,EAAe,cAAA,EAAe,eAAA,EAAe,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA,EAAG,kBAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA,EAAG,kBAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA,EAAG,kBAAA,EAAkB,WAAA,GAAc,MAAA,GAAS,MAAA,EACxO,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,WAAQ,OAAA,EAAS,QAAA,EAAU,QAAA,EAAU,OAAA,EAAS,SAAS,KAAA,EAAO,CAAA;AAAA,IAC9D,OAAA,oBAAW,GAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,2BAAA,EAA4B,EAAA,EAAI,SAAA,EAAY,QAAA,EAAA,WAAA,mBAAc,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,MAAA,EAAO,IAAK,OAAA,EAAQ;AAAA,GAAA,EACrH,CAAA;AACF","file":"chunk-ZQQ6P4SV.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Skeleton } from './skeleton.js';\nimport { Toolbar } from './toolbar.js';\nimport { ToolbarControlGroup } from './toolbar-control-group.js';\nimport { type HeadingLevel, ToolbarText } from './toolbar-text.js';\n\nexport interface PanelHeaderProps {\n title: string;\n titleId: string;\n summary?: string;\n summaryId?: string;\n icon?: SafeHtml;\n iconClassName?: string;\n actions?: SafeHtml;\n /**\n * Expose the title as a heading landmark (`role=\"heading\"` + `aria-level`). Set it\n * for a PAGE or view heading so screen-reader heading navigation works and the view\n * has a primary heading; omit it (the default) for a dialog title, which is instead\n * referenced via `aria-labelledby={titleId}` and needs no heading landmark.\n */\n headingLevel?: HeadingLevel;\n /** Render the title and summary as unanimated loading skeletons, keeping the icon and actions. */\n placeholder?: boolean;\n}\n\n/**\n * The heading of a panel, dialog, or page: a plain `Toolbar` whose leading zone\n * holds an optional icon control group and the title as extra-large `ToolbarText`,\n * whose trailing zone holds the app's action controls, and with an optional\n * subtitle on its own row, left-aligned with the title.\n *\n * PanelHeader overrides no Toolbar styles — it is just a Toolbar with an xl title.\n * The only styling it adds is the icon group's fill/border color and the subtitle.\n * When no icon is provided, the icon group is omitted entirely. The `actions` slot\n * is passed straight into the toolbar's trailing zone; the app supplies whatever\n * trailing controls it needs (typically a `ToolbarControlGroup`).\n */\nexport function PanelHeader({ title, titleId, summary, summaryId, icon, iconClassName = '', actions, headingLevel, placeholder = false }: PanelHeaderProps) {\n const identity = <>\n {icon && <ToolbarControlGroup single className={`kui-panel-header__icon ${iconClassName}`.trim()}>{icon}</ToolbarControlGroup>}\n <ToolbarText text={title} size=\"xlarge\" id={titleId} className=\"kui-panel-header__title\" headingLevel={headingLevel} placeholder={placeholder} />\n </>;\n\n return <div class=\"kui-panel-header\" data-component=\"panel-header\" data-has-icon={String(Boolean(icon))} data-has-actions={String(Boolean(actions))} data-has-summary={String(Boolean(summary))} data-placeholder={placeholder ? 'true' : undefined}>\n <Toolbar leading={identity} trailing={actions} divider={false} />\n {summary && <p class=\"kui-panel-header__summary\" id={summaryId}>{placeholder ? <Skeleton width=\"18em\" /> : summary}</p>}\n </div>;\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { wireResizableRegions } from './chunk-AQNKJRFH.js';
|
|
2
2
|
export { reorderTabs, wireTabBars } from './chunk-XXMOARTH.js';
|
|
3
|
-
export { wireTokenSearchFields } from './chunk-
|
|
3
|
+
export { wireTokenSearchFields } from './chunk-6NLRBSVX.js';
|
|
4
4
|
export { TabBar } from './chunk-AVZHJN6C.js';
|
|
5
5
|
export { TokenSearchField, placeTokenSearchCaret, readTokenSearchField } from './chunk-7LDCTU7D.js';
|
|
6
6
|
export { ValueTable, ValueTableRow } from './chunk-37T3VNIZ.js';
|
|
7
|
-
export { PanelHeader } from './chunk-
|
|
7
|
+
export { PanelHeader } from './chunk-ZQQ6P4SV.js';
|
|
8
8
|
export { SegmentedControl } from './chunk-YGN7OMLT.js';
|
|
9
9
|
export { Select } from './chunk-CH4SQ6H5.js';
|
|
10
10
|
export { ResizableRegion, clampRegionSize, resizeRegionFromPointer } from './chunk-M5JRI6MN.js';
|
|
@@ -12,7 +12,7 @@ export { StateBanner } from './chunk-I7INRJO4.js';
|
|
|
12
12
|
export { ListActionRow } from './chunk-7JB7VPRI.js';
|
|
13
13
|
export { ListInsetControl } from './chunk-VPCTCUED.js';
|
|
14
14
|
export { ListInsetText } from './chunk-AHQI52QO.js';
|
|
15
|
-
export { ToolbarText } from './chunk-
|
|
15
|
+
export { ToolbarText } from './chunk-WXMGNX2Y.js';
|
|
16
16
|
export { AppTab } from './chunk-2Z5TWHKQ.js';
|
|
17
17
|
export { ToolbarControlGroup } from './chunk-RNPGFQ33.js';
|
|
18
18
|
export { Toolbar } from './chunk-DAFFDO33.js';
|
package/dist/nav-stack.js
CHANGED
package/dist/panel-header.js
CHANGED
package/dist/split-view.js
CHANGED
package/dist/styles/catalog.css
CHANGED
|
@@ -104,7 +104,28 @@
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
.kui-catalog__items .kui-list-item {
|
|
107
|
-
grid-template-columns: minmax(0, 1fr);
|
|
107
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.kui-catalog__tags {
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
min-width: 0;
|
|
113
|
+
flex-wrap: wrap;
|
|
114
|
+
justify-content: flex-end;
|
|
115
|
+
gap: var(--kui-space-2xs);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.kui-catalog__tag {
|
|
119
|
+
display: inline-flex;
|
|
120
|
+
padding: 0.125rem 0.375rem;
|
|
121
|
+
border: 1px solid var(--kui-color-warning-border-quiet);
|
|
122
|
+
border-radius: 999px;
|
|
123
|
+
color: var(--kui-color-warning-text);
|
|
124
|
+
background: var(--kui-color-warning);
|
|
125
|
+
font-size: var(--kui-font-2xs);
|
|
126
|
+
font-weight: 650;
|
|
127
|
+
line-height: 1.2;
|
|
128
|
+
white-space: nowrap;
|
|
108
129
|
}
|
|
109
130
|
|
|
110
131
|
.kui-catalog__group--secondary {
|
|
@@ -9,11 +9,19 @@
|
|
|
9
9
|
color: var(--kui-color-neutral-on-quiet);
|
|
10
10
|
font-weight: 650;
|
|
11
11
|
line-height: 1;
|
|
12
|
-
text-overflow: ellipsis;
|
|
13
|
-
white-space: nowrap;
|
|
14
12
|
align-items: center;
|
|
15
13
|
}
|
|
16
14
|
|
|
15
|
+
/* The text lives in a block child, not directly in the flex box, so
|
|
16
|
+
`text-overflow: ellipsis` actually applies (it is a no-op on a flex item). */
|
|
17
|
+
.kui-toolbar-text__text {
|
|
18
|
+
min-width: 0;
|
|
19
|
+
max-width: 100%;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
text-overflow: ellipsis;
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
.kui-toolbar-text[data-size="xlarge"] {
|
|
18
26
|
/* Matches the page/panel title: the same responsive clamp so a PanelHeader
|
|
19
27
|
title reads at the page-title scale without a bespoke override. Height grows
|
|
@@ -38,3 +46,47 @@
|
|
|
38
46
|
font-family: var(--kui-font-mono);
|
|
39
47
|
font-size: var(--kui-font-xs);
|
|
40
48
|
}
|
|
49
|
+
|
|
50
|
+
/* Single line, no ellipsis: hard-clip the overflow instead of showing …. */
|
|
51
|
+
.kui-toolbar-text[data-ellipsis="false"] .kui-toolbar-text__text {
|
|
52
|
+
text-overflow: clip;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Wrap onto multiple lines. The box grows to fit and switches from the centered
|
|
56
|
+
single-line row to a top-aligned block with readable leading; a min-height keeps
|
|
57
|
+
a one-line label at the toolbar row height. */
|
|
58
|
+
.kui-toolbar-text[data-wrap="true"] {
|
|
59
|
+
align-items: flex-start;
|
|
60
|
+
height: auto;
|
|
61
|
+
min-height: 2.5rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.kui-toolbar-text[data-size="xlarge"][data-wrap="true"] {
|
|
65
|
+
min-height: 2.75rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.kui-toolbar-text[data-wrap="true"] .kui-toolbar-text__text {
|
|
69
|
+
white-space: normal;
|
|
70
|
+
line-height: 1.25;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Wrap capped to N lines with an ellipsis at the cut. `-webkit-line-clamp` needs
|
|
74
|
+
the box display and emits the … itself; the unprefixed `line-clamp` follows for
|
|
75
|
+
engines that ship it. */
|
|
76
|
+
.kui-toolbar-text[data-wrap="true"][data-max-lines] .kui-toolbar-text__text {
|
|
77
|
+
display: -webkit-box;
|
|
78
|
+
-webkit-box-orient: vertical;
|
|
79
|
+
-webkit-line-clamp: var(--kui-toolbar-text-max-lines);
|
|
80
|
+
line-clamp: var(--kui-toolbar-text-max-lines);
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Wrap capped to N lines without an ellipsis: clip at a max-height instead of
|
|
85
|
+
line-clamp (which always ellipsizes). */
|
|
86
|
+
.kui-toolbar-text[data-wrap="true"][data-max-lines][data-ellipsis="false"]
|
|
87
|
+
.kui-toolbar-text__text {
|
|
88
|
+
display: block;
|
|
89
|
+
-webkit-line-clamp: none;
|
|
90
|
+
line-clamp: none;
|
|
91
|
+
max-height: calc(var(--kui-toolbar-text-max-lines) * 1.25em);
|
|
92
|
+
}
|
|
@@ -175,6 +175,19 @@
|
|
|
175
175
|
--wa-form-control-border-color: var(--wa-color-neutral-border-normal);
|
|
176
176
|
--wa-form-control-border-radius: var(--wa-border-radius-m);
|
|
177
177
|
--wa-form-control-activated-color: var(--wa-color-brand-fill-loud);
|
|
178
|
+
/* Field inset: a 1px border with 8px inside it, the same content-item
|
|
179
|
+
geometry the Kerf primitives use (docs/layout.md). WA's defaults are a
|
|
180
|
+
larger 0.75em/1em pad; this brings every form field onto our 8px rhythm
|
|
181
|
+
(and a ~40px single-line control height). */
|
|
182
|
+
--wa-form-control-border-width: var(--kui-layout-item-border-width, 1px);
|
|
183
|
+
--wa-form-control-padding-block: var(
|
|
184
|
+
--kui-layout-item-padding,
|
|
185
|
+
0.5rem
|
|
186
|
+
);
|
|
187
|
+
--wa-form-control-padding-inline: var(
|
|
188
|
+
--kui-layout-item-padding,
|
|
189
|
+
0.5rem
|
|
190
|
+
);
|
|
178
191
|
--wa-panel-border-radius: var(--wa-border-radius-l);
|
|
179
192
|
--wa-tooltip-background-color: var(--wa-color-neutral-fill-loud);
|
|
180
193
|
--wa-tooltip-content-color: var(--wa-color-neutral-on-loud);
|
|
@@ -184,6 +197,13 @@
|
|
|
184
197
|
--kui-wa-carousel-dot-hit-size: 20px;
|
|
185
198
|
--kui-wa-carousel-control-size: 28px;
|
|
186
199
|
--kui-wa-carousel-icon-size: 16px;
|
|
200
|
+
/* Non-field chrome uses Kerf's control, surface, and container spacing:
|
|
201
|
+
8px inside controls/surfaces, 8px around bordered surfaces, and 16px
|
|
202
|
+
inside the remaining content containers. */
|
|
203
|
+
--kui-wa-control-inset: var(--kui-layout-item-padding, 0.5rem);
|
|
204
|
+
--kui-wa-surface-inset: var(--kui-layout-item-padding, 0.5rem);
|
|
205
|
+
--kui-wa-surface-margin: var(--kui-layout-inline-margin, 0.5rem);
|
|
206
|
+
--kui-wa-container-inset: var(--kui-space-m, 1rem);
|
|
187
207
|
--kui-disclosure-icon-scale: 0.5;
|
|
188
208
|
--wa-shadow-s: 0 1px 3px rgb(0 0 0 / 0.14);
|
|
189
209
|
--wa-shadow-m: 0 0.35rem 1rem rgb(0 0 0 / 0.11);
|
|
@@ -248,9 +268,120 @@
|
|
|
248
268
|
transform: scale(var(--kui-disclosure-icon-scale));
|
|
249
269
|
}
|
|
250
270
|
|
|
271
|
+
/* Bordered/fill surfaces follow the same 8px outer margin + 8px inner
|
|
272
|
+
padding contract as a Kerf content item. Accordion owns one outer margin
|
|
273
|
+
for the group; its items retain connected borders. */
|
|
274
|
+
:is(wa-accordion, wa-card, wa-details, wa-callout, wa-include) {
|
|
275
|
+
margin-inline: var(--kui-wa-surface-margin);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
wa-card,
|
|
279
|
+
wa-details,
|
|
280
|
+
wa-accordion-item {
|
|
281
|
+
/* WA declares this custom property on :host inside each shadow root. The
|
|
282
|
+
important flag is required for a public host override to cross that
|
|
283
|
+
encapsulation boundary. */
|
|
284
|
+
--spacing: var(--kui-wa-surface-inset) !important;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Card halves --spacing for its header block padding upstream, so pin the
|
|
288
|
+
public part to the same full surface inset as its body and footer. */
|
|
289
|
+
wa-card::part(header) {
|
|
290
|
+
padding: var(--kui-wa-surface-inset);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
wa-callout {
|
|
294
|
+
padding: var(--kui-wa-surface-inset) !important;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
wa-include {
|
|
298
|
+
padding: var(--kui-wa-surface-inset);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* Interactive and item chrome follows the content-item 8px inset. */
|
|
302
|
+
wa-tab::part(tab) {
|
|
303
|
+
padding: var(--kui-wa-control-inset);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
wa-tab-panel {
|
|
307
|
+
--padding: var(--kui-wa-container-inset) 0 !important;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
wa-tree-item::part(item) {
|
|
311
|
+
margin-inline: var(--kui-layout-inline-margin, 0.5rem);
|
|
312
|
+
padding-inline-end: var(--kui-wa-control-inset);
|
|
313
|
+
border-radius: var(--kui-layout-rounded-radius, calc(1px + 0.6875rem));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
wa-tag {
|
|
317
|
+
padding-inline: var(--kui-wa-control-inset) !important;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
wa-dropdown-item {
|
|
321
|
+
padding-inline: var(--kui-wa-control-inset) !important;
|
|
322
|
+
}
|
|
323
|
+
|
|
251
324
|
wa-popover {
|
|
252
325
|
--arrow-size: var(--kui-wa-popover-arrow-size);
|
|
253
326
|
}
|
|
327
|
+
|
|
328
|
+
/* A form field's top label is inset by the field's own border + inline padding
|
|
329
|
+
(1px + 8px = 9px) so the label text lines up with the value inside the field,
|
|
330
|
+
and it is styled exactly like a ListHeader label: uppercase, xs, quiet. */
|
|
331
|
+
:is(
|
|
332
|
+
wa-checkbox-group,
|
|
333
|
+
wa-color-picker,
|
|
334
|
+
wa-input,
|
|
335
|
+
wa-known-date,
|
|
336
|
+
wa-number-input,
|
|
337
|
+
wa-radio-group,
|
|
338
|
+
wa-select,
|
|
339
|
+
wa-textarea,
|
|
340
|
+
wa-time-input
|
|
341
|
+
)::part(form-control-label),
|
|
342
|
+
wa-otp-input::part(label),
|
|
343
|
+
wa-slider::part(label) {
|
|
344
|
+
padding-inline: calc(
|
|
345
|
+
var(--wa-form-control-border-width) +
|
|
346
|
+
var(--wa-form-control-padding-inline)
|
|
347
|
+
);
|
|
348
|
+
color: var(--kui-color-neutral-on-quiet, var(--wa-color-text-quiet));
|
|
349
|
+
font-size: var(--kui-font-xs, var(--wa-font-size-xs));
|
|
350
|
+
font-weight: 650;
|
|
351
|
+
letter-spacing: 0.04em;
|
|
352
|
+
line-height: var(--wa-line-height-condensed);
|
|
353
|
+
text-transform: uppercase;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* Checkbox/radio groups and the color picker have no bordered field shell,
|
|
357
|
+
so their control regions need the content-item's 8px inline outer inset. */
|
|
358
|
+
:is(wa-checkbox-group, wa-color-picker, wa-radio-group)::part(
|
|
359
|
+
form-control-input
|
|
360
|
+
) {
|
|
361
|
+
margin-inline: var(--kui-layout-inline-margin, 0.5rem);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
wa-slider::part(slider) {
|
|
365
|
+
margin-inline: var(--kui-layout-inline-margin, 0.5rem);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Secondary captions and hints align with the value inside bordered fields:
|
|
369
|
+
1px border + 8px field padding = a 9px logical inset. */
|
|
370
|
+
wa-known-date::part(field-label),
|
|
371
|
+
:is(
|
|
372
|
+
wa-input,
|
|
373
|
+
wa-known-date,
|
|
374
|
+
wa-number-input,
|
|
375
|
+
wa-otp-input,
|
|
376
|
+
wa-select,
|
|
377
|
+
wa-textarea,
|
|
378
|
+
wa-time-input
|
|
379
|
+
)::part(hint) {
|
|
380
|
+
padding-inline: calc(
|
|
381
|
+
var(--wa-form-control-border-width) +
|
|
382
|
+
var(--wa-form-control-padding-inline)
|
|
383
|
+
);
|
|
384
|
+
}
|
|
254
385
|
}
|
|
255
386
|
|
|
256
387
|
@media (prefers-contrast: more) {
|
package/dist/toolbar-text.d.ts
CHANGED
|
@@ -17,7 +17,23 @@ interface ToolbarTextProps {
|
|
|
17
17
|
headingLevel?: HeadingLevel;
|
|
18
18
|
/** Render the text as an unanimated loading skeleton instead of its value. */
|
|
19
19
|
placeholder?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Wrap onto multiple lines when the text does not fit, instead of the default
|
|
22
|
+
* single line. Combine with `maxLines` to cap the number of lines. Default false.
|
|
23
|
+
*/
|
|
24
|
+
wrap?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Show a trailing ellipsis (…) where the text is truncated — on the single line
|
|
27
|
+
* (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip
|
|
28
|
+
* instead. Default true.
|
|
29
|
+
*/
|
|
30
|
+
ellipsis?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Cap wrapped text to this many lines, truncating past it. Only takes effect with
|
|
33
|
+
* `wrap`; ignored on a single line. `null`/omitted wraps without a line cap. Default null.
|
|
34
|
+
*/
|
|
35
|
+
maxLines?: number | null;
|
|
20
36
|
}
|
|
21
|
-
declare function ToolbarText({ text, size, className, id, headingLevel, placeholder }: ToolbarTextProps): kerfjs.SafeHtml;
|
|
37
|
+
declare function ToolbarText({ text, size, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, }: ToolbarTextProps): kerfjs.SafeHtml;
|
|
22
38
|
|
|
23
39
|
export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
|
package/dist/toolbar-text.js
CHANGED
package/docs/catalog.md
CHANGED
|
@@ -22,8 +22,9 @@ npm install @kerfjs/ui # kerfjs is a peer; @kerfjs/ui/select/register is neede
|
|
|
22
22
|
## What you supply
|
|
23
23
|
|
|
24
24
|
- **`sections`** — category-grouped entries: `{ category, entries: [{ id, name,
|
|
25
|
-
description?, resources?, related? }] }`. Each entry becomes a sidebar
|
|
26
|
-
under a `ListHeader` for its category.
|
|
25
|
+
description?, tags?, resources?, related? }] }`. Each entry becomes a sidebar
|
|
26
|
+
`ListItem` under a `ListHeader` for its category. Short `tags` render as quiet
|
|
27
|
+
trailing pills for decision metadata such as `Discouraged`.
|
|
27
28
|
- **`content`** — the rendered preview for the active entry. Keep a map of `id →
|
|
28
29
|
() => SafeHtml` in your app and call `renderers[active]()` in your render.
|
|
29
30
|
- **`brand`** — `{ title, subtitle?, logoUrl? }` for the sidebar header.
|
|
@@ -86,7 +87,7 @@ const sections: CatalogSection[] = [
|
|
|
86
87
|
entries: [
|
|
87
88
|
{ id: 'button', name: 'Button', description: 'A pressable control.',
|
|
88
89
|
resources: [{ label: 'Source', href: '/src/button.tsx', detail: 'src/button.tsx' }] },
|
|
89
|
-
{ id: 'field', name: 'Field', description: 'A labeled input.',
|
|
90
|
+
{ id: 'field', name: 'Field', description: 'A labeled input.', tags: ['Discouraged'],
|
|
90
91
|
related: [{ id: 'button', name: 'Button', group: 'Used with' }] },
|
|
91
92
|
],
|
|
92
93
|
},
|
|
@@ -65,7 +65,7 @@ an upstream component or recipe request.
|
|
|
65
65
|
| Application toolbar — `Toolbar` | Leading identity, optional centered content, and trailing controls form one horizontal app bar. | Do not use it for a page, panel, or dialog heading; use `PanelHeader`. | Compose `ToolbarText` and `ToolbarControlGroup` where their contracts fit. | Actions, command availability, responsive relocation, and state. | `@kerfjs/ui/toolbar` | [Toolbar composition](../README.md#component-subpaths) |
|
|
66
66
|
| Toolbar control cluster — `ToolbarControlGroup` | Related toolbar controls need contained, borderless, pressed, or single-control treatment. | Do not use it merely to align unrelated buttons; use toolbar slots or ordinary layout. Web Awesome `wa-button-group` is only for an exceptional grouped-action contract. | Delegate child actions; use `SegmentedControl` for an exclusive choice. | Actions, pressed/expanded state, and policy. | `@kerfjs/ui/toolbar-control-group` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
67
67
|
| Floating controls over content — `FloatingToolbar` | A small cluster of controls (e.g. a drawer restore) must float over scrolling content — distinct and forced-dark — inside a positioned container. | Do not use it for a primary page or panel toolbar (use `Toolbar`), or for anything that must sit above dialogs/overlays — it is not top-layer. | Compose `ToolbarControlGroup`s and delegate their actions; the app owns visibility and position. | Controls, visibility, and position. | `@kerfjs/ui/floating-toolbar` | [Component subpaths](../README.md#component-subpaths) |
|
|
68
|
-
| Toolbar identity text — `ToolbarText` | A toolbar needs extra-large (page/panel title), large, default, or compact textual identity. | Plain text is not a heading by default; when a title needs heading semantics pass `headingLevel` (or prefer `PanelHeader`, which forwards it). | Optional `headingLevel` for `role="heading"` + `aria-level`. | Text, heading level, and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
|
|
68
|
+
| Toolbar identity text — `ToolbarText` | A toolbar needs extra-large (page/panel title), large, default, or compact textual identity. | Plain text is not a heading by default; when a title needs heading semantics pass `headingLevel` (or prefer `PanelHeader`, which forwards it). | Optional `headingLevel` for `role="heading"` + `aria-level`. Defaults to one line, ellipsized when it does not fit; `wrap` flows onto multiple lines, `ellipsis` toggles the trailing …, and `maxLines` caps a wrap. | Text, heading level, overflow behavior, and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
|
|
69
69
|
| Navigation row — `ListItem` | A pane or navigation area needs a selectable, disabled, dormant-trailing, or multiline action row. | Do not put a control in `trailing`; use `ListActionRow` when the trailing region must be independently interactive. Use an `<a>` for navigation that must retain link behavior, a native `<button>` for an ordinary action, or implement the complete ARIA menu widget. | Delegate its `data-action`; compose inside a `.kui-content` section. Put domain event/drop metadata in `rootAttributes` rather than adding wrapper markup. | Routing, selection, permissions, copy, action handling, and domain `data-*` values. | `@kerfjs/ui/list-item` | [Pane geometry](../README.md#pane-and-content-geometry) |
|
|
70
70
|
| Navigation row with trailing action — `ListActionRow` | A full-width row needs a selectable primary action and an independently focusable trailing action. | Use `ListItem` when trailing content is dormant metadata. Do not put controls inside the row's `label`, `icon`, or `trailingActionIcon` SafeHtml slots. Do not use `AppTab` outside tablist semantics or `ToolbarControlGroup` outside a toolbar. | Delegate both action strings; update controlled selection and any popover/context-menu state in the app. | Routing, selection, both action policies, domain metadata, and popover/context-menu behavior. | `@kerfjs/ui/list-action-row` | [Accessibility](./accessibility.md#listactionrow) |
|
|
71
71
|
| Navigation section heading — `ListHeader` | A menu section needs a full-width label, semantic count, non-count badge, logical-end action, or real disclosure state. | Do not concatenate counts into `label` or put numeric content in `badge`; use `count` with the localized full phrase in `countLabel`. Do not add a disclosure arrow to navigation that reveals nothing. Do not shrink its 44px action target to the 18px visual. Do not use it as a page, panel, or dialog title; use `PanelHeader`. | Delegate its optional action; the app controls expanded state and revealed content. Toggle mode supplies `DisclosureArrow` unless `actionIcon` replaces it. Use `triggerAttributes` only for domain `data-*` or a native popover relationship. | Section organization, valid count and localized count label, disclosure state and content, non-count badge content, popover target behavior, and policy. | `@kerfjs/ui/list-header` | [Pane geometry](../README.md#pane-and-content-geometry) |
|
|
@@ -167,7 +167,10 @@ forks the package anatomy and spacing contract.
|
|
|
167
167
|
|
|
168
168
|
Web Awesome catalog coverage means supported and themed, not preferred. Import
|
|
169
169
|
`@kerfjs/ui/webawesome` for Kerf JSX types, individual component modules for
|
|
170
|
-
registration, and the CSS-only `@kerfjs/ui/webawesome.css` theme.
|
|
170
|
+
registration, and the CSS-only `@kerfjs/ui/webawesome.css` theme. The UX catalog
|
|
171
|
+
marks the superseded or exceptional choices below with a visible `Discouraged`
|
|
172
|
+
tag; Popup remains untagged because low-level anchored positioning can be the
|
|
173
|
+
right primitive.
|
|
171
174
|
|
|
172
175
|
| Web Awesome choice | Kerf decision |
|
|
173
176
|
| --- | --- |
|
package/docs/ux-demo.md
CHANGED
|
@@ -10,9 +10,9 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
|
|
|
10
10
|
- The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
|
|
11
11
|
- The `AppTab` route demonstrates a runtime-filtered domain metadata slot and a decorative replacement close icon without changing TabBar selection, close, reorder, or focus ownership. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
|
|
12
12
|
- Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
|
|
13
|
-
- `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Demo paths resolve to `ui/ux-demo/
|
|
13
|
+
- `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Non-supported Web Awesome recommendations project to a visible `Discouraged` sidebar tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<browser-subpath>.tsx` from their canonical `delivery.browserImport`.
|
|
14
14
|
- Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
|
|
15
|
-
- The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
|
|
15
|
+
- The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
|
|
16
16
|
- The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with the subtitle in its own aligned row like `PanelHeader`; the current-view heading is also a toolbar. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to the complete scrollable preview region rather than a nested card. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
|
|
17
17
|
- The composer-form recipe demonstrates one coherent visible form surface with `PanelHeader` title/summary hierarchy, fields and actions on the shared 8px control gutter, and 24px major rhythm. Its conditional error or success `StateBanner` is the only nested semantic surface. Reset synchronizes the upgraded Web Awesome fields' live value properties with their empty controlled attributes and announces `Draft reset`; browser coverage preserves behavior and verifies wide, narrow, dark, validation, success, reset, 200% zoom, and forced-colors states.
|
|
18
18
|
- The list-detail dialog recipe places its header directly on the dialog edge while preserving the header's internal inset, gives the selected project title the complete outer-plus-inner content gutter, lets the value table fill the available detail column between the shared outer margins, and keeps record actions on one outer gutter without an extra content-item inset. Browser geometry coverage protects those relationships at wide and narrow widths.
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -54,6 +54,71 @@ pointer targets. Override `--kui-wa-carousel-icon-size`,
|
|
|
54
54
|
`--kui-wa-carousel-dot-hit-size` on a carousel or containing scope when a
|
|
55
55
|
product needs different geometry.
|
|
56
56
|
|
|
57
|
+
Form fields carry the same content-item inset as the Kerf primitives: a 1px
|
|
58
|
+
border with 8px inside it (`--wa-form-control-border-width`,
|
|
59
|
+
`--wa-form-control-padding-block`, and `--wa-form-control-padding-inline` are set
|
|
60
|
+
to the `--kui-layout-item-border-width` / `--kui-layout-item-padding` values), so
|
|
61
|
+
a single-line control lands at the standard ~40px height and the value sits 9px
|
|
62
|
+
in from the field edge. Each field's top label is inset by that same border +
|
|
63
|
+
padding (9px) so it lines up with the value inside the field, and is styled
|
|
64
|
+
exactly like a `ListHeader` label — uppercase, `--kui-font-xs`, weight 650, quiet
|
|
65
|
+
foreground. This applies to every free field that exposes a top label
|
|
66
|
+
(`::part(form-control-label)` plus the Slider's `::part(label)`). OTP Input
|
|
67
|
+
exposes `label` instead of `form-control-label`; the theme gives it the same
|
|
68
|
+
uppercase 12px/650 treatment. Inline control labels (Checkbox, Switch) keep
|
|
69
|
+
their natural sentence case.
|
|
70
|
+
|
|
71
|
+
Known Date's field captions and bordered text-like field hints use the same 9px
|
|
72
|
+
inline inset, keeping secondary text aligned with the value rather than the
|
|
73
|
+
field's outer border. This covers Input, Known Date, Number Input, OTP Input,
|
|
74
|
+
Select, Textarea, and Time Input hints; unbordered group hints retain their own
|
|
75
|
+
natural geometry. OTP Input's label and hint both use the 9px field-text inset.
|
|
76
|
+
|
|
77
|
+
Checkbox Group and Radio Group option regions receive the shared 8px inline
|
|
78
|
+
outer inset through `::part(form-control-input)`. Unlike bordered fields, these
|
|
79
|
+
groups have no shell of their own, so the explicit margin places their controls
|
|
80
|
+
on the same content-item geometry without adding padding to each option. The
|
|
81
|
+
Color Picker trigger receives the same 8px inline outer inset because it also
|
|
82
|
+
has no bordered field shell. Slider's complete interactive region receives the
|
|
83
|
+
shared 8px logical inline outer inset through `::part(slider)`, keeping its
|
|
84
|
+
track, markers, references, and hint together without moving the label.
|
|
85
|
+
|
|
86
|
+
Non-field chrome follows explicit, overridable control, surface, and container
|
|
87
|
+
tiers:
|
|
88
|
+
|
|
89
|
+
- `--kui-wa-control-inset` defaults to the 8px content-item padding. It applies
|
|
90
|
+
to tabs, tree-item trailing content, tags, and dropdown items. Tree selection
|
|
91
|
+
also uses Kerf's 8px inline outer margin, so its background reads as an inset
|
|
92
|
+
list item instead of an edge-to-edge stripe. Buttons already consume the same
|
|
93
|
+
`--wa-form-control-padding-inline` 8px value at every size, so no extra part
|
|
94
|
+
override is needed.
|
|
95
|
+
- `--kui-wa-surface-margin` and `--kui-wa-surface-inset` default to 8px.
|
|
96
|
+
Accordion, Card, Details, Callout, and Include use the margin around their
|
|
97
|
+
complete surface and the inset within it. Accordion applies the outer margin
|
|
98
|
+
once to the group rather than separating connected items; Card's header,
|
|
99
|
+
body, and footer all receive the full inset.
|
|
100
|
+
- `--kui-wa-container-inset` defaults to the 16px homogeneous-group step. Tab
|
|
101
|
+
Panel retains this roomier tier for unframed panel content.
|
|
102
|
+
|
|
103
|
+
Badge remains intentionally compact at Web Awesome's intrinsic `0.375em` block /
|
|
104
|
+
`0.625em` inline padding (4.5px / 7.5px at its default 12px text size): it is a
|
|
105
|
+
short status or count, not a content item. Breadcrumb has no bordered or filled
|
|
106
|
+
container, and Scroller delegates item chrome to its slotted children, so both
|
|
107
|
+
retain their upstream geometry. Web Awesome 3.12's free catalog has
|
|
108
|
+
`wa-dropdown-item`, but no `wa-menu-item`; the theme therefore styles the
|
|
109
|
+
supported dropdown item rather than carrying a dead selector.
|
|
110
|
+
|
|
111
|
+
Override a tier for a product scope after importing the theme:
|
|
112
|
+
|
|
113
|
+
```css
|
|
114
|
+
.roomy-editor {
|
|
115
|
+
--kui-wa-control-inset: 0.75rem;
|
|
116
|
+
--kui-wa-surface-margin: 0.75rem;
|
|
117
|
+
--kui-wa-surface-inset: 0.75rem;
|
|
118
|
+
--kui-wa-container-inset: 1.5rem;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
57
122
|
Accordion, Details, Breadcrumb, and Kerf Select share
|
|
58
123
|
`--kui-disclosure-icon-scale` (default `.5`) so disclosure and traversal
|
|
59
124
|
chevrons keep the same visual weight. The Accordion and Details components
|
package/llms.txt
CHANGED
|
@@ -40,6 +40,21 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
40
40
|
own the geometry (8px inline margin + 1px border + 8px padding). Do not add a
|
|
41
41
|
container padding on top of a content-item's own margin — pick one owner. Use
|
|
42
42
|
`.kui-content` for 24px major vertical rhythm.
|
|
43
|
+
- **Keep Web Awesome on the shared inset tiers.** `webawesome.css` uses
|
|
44
|
+
`--kui-wa-control-inset` (8px controls/items) and
|
|
45
|
+
`--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside
|
|
46
|
+
Accordion, Card, Details, Callout, and Include), while
|
|
47
|
+
`--kui-wa-container-inset` keeps Tab Panel at 16px. Override the tier at the
|
|
48
|
+
narrowest useful scope instead of restyling individual shadow parts.
|
|
49
|
+
Checkbox and Radio Group option regions also receive the shared 8px inline
|
|
50
|
+
outer inset because those groups have no bordered field shell. The Color
|
|
51
|
+
Picker trigger receives the same inset for the same unbordered geometry.
|
|
52
|
+
Slider's complete interactive region uses the shared 8px logical inline outer
|
|
53
|
+
inset without shifting its already-aligned label.
|
|
54
|
+
Known Date's Month/Day/Year captions and bordered text-like field hints align
|
|
55
|
+
with field values at the shared 9px border-plus-padding inset. OTP Input's
|
|
56
|
+
label uses the same uppercase xs/650 treatment and its label and hint share
|
|
57
|
+
that 9px alignment.
|
|
43
58
|
- **Trust the defaults.** Render a control at its natural size and color (a
|
|
44
59
|
`LucideIcon` is 24px); if it looks wrong, fix the surrounding layout, not the
|
|
45
60
|
control. Never force a component's width/height to size it — that leaves a halo
|
|
@@ -130,6 +145,6 @@ declared side effects.
|
|
|
130
145
|
- [accessibility.md](./docs/accessibility.md): keyboard and ARIA contracts per component.
|
|
131
146
|
- [recipes.md](./docs/recipes.md): production compositions (app shell, list-detail dialog, composer form, …) as copyable references.
|
|
132
147
|
- [app-layouts.md](./docs/app-layouts.md): choosing `NavStack`/`SplitView`/`Workbench`/`TabScaffold` by data + interaction + device, and dialog presentation.
|
|
133
|
-
- [webawesome-theme.md](./docs/webawesome-theme.md): the optional Web Awesome theme; catalog coverage means themed-and-supported, not preferred (prefer the Kerf primitive when one exists).
|
|
148
|
+
- [webawesome-theme.md](./docs/webawesome-theme.md): the optional Web Awesome theme; catalog coverage means themed-and-supported, not preferred (prefer the Kerf primitive when one exists). The UX sidebar visibly tags superseded and exceptional Web Awesome choices as `Discouraged`; Popup remains an available conditional positioning primitive.
|
|
134
149
|
- [component-catalog.json](./ai/component-catalog.json) + [public-api-signatures-v1.md](./ai/public-api-signatures-v1.md) + [webawesome-jsx-signatures-v1.md](./ai/webawesome-jsx-signatures-v1.md): machine-readable facts and exact signatures.
|
|
135
150
|
- [README.md](./README.md): package overview and the component subpath table.
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/wire-token-search-fields.ts"],"names":["block"],"mappings":"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","file":"chunk-LCOBDOCC.js","sourcesContent":["import { delegate, type Signal,signal } from 'kerfjs';\n\nimport { placeTokenSearchCaret, readTokenSearchField } from './token-search-field.js';\n\nexport interface TokenSearchSubmit {\n id: string;\n editor: HTMLElement;\n}\n\n/** Reported to {@link WireTokenSearchFieldsOptions.onEdit} on every editor input. */\nexport interface TokenSearchEdit extends TokenSearchSubmit {\n /**\n * The `InputEvent` that mutated the editor — read `event.inputType` / `event.data`\n * to gate commit behavior (e.g. only parse a chip on whitespace-terminated input)\n * without keeping a separate `input` listener.\n */\n event: InputEvent;\n}\n\n/** Reported when adjacent-token keyboard deletion asks the app to drop a chip. */\nexport interface TokenSearchTokenRemoval {\n id: string;\n /** The `data-token-value` of the token the app should remove from its state. */\n value: string;\n editor: HTMLElement;\n /** `'backward'` = the token before the caret (Backspace); `'forward'` = after (Delete). */\n direction: 'backward' | 'forward';\n}\n\n/**\n * Opt-in keyboard behavior for the atomic token chips. Off unless `keyboard` is\n * set; each piece defaults on once opted in. The helper never mutates app state:\n * a removal is reported through {@link TokenSearchKeyboardOptions.onRemoveToken}\n * for the caller to apply, while caret movement past a chip is a pure ephemeral\n * mechanic the helper performs itself.\n */\nexport interface TokenSearchKeyboardOptions {\n /**\n * From a collapsed caret with no selection, Backspace removes the token\n * immediately before it and Delete the token immediately after — reported via\n * `onRemoveToken` — instead of deleting a character. Default: true.\n */\n removeAdjacentToken?: boolean;\n /**\n * ArrowRight moves the caret past a trailing atomic token so text typed next\n * lands after the chip. Default: true.\n */\n moveCaretPastToken?: boolean;\n /** Apply the reported removal to your controlled state, then re-render. */\n onRemoveToken?: (removal: TokenSearchTokenRemoval) => void;\n}\n\n/**\n * Managed collapsible behavior for the iconic TokenSearchField. Every piece is on\n * by default; disable a specific one to own it in the app. Provide `signals` to\n * drive app-owned `expanded` signals per field id instead of helper-created ones.\n */\nexport interface TokenSearchCollapsibleOptions {\n /** Expand the field and focus its editor when the iconic trigger is activated. Default: true. */\n expandOnActivate?: boolean;\n /** Collapse the field when focus leaves it while it is empty. Default: true. */\n collapseOnEmptyBlur?: boolean;\n /** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */\n collapseOnEscape?: boolean;\n /** Focus the editor on expand and the trigger on Escape-collapse. Default: true. */\n manageFocus?: boolean;\n /**\n * Keep an empty field expanded when focus moves to a caller-owned surface\n * rendered outside the field — a suggestions dropdown, date picker, or help\n * popover shown beside it. Return true for any focus target that must NOT\n * trigger collapse-on-empty-blur. An element carrying `data-token-search-keep-open`\n * (or any node inside one) is always exempt, so this predicate is only needed\n * for surfaces you cannot mark declaratively.\n */\n keepOpenOn?: (target: Node | null) => boolean;\n /** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */\n signals?: Readonly<Record<string, Signal<boolean>>>;\n}\n\nexport interface WireTokenSearchFieldsOptions {\n onSubmit?: (submission: TokenSearchSubmit) => void;\n /** Fired on every editor `input`, after the browser mutates it, so a caller can drop its own `input` listener. */\n onEdit?: (edit: TokenSearchEdit) => void;\n /** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */\n collapsible?: boolean | TokenSearchCollapsibleOptions;\n /** Opt-in atomic-chip keyboard behavior (off by default). `true` = on with defaults. */\n keyboard?: boolean | TokenSearchKeyboardOptions;\n}\n\n/**\n * The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to\n * tear down. When collapsible behavior is managed, it also exposes the transient\n * `expanded` state per field id so the app can read it in render, hand in its own\n * signal, or drive it imperatively.\n */\nexport interface TokenSearchFieldsHandle {\n (): void;\n dispose(): void;\n /** The managed `expanded` signal for a field id (adopted or helper-created); undefined when unmanaged. */\n expanded(id: string): Signal<boolean> | undefined;\n /** Expand the field (and, when focus is managed, focus its editor). */\n open(id: string): void;\n /** Collapse the field (and, when focus is managed, restore focus to its trigger). */\n close(id: string): void;\n}\n\ninterface PendingTokenDeletion {\n id: string;\n offset: number;\n tokenCount: number;\n}\n\nfunction editorFromEvent(root: HTMLElement, event: Event): HTMLElement | undefined {\n const target = event.target;\n if (!(target instanceof Element)) return;\n const editor = target.closest<HTMLElement>('[data-token-search-editor]');\n return editor && root.contains(editor) ? editor : undefined;\n}\n\nfunction caretQueryOffset(editor: HTMLElement): number | undefined {\n const selection = editor.ownerDocument.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n const selectionRange = selection.getRangeAt(0);\n if (!editor.contains(selectionRange.startContainer)) return;\n const prefixRange = editor.ownerDocument.createRange();\n prefixRange.selectNodeContents(editor);\n prefixRange.setEnd(selectionRange.startContainer, selectionRange.startOffset);\n const prefix = editor.ownerDocument.createElement('div');\n prefix.append(prefixRange.cloneContents());\n return readTokenSearchField(prefix).query.length;\n}\n\nfunction replacementEditor(root: HTMLElement, id: string): HTMLElement | undefined {\n return [...root.querySelectorAll<HTMLElement>('[data-token-search-editor]')]\n .find((editor) => editor.dataset.tokenSearchEditor === id);\n}\n\nfunction editorIsEmpty(editor: HTMLElement): boolean {\n const value = readTokenSearchField(editor);\n return value.query.length === 0 && value.tokens.length === 0;\n}\n\nconst TOKEN_SELECTOR = '[data-component=\"token-search-token\"]';\nconst TEXT_SELECTOR = '[data-token-search-text]';\nconst stripZwsp = (text: string): string => text.replaceAll('', '');\n\n/** The collapsed, in-editor caret range, or undefined when there is a selection or none. */\nfunction collapsedCaret(editor: HTMLElement): Range | undefined {\n const selection = editor.ownerDocument.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n const range = selection.getRangeAt(0);\n if (!range.collapsed || !editor.contains(range.startContainer)) return;\n return range;\n}\n\n/** The direct child of `editor` that contains `node` (or `node` itself), else null. */\nfunction topBlock(editor: HTMLElement, node: Node): Node | null {\n let current: Node = node;\n while (current.parentNode && current.parentNode !== editor) current = current.parentNode;\n return current.parentNode === editor ? current : null;\n}\n\n/** A text node or text span holding no visible character (only zero-width spacers). */\nfunction isBlankText(node: Node): boolean {\n if (node.nodeType === Node.TEXT_NODE) return stripZwsp((node as CharacterData).data) === '';\n return node instanceof Element && node.matches(TEXT_SELECTOR) && stripZwsp(node.textContent ?? '') === '';\n}\n\n/** The nearest sibling block on `direction`, skipping blank text spacers. */\nfunction meaningfulSibling(block: Node, direction: 'backward' | 'forward'): Node | null {\n let sibling = direction === 'backward' ? block.previousSibling : block.nextSibling;\n while (sibling && isBlankText(sibling)) sibling = direction === 'backward' ? sibling.previousSibling : sibling.nextSibling;\n return sibling;\n}\n\n/** The node immediately on `direction` of the caret, only when no visible character separates them. */\nfunction caretSideNode(editor: HTMLElement, range: Range, direction: 'backward' | 'forward'): Node | null {\n const { startContainer: container, startOffset: offset } = range;\n if (container.nodeType === Node.TEXT_NODE) {\n const text = (container as CharacterData).data;\n const side = direction === 'backward' ? text.slice(0, offset) : text.slice(offset);\n if (stripZwsp(side).length > 0) return null;\n // An in-editor text node always has a top block (itself or its wrapping span).\n return meaningfulSibling(topBlock(editor, container)!, direction);\n }\n const child = container.childNodes[direction === 'backward' ? offset - 1 : offset] as Node | undefined;\n if (child) {\n // `child` is a descendant of the in-editor caret container, so it has a top block.\n const block = topBlock(editor, child)!;\n return isBlankText(child) ? meaningfulSibling(block, direction) : block;\n }\n const block = topBlock(editor, container);\n return block ? meaningfulSibling(block, direction) : null;\n}\n\n/** The atomic token chip adjacent to a collapsed caret on `direction`, if any. */\nfunction adjacentToken(editor: HTMLElement, direction: 'backward' | 'forward'): HTMLElement | undefined {\n const range = collapsedCaret(editor);\n if (!range) return;\n const node = caretSideNode(editor, range, direction);\n if (!node) return;\n const base = node instanceof Element ? node : node.parentElement;\n const chip = base?.closest<HTMLElement>(TOKEN_SELECTOR) ?? null;\n return chip && editor.contains(chip) ? chip : undefined;\n}\n\n/** Move the caret to just after an atomic token chip so typed text lands after it.\n * Only reached after {@link collapsedCaret} already found a live selection, and the\n * chip is a direct block of the editor. */\nfunction placeCaretAfterToken(editor: HTMLElement, chip: HTMLElement): void {\n const selection = editor.ownerDocument.getSelection()!;\n const range = editor.ownerDocument.createRange();\n range.setStartAfter(topBlock(editor, chip)!);\n range.collapse(true);\n selection.removeAllRanges();\n selection.addRange(range);\n editor.focus({ preventScroll: true });\n}\n\n/**\n * Wire every TokenSearchField under `root`: submit on Enter, preserve the caret across\n * controlled token deletion, and (by default) manage the collapsible field's transient\n * expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also\n * exposes the managed `expanded` state per field id.\n */\nexport function wireTokenSearchFields(\n root: HTMLElement,\n { onSubmit, onEdit, collapsible = true, keyboard = false }: WireTokenSearchFieldsOptions = {},\n): TokenSearchFieldsHandle {\n const managed = collapsible !== false;\n const config = typeof collapsible === 'object' ? collapsible : {};\n const expandOnActivate = managed && (config.expandOnActivate ?? true);\n const collapseOnEmptyBlur = managed && (config.collapseOnEmptyBlur ?? true);\n const collapseOnEscape = managed && (config.collapseOnEscape ?? true);\n const manageFocus = managed && (config.manageFocus ?? true);\n const keepOpenOn = config.keepOpenOn;\n const keyboardOn = keyboard !== false;\n const keyboardConfig = typeof keyboard === 'object' ? keyboard : {};\n const removeAdjacentToken = keyboardOn && (keyboardConfig.removeAdjacentToken ?? true);\n const moveCaretPastToken = keyboardOn && (keyboardConfig.moveCaretPastToken ?? true);\n const onRemoveToken = keyboardConfig.onRemoveToken;\n const adopted = config.signals ?? {};\n const created = new Map<string, Signal<boolean>>();\n\n /** True when focus moving to `element` should keep an empty field expanded. */\n const isExemptTarget = (element: Element | null): boolean => {\n if (!element) return false;\n if (element.closest('[data-token-search-keep-open]')) return true;\n return keepOpenOn ? keepOpenOn(element) : false;\n };\n\n const signalFor = (id: string): Signal<boolean> => {\n const own = adopted[id];\n if (own) return own;\n let field = created.get(id);\n if (!field) {\n field = signal(false);\n created.set(id, field);\n }\n return field;\n };\n const setExpanded = (id: string, next: boolean) => {\n const state = signalFor(id);\n if (state.value !== next) state.value = next;\n };\n const view = () => root.ownerDocument.defaultView!;\n const focusAfterRender = (id: string, selector: string) => {\n view().requestAnimationFrame(() => {\n const target = root.querySelector<HTMLElement>(\n `[data-component=\"token-search-field\"][data-token-search-id=\"${CSS.escape(id)}\"] ${selector}`,\n );\n if (!target) return;\n if (target.matches('[data-token-search-editor]')) placeTokenSearchCaret(target);\n else target.focus();\n });\n };\n const openField = (id: string) => {\n setExpanded(id, true);\n if (manageFocus) focusAfterRender(id, '[data-token-search-editor]');\n };\n const closeField = (id: string) => {\n setExpanded(id, false);\n if (manageFocus) focusAfterRender(id, '.kui-token-search__expand');\n };\n\n const pending = new WeakMap<HTMLElement, PendingTokenDeletion>();\n const onBeforeInput = (event: Event) => {\n const inputEvent = event as InputEvent;\n const editor = editorFromEvent(root, event);\n if (!editor || !inputEvent.inputType.startsWith('delete') || editor.ownerDocument.activeElement !== editor) return;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n const offset = caretQueryOffset(editor);\n if (!id || offset === undefined || field?.dataset.disabled === 'true') return;\n pending.set(editor, {\n id,\n offset,\n tokenCount: editor.querySelectorAll('[data-component=\"token-search-token\"]').length,\n });\n };\n const onInput = (event: Event) => {\n const editor = editorFromEvent(root, event);\n if (!editor) return;\n if (onEdit) {\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (id && field?.dataset.disabled !== 'true') onEdit({ id, editor, event: event as InputEvent });\n }\n const deletion = pending.get(editor);\n pending.delete(editor);\n if (!deletion || editor.querySelectorAll('[data-component=\"token-search-token\"]').length >= deletion.tokenCount) return;\n editor.ownerDocument.defaultView!.requestAnimationFrame(() => {\n const replacement = replacementEditor(root, deletion.id);\n if (!replacement) return;\n const active = replacement.ownerDocument.activeElement;\n if (active !== editor && active !== replacement && active !== replacement.ownerDocument.body) return;\n placeTokenSearchCaret(replacement, deletion.offset);\n });\n };\n root.addEventListener('beforeinput', onBeforeInput, true);\n root.addEventListener('input', onInput, true);\n\n const disposers: Array<() => void> = [\n () => root.removeEventListener('beforeinput', onBeforeInput, true),\n () => root.removeEventListener('input', onInput, true),\n ];\n\n disposers.push(\n delegate(root, 'keydown', '[data-token-search-editor]', (event, element) => {\n const keyboardEvent = event as KeyboardEvent;\n const editor = element as HTMLElement;\n if (keyboardEvent.isComposing) return;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (!id || field?.dataset.disabled === 'true') return;\n if (keyboardEvent.key === 'Enter') {\n keyboardEvent.preventDefault();\n onSubmit?.({ id, editor });\n return;\n }\n if (removeAdjacentToken && (keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete')) {\n const direction = keyboardEvent.key === 'Backspace' ? 'backward' : 'forward';\n const chip = adjacentToken(editor, direction);\n if (chip) {\n keyboardEvent.preventDefault();\n onRemoveToken?.({ id, value: chip.dataset.tokenValue ?? '', editor, direction });\n return;\n }\n }\n if (moveCaretPastToken && keyboardEvent.key === 'ArrowRight') {\n const chip = adjacentToken(editor, 'forward');\n if (chip) {\n keyboardEvent.preventDefault();\n placeCaretAfterToken(editor, chip);\n return;\n }\n }\n if (collapseOnEscape && keyboardEvent.key === 'Escape' && field?.dataset.collapsible === 'true' && editorIsEmpty(editor)) {\n keyboardEvent.preventDefault();\n closeField(id);\n }\n }),\n );\n\n if (expandOnActivate) {\n disposers.push(\n delegate(root, 'click', '.kui-token-search__expand', (_event, element) => {\n // The selector guarantees a collapsible field ancestor; only skip a disabled one.\n const field = (element as HTMLElement).closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (id && field?.dataset.disabled !== 'true') openField(id);\n }),\n );\n }\n\n if (collapseOnEmptyBlur) {\n // Pressing an in-field control (clear, trailing, token, or the trigger) must not\n // blur the editor — otherwise the transient blur would collapse the field before\n // the control's own handler runs. Keeping focus also keeps the click firing.\n disposers.push(\n delegate(root, 'mousedown', '[data-component=\"token-search-field\"][data-collapsible=\"true\"] button', (event) => {\n event.preventDefault();\n }),\n );\n disposers.push(\n delegate(root, 'focusout', '[data-token-search-editor]', (event, element) => {\n const editor = element as HTMLElement;\n const field = editor.closest<HTMLElement>('[data-component=\"token-search-field\"]');\n const id = field?.dataset.tokenSearchId;\n if (!id || field?.dataset.collapsible !== 'true' || field.dataset.disabled === 'true') return;\n const next = (event as FocusEvent).relatedTarget;\n if (next instanceof Node && field.contains(next)) return;\n if (isExemptTarget(next instanceof Element ? next : null)) return;\n if (!editorIsEmpty(editor)) return;\n view().queueMicrotask(() => {\n const active = field.ownerDocument.activeElement;\n if (!field.contains(active) && !isExemptTarget(active)) setExpanded(id, false);\n });\n }),\n );\n }\n\n const dispose = () => {\n for (const stop of disposers.splice(0)) stop();\n };\n const handle = (() => dispose()) as TokenSearchFieldsHandle;\n handle.dispose = dispose;\n handle.expanded = (id) => (managed ? signalFor(id) : undefined);\n handle.open = (id) => {\n if (managed) openField(id);\n };\n handle.close = (id) => {\n if (managed) closeField(id);\n };\n return handle;\n}\n"]}
|
package/dist/chunk-RUNWEPR4.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Skeleton } from './chunk-B24C4A6T.js';
|
|
2
|
-
import { jsx } from 'kerfjs/jsx-runtime';
|
|
3
|
-
|
|
4
|
-
function ToolbarText({ text, size = "default", className = "", id, headingLevel, placeholder = false }) {
|
|
5
|
-
return /* @__PURE__ */ jsx("span", { class: `kui-toolbar-text ${className}`.trim(), "data-component": "toolbar-text", "data-size": size, "data-placeholder": placeholder ? "true" : void 0, id, role: headingLevel ? "heading" : void 0, "aria-level": headingLevel ? String(headingLevel) : void 0, "aria-busy": placeholder ? "true" : void 0, children: placeholder ? /* @__PURE__ */ jsx(Skeleton, { width: "8em" }) : text });
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export { ToolbarText };
|
|
9
|
-
|
|
10
|
-
//# sourceMappingURL=chunk-RUNWEPR4.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/toolbar-text.tsx"],"names":[],"mappings":";;;AAuBO,SAAS,WAAA,CAAY,EAAE,IAAA,EAAM,IAAA,GAAO,SAAA,EAAW,SAAA,GAAY,EAAA,EAAI,EAAA,EAAI,YAAA,EAAc,WAAA,GAAc,KAAA,EAAM,EAAqB;AAC/H,EAAA,2BAAQ,MAAA,EAAA,EAAK,KAAA,EAAO,CAAA,iBAAA,EAAoB,SAAS,GAAG,IAAA,EAAK,EAAG,gBAAA,EAAe,cAAA,EAAe,aAAW,IAAA,EAAM,kBAAA,EAAkB,WAAA,GAAc,MAAA,GAAS,QAAW,EAAA,EAAQ,IAAA,EAAM,YAAA,GAAe,SAAA,GAAY,QAAW,YAAA,EAAY,YAAA,GAAe,MAAA,CAAO,YAAY,IAAI,MAAA,EAAW,WAAA,EAAW,WAAA,GAAc,MAAA,GAAS,QAAY,QAAA,EAAA,WAAA,mBAAc,GAAA,CAAC,YAAS,KAAA,EAAM,KAAA,EAAM,IAAK,IAAA,EAAK,CAAA;AAC9W","file":"chunk-RUNWEPR4.js","sourcesContent":["import { Skeleton } from './skeleton.js';\n\nexport type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small';\n\n/** ARIA heading level for a title exposed as a heading landmark. */\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport interface ToolbarTextProps {\n text: string;\n size?: ToolbarTextSize;\n className?: string;\n /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */\n id?: string;\n /**\n * Expose heading semantics (`role=\"heading\"` + `aria-level`) so the text acts as\n * a heading landmark — e.g. a page's primary title. Omit to keep the plain span\n * (the default), which suits a dialog title referenced via `aria-labelledby`.\n */\n headingLevel?: HeadingLevel;\n /** Render the text as an unanimated loading skeleton instead of its value. */\n placeholder?: boolean;\n}\n\nexport function ToolbarText({ text, size = 'default', className = '', id, headingLevel, placeholder = false }: ToolbarTextProps) {\n return <span class={`kui-toolbar-text ${className}`.trim()} data-component=\"toolbar-text\" data-size={size} data-placeholder={placeholder ? 'true' : undefined} id={id} role={headingLevel ? 'heading' : undefined} aria-level={headingLevel ? String(headingLevel) : undefined} aria-busy={placeholder ? 'true' : undefined}>{placeholder ? <Skeleton width=\"8em\" /> : text}</span>;\n}\n"]}
|