diff --git a/apps/docs/src/data/navigation.ts b/apps/docs/src/data/navigation.ts index 1161f20..b51f2a4 100644 --- a/apps/docs/src/data/navigation.ts +++ b/apps/docs/src/data/navigation.ts @@ -120,6 +120,7 @@ export const componentsNavigationItems: NavItem[] = [ children: [ { label: "Popover", href: "/components/le-popover" }, { label: "Popup", href: "/components/le-popup" }, + { label: "Context Menu", href: "/components/le-context-menu" }, ], }, { diff --git a/apps/docs/src/pages/components/le-context-menu.astro b/apps/docs/src/pages/components/le-context-menu.astro new file mode 100644 index 0000000..66cdd89 --- /dev/null +++ b/apps/docs/src/pages/components/le-context-menu.astro @@ -0,0 +1,305 @@ +--- +import ComponentsPage from "../../layouts/ComponentsPage.astro"; +import CodeBlock from "../../components/CodeBlock.astro"; +--- + + +

<le-context-menu>

+

+ A custom component that triggers a vertical navigation menu upon right-clicking (mouse/desktop) + or long-pressing (touch/mobile) its children. It is built on top of <le-popover> + and <le-navigation>. +

+ +

Examples

+ +

Standard Right-Click Menu

+

+ Right-click inside the dashed area below to trigger the context menu. By default, the menu positions itself next to the mouse click. +

+ +
+ +
+ Right-click here to open the menu. +
+
+ + +
+ + +
Right-click here.
+ + + +`} + /> + +

iOS-Style Touch Long-Press with Backdrop

+

+ On touch interfaces, long-pressing triggers the menu. The backdrop property dims the screen and lifts the active item above the backdrop overlay, matching native mobile UI patterns. +

+ +
+ +
+ Long-press (touch) or right-click here. +
+
+
+ + +
Long-press or right-click here.
+ +`} + /> + +

Page Scroll Behavior

+

+ You can customize what happens to the menu when the user scrolls the page. Use the page-scroll-behavior property: +

+
    +
  • menu-close (default) — Closes the context menu automatically when page scrolling is detected.
  • +
  • fixed-menu — The context menu remains open and moves relative to the scrolling page/trigger.
  • +
  • blocked — Blocks scroll interactions on the document body entirely while the menu is open.
  • +
+ +
+
+ + Scroll: Close (menu-close) + + + + Scroll: Follow (fixed-menu) + + + + Scroll: Blocked (blocked) + +
+
+ + + + Scroll Close + + + + + Scroll Follow + + + + + Scroll Blocked + +`} + /> + +

Positioning and Alignment Options

+

+ When you specify a layout position other than mouse, the menu aligns itself relative to the trigger element's bounding box. You can configure both position (top, bottom, left, right) and align (start, center, end): +

+ +
+
+ + Top Center + + + + Bottom End + + + + Right Start + +
+
+ + + + Top Center + + + + + Bottom End + + + + + Right Start + +`} + /> + +

Properties / Attributes

+ +

@open

+

+ Controls the visibility of the menu. Reflects as an attribute. Can be toggled programmatically. +

+
    +
  • Type: boolean
  • +
  • Default: false
  • +
+ +

@disabled

+

+ When present or set to true, blocks right-click and touch long-press triggers. +

+
    +
  • Type: boolean
  • +
  • Default: false
  • +
+ +

@items

+

+ An array of menu options to display. Supports nested icons, actions, selection states (checkmarks), and disabling. Passed as a JSON string in HTML or an array in frameworks. +

+
    +
  • Type: LeOption[] | string
  • +
  • Default: []
  • +
+ +

@backdrop

+

+ Whether to show a dimming overlay behind the menu, lifting the active trigger item above the backdrop overlay. +

+
    +
  • Type: boolean
  • +
  • Default: false
  • +
+ +

@page-scroll-behavior

+

+ Determines how the menu reacts when the page is scrolled: +

+
    +
  • menu-close (default) — Closes the context menu automatically when page scrolling is detected.
  • +
  • fixed-menu — The context menu remains open and smoothly scrolls/moves along with the page.
  • +
  • blocked — Blocks scroll interactions on the document body entirely while the menu is open.
  • +
+ +

@position

+

+ Placement direction relative to the trigger container. +

+
    +
  • mouse (default) — Dynamic placement following the click coordinate. Falls back to bottom alignment on touch devices.
  • +
  • top, bottom, left, right — Align next to the specified side of the trigger boundary box.
  • +
+ +

@align

+

+ Alignment relative to the target boundary: start (default), center, or end. +

+ +

Slots

+ +

Default Slot

+

+ The trigger content. Any DOM nodes placed in this slot will act as the right-click/long-press trigger zone. +

+ +

Events

+ +

leContextMenuSelect

+

+ Fired when a menu navigation item is selected. +

+ + +

leContextMenuClose

+

+ Fired when the context menu is closed. +

+
diff --git a/packages/core/LLM_CONTEXT.md b/packages/core/LLM_CONTEXT.md index c2118b3..31e357f 100644 --- a/packages/core/LLM_CONTEXT.md +++ b/packages/core/LLM_CONTEXT.md @@ -17,6 +17,7 @@ This file is auto-generated and contains documentation for all Le-Kit web compon - [le-collapse](#le-collapse) - [le-combobox](#le-combobox) - [le-component](#le-component) +- [le-context-menu](#le-context-menu) - [le-current-heading](#le-current-heading) - [le-drag-handle](#le-drag-handle) - [le-dropdown-base](#le-dropdown-base) @@ -574,6 +575,39 @@ render() { --- +## + +Context menu component that displays a vertical navigation menu +when the user right-clicks or long-presses on its children. + +### Properties + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| `el` | `HTMLElement` | | | +| `open` | `boolean` | `false` | Whether the context menu is open. | +| `disabled` | `boolean` | `false` | Disables right-click and touch interactions. | +| `items` | `LeOption[] \| string` | `[]` | List of menu items represented as options. | +| `backdrop` | `boolean` | `false` | Whether to show a backdrop behind the menu, lifting the active item. | +| `pageScrollBehavior` | `'blocked' \| 'menu-close' \| 'fixed-menu'` | `'menu-close'` | Behavior of the menu on page scroll: - 'blocked': blocks page scroll - 'menu-close': closes the menu automatically on scroll (default) - 'fixed-menu': menu scrolls with the page | +| `position` | `'top' \| 'bottom' \| 'left' \| 'right' \| 'mouse'` | `'mouse'` | Position of the menu relative to the trigger. If 'mouse', positions next to mouse/touch coords. | +| `align` | `'start' \| 'center' \| 'end'` | `'start'` | Alignment of the menu relative to the trigger. | + +### Events + +| Event | Type | Description | +|-------|------|-------------| +| `leContextMenuSelect` | `EventEmitter` | Emitted when a menu item is selected. | +| `leContextMenuClose` | `EventEmitter` | Emitted when the context menu is closed. | + +### Slots + +| Name | Description | +|------|-------------| +| Default | Trigger content | + +--- + ## Shows a "smart" header title based on what has scrolled out of view. diff --git a/packages/core/package.json b/packages/core/package.json index 9c530ae..28bafcc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "le-kit", - "version": "0.7.0", + "version": "0.7.1", "description": "Themable web components library with CMS admin mode support", "main": "dist/index.cjs.js", "module": "dist/index.js", diff --git a/packages/core/src/assets/custom-elements.json b/packages/core/src/assets/custom-elements.json index d862b19..f39c07b 100644 --- a/packages/core/src/assets/custom-elements.json +++ b/packages/core/src/assets/custom-elements.json @@ -1622,30 +1622,60 @@ }, { "kind": "javascript-module", - "path": "src/components/le-button-group/le-button-group.tsx", + "path": "src/components/le-button/le-button.tsx", "declarations": [ { "kind": "class", - "description": "Groups multiple `le-button` elements and optionally collapses low-priority actions\ninto an overflow \"more\" menu.", - "name": "LeButtonGroup", + "description": "A flexible button component with multiple variants and states.", + "name": "LeButton", + "cssProperties": [ + { + "description": "Button background color", + "name": "--le-button-bg" + }, + { + "description": "Button text color", + "name": "--le-button-color" + }, + { + "description": "Button border radius", + "name": "--le-button-border-radius" + }, + { + "description": "Button horizontal padding", + "name": "--le-button-padding-x" + }, + { + "description": "Button vertical padding", + "name": "--le-button-padding-y" + } + ], "cssParts": [ { - "description": "The grouped buttons frame", - "name": "group" + "description": "The native button element", + "name": "button" }, { - "description": "The overflow trigger button", - "name": "more-button" + "description": "The button content wrapper", + "name": "content" + }, + { + "description": "The start icon slot", + "name": "icon-start" + }, + { + "description": "The end icon slot", + "name": "icon-end" } ], "slots": [ { - "description": "Group button elements (`le-button` children)", + "description": "Button text content", "name": "" }, { - "description": "Custom icon/content for the overflow trigger button", - "name": "more" + "description": "Icon for icon-only buttons", + "name": "icon-only" } ], "members": [ @@ -1658,411 +1688,353 @@ }, { "kind": "field", - "name": "label", + "name": "hasIconStartSlot", "type": { - "text": "string | undefined" + "text": "boolean" }, - "description": "Optional label used when the whole group is represented as a parent item\ninside another component's overflow menu." + "privacy": "private", + "default": "false" }, { "kind": "field", - "name": "collapse", + "name": "hasIconEndSlot", "type": { - "text": "boolean | number | string | undefined" + "text": "boolean" }, - "description": "Collapse mode.\n\n- `true`: show only the top-priority button\n- positive number: show top N buttons\n- `0`: show only the more button\n- negative number: hide abs(N) lowest-priority buttons\n\nNon-integers are rounded with `Math.round`." + "privacy": "private", + "default": "false" }, { "kind": "field", - "name": "overflowIcons", + "name": "hasIconOnlySlot", "type": { "text": "boolean" }, - "default": "false", - "description": "When true, icons from collapsed buttons are shown in the overflow navigation list." + "privacy": "private", + "default": "false" }, { "kind": "field", - "name": "disabled", + "name": "disconnectSlotObserver", "type": { - "text": "boolean" + "text": "() => void | undefined" }, - "default": "false", - "description": "Disabled attribute, when the button group is disabled,\nall buttons inside will be disabled and the overflow menu will not be accessible." + "privacy": "private" }, { "kind": "field", - "name": "visibility", + "name": "mode", "type": { - "text": "'visible' | 'collapsing' | 'collapsed' | 'expanding'" + "text": "'default' | 'admin' | undefined" }, - "default": "'visible'", - "description": "Visibility state used by responsive containers such as le-toolbar." + "description": "Mode of the popover should be 'default' for internal use" }, { "kind": "field", - "name": "overflowItems", + "name": "variant", "type": { - "text": "LeOption[]" + "text": "'solid' | 'outlined' | 'clear' | 'system'" }, - "privacy": "private", - "default": "[]" + "default": "'outlined'", + "description": "Button variant style" }, { "kind": "field", - "name": "hasOverflow", + "name": "color", "type": { - "text": "boolean" + "text": "| 'primary'\n | 'secondary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'info'\n | 'transparent' | undefined" }, - "privacy": "private", - "default": "false" + "description": "Button color theme (uses theme semantic colors)" }, { "kind": "field", - "name": "buttonSlots", + "name": "size", "type": { - "text": "string[]" + "text": "'small' | 'medium' | 'large'" }, - "privacy": "private", - "default": "[]" + "default": "'medium'", + "description": "Button size" }, { "kind": "field", - "name": "mutationObserver", + "name": "selected", "type": { - "text": "MutationObserver | undefined" + "text": "boolean" }, - "privacy": "private" + "default": "false", + "description": "Whether the button is in a selected/active state" }, { "kind": "field", - "name": "instanceId", + "name": "label", "type": { - "text": "string" + "text": "string | undefined" }, - "privacy": "private" + "description": "Optional label for the button,\nused for accessibility and tooltips when the button is icon-only." }, { "kind": "field", - "name": "syncingLayout", + "name": "tooltip", "type": { - "text": "boolean" + "text": "string | undefined" }, - "privacy": "private", - "default": "false" + "description": "Tooltip text to show on hover" }, { "kind": "field", - "name": "pendingSync", + "name": "tooltipPosition", "type": { - "text": "boolean" + "text": "TooltipPlacement" }, - "privacy": "private", - "default": "false" + "default": "'top'", + "description": "Tooltip position around the button" }, { "kind": "field", - "name": "hasAuthorCollapse", + "name": "fullWidth", "type": { "text": "boolean" }, - "privacy": "private", - "default": "false" + "default": "false", + "description": "Whether the button takes full width of its container" }, { "kind": "field", - "name": "buttonMap", + "name": "visibility", "type": { - "text": "Map" + "text": "'visible' | 'collapsing' | 'collapsed' | 'expanding'" }, - "privacy": "private", - "default": "new Map()" + "default": "'visible'", + "description": "Visibility state used by responsive containers to animate show/hide transitions." }, { - "kind": "method", - "name": "handleCollapseChange" + "kind": "field", + "name": "groupShape", + "type": { + "text": "'start' | 'middle' | 'end' | 'single'" + }, + "default": "'single'", + "description": "Shape of the button when rendered inside grouped containers." }, { - "kind": "method", - "name": "handleOverflowIconsChange" + "kind": "field", + "name": "motionPreset", + "type": { + "text": "'none' | 'soft' | 'fluid' | 'spring' | undefined" + }, + "description": "Optional per-instance motion preset override." }, { - "kind": "method", - "name": "handleDisabledChange", - "parameters": [ - { - "name": "newValue", - "type": { - "text": "boolean" - } - } - ] + "kind": "field", + "name": "iconOnly", + "type": { + "text": "string | Node | undefined" + }, + "description": "Icon only button image or emoji\nif this prop is set, the button will render only the icon slot" }, { - "kind": "method", - "name": "getItemsMeta", - "return": { - "type": { - "text": "Promise" - } - } + "kind": "field", + "name": "iconStart", + "type": { + "text": "string | Node | undefined" + }, + "description": "Start icon image or emoji" }, { - "kind": "method", - "name": "getToolbarOverflowGroupOption", - "return": { - "type": { - "text": "Promise" - } - } + "kind": "field", + "name": "collapsible", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Enables responsive collapse to icon-only when the toolbar applies\n`collapse=\"icon\"`." }, { - "kind": "method", - "name": "whenLayoutSettled", - "return": { - "type": { - "text": "Promise" - } - } + "kind": "field", + "name": "collapse", + "type": { + "text": "string | undefined" + }, + "description": "Runtime collapse state controlled by responsive containers." }, { - "kind": "method", - "name": "getToolbarOverflowItems", - "return": { - "type": { - "text": "Promise" - } - } + "kind": "field", + "name": "collapsePriorityOffset", + "type": { + "text": "number" + }, + "default": "100", + "description": "Relative collapse priority offset for toolbar stepping.\nHigher numbers collapse earlier while keeping the button visible longer." }, { - "kind": "method", - "name": "getCollapseMeta", - "return": { - "type": { - "text": "Promise" - } + "kind": "field", + "name": "iconEnd", + "type": { + "text": "string | Node | undefined" }, - "description": "Returns collapse meta for toolbar integration." + "description": "End icon image or emoji" }, { - "kind": "method", - "name": "getButtonChildren", - "privacy": "private", - "return": { - "type": { - "text": "HTMLElement[]" - } - } + "kind": "field", + "name": "disabled", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Whether the button is disabled" }, { - "kind": "method", - "name": "syncButtonSlots", - "privacy": "private", - "return": { - "type": { - "text": "string[]" - } + "kind": "field", + "name": "type", + "type": { + "text": "'button' | 'submit' | 'reset'" }, - "parameters": [ - { - "name": "buttons", - "type": { - "text": "HTMLElement[]" - } - } - ] + "default": "'button'", + "description": "The button type attribute" }, { - "kind": "method", - "name": "getVisibilityState", - "privacy": "private", - "return": { - "type": { - "text": "'visible' | 'collapsed'" - } + "kind": "field", + "name": "href", + "type": { + "text": "string | undefined" }, - "parameters": [ - { - "name": "value", - "type": { - "text": "string | null" - } - } - ] + "description": "Optional href to make the button act as a link" }, { - "kind": "method", - "name": "setDisabledState", - "privacy": "private", - "parameters": [ - { - "name": "disabled", - "type": { - "text": "boolean" - } - } - ] + "kind": "field", + "name": "target", + "type": { + "text": "string | undefined" + }, + "description": "Link target when href is set" }, { - "kind": "method", - "name": "getGroupLabel", - "privacy": "private", - "return": { - "type": { - "text": "string" - } - } + "kind": "field", + "name": "align", + "type": { + "text": "'start' | 'center' | 'space-between' | 'end'" + }, + "default": "'center'", + "description": "Alignment of the button label without the end icon" }, { - "kind": "method", - "name": "isFullyCollapsed", - "privacy": "private", - "return": { - "type": { - "text": "boolean" - } - } + "kind": "field", + "name": "handleClick", + "privacy": "private" }, { "kind": "method", - "name": "getToolbarVisibleCountsSync", - "privacy": "private", + "name": "getCollapseMeta", "return": { "type": { - "text": "number[]" + "text": "Promise" } - } + }, + "description": "Returns collapse meta for toolbar integration." }, { "kind": "method", - "name": "getButtonId", - "privacy": "private", - "return": { - "type": { - "text": "string" - } - }, - "parameters": [ - { - "name": "button", - "type": { - "text": "HTMLElement" - } - }, - { - "name": "index", - "type": { - "text": "number" - } - } - ] + "name": "initSlotObserver", + "privacy": "private" }, { "kind": "method", - "name": "getButtonPriority", - "privacy": "private", + "name": "getOption", "return": { "type": { - "text": "number" - } - }, - "parameters": [ - { - "name": "button", - "type": { - "text": "HTMLElement" - } - }, - { - "name": "index", - "type": { - "text": "number" - } + "text": "Promise" } - ] + } }, { "kind": "method", - "name": "parseCollapseValue", + "name": "getResolvedLabel", "privacy": "private", "return": { "type": { - "text": "{ active: boolean; visibleCount: number }" - } - }, - "parameters": [ - { - "name": "totalButtons", - "type": { - "text": "number" - } + "text": "string | undefined" } - ] + } }, { "kind": "method", - "name": "buildOverflowOption", - "privacy": "private", - "return": { - "type": { - "text": "Promise" - } - }, - "parameters": [ - { - "name": "item", - "type": { - "text": "RankedButton" - } - } - ] + "name": "render" + } + ], + "attributes": [ + { + "name": "mode", + "fieldName": "mode", + "description": "Mode of the popover should be 'default' for internal use", + "type": { + "text": "'default' | 'admin'" + } }, { - "kind": "method", - "name": "syncLayout", - "privacy": "private" + "name": "variant", + "fieldName": "variant", + "default": "'outlined'", + "description": "Button variant style", + "type": { + "text": "'solid' | 'outlined' | 'clear' | 'system'" + } }, { - "kind": "field", - "name": "handleOverflowSelect", - "privacy": "private" + "name": "color", + "fieldName": "color", + "description": "Button color theme (uses theme semantic colors)", + "type": { + "text": "| 'primary'\n | 'secondary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'info'\n | 'transparent'" + } }, { - "kind": "method", - "name": "render" - } - ], - "attributes": [ + "name": "size", + "fieldName": "size", + "default": "'medium'", + "description": "Button size", + "type": { + "text": "'small' | 'medium' | 'large'" + } + }, + { + "name": "selected", + "fieldName": "selected", + "default": "false", + "description": "Whether the button is in a selected/active state", + "type": { + "text": "boolean" + } + }, { "name": "label", "fieldName": "label", - "description": "Optional label used when the whole group is represented as a parent item\ninside another component's overflow menu.", + "description": "Optional label for the button,\nused for accessibility and tooltips when the button is icon-only.", "type": { "text": "string" } }, { - "name": "collapse", - "fieldName": "collapse", - "description": "Collapse mode.\n\n- `true`: show only the top-priority button\n- positive number: show top N buttons\n- `0`: show only the more button\n- negative number: hide abs(N) lowest-priority buttons\n\nNon-integers are rounded with `Math.round`.", + "name": "tooltip", + "fieldName": "tooltip", + "description": "Tooltip text to show on hover", "type": { - "text": "boolean | number | string" + "text": "string" } }, { - "name": "overflow-icons", - "fieldName": "overflowIcons", - "default": "false", - "description": "When true, icons from collapsed buttons are shown in the overflow navigation list.", + "name": "tooltip-position", + "fieldName": "tooltipPosition", + "default": "'top'", + "description": "Tooltip position around the button", "type": { - "text": "boolean" + "text": "TooltipPlacement" } }, { - "name": "disabled", - "fieldName": "disabled", + "name": "full-width", + "fieldName": "fullWidth", "default": "false", - "description": "Disabled attribute, when the button group is disabled,\nall buttons inside will be disabled and the overflow menu will not be accessible.", + "description": "Whether the button takes full width of its container", "type": { "text": "boolean" } @@ -2071,77 +2043,180 @@ "name": "visibility", "fieldName": "visibility", "default": "'visible'", - "description": "Visibility state used by responsive containers such as le-toolbar.", + "description": "Visibility state used by responsive containers to animate show/hide transitions.", "type": { "text": "'visible' | 'collapsing' | 'collapsed' | 'expanding'" } - } - ], - "tagName": "le-button-group", - "events": [ + }, { - "name": "leOverflowSelect", + "name": "group-shape", + "fieldName": "groupShape", + "default": "'single'", + "description": "Shape of the button when rendered inside grouped containers.", "type": { - "text": "EventEmitter<{ id: string }>" + "text": "'start' | 'middle' | 'end' | 'single'" + } + }, + { + "name": "motion-preset", + "fieldName": "motionPreset", + "description": "Optional per-instance motion preset override.", + "type": { + "text": "'none' | 'soft' | 'fluid' | 'spring'" + } + }, + { + "name": "icon-only", + "fieldName": "iconOnly", + "description": "Icon only button image or emoji\nif this prop is set, the button will render only the icon slot", + "type": { + "text": "string | Node" + } + }, + { + "name": "icon-start", + "fieldName": "iconStart", + "description": "Start icon image or emoji", + "type": { + "text": "string | Node" + } + }, + { + "name": "collapsible", + "fieldName": "collapsible", + "default": "false", + "description": "Enables responsive collapse to icon-only when the toolbar applies\n`collapse=\"icon\"`.", + "type": { + "text": "boolean" + } + }, + { + "name": "collapse", + "fieldName": "collapse", + "description": "Runtime collapse state controlled by responsive containers.", + "type": { + "text": "string" + } + }, + { + "name": "collapse-priority-offset", + "fieldName": "collapsePriorityOffset", + "default": "100", + "description": "Relative collapse priority offset for toolbar stepping.\nHigher numbers collapse earlier while keeping the button visible longer.", + "type": { + "text": "number" + } + }, + { + "name": "icon-end", + "fieldName": "iconEnd", + "description": "End icon image or emoji", + "type": { + "text": "string | Node" + } + }, + { + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Whether the button is disabled", + "type": { + "text": "boolean" + } + }, + { + "name": "type", + "fieldName": "type", + "default": "'button'", + "description": "The button type attribute", + "type": { + "text": "'button' | 'submit' | 'reset'" + } + }, + { + "name": "href", + "fieldName": "href", + "description": "Optional href to make the button act as a link", + "type": { + "text": "string" + } + }, + { + "name": "target", + "fieldName": "target", + "description": "Link target when href is set", + "type": { + "text": "string" + } + }, + { + "name": "align", + "fieldName": "align", + "default": "'center'", + "description": "Alignment of the button label without the end icon", + "type": { + "text": "'start' | 'center' | 'space-between' | 'end'" } } ], + "tagName": "le-button", + "events": [ + { + "name": "click", + "type": { + "text": "EventEmitter" + }, + "description": "Emitted when the button is clicked.\nThis is a custom event that wraps the native click but ensures the target is the le-button." + } + ], "customElement": true } ], "exports": [ { "kind": "js", - "name": "LeButtonGroup", + "name": "LeButton", "declaration": { - "name": "LeButtonGroup", - "module": "src/components/le-button-group/le-button-group.tsx" + "name": "LeButton", + "module": "src/components/le-button/le-button.tsx" } }, { "kind": "custom-element-definition", - "name": "le-button-group", + "name": "le-button", "declaration": { - "name": "LeButtonGroup", - "module": "src/components/le-button-group/le-button-group.tsx" + "name": "LeButton", + "module": "src/components/le-button/le-button.tsx" } } ] }, { "kind": "javascript-module", - "path": "src/components/le-checkbox/le-checkbox.tsx", + "path": "src/components/le-button-group/le-button-group.tsx", "declarations": [ { "kind": "class", - "description": "A checkbox component with support for labels, descriptions, and external IDs.", - "name": "LeCheckbox", - "cssProperties": [ - { - "description": "Size of the checkbox input", - "name": "--le-checkbox-size" - }, - { - "description": "Color of the checkbox when checked", - "name": "--le-checkbox-color" - }, + "description": "Groups multiple `le-button` elements and optionally collapses low-priority actions\ninto an overflow \"more\" menu.", + "name": "LeButtonGroup", + "cssParts": [ { - "description": "Color of the label text", - "name": "--le-checkbox-label-color" + "description": "The grouped buttons frame", + "name": "group" }, { - "description": "Color of the description text", - "name": "--le-checkbox-desc-color" + "description": "The overflow trigger button", + "name": "more-button" } ], "slots": [ { - "description": "The label text for the checkbox", + "description": "Group button elements (`le-button` children)", "name": "" }, { - "description": "Additional description text displayed below the label", - "name": "description" + "description": "Custom icon/content for the overflow trigger button", + "name": "more" } ], "members": [ @@ -2154,21 +2229,28 @@ }, { "kind": "field", - "name": "id", + "name": "label", "type": { - "text": "string" + "text": "string | undefined" }, - "default": "`le-checkbox-${Math.random().toString(36).substring(2, 9)}`", - "description": "The ID of the checkbox input. This is used for linking the label to the input for accessibility.\nIn case there is no ID provided, a random one will be generated internally." + "description": "Optional label used when the whole group is represented as a parent item\ninside another component's overflow menu." }, { "kind": "field", - "name": "checked", + "name": "collapse", + "type": { + "text": "boolean | number | string | undefined" + }, + "description": "Collapse mode.\n\n- `true`: show only the top-priority button\n- positive number: show top N buttons\n- `0`: show only the more button\n- negative number: hide abs(N) lowest-priority buttons\n\nNon-integers are rounded with `Math.round`." + }, + { + "kind": "field", + "name": "overflowIcons", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the checkbox is checked" + "description": "When true, icons from collapsed buttons are shown in the overflow navigation list." }, { "kind": "field", @@ -2177,75 +2259,564 @@ "text": "boolean" }, "default": "false", - "description": "Whether the checkbox is disabled" + "description": "Disabled attribute, when the button group is disabled,\nall buttons inside will be disabled and the overflow menu will not be accessible." }, { "kind": "field", - "name": "name", + "name": "visibility", "type": { - "text": "string | undefined" + "text": "'visible' | 'collapsing' | 'collapsed' | 'expanding'" }, - "description": "The name of the checkbox input" + "default": "'visible'", + "description": "Visibility state used by responsive containers such as le-toolbar." }, { "kind": "field", - "name": "value", + "name": "overflowItems", "type": { - "text": "string | undefined" + "text": "LeOption[]" }, - "description": "The value of the checkbox input" + "privacy": "private", + "default": "[]" }, { "kind": "field", - "name": "externalId", + "name": "hasOverflow", "type": { - "text": "string | undefined" + "text": "boolean" }, - "description": "External ID for linking with external systems (e.g. database ID, PDF form field ID)" + "privacy": "private", + "default": "false" }, { "kind": "field", - "name": "handleChange", - "privacy": "private" + "name": "buttonSlots", + "type": { + "text": "string[]" + }, + "privacy": "private", + "default": "[]" }, { - "kind": "method", - "name": "render" - } - ], - "attributes": [ + "kind": "field", + "name": "mutationObserver", + "type": { + "text": "MutationObserver | undefined" + }, + "privacy": "private" + }, { - "name": "id", - "fieldName": "id", - "default": "`le-checkbox-${Math.random().toString(36).substring(2, 9)}`", - "description": "The ID of the checkbox input. This is used for linking the label to the input for accessibility.\nIn case there is no ID provided, a random one will be generated internally.", + "kind": "field", + "name": "instanceId", "type": { "text": "string" - } + }, + "privacy": "private" }, { - "name": "checked", - "fieldName": "checked", - "default": "false", - "description": "Whether the checkbox is checked", + "kind": "field", + "name": "syncingLayout", "type": { "text": "boolean" - } + }, + "privacy": "private", + "default": "false" }, { - "name": "disabled", - "fieldName": "disabled", - "default": "false", - "description": "Whether the checkbox is disabled", + "kind": "field", + "name": "pendingSync", "type": { "text": "boolean" - } + }, + "privacy": "private", + "default": "false" }, { - "name": "name", - "fieldName": "name", - "description": "The name of the checkbox input", - "type": { + "kind": "field", + "name": "hasAuthorCollapse", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" + }, + { + "kind": "field", + "name": "buttonMap", + "type": { + "text": "Map" + }, + "privacy": "private", + "default": "new Map()" + }, + { + "kind": "method", + "name": "handleCollapseChange" + }, + { + "kind": "method", + "name": "handleOverflowIconsChange" + }, + { + "kind": "method", + "name": "handleDisabledChange", + "parameters": [ + { + "name": "newValue", + "type": { + "text": "boolean" + } + } + ] + }, + { + "kind": "method", + "name": "getItemsMeta", + "return": { + "type": { + "text": "Promise" + } + } + }, + { + "kind": "method", + "name": "getToolbarOverflowGroupOption", + "return": { + "type": { + "text": "Promise" + } + } + }, + { + "kind": "method", + "name": "whenLayoutSettled", + "return": { + "type": { + "text": "Promise" + } + } + }, + { + "kind": "method", + "name": "getToolbarOverflowItems", + "return": { + "type": { + "text": "Promise" + } + } + }, + { + "kind": "method", + "name": "getCollapseMeta", + "return": { + "type": { + "text": "Promise" + } + }, + "description": "Returns collapse meta for toolbar integration." + }, + { + "kind": "method", + "name": "getButtonChildren", + "privacy": "private", + "return": { + "type": { + "text": "HTMLElement[]" + } + } + }, + { + "kind": "method", + "name": "syncButtonSlots", + "privacy": "private", + "return": { + "type": { + "text": "string[]" + } + }, + "parameters": [ + { + "name": "buttons", + "type": { + "text": "HTMLElement[]" + } + } + ] + }, + { + "kind": "method", + "name": "getVisibilityState", + "privacy": "private", + "return": { + "type": { + "text": "'visible' | 'collapsed'" + } + }, + "parameters": [ + { + "name": "value", + "type": { + "text": "string | null" + } + } + ] + }, + { + "kind": "method", + "name": "setDisabledState", + "privacy": "private", + "parameters": [ + { + "name": "disabled", + "type": { + "text": "boolean" + } + } + ] + }, + { + "kind": "method", + "name": "getGroupLabel", + "privacy": "private", + "return": { + "type": { + "text": "string" + } + } + }, + { + "kind": "method", + "name": "isFullyCollapsed", + "privacy": "private", + "return": { + "type": { + "text": "boolean" + } + } + }, + { + "kind": "method", + "name": "getToolbarVisibleCountsSync", + "privacy": "private", + "return": { + "type": { + "text": "number[]" + } + } + }, + { + "kind": "method", + "name": "getButtonId", + "privacy": "private", + "return": { + "type": { + "text": "string" + } + }, + "parameters": [ + { + "name": "button", + "type": { + "text": "HTMLElement" + } + }, + { + "name": "index", + "type": { + "text": "number" + } + } + ] + }, + { + "kind": "method", + "name": "getButtonPriority", + "privacy": "private", + "return": { + "type": { + "text": "number" + } + }, + "parameters": [ + { + "name": "button", + "type": { + "text": "HTMLElement" + } + }, + { + "name": "index", + "type": { + "text": "number" + } + } + ] + }, + { + "kind": "method", + "name": "parseCollapseValue", + "privacy": "private", + "return": { + "type": { + "text": "{ active: boolean; visibleCount: number }" + } + }, + "parameters": [ + { + "name": "totalButtons", + "type": { + "text": "number" + } + } + ] + }, + { + "kind": "method", + "name": "buildOverflowOption", + "privacy": "private", + "return": { + "type": { + "text": "Promise" + } + }, + "parameters": [ + { + "name": "item", + "type": { + "text": "RankedButton" + } + } + ] + }, + { + "kind": "method", + "name": "syncLayout", + "privacy": "private" + }, + { + "kind": "field", + "name": "handleOverflowSelect", + "privacy": "private" + }, + { + "kind": "method", + "name": "render" + } + ], + "attributes": [ + { + "name": "label", + "fieldName": "label", + "description": "Optional label used when the whole group is represented as a parent item\ninside another component's overflow menu.", + "type": { + "text": "string" + } + }, + { + "name": "collapse", + "fieldName": "collapse", + "description": "Collapse mode.\n\n- `true`: show only the top-priority button\n- positive number: show top N buttons\n- `0`: show only the more button\n- negative number: hide abs(N) lowest-priority buttons\n\nNon-integers are rounded with `Math.round`.", + "type": { + "text": "boolean | number | string" + } + }, + { + "name": "overflow-icons", + "fieldName": "overflowIcons", + "default": "false", + "description": "When true, icons from collapsed buttons are shown in the overflow navigation list.", + "type": { + "text": "boolean" + } + }, + { + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Disabled attribute, when the button group is disabled,\nall buttons inside will be disabled and the overflow menu will not be accessible.", + "type": { + "text": "boolean" + } + }, + { + "name": "visibility", + "fieldName": "visibility", + "default": "'visible'", + "description": "Visibility state used by responsive containers such as le-toolbar.", + "type": { + "text": "'visible' | 'collapsing' | 'collapsed' | 'expanding'" + } + } + ], + "tagName": "le-button-group", + "events": [ + { + "name": "leOverflowSelect", + "type": { + "text": "EventEmitter<{ id: string }>" + } + } + ], + "customElement": true + } + ], + "exports": [ + { + "kind": "js", + "name": "LeButtonGroup", + "declaration": { + "name": "LeButtonGroup", + "module": "src/components/le-button-group/le-button-group.tsx" + } + }, + { + "kind": "custom-element-definition", + "name": "le-button-group", + "declaration": { + "name": "LeButtonGroup", + "module": "src/components/le-button-group/le-button-group.tsx" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/components/le-checkbox/le-checkbox.tsx", + "declarations": [ + { + "kind": "class", + "description": "A checkbox component with support for labels, descriptions, and external IDs.", + "name": "LeCheckbox", + "cssProperties": [ + { + "description": "Size of the checkbox input", + "name": "--le-checkbox-size" + }, + { + "description": "Color of the checkbox when checked", + "name": "--le-checkbox-color" + }, + { + "description": "Color of the label text", + "name": "--le-checkbox-label-color" + }, + { + "description": "Color of the description text", + "name": "--le-checkbox-desc-color" + } + ], + "slots": [ + { + "description": "The label text for the checkbox", + "name": "" + }, + { + "description": "Additional description text displayed below the label", + "name": "description" + } + ], + "members": [ + { + "kind": "field", + "name": "el", + "type": { + "text": "HTMLElement" + } + }, + { + "kind": "field", + "name": "id", + "type": { + "text": "string" + }, + "default": "`le-checkbox-${Math.random().toString(36).substring(2, 9)}`", + "description": "The ID of the checkbox input. This is used for linking the label to the input for accessibility.\nIn case there is no ID provided, a random one will be generated internally." + }, + { + "kind": "field", + "name": "checked", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Whether the checkbox is checked" + }, + { + "kind": "field", + "name": "disabled", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Whether the checkbox is disabled" + }, + { + "kind": "field", + "name": "name", + "type": { + "text": "string | undefined" + }, + "description": "The name of the checkbox input" + }, + { + "kind": "field", + "name": "value", + "type": { + "text": "string | undefined" + }, + "description": "The value of the checkbox input" + }, + { + "kind": "field", + "name": "externalId", + "type": { + "text": "string | undefined" + }, + "description": "External ID for linking with external systems (e.g. database ID, PDF form field ID)" + }, + { + "kind": "field", + "name": "handleChange", + "privacy": "private" + }, + { + "kind": "method", + "name": "render" + } + ], + "attributes": [ + { + "name": "id", + "fieldName": "id", + "default": "`le-checkbox-${Math.random().toString(36).substring(2, 9)}`", + "description": "The ID of the checkbox input. This is used for linking the label to the input for accessibility.\nIn case there is no ID provided, a random one will be generated internally.", + "type": { + "text": "string" + } + }, + { + "name": "checked", + "fieldName": "checked", + "default": "false", + "description": "Whether the checkbox is checked", + "type": { + "text": "boolean" + } + }, + { + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Whether the checkbox is disabled", + "type": { + "text": "boolean" + } + }, + { + "name": "name", + "fieldName": "name", + "description": "The name of the checkbox input", + "type": { "text": "string" } }, @@ -2433,60 +3004,46 @@ }, { "kind": "javascript-module", - "path": "src/components/le-button/le-button.tsx", + "path": "src/components/le-code-input/le-code-input.tsx", "declarations": [ { "kind": "class", - "description": "A flexible button component with multiple variants and states.", - "name": "LeButton", + "description": "A one-time code input component with individual frames for each character.\nSupports standard copy/paste and range selection behaviors.", + "name": "LeCodeInput", "cssProperties": [ { - "description": "Button background color", - "name": "--le-button-bg" - }, - { - "description": "Button text color", - "name": "--le-button-color" + "description": "Size of each character box (default: 40px width, 48px height)", + "name": "--le-code-box-size" }, { - "description": "Button border radius", - "name": "--le-button-border-radius" + "description": "Input background color", + "name": "--le-input-bg" }, { - "description": "Button horizontal padding", - "name": "--le-button-padding-x" + "description": "Input text color", + "name": "--le-input-color" }, { - "description": "Button vertical padding", - "name": "--le-button-padding-y" - } - ], - "cssParts": [ - { - "description": "The native button element", - "name": "button" + "description": "Input border style", + "name": "--le-input-border" }, { - "description": "The button content wrapper", - "name": "content" + "description": "Input border style when focused", + "name": "--le-input-border-focus" }, { - "description": "The start icon slot", - "name": "icon-start" + "description": "Input border style when invalid", + "name": "--le-input-border-error" }, { - "description": "The end icon slot", - "name": "icon-end" + "description": "Input border radius", + "name": "--le-input-radius" } ], "slots": [ { - "description": "Button text content", - "name": "" - }, - { - "description": "Icon for icon-only buttons", - "name": "icon-only" + "description": "Additional description text displayed below the input", + "name": "description" } ], "members": [ @@ -2499,273 +3056,182 @@ }, { "kind": "field", - "name": "hasIconStartSlot", - "type": { - "text": "boolean" - }, - "privacy": "private", - "default": "false" - }, - { - "kind": "field", - "name": "hasIconEndSlot", - "type": { - "text": "boolean" - }, - "privacy": "private", - "default": "false" - }, - { - "kind": "field", - "name": "hasIconOnlySlot", - "type": { - "text": "boolean" - }, - "privacy": "private", - "default": "false" - }, - { - "kind": "field", - "name": "disconnectSlotObserver", - "type": { - "text": "() => void | undefined" - }, - "privacy": "private" - }, - { - "kind": "field", - "name": "mode", - "type": { - "text": "'default' | 'admin' | undefined" - }, - "description": "Mode of the popover should be 'default' for internal use" - }, - { - "kind": "field", - "name": "variant", - "type": { - "text": "'solid' | 'outlined' | 'clear' | 'system'" - }, - "default": "'outlined'", - "description": "Button variant style" - }, - { - "kind": "field", - "name": "color", + "name": "value", "type": { - "text": "| 'primary'\n | 'secondary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'info'\n | 'transparent' | undefined" + "text": "string" }, - "description": "Button color theme (uses theme semantic colors)" + "default": "''", + "description": "The value of the input" }, { "kind": "field", - "name": "size", + "name": "name", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "string | undefined" }, - "default": "'medium'", - "description": "Button size" + "description": "The name of the input" }, { "kind": "field", - "name": "selected", + "name": "label", "type": { - "text": "boolean" + "text": "string | undefined" }, - "default": "false", - "description": "Whether the button is in a selected/active state" + "description": "Label for the input" }, { "kind": "field", - "name": "label", + "name": "length", "type": { - "text": "string | undefined" + "text": "number" }, - "description": "Optional label for the button,\nused for accessibility and tooltips when the button is icon-only." + "default": "6", + "description": "Length of the code (number of characters)" }, { "kind": "field", - "name": "tooltip", + "name": "description", "type": { "text": "string | undefined" }, - "description": "Tooltip text to show on hover" + "description": "Description text displayed below the input\nin case there is a more complex markup,\nit can be provided via slot as well" }, { "kind": "field", - "name": "tooltipPosition", + "name": "type", "type": { - "text": "TooltipPlacement" + "text": "'text' | 'number'" }, - "default": "'top'", - "description": "Tooltip position around the button" + "default": "'text'", + "description": "The type of code (numeric or alphanumeric)\nThis affects the keyboard layout on mobile devices." }, { "kind": "field", - "name": "fullWidth", + "name": "disabled", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the button takes full width of its container" - }, - { - "kind": "field", - "name": "visibility", - "type": { - "text": "'visible' | 'collapsing' | 'collapsed' | 'expanding'" - }, - "default": "'visible'", - "description": "Visibility state used by responsive containers to animate show/hide transitions." - }, - { - "kind": "field", - "name": "groupShape", - "type": { - "text": "'start' | 'middle' | 'end' | 'single'" - }, - "default": "'single'", - "description": "Shape of the button when rendered inside grouped containers." - }, - { - "kind": "field", - "name": "motionPreset", - "type": { - "text": "'none' | 'soft' | 'fluid' | 'spring' | undefined" - }, - "description": "Optional per-instance motion preset override." - }, - { - "kind": "field", - "name": "iconOnly", - "type": { - "text": "string | Node | undefined" - }, - "description": "Icon only button image or emoji\nif this prop is set, the button will render only the icon slot" - }, - { - "kind": "field", - "name": "iconStart", - "type": { - "text": "string | Node | undefined" - }, - "description": "Start icon image or emoji" + "description": "Whether the input is disabled" }, { "kind": "field", - "name": "collapsible", + "name": "readonly", "type": { "text": "boolean" }, "default": "false", - "description": "Enables responsive collapse to icon-only when the toolbar applies\n`collapse=\"icon\"`." - }, - { - "kind": "field", - "name": "collapse", - "type": { - "text": "string | undefined" - }, - "description": "Runtime collapse state controlled by responsive containers." - }, - { - "kind": "field", - "name": "collapsePriorityOffset", - "type": { - "text": "number" - }, - "default": "100", - "description": "Relative collapse priority offset for toolbar stepping.\nHigher numbers collapse earlier while keeping the button visible longer." + "description": "Whether the input is read-only" }, { "kind": "field", - "name": "iconEnd", + "name": "externalId", "type": { - "text": "string | Node | undefined" + "text": "string | undefined" }, - "description": "End icon image or emoji" + "description": "External ID for linking with external systems" }, { "kind": "field", - "name": "disabled", + "name": "error", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the button is disabled" + "description": "Internal validation state (can be set externally manually or via simple check)" }, { "kind": "field", - "name": "type", + "name": "isFocused", "type": { - "text": "'button' | 'submit' | 'reset'" + "text": "boolean" }, - "default": "'button'", - "description": "The button type attribute" + "privacy": "private", + "default": "false" }, { "kind": "field", - "name": "href", + "name": "selectionStart", "type": { - "text": "string | undefined" + "text": "number" }, - "description": "Optional href to make the button act as a link" + "privacy": "private", + "default": "0" }, { "kind": "field", - "name": "target", + "name": "selectionEnd", "type": { - "text": "string | undefined" + "text": "number" }, - "description": "Link target when href is set" + "privacy": "private", + "default": "0" + }, + { + "kind": "method", + "name": "valueChanged", + "parameters": [ + { + "name": "newValue", + "type": { + "text": "string" + } + } + ] }, { "kind": "field", - "name": "align", - "type": { - "text": "'start' | 'center' | 'space-between' | 'end'" - }, - "default": "'center'", - "description": "Alignment of the button label without the end icon" + "name": "handleInput", + "privacy": "private" }, { "kind": "field", - "name": "handleClick", + "name": "handleChange", "privacy": "private" }, { - "kind": "method", - "name": "getCollapseMeta", - "return": { - "type": { - "text": "Promise" - } - }, - "description": "Returns collapse meta for toolbar integration." + "kind": "field", + "name": "handleFocus", + "privacy": "private" }, { - "kind": "method", - "name": "initSlotObserver", + "kind": "field", + "name": "handleBlur", + "privacy": "private" + }, + { + "kind": "field", + "name": "handleSelect", "privacy": "private" }, { "kind": "method", - "name": "getOption", - "return": { - "type": { - "text": "Promise" + "name": "updateSelection", + "privacy": "private", + "parameters": [ + { + "name": "input", + "type": { + "text": "HTMLInputElement" + } } - } + ] }, { "kind": "method", - "name": "getResolvedLabel", + "name": "getActiveIndex", "privacy": "private", "return": { "type": { - "text": "string | undefined" + "text": "number" } - } + }, + "description": "Helper to determine active index for focus ring" + }, + { + "kind": "method", + "name": "renderBoxes", + "privacy": "private" }, { "kind": "method", @@ -2774,210 +3240,121 @@ ], "attributes": [ { - "name": "mode", - "fieldName": "mode", - "description": "Mode of the popover should be 'default' for internal use", - "type": { - "text": "'default' | 'admin'" - } - }, - { - "name": "variant", - "fieldName": "variant", - "default": "'outlined'", - "description": "Button variant style", - "type": { - "text": "'solid' | 'outlined' | 'clear' | 'system'" - } - }, - { - "name": "color", - "fieldName": "color", - "description": "Button color theme (uses theme semantic colors)", - "type": { - "text": "| 'primary'\n | 'secondary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'info'\n | 'transparent'" - } - }, - { - "name": "size", - "fieldName": "size", - "default": "'medium'", - "description": "Button size", + "name": "value", + "fieldName": "value", + "default": "''", + "description": "The value of the input", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "string" } }, { - "name": "selected", - "fieldName": "selected", - "default": "false", - "description": "Whether the button is in a selected/active state", + "name": "name", + "fieldName": "name", + "description": "The name of the input", "type": { - "text": "boolean" + "text": "string" } }, { "name": "label", "fieldName": "label", - "description": "Optional label for the button,\nused for accessibility and tooltips when the button is icon-only.", - "type": { - "text": "string" - } - }, - { - "name": "tooltip", - "fieldName": "tooltip", - "description": "Tooltip text to show on hover", + "description": "Label for the input", "type": { "text": "string" } }, { - "name": "tooltip-position", - "fieldName": "tooltipPosition", - "default": "'top'", - "description": "Tooltip position around the button", - "type": { - "text": "TooltipPlacement" - } - }, - { - "name": "full-width", - "fieldName": "fullWidth", - "default": "false", - "description": "Whether the button takes full width of its container", - "type": { - "text": "boolean" - } - }, - { - "name": "visibility", - "fieldName": "visibility", - "default": "'visible'", - "description": "Visibility state used by responsive containers to animate show/hide transitions.", - "type": { - "text": "'visible' | 'collapsing' | 'collapsed' | 'expanding'" - } - }, - { - "name": "group-shape", - "fieldName": "groupShape", - "default": "'single'", - "description": "Shape of the button when rendered inside grouped containers.", - "type": { - "text": "'start' | 'middle' | 'end' | 'single'" - } - }, - { - "name": "motion-preset", - "fieldName": "motionPreset", - "description": "Optional per-instance motion preset override.", - "type": { - "text": "'none' | 'soft' | 'fluid' | 'spring'" - } - }, - { - "name": "icon-only", - "fieldName": "iconOnly", - "description": "Icon only button image or emoji\nif this prop is set, the button will render only the icon slot", - "type": { - "text": "string | Node" - } - }, - { - "name": "icon-start", - "fieldName": "iconStart", - "description": "Start icon image or emoji", - "type": { - "text": "string | Node" - } - }, - { - "name": "collapsible", - "fieldName": "collapsible", - "default": "false", - "description": "Enables responsive collapse to icon-only when the toolbar applies\n`collapse=\"icon\"`.", + "name": "length", + "fieldName": "length", + "default": "6", + "description": "Length of the code (number of characters)", "type": { - "text": "boolean" + "text": "number" } }, { - "name": "collapse", - "fieldName": "collapse", - "description": "Runtime collapse state controlled by responsive containers.", + "name": "description", + "fieldName": "description", + "description": "Description text displayed below the input\nin case there is a more complex markup,\nit can be provided via slot as well", "type": { "text": "string" } }, { - "name": "collapse-priority-offset", - "fieldName": "collapsePriorityOffset", - "default": "100", - "description": "Relative collapse priority offset for toolbar stepping.\nHigher numbers collapse earlier while keeping the button visible longer.", - "type": { - "text": "number" - } - }, - { - "name": "icon-end", - "fieldName": "iconEnd", - "description": "End icon image or emoji", + "name": "type", + "fieldName": "type", + "default": "'text'", + "description": "The type of code (numeric or alphanumeric)\nThis affects the keyboard layout on mobile devices.", "type": { - "text": "string | Node" + "text": "'text' | 'number'" } }, { "name": "disabled", "fieldName": "disabled", - "default": "false", - "description": "Whether the button is disabled", - "type": { - "text": "boolean" - } - }, - { - "name": "type", - "fieldName": "type", - "default": "'button'", - "description": "The button type attribute", + "default": "false", + "description": "Whether the input is disabled", "type": { - "text": "'button' | 'submit' | 'reset'" + "text": "boolean" } }, { - "name": "href", - "fieldName": "href", - "description": "Optional href to make the button act as a link", + "name": "readonly", + "fieldName": "readonly", + "default": "false", + "description": "Whether the input is read-only", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "target", - "fieldName": "target", - "description": "Link target when href is set", + "name": "external-id", + "fieldName": "externalId", + "description": "External ID for linking with external systems", "type": { "text": "string" } }, { - "name": "align", - "fieldName": "align", - "default": "'center'", - "description": "Alignment of the button label without the end icon", + "name": "error", + "fieldName": "error", + "default": "false", + "description": "Internal validation state (can be set externally manually or via simple check)", "type": { - "text": "'start' | 'center' | 'space-between' | 'end'" + "text": "boolean" } } ], - "tagName": "le-button", + "tagName": "le-code-input", "events": [ { - "name": "click", + "name": "leChange", "type": { - "text": "EventEmitter" + "text": "EventEmitter<{ value: string; name?: string; externalId?: string }> | undefined" }, - "description": "Emitted when the button is clicked.\nThis is a custom event that wraps the native click but ensures the target is the le-button." + "description": "Emitted when the value changes (on blur or Enter)" + }, + { + "name": "leInput", + "type": { + "text": "EventEmitter<{ value: string; name?: string; externalId?: string }> | undefined" + }, + "description": "Emitted when the input value changes (on keystroke)" + }, + { + "name": "leFocus", + "type": { + "text": "EventEmitter | undefined" + }, + "description": "Emitted when the input is focused" + }, + { + "name": "leBlur", + "type": { + "text": "EventEmitter | undefined" + }, + "description": "Emitted when the input is blurred" } ], "customElement": true @@ -2986,64 +3363,50 @@ "exports": [ { "kind": "js", - "name": "LeButton", + "name": "LeCodeInput", "declaration": { - "name": "LeButton", - "module": "src/components/le-button/le-button.tsx" + "name": "LeCodeInput", + "module": "src/components/le-code-input/le-code-input.tsx" } }, { "kind": "custom-element-definition", - "name": "le-button", + "name": "le-code-input", "declaration": { - "name": "LeButton", - "module": "src/components/le-button/le-button.tsx" + "name": "LeCodeInput", + "module": "src/components/le-code-input/le-code-input.tsx" } } ] }, { "kind": "javascript-module", - "path": "src/components/le-code-input/le-code-input.tsx", + "path": "src/components/le-collapse/le-collapse.tsx", "declarations": [ { "kind": "class", - "description": "A one-time code input component with individual frames for each character.\nSupports standard copy/paste and range selection behaviors.", - "name": "LeCodeInput", + "description": "Animated show/hide wrapper.\n\nSupports height collapse (auto->0) and/or fading.\nCan optionally listen to the nearest `le-header` shrink events.", + "name": "LeCollapse", "cssProperties": [ { - "description": "Size of each character box (default: 40px width, 48px height)", - "name": "--le-code-box-size" - }, - { - "description": "Input background color", - "name": "--le-input-bg" - }, - { - "description": "Input text color", - "name": "--le-input-color" - }, - { - "description": "Input border style", - "name": "--le-input-border" - }, - { - "description": "Input border style when focused", - "name": "--le-input-border-focus" - }, + "description": "Transition duration", + "name": "--le-collapse-duration" + } + ], + "cssParts": [ { - "description": "Input border style when invalid", - "name": "--le-input-border-error" + "description": "Collapsible region", + "name": "region" }, { - "description": "Input border radius", - "name": "--le-input-radius" + "description": "Inner content", + "name": "content" } ], "slots": [ { - "description": "Additional description text displayed below the input", - "name": "description" + "description": "Content to animate", + "name": "" } ], "members": [ @@ -3056,181 +3419,80 @@ }, { "kind": "field", - "name": "value", - "type": { - "text": "string" - }, - "default": "''", - "description": "The value of the input" - }, - { - "kind": "field", - "name": "name", - "type": { - "text": "string | undefined" - }, - "description": "The name of the input" - }, - { - "kind": "field", - "name": "label", - "type": { - "text": "string | undefined" - }, - "description": "Label for the input" - }, - { - "kind": "field", - "name": "length", - "type": { - "text": "number" - }, - "default": "6", - "description": "Length of the code (number of characters)" - }, - { - "kind": "field", - "name": "description", - "type": { - "text": "string | undefined" - }, - "description": "Description text displayed below the input\nin case there is a more complex markup,\nit can be provided via slot as well" - }, - { - "kind": "field", - "name": "type", - "type": { - "text": "'text' | 'number'" - }, - "default": "'text'", - "description": "The type of code (numeric or alphanumeric)\nThis affects the keyboard layout on mobile devices." - }, - { - "kind": "field", - "name": "disabled", + "name": "closed", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the input is disabled" + "description": "Since Stencil boolean props default to `false` when the attribute is missing.\ninstead of `open` defaulting to `true`, using a `closed` prop." }, { "kind": "field", - "name": "readonly", + "name": "scrollDown", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the input is read-only" - }, - { - "kind": "field", - "name": "externalId", - "type": { - "text": "string | undefined" - }, - "description": "External ID for linking with external systems" + "description": "Whether the content should scroll down from the top when open." }, { "kind": "field", - "name": "error", + "name": "noFading", "type": { "text": "boolean" }, "default": "false", - "description": "Internal validation state (can be set externally manually or via simple check)" + "description": "Stop fading the content when collapsing/expanding." }, { "kind": "field", - "name": "isFocused", + "name": "collapseOnHeaderShrink", "type": { "text": "boolean" }, - "privacy": "private", - "default": "false" - }, - { - "kind": "field", - "name": "selectionStart", - "type": { - "text": "number" - }, - "privacy": "private", - "default": "0" - }, - { - "kind": "field", - "name": "selectionEnd", - "type": { - "text": "number" - }, - "privacy": "private", - "default": "0" + "default": "false", + "description": "If true, collapse/expand based on the nearest header shrink event." }, { "kind": "method", - "name": "valueChanged", + "name": "handleHeaderShrink", "parameters": [ { - "name": "newValue", + "name": "ev", "type": { - "text": "string" + "text": "Event" } } - ] - }, - { - "kind": "field", - "name": "handleInput", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleChange", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleFocus", - "privacy": "private" + ], + "description": "Handles `leHeaderShrinkChange` events from the `le-header`.\nIn case multiple headers are present, only the nearest one in the DOM tree is used." }, { "kind": "field", - "name": "handleBlur", - "privacy": "private" + "name": "headerShrunk", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" }, { - "kind": "field", - "name": "handleSelect", - "privacy": "private" + "kind": "method", + "name": "onOpenChange", + "privacy": "protected" }, { "kind": "method", - "name": "updateSelection", - "privacy": "private", - "parameters": [ - { - "name": "input", - "type": { - "text": "HTMLInputElement" - } - } - ] + "name": "onDrivenStateChange", + "privacy": "protected" }, { "kind": "method", - "name": "getActiveIndex", - "privacy": "private", - "return": { - "type": { - "text": "number" - } - }, - "description": "Helper to determine active index for focus ring" + "name": "shouldBeOpen", + "privacy": "private" }, { "kind": "method", - "name": "renderBoxes", + "name": "applyOpenState", "privacy": "private" }, { @@ -3240,784 +3502,839 @@ ], "attributes": [ { - "name": "value", - "fieldName": "value", - "default": "''", - "description": "The value of the input", + "name": "closed", + "fieldName": "closed", + "default": "false", + "description": "Since Stencil boolean props default to `false` when the attribute is missing.\ninstead of `open` defaulting to `true`, using a `closed` prop.", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "name", - "fieldName": "name", - "description": "The name of the input", + "name": "scroll-down", + "fieldName": "scrollDown", + "default": "false", + "description": "Whether the content should scroll down from the top when open.", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "label", - "fieldName": "label", - "description": "Label for the input", + "name": "no-fading", + "fieldName": "noFading", + "default": "false", + "description": "Stop fading the content when collapsing/expanding.", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "length", - "fieldName": "length", - "default": "6", - "description": "Length of the code (number of characters)", + "name": "collapse-on-header-shrink", + "fieldName": "collapseOnHeaderShrink", + "default": "false", + "description": "If true, collapse/expand based on the nearest header shrink event.", "type": { - "text": "number" + "text": "boolean" } - }, + } + ], + "tagName": "le-collapse", + "events": [], + "customElement": true + } + ], + "exports": [ + { + "kind": "js", + "name": "LeCollapse", + "declaration": { + "name": "LeCollapse", + "module": "src/components/le-collapse/le-collapse.tsx" + } + }, + { + "kind": "custom-element-definition", + "name": "le-collapse", + "declaration": { + "name": "LeCollapse", + "module": "src/components/le-collapse/le-collapse.tsx" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/components/le-combobox/le-combobox.tsx", + "declarations": [ + { + "kind": "class", + "description": "A combobox component with searchable dropdown.\n\nCombines a text input with a dropdown list, allowing users to\nfilter options by typing or select from the list.", + "name": "LeCombobox", + "members": [ { - "name": "description", - "fieldName": "description", - "description": "Description text displayed below the input\nin case there is a more complex markup,\nit can be provided via slot as well", + "kind": "field", + "name": "el", "type": { - "text": "string" + "text": "HTMLElement" } }, { - "name": "type", - "fieldName": "type", - "default": "'text'", - "description": "The type of code (numeric or alphanumeric)\nThis affects the keyboard layout on mobile devices.", + "kind": "field", + "name": "options", "type": { - "text": "'text' | 'number'" - } + "text": "LeOption[] | string" + }, + "default": "[]", + "description": "The options to display in the dropdown." + }, + { + "kind": "field", + "name": "value", + "type": { + "text": "LeOptionValue | undefined" + }, + "description": "The currently selected value." + }, + { + "kind": "field", + "name": "placeholder", + "type": { + "text": "string" + }, + "default": "'Type to search...'", + "description": "Placeholder text for the input." }, { + "kind": "field", "name": "disabled", - "fieldName": "disabled", - "default": "false", - "description": "Whether the input is disabled", "type": { "text": "boolean" - } + }, + "default": "false", + "description": "Whether the combobox is disabled." }, { - "name": "readonly", - "fieldName": "readonly", - "default": "false", - "description": "Whether the input is read-only", + "kind": "field", + "name": "required", "type": { "text": "boolean" - } + }, + "default": "false", + "description": "Whether selection is required." }, { - "name": "external-id", - "fieldName": "externalId", - "description": "External ID for linking with external systems", + "kind": "field", + "name": "name", "type": { - "text": "string" - } + "text": "string | undefined" + }, + "description": "Name attribute for form submission." }, { - "name": "error", - "fieldName": "error", - "default": "false", - "description": "Internal validation state (can be set externally manually or via simple check)", + "kind": "field", + "name": "fullWidth", "type": { "text": "boolean" - } - } - ], - "tagName": "le-code-input", - "events": [ + }, + "default": "false", + "description": "Whether the multiselect should take full width of its container." + }, { - "name": "leChange", + "kind": "field", + "name": "size", "type": { - "text": "EventEmitter<{ value: string; name?: string; externalId?: string }> | undefined" + "text": "'small' | 'medium' | 'large'" }, - "description": "Emitted when the value changes (on blur or Enter)" + "default": "'medium'", + "description": "Size variant of the combobox." }, { - "name": "leInput", + "kind": "field", + "name": "allowCustom", "type": { - "text": "EventEmitter<{ value: string; name?: string; externalId?: string }> | undefined" + "text": "boolean" }, - "description": "Emitted when the input value changes (on keystroke)" + "default": "false", + "description": "Whether to allow custom values not in the options list." }, { - "name": "leFocus", + "kind": "field", + "name": "minSearchLength", "type": { - "text": "EventEmitter | undefined" + "text": "number" }, - "description": "Emitted when the input is focused" + "default": "0", + "description": "Minimum characters before showing filtered results." }, { - "name": "leBlur", + "kind": "field", + "name": "emptyText", "type": { - "text": "EventEmitter | undefined" + "text": "string" }, - "description": "Emitted when the input is blurred" - } - ], - "customElement": true - } - ], - "exports": [ - { - "kind": "js", - "name": "LeCodeInput", - "declaration": { - "name": "LeCodeInput", - "module": "src/components/le-code-input/le-code-input.tsx" - } - }, - { - "kind": "custom-element-definition", - "name": "le-code-input", - "declaration": { - "name": "LeCodeInput", - "module": "src/components/le-code-input/le-code-input.tsx" - } - } - ] - }, - { - "kind": "javascript-module", - "path": "src/components/le-collapse/le-collapse.tsx", - "declarations": [ - { - "kind": "class", - "description": "Animated show/hide wrapper.\n\nSupports height collapse (auto->0) and/or fading.\nCan optionally listen to the nearest `le-header` shrink events.", - "name": "LeCollapse", - "cssProperties": [ + "default": "'No results found'", + "description": "Text to show when no options match the search." + }, { - "description": "Transition duration", - "name": "--le-collapse-duration" - } - ], - "cssParts": [ + "kind": "field", + "name": "open", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Whether the dropdown is currently open." + }, { - "description": "Collapsible region", - "name": "region" + "kind": "method", + "name": "handleWindowClick", + "parameters": [ + { + "name": "event", + "type": { + "text": "MouseEvent" + } + } + ] }, - { - "description": "Inner content", - "name": "content" - } - ], - "slots": [ - { - "description": "Content to animate", - "name": "" - } - ], - "members": [ { "kind": "field", - "name": "el", + "name": "inputValue", "type": { - "text": "HTMLElement" - } + "text": "string" + }, + "privacy": "private", + "default": "''" }, { "kind": "field", - "name": "closed", + "name": "selectedOption", "type": { - "text": "boolean" + "text": "LeOption | undefined" }, - "default": "false", - "description": "Since Stencil boolean props default to `false` when the attribute is missing.\ninstead of `open` defaulting to `true`, using a `closed` prop." + "privacy": "private" }, { "kind": "field", - "name": "scrollDown", + "name": "declarativeOptions", "type": { - "text": "boolean" + "text": "LeOption[]" }, - "default": "false", - "description": "Whether the content should scroll down from the top when open." + "privacy": "private", + "default": "[]" }, { "kind": "field", - "name": "noFading", + "name": "isDeclarativeMode", "type": { "text": "boolean" }, - "default": "false", - "description": "Stop fading the content when collapsing/expanding." + "privacy": "private", + "default": "false" }, { "kind": "field", - "name": "collapseOnHeaderShrink", + "name": "dropdownEl", "type": { - "text": "boolean" + "text": "HTMLLeDropdownBaseElement | undefined" }, - "default": "false", - "description": "If true, collapse/expand based on the nearest header shrink event." + "privacy": "private" }, { - "kind": "method", - "name": "handleHeaderShrink", - "parameters": [ - { - "name": "ev", - "type": { - "text": "Event" - } - } - ], - "description": "Handles `leHeaderShrinkChange` events from the `le-header`.\nIn case multiple headers are present, only the nearest one in the DOM tree is used." + "kind": "field", + "name": "inputEl", + "type": { + "text": "HTMLInputElement | undefined" + }, + "privacy": "private" }, { "kind": "field", - "name": "headerShrunk", + "name": "mutationObserver", "type": { - "text": "boolean" + "text": "MutationObserver | undefined" }, - "privacy": "private", - "default": "false" + "privacy": "private" }, { "kind": "method", - "name": "onOpenChange", - "privacy": "protected" + "name": "handleValueChange" }, { "kind": "method", - "name": "onDrivenStateChange", - "privacy": "protected" + "name": "handleOptionsChange" }, { "kind": "method", - "name": "shouldBeOpen", - "privacy": "private" + "name": "handleSlotChange" }, { "kind": "method", - "name": "applyOpenState", + "name": "buildDeclarativeOptions", "privacy": "private" }, + { + "kind": "field", + "name": "parsedOptions", + "type": { + "text": "LeOption[]" + }, + "privacy": "private", + "readonly": true + }, { "kind": "method", - "name": "render" - } - ], - "attributes": [ + "name": "updateSelectedOption", + "privacy": "private" + }, { - "name": "closed", - "fieldName": "closed", - "default": "false", - "description": "Since Stencil boolean props default to `false` when the attribute is missing.\ninstead of `open` defaulting to `true`, using a `closed` prop.", - "type": { - "text": "boolean" - } + "kind": "field", + "name": "filterOption", + "privacy": "private" }, { - "name": "scroll-down", - "fieldName": "scrollDown", - "default": "false", - "description": "Whether the content should scroll down from the top when open.", - "type": { - "text": "boolean" - } + "kind": "field", + "name": "handleOptionSelect", + "privacy": "private" }, { - "name": "no-fading", - "fieldName": "noFading", - "default": "false", - "description": "Stop fading the content when collapsing/expanding.", - "type": { - "text": "boolean" - } + "kind": "field", + "name": "handleDropdownOpen", + "privacy": "private" }, { - "name": "collapse-on-header-shrink", - "fieldName": "collapseOnHeaderShrink", - "default": "false", - "description": "If true, collapse/expand based on the nearest header shrink event.", - "type": { - "text": "boolean" - } - } - ], - "tagName": "le-collapse", - "events": [], - "customElement": true - } - ], - "exports": [ - { - "kind": "js", - "name": "LeCollapse", - "declaration": { - "name": "LeCollapse", - "module": "src/components/le-collapse/le-collapse.tsx" - } - }, - { - "kind": "custom-element-definition", - "name": "le-collapse", - "declaration": { - "name": "LeCollapse", - "module": "src/components/le-collapse/le-collapse.tsx" - } - } - ] - }, - { - "kind": "javascript-module", - "path": "src/components/le-combobox/le-combobox.tsx", - "declarations": [ - { - "kind": "class", - "description": "A combobox component with searchable dropdown.\n\nCombines a text input with a dropdown list, allowing users to\nfilter options by typing or select from the list.", - "name": "LeCombobox", - "members": [ + "kind": "field", + "name": "handleDropdownClose", + "privacy": "private" + }, { "kind": "field", - "name": "el", - "type": { - "text": "HTMLElement" - } + "name": "handleInputChange", + "privacy": "private" + }, + { + "kind": "field", + "name": "handleInputFocus", + "privacy": "private" + }, + { + "kind": "field", + "name": "handleInputKeyDown", + "privacy": "private" }, { "kind": "field", + "name": "handleClear", + "privacy": "private" + }, + { + "kind": "method", + "name": "showDropdown", + "description": "Opens the dropdown." + }, + { + "kind": "method", + "name": "hideDropdown", + "description": "Closes the dropdown." + }, + { + "kind": "method", + "name": "focusInput", + "description": "Focuses the input element." + }, + { + "kind": "method", + "name": "render" + } + ], + "attributes": [ + { "name": "options", + "fieldName": "options", + "default": "[]", + "description": "The options to display in the dropdown.", "type": { "text": "LeOption[] | string" - }, - "default": "[]", - "description": "The options to display in the dropdown." + } }, { - "kind": "field", "name": "value", + "fieldName": "value", + "description": "The currently selected value.", "type": { - "text": "LeOptionValue | undefined" - }, - "description": "The currently selected value." + "text": "LeOptionValue" + } }, { - "kind": "field", "name": "placeholder", + "fieldName": "placeholder", + "default": "'Type to search...'", + "description": "Placeholder text for the input.", "type": { "text": "string" - }, - "default": "'Type to search...'", - "description": "Placeholder text for the input." + } }, { - "kind": "field", "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Whether the combobox is disabled.", "type": { "text": "boolean" - }, - "default": "false", - "description": "Whether the combobox is disabled." + } }, { - "kind": "field", "name": "required", - "type": { - "text": "boolean" - }, + "fieldName": "required", "default": "false", - "description": "Whether selection is required." + "description": "Whether selection is required.", + "type": { + "text": "boolean" + } }, { - "kind": "field", "name": "name", + "fieldName": "name", + "description": "Name attribute for form submission.", "type": { - "text": "string | undefined" - }, - "description": "Name attribute for form submission." + "text": "string" + } }, { - "kind": "field", - "name": "fullWidth", + "name": "full-width", + "fieldName": "fullWidth", + "default": "false", + "description": "Whether the multiselect should take full width of its container.", "type": { "text": "boolean" - }, - "default": "false", - "description": "Whether the multiselect should take full width of its container." + } }, { - "kind": "field", "name": "size", + "fieldName": "size", + "default": "'medium'", + "description": "Size variant of the combobox.", "type": { "text": "'small' | 'medium' | 'large'" - }, - "default": "'medium'", - "description": "Size variant of the combobox." + } }, { - "kind": "field", - "name": "allowCustom", + "name": "allow-custom", + "fieldName": "allowCustom", + "default": "false", + "description": "Whether to allow custom values not in the options list.", "type": { "text": "boolean" - }, - "default": "false", - "description": "Whether to allow custom values not in the options list." + } }, { - "kind": "field", - "name": "minSearchLength", + "name": "min-search-length", + "fieldName": "minSearchLength", + "default": "0", + "description": "Minimum characters before showing filtered results.", "type": { "text": "number" - }, - "default": "0", - "description": "Minimum characters before showing filtered results." + } }, { - "kind": "field", - "name": "emptyText", + "name": "empty-text", + "fieldName": "emptyText", + "default": "'No results found'", + "description": "Text to show when no options match the search.", "type": { "text": "string" - }, - "default": "'No results found'", - "description": "Text to show when no options match the search." + } }, { - "kind": "field", "name": "open", + "fieldName": "open", + "default": "false", + "description": "Whether the dropdown is currently open.", "type": { "text": "boolean" + } + } + ], + "tagName": "le-combobox", + "events": [ + { + "name": "leChange", + "type": { + "text": "EventEmitter" }, - "default": "false", - "description": "Whether the dropdown is currently open." + "description": "Emitted when the selected value changes." }, { - "kind": "method", - "name": "handleWindowClick", - "parameters": [ - { - "name": "event", - "type": { - "text": "MouseEvent" - } - } - ] + "name": "leInput", + "type": { + "text": "EventEmitter<{ value: string }>" + }, + "description": "Emitted when the input value changes (for custom values)." }, { - "kind": "field", - "name": "inputValue", + "name": "leOpen", "type": { - "text": "string" + "text": "EventEmitter" }, - "privacy": "private", - "default": "''" + "description": "Emitted when the dropdown opens." }, { - "kind": "field", - "name": "selectedOption", + "name": "leClose", "type": { - "text": "LeOption | undefined" + "text": "EventEmitter" }, - "privacy": "private" - }, + "description": "Emitted when the dropdown closes." + } + ], + "customElement": true + } + ], + "exports": [ + { + "kind": "js", + "name": "LeCombobox", + "declaration": { + "name": "LeCombobox", + "module": "src/components/le-combobox/le-combobox.tsx" + } + }, + { + "kind": "custom-element-definition", + "name": "le-combobox", + "declaration": { + "name": "LeCombobox", + "module": "src/components/le-combobox/le-combobox.tsx" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/components/le-component/le-component.tsx", + "declarations": [ + { + "kind": "class", + "description": "Component wrapper for admin mode editing.\n\nThis component is used internally by other components to provide admin-mode\nediting capabilities. It wraps the component's rendered output and shows\na settings popover for editing properties.\n\nIn default mode, it acts as a simple passthrough (display: contents).\nIn admin mode, it shows a border, component name header, and settings popover.\n\nThe host element is found automatically by traversing up through the shadow DOM.\n\nUsage inside a component's render method:\n```tsx\nrender() {\n return (\n \n ...\n \n );\n}\n```", + "name": "LeComponent", + "slots": [ + { + "description": "The component's rendered content", + "name": "" + } + ], + "members": [ { "kind": "field", - "name": "declarativeOptions", + "name": "el", "type": { - "text": "LeOption[]" - }, - "privacy": "private", - "default": "[]" + "text": "HTMLElement" + } }, { "kind": "field", - "name": "isDeclarativeMode", + "name": "component", "type": { - "text": "boolean" + "text": "string" }, - "privacy": "private", - "default": "false" + "description": "The tag name of the component (e.g., 'le-card').\nUsed to look up property metadata and display the component name." }, { "kind": "field", - "name": "dropdownEl", + "name": "displayName", "type": { - "text": "HTMLLeDropdownBaseElement | undefined" + "text": "string | undefined" }, - "privacy": "private" + "description": "Optional display name for the component.\nIf not provided, the tag name will be formatted as the display name." }, { "kind": "field", - "name": "inputEl", + "name": "hostClass", "type": { - "text": "HTMLInputElement | undefined" + "text": "string | undefined" }, - "privacy": "private" + "description": "Classes to apply to the host element.\nAllows parent components to pass their styling classes." }, { "kind": "field", - "name": "mutationObserver", + "name": "hostStyle", "type": { - "text": "MutationObserver | undefined" + "text": "{ [key: string]: string } | undefined" }, - "privacy": "private" - }, - { - "kind": "method", - "name": "handleValueChange" - }, - { - "kind": "method", - "name": "handleOptionsChange" - }, - { - "kind": "method", - "name": "handleSlotChange" - }, - { - "kind": "method", - "name": "buildDeclarativeOptions", - "privacy": "private" + "description": "Inline styles to apply to the host element.\nAllows parent components to pass dynamic styles (e.g., flex properties)." }, { "kind": "field", - "name": "parsedOptions", + "name": "hostElement", "type": { - "text": "LeOption[]" + "text": "HTMLElement | undefined" }, "privacy": "private", - "readonly": true - }, - { - "kind": "method", - "name": "updateSelectedOption", - "privacy": "private" - }, - { - "kind": "field", - "name": "filterOption", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleOptionSelect", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleDropdownOpen", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleDropdownClose", - "privacy": "private" + "description": "Reference to the host element (found automatically from parent)" }, { "kind": "field", - "name": "handleInputChange", - "privacy": "private" + "name": "adminMode", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false", + "description": "Internal state to track admin mode" }, { "kind": "field", - "name": "handleInputFocus", - "privacy": "private" + "name": "componentMeta", + "type": { + "text": "ComponentMetadata | null" + }, + "privacy": "private", + "default": "null", + "description": "Component metadata loaded from Custom Elements Manifest" }, { "kind": "field", - "name": "handleInputKeyDown", - "privacy": "private" + "name": "propertyValues", + "type": { + "text": "Record" + }, + "privacy": "private", + "default": "{}", + "description": "Current property values of the host component" }, { "kind": "field", - "name": "handleClear", + "name": "disconnectModeObserver", + "type": { + "text": "() => void | undefined" + }, "privacy": "private" }, { "kind": "method", - "name": "showDropdown", - "description": "Opens the dropdown." + "name": "findHostElement", + "privacy": "private", + "description": "Find the host element by traversing up through shadow DOM" }, { "kind": "method", - "name": "hideDropdown", - "description": "Closes the dropdown." + "name": "formatDisplayName", + "privacy": "private", + "return": { + "type": { + "text": "string" + } + }, + "parameters": [ + { + "name": "tagName", + "type": { + "text": "string" + } + } + ], + "description": "Formats a tag name into a display name\ne.g., 'le-card' -> 'Card'" }, { "kind": "method", - "name": "focusInput", - "description": "Focuses the input element." + "name": "loadComponentMetadata", + "privacy": "private", + "description": "Load component metadata from the Custom Elements Manifest" }, { "kind": "method", - "name": "render" - } - ], - "attributes": [ - { - "name": "options", - "fieldName": "options", - "default": "[]", - "description": "The options to display in the dropdown.", - "type": { - "text": "LeOption[] | string" - } - }, - { - "name": "value", - "fieldName": "value", - "description": "The currently selected value.", - "type": { - "text": "LeOptionValue" - } + "name": "isInternalAttribute", + "privacy": "private", + "return": { + "type": { + "text": "boolean" + } + }, + "parameters": [ + { + "name": "name", + "type": { + "text": "string" + } + } + ], + "description": "Check if an attribute is internal (should not be shown in editor)" }, { - "name": "placeholder", - "fieldName": "placeholder", - "default": "'Type to search...'", - "description": "Placeholder text for the input.", - "type": { - "text": "string" - } + "kind": "method", + "name": "readPropertyValues", + "privacy": "private", + "description": "Read current property values from the host element" }, { - "name": "disabled", - "fieldName": "disabled", - "default": "false", - "description": "Whether the combobox is disabled.", - "type": { - "text": "boolean" - } + "kind": "method", + "name": "parseAttributeValue", + "privacy": "private", + "return": { + "type": { + "text": "any" + } + }, + "parameters": [ + { + "name": "value", + "type": { + "text": "string | null" + } + }, + { + "name": "type", + "optional": true, + "type": { + "text": "string" + } + } + ], + "description": "Parse an attribute value based on its type" }, { - "name": "required", - "fieldName": "required", - "default": "false", - "description": "Whether selection is required.", - "type": { - "text": "boolean" - } + "kind": "method", + "name": "handlePropertyChange", + "privacy": "private", + "parameters": [ + { + "name": "attrName", + "type": { + "text": "string" + } + }, + { + "name": "value", + "type": { + "text": "any" + } + }, + { + "name": "type", + "optional": true, + "type": { + "text": "string" + } + } + ], + "description": "Handle property value changes from the editor" }, { - "name": "name", - "fieldName": "name", - "description": "Name attribute for form submission.", - "type": { - "text": "string" - } + "kind": "method", + "name": "deleteComponent", + "privacy": "private", + "description": "Delete this component from the DOM" }, { - "name": "full-width", - "fieldName": "fullWidth", - "default": "false", - "description": "Whether the multiselect should take full width of its container.", - "type": { - "text": "boolean" - } + "kind": "method", + "name": "renderPropertyEditor", + "privacy": "private", + "description": "Render the property editor form" }, { - "name": "size", - "fieldName": "size", - "default": "'medium'", - "description": "Size variant of the combobox.", - "type": { - "text": "'small' | 'medium' | 'large'" - } + "kind": "method", + "name": "renderPropertyField", + "privacy": "private", + "parameters": [ + { + "name": "attr", + "type": { + "text": "AttributeMetadata" + } + } + ], + "description": "Render a single property field based on its type" }, { - "name": "allow-custom", - "fieldName": "allowCustom", - "default": "false", - "description": "Whether to allow custom values not in the options list.", - "type": { - "text": "boolean" - } + "kind": "method", + "name": "extractLiteralUnionOptions", + "privacy": "private", + "return": { + "type": { + "text": "string[]" + } + }, + "parameters": [ + { + "name": "typeText", + "type": { + "text": "string" + } + } + ] }, { - "name": "min-search-length", - "fieldName": "minSearchLength", - "default": "0", - "description": "Minimum characters before showing filtered results.", - "type": { - "text": "number" - } - }, + "kind": "method", + "name": "render" + } + ], + "attributes": [ { - "name": "empty-text", - "fieldName": "emptyText", - "default": "'No results found'", - "description": "Text to show when no options match the search.", + "name": "component", + "fieldName": "component", + "description": "The tag name of the component (e.g., 'le-card').\nUsed to look up property metadata and display the component name.", "type": { "text": "string" } }, { - "name": "open", - "fieldName": "open", - "default": "false", - "description": "Whether the dropdown is currently open.", + "name": "display-name", + "fieldName": "displayName", + "description": "Optional display name for the component.\nIf not provided, the tag name will be formatted as the display name.", "type": { - "text": "boolean" + "text": "string" } - } - ], - "tagName": "le-combobox", - "events": [ - { - "name": "leChange", - "type": { - "text": "EventEmitter" - }, - "description": "Emitted when the selected value changes." - }, - { - "name": "leInput", - "type": { - "text": "EventEmitter<{ value: string }>" - }, - "description": "Emitted when the input value changes (for custom values)." }, { - "name": "leOpen", - "type": { - "text": "EventEmitter" - }, - "description": "Emitted when the dropdown opens." + "name": "host-class", + "fieldName": "hostClass", + "description": "Classes to apply to the host element.\nAllows parent components to pass their styling classes.", + "type": { + "text": "string" + } }, { - "name": "leClose", + "name": "host-style", + "fieldName": "hostStyle", + "description": "Inline styles to apply to the host element.\nAllows parent components to pass dynamic styles (e.g., flex properties).", "type": { - "text": "EventEmitter" - }, - "description": "Emitted when the dropdown closes." + "text": "{ [key: string]: string }" + } } ], + "tagName": "le-component", + "events": [], "customElement": true } ], "exports": [ { "kind": "js", - "name": "LeCombobox", + "name": "LeComponent", "declaration": { - "name": "LeCombobox", - "module": "src/components/le-combobox/le-combobox.tsx" + "name": "LeComponent", + "module": "src/components/le-component/le-component.tsx" } }, { "kind": "custom-element-definition", - "name": "le-combobox", + "name": "le-component", "declaration": { - "name": "LeCombobox", - "module": "src/components/le-combobox/le-combobox.tsx" + "name": "LeComponent", + "module": "src/components/le-component/le-component.tsx" } } ] }, { "kind": "javascript-module", - "path": "src/components/le-component/le-component.tsx", + "path": "src/components/le-context-menu/le-context-menu.tsx", "declarations": [ { "kind": "class", - "description": "Component wrapper for admin mode editing.\n\nThis component is used internally by other components to provide admin-mode\nediting capabilities. It wraps the component's rendered output and shows\na settings popover for editing properties.\n\nIn default mode, it acts as a simple passthrough (display: contents).\nIn admin mode, it shows a border, component name header, and settings popover.\n\nThe host element is found automatically by traversing up through the shadow DOM.\n\nUsage inside a component's render method:\n```tsx\nrender() {\n return (\n \n ...\n \n );\n}\n```", - "name": "LeComponent", + "description": "Context menu component that displays a vertical navigation menu\nwhen the user right-clicks or long-presses on its children.", + "name": "LeContextMenu", "slots": [ { - "description": "The component's rendered content", + "description": "Trigger content", "name": "" } ], @@ -4031,235 +4348,336 @@ }, { "kind": "field", - "name": "component", + "name": "popoverEl", "type": { - "text": "string" + "text": "HTMLLePopoverElement | undefined" }, - "description": "The tag name of the component (e.g., 'le-card').\nUsed to look up property metadata and display the component name." + "privacy": "private" }, { "kind": "field", - "name": "displayName", + "name": "navigationEl", "type": { - "text": "string | undefined" + "text": "HTMLLeNavigationElement | undefined" }, - "description": "Optional display name for the component.\nIf not provided, the tag name will be formatted as the display name." + "privacy": "private" }, { "kind": "field", - "name": "hostClass", + "name": "containerEl", "type": { - "text": "string | undefined" + "text": "HTMLElement | undefined" }, - "description": "Classes to apply to the host element.\nAllows parent components to pass their styling classes." + "privacy": "private" }, { "kind": "field", - "name": "hostStyle", + "name": "activeTriggerEl", "type": { - "text": "{ [key: string]: string } | undefined" + "text": "HTMLElement | undefined" }, - "description": "Inline styles to apply to the host element.\nAllows parent components to pass dynamic styles (e.g., flex properties)." + "privacy": "private" }, { "kind": "field", - "name": "hostElement", + "name": "touchTimeout", "type": { - "text": "HTMLElement | undefined" + "text": "any | undefined" + }, + "privacy": "private" + }, + { + "kind": "field", + "name": "startX", + "type": { + "text": "number" }, "privacy": "private", - "description": "Reference to the host element (found automatically from parent)" + "default": "0" }, { "kind": "field", - "name": "adminMode", + "name": "startY", + "type": { + "text": "number" + }, + "privacy": "private", + "default": "0" + }, + { + "kind": "field", + "name": "isLongPressActive", "type": { "text": "boolean" }, "privacy": "private", - "default": "false", - "description": "Internal state to track admin mode" + "default": "false" }, { "kind": "field", - "name": "componentMeta", + "name": "initialTriggerRect", "type": { - "text": "ComponentMetadata | null" + "text": "DOMRect | undefined" + }, + "privacy": "private" + }, + { + "kind": "field", + "name": "initialCoords", + "type": { + "text": "object" }, "privacy": "private", - "default": "null", - "description": "Component metadata loaded from Custom Elements Manifest" + "default": "{ x: 0, y: 0 }" }, { "kind": "field", - "name": "propertyValues", + "name": "isMenuOpen", "type": { - "text": "Record" + "text": "boolean" }, "privacy": "private", - "default": "{}", - "description": "Current property values of the host component" + "default": "false" }, { "kind": "field", - "name": "disconnectModeObserver", + "name": "open", "type": { - "text": "() => void | undefined" + "text": "boolean" }, - "privacy": "private" + "default": "false", + "description": "Whether the context menu is open." }, { - "kind": "method", - "name": "findHostElement", + "kind": "field", + "name": "disabled", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Disables right-click and touch interactions." + }, + { + "kind": "field", + "name": "items", + "type": { + "text": "LeOption[] | string" + }, + "default": "[]", + "description": "List of menu items represented as options." + }, + { + "kind": "field", + "name": "backdrop", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Whether to show a backdrop behind the menu, lifting the active item." + }, + { + "kind": "field", + "name": "pageScrollBehavior", + "type": { + "text": "'blocked' | 'menu-close' | 'fixed-menu'" + }, + "default": "'menu-close'", + "description": "Behavior of the menu on page scroll:\n- 'blocked': blocks page scroll\n- 'menu-close': closes the menu automatically on scroll (default)\n- 'fixed-menu': menu scrolls with the page" + }, + { + "kind": "field", + "name": "position", + "type": { + "text": "'top' | 'bottom' | 'left' | 'right' | 'mouse'" + }, + "default": "'mouse'", + "description": "Position of the menu relative to the trigger.\nIf 'mouse', positions next to mouse/touch coords." + }, + { + "kind": "field", + "name": "align", + "type": { + "text": "'start' | 'center' | 'end'" + }, + "default": "'start'", + "description": "Alignment of the menu relative to the trigger." + }, + { + "kind": "field", + "name": "coords", + "type": { + "text": "object" + }, "privacy": "private", - "description": "Find the host element by traversing up through shadow DOM" + "default": "{ x: 0, y: 0 }" }, { - "kind": "method", - "name": "formatDisplayName", + "kind": "field", + "name": "lastTriggerType", + "type": { + "text": "'mouse' | 'touch'" + }, "privacy": "private", - "return": { - "type": { - "text": "string" + "default": "'mouse'" + }, + { + "kind": "method", + "name": "handleOpenChange", + "parameters": [ + { + "name": "nextOpen", + "type": { + "text": "boolean" + } } - }, + ] + }, + { + "kind": "method", + "name": "show", "parameters": [ { - "name": "tagName", + "name": "x", + "optional": true, "type": { - "text": "string" + "text": "number" + } + }, + { + "name": "y", + "optional": true, + "type": { + "text": "number" } } - ], - "description": "Formats a tag name into a display name\ne.g., 'le-card' -> 'Card'" + ] }, { "kind": "method", - "name": "loadComponentMetadata", - "privacy": "private", - "description": "Load component metadata from the Custom Elements Manifest" + "name": "hide" }, { "kind": "method", - "name": "isInternalAttribute", - "privacy": "private", - "return": { - "type": { - "text": "boolean" - } - }, + "name": "toggle", "parameters": [ { - "name": "name", + "name": "x", + "optional": true, "type": { - "text": "string" + "text": "number" + } + }, + { + "name": "y", + "optional": true, + "type": { + "text": "number" } } - ], - "description": "Check if an attribute is internal (should not be shown in editor)" - }, - { - "kind": "method", - "name": "readPropertyValues", - "privacy": "private", - "description": "Read current property values from the host element" + ] }, { "kind": "method", - "name": "parseAttributeValue", + "name": "parseItems", "privacy": "private", "return": { "type": { - "text": "any" + "text": "LeOption[]" } }, "parameters": [ { - "name": "value", - "type": { - "text": "string | null" - } - }, - { - "name": "type", - "optional": true, + "name": "input", "type": { - "text": "string" + "text": "LeOption[] | string" } } - ], - "description": "Parse an attribute value based on its type" + ] + }, + { + "kind": "field", + "name": "handlePopoverOpen", + "privacy": "private" + }, + { + "kind": "field", + "name": "handlePopoverClose", + "privacy": "private" + }, + { + "kind": "field", + "name": "handleScroll", + "privacy": "private" + }, + { + "kind": "method", + "name": "updateCoordsOnScroll", + "privacy": "private" + }, + { + "kind": "method", + "name": "addScrollListener", + "privacy": "private" }, { "kind": "method", - "name": "handlePropertyChange", + "name": "removeScrollListener", + "privacy": "private" + }, + { + "kind": "method", + "name": "triggerMenu", "privacy": "private", "parameters": [ { - "name": "attrName", + "name": "x", "type": { - "text": "string" + "text": "number" } }, { - "name": "value", + "name": "y", "type": { - "text": "any" + "text": "number" } }, { - "name": "type", - "optional": true, + "name": "event", "type": { - "text": "string" + "text": "MouseEvent | TouchEvent" } } - ], - "description": "Handle property value changes from the editor" + ] }, { - "kind": "method", - "name": "deleteComponent", - "privacy": "private", - "description": "Delete this component from the DOM" + "kind": "field", + "name": "handleTouchStart", + "privacy": "private" }, { - "kind": "method", - "name": "renderPropertyEditor", - "privacy": "private", - "description": "Render the property editor form" + "kind": "field", + "name": "handleTouchMove", + "privacy": "private" }, { - "kind": "method", - "name": "renderPropertyField", - "privacy": "private", - "parameters": [ - { - "name": "attr", - "type": { - "text": "AttributeMetadata" - } - } - ], - "description": "Render a single property field based on its type" + "kind": "field", + "name": "handleTouchEnd", + "privacy": "private" }, { "kind": "method", - "name": "extractLiteralUnionOptions", - "privacy": "private", - "return": { - "type": { - "text": "string[]" - } - }, - "parameters": [ - { - "name": "typeText", - "type": { - "text": "string" - } - } - ] + "name": "clearTouchTimeout", + "privacy": "private" + }, + { + "kind": "field", + "name": "handleContextMenu", + "privacy": "private" + }, + { + "kind": "field", + "name": "handleNavigationSelect", + "privacy": "private" }, { "kind": "method", @@ -4268,58 +4686,104 @@ ], "attributes": [ { - "name": "component", - "fieldName": "component", - "description": "The tag name of the component (e.g., 'le-card').\nUsed to look up property metadata and display the component name.", + "name": "open", + "fieldName": "open", + "default": "false", + "description": "Whether the context menu is open.", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "display-name", - "fieldName": "displayName", - "description": "Optional display name for the component.\nIf not provided, the tag name will be formatted as the display name.", + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Disables right-click and touch interactions.", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "host-class", - "fieldName": "hostClass", - "description": "Classes to apply to the host element.\nAllows parent components to pass their styling classes.", + "name": "items", + "fieldName": "items", + "default": "[]", + "description": "List of menu items represented as options.", "type": { - "text": "string" + "text": "LeOption[] | string" } }, { - "name": "host-style", - "fieldName": "hostStyle", - "description": "Inline styles to apply to the host element.\nAllows parent components to pass dynamic styles (e.g., flex properties).", + "name": "backdrop", + "fieldName": "backdrop", + "default": "false", + "description": "Whether to show a backdrop behind the menu, lifting the active item.", "type": { - "text": "{ [key: string]: string }" + "text": "boolean" + } + }, + { + "name": "page-scroll-behavior", + "fieldName": "pageScrollBehavior", + "default": "'menu-close'", + "description": "Behavior of the menu on page scroll:\n- 'blocked': blocks page scroll\n- 'menu-close': closes the menu automatically on scroll (default)\n- 'fixed-menu': menu scrolls with the page", + "type": { + "text": "'blocked' | 'menu-close' | 'fixed-menu'" + } + }, + { + "name": "position", + "fieldName": "position", + "default": "'mouse'", + "description": "Position of the menu relative to the trigger.\nIf 'mouse', positions next to mouse/touch coords.", + "type": { + "text": "'top' | 'bottom' | 'left' | 'right' | 'mouse'" + } + }, + { + "name": "align", + "fieldName": "align", + "default": "'start'", + "description": "Alignment of the menu relative to the trigger.", + "type": { + "text": "'start' | 'center' | 'end'" } } ], - "tagName": "le-component", - "events": [], + "tagName": "le-context-menu", + "events": [ + { + "name": "leContextMenuSelect", + "type": { + "text": "EventEmitter" + }, + "description": "Emitted when a menu item is selected." + }, + { + "name": "leContextMenuClose", + "type": { + "text": "EventEmitter" + }, + "description": "Emitted when the context menu is closed." + } + ], "customElement": true } ], "exports": [ { "kind": "js", - "name": "LeComponent", + "name": "LeContextMenu", "declaration": { - "name": "LeComponent", - "module": "src/components/le-component/le-component.tsx" + "name": "LeContextMenu", + "module": "src/components/le-context-menu/le-context-menu.tsx" } }, { "kind": "custom-element-definition", - "name": "le-component", + "name": "le-context-menu", "declaration": { - "name": "LeComponent", - "module": "src/components/le-component/le-component.tsx" + "name": "LeContextMenu", + "module": "src/components/le-context-menu/le-context-menu.tsx" } } ] @@ -4476,146 +4940,54 @@ "description": "Handle orientation (vertical = width drag, horizontal = height drag).", "type": { "text": "LeDragHandleOrientation" - } - }, - { - "name": "placement", - "fieldName": "placement", - "default": "'end'", - "description": "Handle position on the owning edge.", - "type": { - "text": "LeDragHandlePlacement" - } - } - ], - "tagName": "le-drag-handle", - "events": [], - "customElement": true - } - ], - "exports": [ - { - "kind": "js", - "name": "LeDragHandle", - "declaration": { - "name": "LeDragHandle", - "module": "src/components/le-drag-handle/le-drag-handle.tsx" - } - }, - { - "kind": "custom-element-definition", - "name": "le-drag-handle", - "declaration": { - "name": "LeDragHandle", - "module": "src/components/le-drag-handle/le-drag-handle.tsx" - } - } - ] - }, - { - "kind": "javascript-module", - "path": "src/components/le-header/le-header.tsx", - "declarations": [ - { - "kind": "class", - "description": "A functional page header with scroll-aware behaviors.\n\nFeatures:\n- Static (default), sticky, or fixed positioning\n- Optional shrink-on-scroll behavior via `shrink-offset`\n- Optional reveal-on-scroll-up via `reveal-on-scroll` (sticky only)\n\nSlots:\n- `start`: left side (logo/back button)\n- `title`: centered/primary title content\n- `end`: right side actions\n- default: extra content row (e.g., tabs/search) rendered below main row", - "name": "LeHeader", - "cssProperties": [ - { - "description": "Background (color/gradient)", - "name": "--le-header-bg" - }, - { - "description": "Text color", - "name": "--le-header-color" - }, - { - "description": "Border (e.g. 1px solid ...)", - "name": "--le-header-border" - }, - { - "description": "Shadow/elevation", - "name": "--le-header-shadow" - }, - { - "description": "Inner content max width", - "name": "--le-header-max-width" - }, - { - "description": "Horizontal padding", - "name": "--le-header-padding-x" - }, - { - "description": "Vertical padding", - "name": "--le-header-padding-y" - }, - { - "description": "Gap between zones", - "name": "--le-header-gap" - }, - { - "description": "Base height (main row)", - "name": "--le-header-height" - }, - { - "description": "Condensed height when shrunk", - "name": "--le-header-height-condensed" - }, - { - "description": "Transition timing", - "name": "--le-header-transition" - }, - { - "description": "Z-index (fixed mode)", - "name": "--le-z-header" - } - ], - "cssParts": [ - { - "description": "The header container", - "name": "header" - }, - { - "description": "Inner max-width container", - "name": "inner" - }, - { - "description": "Main row", - "name": "row" - }, - { - "description": "Start zone", - "name": "start" - }, - { - "description": "Title zone", - "name": "title" - }, - { - "description": "End zone", - "name": "end" + } }, { - "description": "Secondary row", - "name": "secondary" + "name": "placement", + "fieldName": "placement", + "default": "'end'", + "description": "Handle position on the owning edge.", + "type": { + "text": "LeDragHandlePlacement" + } } ], + "tagName": "le-drag-handle", + "events": [], + "customElement": true + } + ], + "exports": [ + { + "kind": "js", + "name": "LeDragHandle", + "declaration": { + "name": "LeDragHandle", + "module": "src/components/le-drag-handle/le-drag-handle.tsx" + } + }, + { + "kind": "custom-element-definition", + "name": "le-drag-handle", + "declaration": { + "name": "LeDragHandle", + "module": "src/components/le-drag-handle/le-drag-handle.tsx" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/components/le-dropdown-base/le-dropdown-base.tsx", + "declarations": [ + { + "kind": "class", + "description": "Internal dropdown base component that provides shared functionality\nfor select, combobox, and multiselect components.\n\nWraps le-popover for positioning and provides:\n- Option list rendering with groups\n- Keyboard navigation (↑↓, Enter, Escape, Home/End)\n- Option filtering support\n- Single and multi-select modes", + "name": "LeDropdownBase", "slots": [ { - "description": "Start area content", - "name": "start" - }, - { - "description": "Title content", - "name": "title" - }, - { - "description": "End area content", - "name": "end" - }, - { - "description": "Optional secondary row content", - "name": "" + "description": "The element that triggers the dropdown", + "name": "trigger" } ], "members": [ @@ -4628,279 +5000,438 @@ }, { "kind": "field", - "name": "isStatic", + "name": "options", + "type": { + "text": "LeOption[]" + }, + "default": "[]", + "description": "The options to display in the dropdown." + }, + { + "kind": "field", + "name": "value", + "type": { + "text": "LeOptionValue | LeOptionValue[] | undefined" + }, + "description": "Current value(s) - single value or array for multiselect." + }, + { + "kind": "field", + "name": "multiple", "type": { "text": "boolean" }, "default": "false", - "description": "Force static positioning (default). Ignored if `sticky` or `fixed` are true." + "description": "Whether multiple selection is allowed." }, { "kind": "field", - "name": "sticky", + "name": "open", "type": { "text": "boolean" }, "default": "false", - "description": "Sticky positioning (in-flow). Ignored if `fixed` is true." + "description": "Whether the dropdown is open." }, { "kind": "field", - "name": "fixed", + "name": "disabled", "type": { "text": "boolean" }, "default": "false", - "description": "Fixed positioning (out-of-flow). Takes precedence over `sticky`/`static`." + "description": "Whether the dropdown is disabled." }, { "kind": "field", - "name": "revealOnScroll", + "name": "filterFn", "type": { - "text": "string | undefined" + "text": "(option: LeOption, query: string) => boolean | undefined" }, - "description": "Sticky-only reveal behavior (hide on scroll down, show on scroll up).\n- missing/false: disabled\n- true/empty attribute: enabled with default threshold (16)\n- number (as string): enabled and used as threshold" + "description": "Filter function for options.\nReturn true to include the option." }, { "kind": "field", - "name": "shrinkOffset", + "name": "filterQuery", "type": { - "text": "string | undefined" + "text": "string" }, - "description": "Shrink trigger.\n- missing/0: disabled\n- number (px): shrink when scrollY >= that value (but never before header height)\n- css var name (e.g. --foo): shrink when scrollY >= resolved var value\n- selector (e.g. .page-title): shrink when that element scrolls out of view above the viewport" + "default": "''", + "description": "Current filter query string." }, { "kind": "field", - "name": "expandOnHover", + "name": "emptyText", + "type": { + "text": "string" + }, + "default": "'No options'", + "description": "Placeholder text when no options match filter." + }, + { + "kind": "field", + "name": "showCheckboxes", "type": { "text": "boolean" }, - "default": "false", - "description": "If true, expand the header when hovered" + "default": "true", + "description": "Whether to show checkboxes for multiselect mode." }, { "kind": "field", - "name": "layout", + "name": "maxHeight", "type": { - "text": "'default' | 'space-between' | undefined" + "text": "string" }, - "default": "'default'", - "description": "Layout Type\n- default is three cells in the grid: start, title, end\n- space-between will use the start and end slots but put space between them and hide the title slot" + "default": "'300px'", + "description": "Maximum height of the dropdown list." }, { "kind": "field", - "name": "revealed", + "name": "width", + "type": { + "text": "string | undefined" + }, + "description": "Width of the dropdown. If not set, matches trigger width." + }, + { + "kind": "field", + "name": "fullWidth", "type": { "text": "boolean" }, - "privacy": "private", - "default": "true" + "default": "false", + "description": "Sets the dropdown to full width of the trigger." }, { "kind": "field", - "name": "shrunk", + "name": "closeOnClickOutside", "type": { "text": "boolean" }, - "privacy": "private", - "default": "false" + "default": "true", + "description": "Whether to close the dropdown when clicking outside.\n(used to support combobox with input focus)" }, { "kind": "field", - "name": "placeholderHeight", + "name": "focusedIndex", "type": { - "text": "number | null" + "text": "number" }, "privacy": "private", - "default": "null" + "default": "-1" }, { "kind": "field", - "name": "hoverActive", + "name": "filteredOptions", "type": { - "text": "boolean" + "text": "LeOption[]" }, "privacy": "private", - "default": "false" + "default": "[]" }, { "kind": "field", - "name": "disconnectModeObserver", + "name": "popoverEl", "type": { - "text": "() => void | undefined" + "text": "HTMLLePopoverElement | undefined" }, "privacy": "private" }, { "kind": "field", - "name": "rafId", + "name": "listEl", "type": { - "text": "number | null" + "text": "HTMLElement | undefined" + }, + "privacy": "private" + }, + { + "kind": "field", + "name": "triggerWidth", + "type": { + "text": "number" }, "privacy": "private", - "default": "null" + "default": "0" }, { "kind": "field", - "name": "measureRafId", + "name": "typeaheadBuffer", "type": { - "text": "number | null" + "text": "string" }, "privacy": "private", - "default": "null" + "default": "''" + }, + { + "kind": "field", + "name": "typeaheadResetTimer", + "type": { + "text": "ReturnType | undefined" + }, + "privacy": "private" + }, + { + "kind": "field", + "name": "typeaheadResetMs", + "type": { + "text": "number" + }, + "privacy": "private", + "readonly": true, + "default": "750" + }, + { + "kind": "method", + "name": "handleOptionsChange" + }, + { + "kind": "method", + "name": "updateFilteredOptions", + "privacy": "private" + }, + { + "kind": "method", + "name": "getSelectableOptions", + "privacy": "private", + "return": { + "type": { + "text": "LeOption[]" + } + } + }, + { + "kind": "method", + "name": "isSelected", + "privacy": "private", + "return": { + "type": { + "text": "boolean" + } + }, + "parameters": [ + { + "name": "option", + "type": { + "text": "LeOption" + } + } + ] + }, + { + "kind": "method", + "name": "handleOptionClick", + "privacy": "private", + "parameters": [ + { + "name": "option", + "type": { + "text": "LeOption" + } + }, + { + "name": "e", + "type": { + "text": "MouseEvent" + } + } + ] + }, + { + "kind": "field", + "name": "handleKeyDown", + "privacy": "private" }, { - "kind": "field", - "name": "lastY", - "type": { - "text": "number" - }, + "kind": "method", + "name": "isEditableTarget", "privacy": "private", - "default": "0" + "return": { + "type": { + "text": "boolean" + } + }, + "parameters": [ + { + "name": "target", + "type": { + "text": "EventTarget | null" + } + } + ] }, { - "kind": "field", - "name": "lastEmittedDirection", - "type": { - "text": "'up' | 'down'" - }, + "kind": "method", + "name": "isTypeaheadKey", "privacy": "private", - "default": "'down'" + "return": { + "type": { + "text": "boolean" + } + }, + "parameters": [ + { + "name": "key", + "type": { + "text": "string" + } + } + ] }, { - "kind": "field", - "name": "headerEl", - "type": { - "text": "HTMLElement | undefined" + "kind": "method", + "name": "normalizeText", + "privacy": "private", + "return": { + "type": { + "text": "string" + } }, - "privacy": "private" + "parameters": [ + { + "name": "value", + "type": { + "text": "string" + } + } + ] }, { - "kind": "field", - "name": "headerResizeObserver", - "type": { - "text": "ResizeObserver | undefined" - }, + "kind": "method", + "name": "resetTypeaheadBuffer", "privacy": "private" }, { - "kind": "field", - "name": "shrinkSelectorEl", - "type": { - "text": "Element | null | undefined" - }, + "kind": "method", + "name": "scheduleTypeaheadReset", "privacy": "private" }, { "kind": "method", - "name": "setShrunk", + "name": "findTypeaheadMatchIndex", "privacy": "private", + "return": { + "type": { + "text": "number" + } + }, "parameters": [ { - "name": "next", - "type": { - "text": "boolean" - } - }, - { - "name": "y", + "name": "query", "type": { - "text": "number" + "text": "string" } } ] }, { "kind": "method", - "name": "onBehaviorPropsChange", - "privacy": "protected" + "name": "handleTypeaheadKey", + "privacy": "private", + "parameters": [ + { + "name": "key", + "type": { + "text": "string" + } + } + ] }, { "kind": "method", - "name": "onWindowScroll", - "privacy": "protected" + "name": "scrollToFocused", + "privacy": "private" }, { - "kind": "method", - "name": "onWindowResize", - "privacy": "protected" + "kind": "field", + "name": "handlePopoverOpen", + "privacy": "private" }, { - "kind": "method", - "name": "onChildLayoutChange", - "privacy": "protected" + "kind": "field", + "name": "handlePopoverClose", + "privacy": "private" }, { "kind": "method", - "name": "getPosition", + "name": "getInitialFocusIndex", "privacy": "private", "return": { "type": { - "text": "LeHeaderPosition" + "text": "number" } } }, { "kind": "method", - "name": "parseRevealThreshold", - "privacy": "private", - "return": { - "type": { - "text": "number | null" - } - } + "name": "show", + "description": "Opens the dropdown." }, { "kind": "method", - "name": "resolveShrinkStartPx", - "privacy": "private", - "return": { - "type": { - "text": "number | null" - } - } + "name": "hide", + "description": "Closes the dropdown." }, { "kind": "method", - "name": "scheduleUpdate", - "privacy": "private", + "name": "toggle", + "description": "Toggles the dropdown." + }, + { + "kind": "method", + "name": "typeahead", "parameters": [ { - "name": "force", - "default": "false", + "name": "key", "type": { - "text": "boolean" + "text": "string" } } - ] + ], + "description": "Applies a typeahead key to focus matching option.\nIf the dropdown is closed, it will be opened first." }, { "kind": "method", - "name": "scheduleMeasure", + "name": "renderIcon", "privacy": "private", "parameters": [ { - "name": "force", - "default": "false", + "name": "icon", "type": { - "text": "boolean" + "text": "string | undefined" + } + }, + { + "name": "className", + "type": { + "text": "string" } } ] }, { "kind": "method", - "name": "setupHeaderResizeObserver", - "privacy": "private" - }, - { - "kind": "method", - "name": "measurePlaceholderHeight", - "privacy": "private" + "name": "renderOption", + "privacy": "private", + "parameters": [ + { + "name": "option", + "type": { + "text": "LeOption" + } + }, + { + "name": "index", + "type": { + "text": "number" + } + } + ] }, { "kind": "method", - "name": "updateFromScroll", + "name": "renderOptions", "privacy": "private" }, { @@ -4910,89 +5441,142 @@ ], "attributes": [ { - "name": "static", - "fieldName": "isStatic", + "name": "options", + "fieldName": "options", + "default": "[]", + "description": "The options to display in the dropdown.", + "type": { + "text": "LeOption[]" + } + }, + { + "name": "value", + "fieldName": "value", + "description": "Current value(s) - single value or array for multiselect.", + "type": { + "text": "LeOptionValue | LeOptionValue[]" + } + }, + { + "name": "multiple", + "fieldName": "multiple", "default": "false", - "description": "Force static positioning (default). Ignored if `sticky` or `fixed` are true.", + "description": "Whether multiple selection is allowed.", + "type": { + "text": "boolean" + } + }, + { + "name": "open", + "fieldName": "open", + "default": "false", + "description": "Whether the dropdown is open.", + "type": { + "text": "boolean" + } + }, + { + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Whether the dropdown is disabled.", + "type": { + "text": "boolean" + } + }, + { + "name": "filter-fn", + "fieldName": "filterFn", + "description": "Filter function for options.\nReturn true to include the option.", + "type": { + "text": "(option: LeOption, query: string) => boolean" + } + }, + { + "name": "filter-query", + "fieldName": "filterQuery", + "default": "''", + "description": "Current filter query string.", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "sticky", - "fieldName": "sticky", - "default": "false", - "description": "Sticky positioning (in-flow). Ignored if `fixed` is true.", + "name": "empty-text", + "fieldName": "emptyText", + "default": "'No options'", + "description": "Placeholder text when no options match filter.", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "fixed", - "fieldName": "fixed", - "default": "false", - "description": "Fixed positioning (out-of-flow). Takes precedence over `sticky`/`static`.", + "name": "show-checkboxes", + "fieldName": "showCheckboxes", + "default": "true", + "description": "Whether to show checkboxes for multiselect mode.", "type": { "text": "boolean" } }, { - "name": "reveal-on-scroll", - "fieldName": "revealOnScroll", - "description": "Sticky-only reveal behavior (hide on scroll down, show on scroll up).\n- missing/false: disabled\n- true/empty attribute: enabled with default threshold (16)\n- number (as string): enabled and used as threshold", + "name": "max-height", + "fieldName": "maxHeight", + "default": "'300px'", + "description": "Maximum height of the dropdown list.", "type": { "text": "string" } }, { - "name": "shrink-offset", - "fieldName": "shrinkOffset", - "description": "Shrink trigger.\n- missing/0: disabled\n- number (px): shrink when scrollY >= that value (but never before header height)\n- css var name (e.g. --foo): shrink when scrollY >= resolved var value\n- selector (e.g. .page-title): shrink when that element scrolls out of view above the viewport", + "name": "width", + "fieldName": "width", + "description": "Width of the dropdown. If not set, matches trigger width.", "type": { "text": "string" } }, { - "name": "expand-on-hover", - "fieldName": "expandOnHover", + "name": "full-width", + "fieldName": "fullWidth", "default": "false", - "description": "If true, expand the header when hovered", + "description": "Sets the dropdown to full width of the trigger.", "type": { "text": "boolean" } }, { - "name": "layout", - "fieldName": "layout", - "default": "'default'", - "description": "Layout Type\n- default is three cells in the grid: start, title, end\n- space-between will use the start and end slots but put space between them and hide the title slot", + "name": "close-on-click-outside", + "fieldName": "closeOnClickOutside", + "default": "true", + "description": "Whether to close the dropdown when clicking outside.\n(used to support combobox with input focus)", "type": { - "text": "'default' | 'space-between'" + "text": "boolean" } } ], - "tagName": "le-header", + "tagName": "le-dropdown-base", "events": [ { - "name": "leHeaderState", + "name": "leOptionSelect", "type": { - "text": "EventEmitter<{\n y: number;\n direction: 'up' | 'down';\n revealed: boolean;\n shrunk: boolean;\n }>" + "text": "EventEmitter" }, - "description": "Emits whenever scroll-driven state changes." + "description": "Emitted when an option is selected." }, { - "name": "leHeaderShrinkChange", + "name": "leDropdownOpen", "type": { - "text": "EventEmitter<{ shrunk: boolean; y: number }>" + "text": "EventEmitter" }, - "description": "Emits when the header shrinks/expands (only on change)." + "description": "Emitted when the dropdown opens." }, { - "name": "leHeaderVisibilityChange", + "name": "leDropdownClose", "type": { - "text": "EventEmitter<{ visible: boolean; y: number }>" + "text": "EventEmitter" }, - "description": "Emits when the header hides/shows (only on change)." + "description": "Emitted when the dropdown closes." } ], "customElement": true @@ -5001,34 +5585,126 @@ "exports": [ { "kind": "js", - "name": "LeHeader", + "name": "LeDropdownBase", "declaration": { - "name": "LeHeader", - "module": "src/components/le-header/le-header.tsx" + "name": "LeDropdownBase", + "module": "src/components/le-dropdown-base/le-dropdown-base.tsx" } }, { "kind": "custom-element-definition", - "name": "le-header", + "name": "le-dropdown-base", "declaration": { - "name": "LeHeader", - "module": "src/components/le-header/le-header.tsx" + "name": "LeDropdownBase", + "module": "src/components/le-dropdown-base/le-dropdown-base.tsx" } } ] }, { "kind": "javascript-module", - "path": "src/components/le-dropdown-base/le-dropdown-base.tsx", + "path": "src/components/le-header/le-header.tsx", "declarations": [ { "kind": "class", - "description": "Internal dropdown base component that provides shared functionality\nfor select, combobox, and multiselect components.\n\nWraps le-popover for positioning and provides:\n- Option list rendering with groups\n- Keyboard navigation (↑↓, Enter, Escape, Home/End)\n- Option filtering support\n- Single and multi-select modes", - "name": "LeDropdownBase", + "description": "A functional page header with scroll-aware behaviors.\n\nFeatures:\n- Static (default), sticky, or fixed positioning\n- Optional shrink-on-scroll behavior via `shrink-offset`\n- Optional reveal-on-scroll-up via `reveal-on-scroll` (sticky only)\n\nSlots:\n- `start`: left side (logo/back button)\n- `title`: centered/primary title content\n- `end`: right side actions\n- default: extra content row (e.g., tabs/search) rendered below main row", + "name": "LeHeader", + "cssProperties": [ + { + "description": "Background (color/gradient)", + "name": "--le-header-bg" + }, + { + "description": "Text color", + "name": "--le-header-color" + }, + { + "description": "Border (e.g. 1px solid ...)", + "name": "--le-header-border" + }, + { + "description": "Shadow/elevation", + "name": "--le-header-shadow" + }, + { + "description": "Inner content max width", + "name": "--le-header-max-width" + }, + { + "description": "Horizontal padding", + "name": "--le-header-padding-x" + }, + { + "description": "Vertical padding", + "name": "--le-header-padding-y" + }, + { + "description": "Gap between zones", + "name": "--le-header-gap" + }, + { + "description": "Base height (main row)", + "name": "--le-header-height" + }, + { + "description": "Condensed height when shrunk", + "name": "--le-header-height-condensed" + }, + { + "description": "Transition timing", + "name": "--le-header-transition" + }, + { + "description": "Z-index (fixed mode)", + "name": "--le-z-header" + } + ], + "cssParts": [ + { + "description": "The header container", + "name": "header" + }, + { + "description": "Inner max-width container", + "name": "inner" + }, + { + "description": "Main row", + "name": "row" + }, + { + "description": "Start zone", + "name": "start" + }, + { + "description": "Title zone", + "name": "title" + }, + { + "description": "End zone", + "name": "end" + }, + { + "description": "Secondary row", + "name": "secondary" + } + ], "slots": [ { - "description": "The element that triggers the dropdown", - "name": "trigger" + "description": "Start area content", + "name": "start" + }, + { + "description": "Title content", + "name": "title" + }, + { + "description": "End area content", + "name": "end" + }, + { + "description": "Optional secondary row content", + "name": "" } ], "members": [ @@ -5041,155 +5717,130 @@ }, { "kind": "field", - "name": "options", - "type": { - "text": "LeOption[]" - }, - "default": "[]", - "description": "The options to display in the dropdown." - }, - { - "kind": "field", - "name": "value", - "type": { - "text": "LeOptionValue | LeOptionValue[] | undefined" - }, - "description": "Current value(s) - single value or array for multiselect." - }, - { - "kind": "field", - "name": "multiple", + "name": "isStatic", "type": { "text": "boolean" }, "default": "false", - "description": "Whether multiple selection is allowed." + "description": "Force static positioning (default). Ignored if `sticky` or `fixed` are true." }, { "kind": "field", - "name": "open", + "name": "sticky", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the dropdown is open." + "description": "Sticky positioning (in-flow). Ignored if `fixed` is true." }, { "kind": "field", - "name": "disabled", + "name": "fixed", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the dropdown is disabled." - }, - { - "kind": "field", - "name": "filterFn", - "type": { - "text": "(option: LeOption, query: string) => boolean | undefined" - }, - "description": "Filter function for options.\nReturn true to include the option." + "description": "Fixed positioning (out-of-flow). Takes precedence over `sticky`/`static`." }, { "kind": "field", - "name": "filterQuery", + "name": "revealOnScroll", "type": { - "text": "string" + "text": "string | undefined" }, - "default": "''", - "description": "Current filter query string." + "description": "Sticky-only reveal behavior (hide on scroll down, show on scroll up).\n- missing/false: disabled\n- true/empty attribute: enabled with default threshold (16)\n- number (as string): enabled and used as threshold" }, { "kind": "field", - "name": "emptyText", + "name": "shrinkOffset", "type": { - "text": "string" + "text": "string | undefined" }, - "default": "'No options'", - "description": "Placeholder text when no options match filter." + "description": "Shrink trigger.\n- missing/0: disabled\n- number (px): shrink when scrollY >= that value (but never before header height)\n- css var name (e.g. --foo): shrink when scrollY >= resolved var value\n- selector (e.g. .page-title): shrink when that element scrolls out of view above the viewport" }, { "kind": "field", - "name": "showCheckboxes", + "name": "expandOnHover", "type": { "text": "boolean" }, - "default": "true", - "description": "Whether to show checkboxes for multiselect mode." + "default": "false", + "description": "If true, expand the header when hovered" }, { "kind": "field", - "name": "maxHeight", + "name": "layout", "type": { - "text": "string" + "text": "'default' | 'space-between' | undefined" }, - "default": "'300px'", - "description": "Maximum height of the dropdown list." + "default": "'default'", + "description": "Layout Type\n- default is three cells in the grid: start, title, end\n- space-between will use the start and end slots but put space between them and hide the title slot" }, { "kind": "field", - "name": "width", + "name": "revealed", "type": { - "text": "string | undefined" + "text": "boolean" }, - "description": "Width of the dropdown. If not set, matches trigger width." + "privacy": "private", + "default": "true" }, { "kind": "field", - "name": "fullWidth", + "name": "shrunk", "type": { "text": "boolean" }, - "default": "false", - "description": "Sets the dropdown to full width of the trigger." + "privacy": "private", + "default": "false" }, { "kind": "field", - "name": "closeOnClickOutside", + "name": "placeholderHeight", "type": { - "text": "boolean" + "text": "number | null" }, - "default": "true", - "description": "Whether to close the dropdown when clicking outside.\n(used to support combobox with input focus)" + "privacy": "private", + "default": "null" }, { "kind": "field", - "name": "focusedIndex", + "name": "hoverActive", "type": { - "text": "number" + "text": "boolean" }, "privacy": "private", - "default": "-1" + "default": "false" }, { "kind": "field", - "name": "filteredOptions", + "name": "disconnectModeObserver", "type": { - "text": "LeOption[]" + "text": "() => void | undefined" }, - "privacy": "private", - "default": "[]" + "privacy": "private" }, { "kind": "field", - "name": "popoverEl", + "name": "rafId", "type": { - "text": "HTMLLePopoverElement | undefined" + "text": "number | null" }, - "privacy": "private" + "privacy": "private", + "default": "null" }, { "kind": "field", - "name": "listEl", + "name": "measureRafId", "type": { - "text": "HTMLElement | undefined" + "text": "number | null" }, - "privacy": "private" + "privacy": "private", + "default": "null" }, { "kind": "field", - "name": "triggerWidth", + "name": "lastY", "type": { "text": "number" }, @@ -5198,281 +5849,147 @@ }, { "kind": "field", - "name": "typeaheadBuffer", + "name": "lastEmittedDirection", "type": { - "text": "string" + "text": "'up' | 'down'" }, "privacy": "private", - "default": "''" + "default": "'down'" }, { "kind": "field", - "name": "typeaheadResetTimer", + "name": "headerEl", "type": { - "text": "ReturnType | undefined" + "text": "HTMLElement | undefined" }, "privacy": "private" }, { "kind": "field", - "name": "typeaheadResetMs", + "name": "headerResizeObserver", "type": { - "text": "number" + "text": "ResizeObserver | undefined" }, - "privacy": "private", - "readonly": true, - "default": "750" - }, - { - "kind": "method", - "name": "handleOptionsChange" - }, - { - "kind": "method", - "name": "updateFilteredOptions", "privacy": "private" }, { - "kind": "method", - "name": "getSelectableOptions", - "privacy": "private", - "return": { - "type": { - "text": "LeOption[]" - } - } - }, - { - "kind": "method", - "name": "isSelected", - "privacy": "private", - "return": { - "type": { - "text": "boolean" - } + "kind": "field", + "name": "shrinkSelectorEl", + "type": { + "text": "Element | null | undefined" }, - "parameters": [ - { - "name": "option", - "type": { - "text": "LeOption" - } - } - ] + "privacy": "private" }, { "kind": "method", - "name": "handleOptionClick", + "name": "setShrunk", "privacy": "private", "parameters": [ { - "name": "option", + "name": "next", "type": { - "text": "LeOption" + "text": "boolean" } }, { - "name": "e", - "type": { - "text": "MouseEvent" - } - } - ] - }, - { - "kind": "field", - "name": "handleKeyDown", - "privacy": "private" - }, - { - "kind": "method", - "name": "isEditableTarget", - "privacy": "private", - "return": { - "type": { - "text": "boolean" - } - }, - "parameters": [ - { - "name": "target", + "name": "y", "type": { - "text": "EventTarget | null" + "text": "number" } } ] }, { "kind": "method", - "name": "isTypeaheadKey", - "privacy": "private", - "return": { - "type": { - "text": "boolean" - } - }, - "parameters": [ - { - "name": "key", - "type": { - "text": "string" - } - } - ] + "name": "onBehaviorPropsChange", + "privacy": "protected" }, { "kind": "method", - "name": "normalizeText", - "privacy": "private", - "return": { - "type": { - "text": "string" - } - }, - "parameters": [ - { - "name": "value", - "type": { - "text": "string" - } - } - ] + "name": "onWindowScroll", + "privacy": "protected" }, { "kind": "method", - "name": "resetTypeaheadBuffer", - "privacy": "private" + "name": "onWindowResize", + "privacy": "protected" }, { "kind": "method", - "name": "scheduleTypeaheadReset", - "privacy": "private" + "name": "onChildLayoutChange", + "privacy": "protected" }, { "kind": "method", - "name": "findTypeaheadMatchIndex", + "name": "getPosition", "privacy": "private", "return": { "type": { - "text": "number" - } - }, - "parameters": [ - { - "name": "query", - "type": { - "text": "string" - } + "text": "LeHeaderPosition" } - ] + } }, { "kind": "method", - "name": "handleTypeaheadKey", + "name": "parseRevealThreshold", "privacy": "private", - "parameters": [ - { - "name": "key", - "type": { - "text": "string" - } + "return": { + "type": { + "text": "number | null" } - ] - }, - { - "kind": "method", - "name": "scrollToFocused", - "privacy": "private" - }, - { - "kind": "field", - "name": "handlePopoverOpen", - "privacy": "private" - }, - { - "kind": "field", - "name": "handlePopoverClose", - "privacy": "private" + } }, { "kind": "method", - "name": "getInitialFocusIndex", + "name": "resolveShrinkStartPx", "privacy": "private", "return": { "type": { - "text": "number" + "text": "number | null" } } }, { "kind": "method", - "name": "show", - "description": "Opens the dropdown." - }, - { - "kind": "method", - "name": "hide", - "description": "Closes the dropdown." - }, - { - "kind": "method", - "name": "toggle", - "description": "Toggles the dropdown." - }, - { - "kind": "method", - "name": "typeahead", - "parameters": [ - { - "name": "key", - "type": { - "text": "string" - } - } - ], - "description": "Applies a typeahead key to focus matching option.\nIf the dropdown is closed, it will be opened first." - }, - { - "kind": "method", - "name": "renderIcon", - "privacy": "private", - "parameters": [ - { - "name": "icon", - "type": { - "text": "string | undefined" - } - }, + "name": "scheduleUpdate", + "privacy": "private", + "parameters": [ { - "name": "className", + "name": "force", + "default": "false", "type": { - "text": "string" + "text": "boolean" } } ] }, { "kind": "method", - "name": "renderOption", + "name": "scheduleMeasure", "privacy": "private", "parameters": [ { - "name": "option", - "type": { - "text": "LeOption" - } - }, - { - "name": "index", + "name": "force", + "default": "false", "type": { - "text": "number" + "text": "boolean" } } ] }, { "kind": "method", - "name": "renderOptions", + "name": "setupHeaderResizeObserver", + "privacy": "private" + }, + { + "kind": "method", + "name": "measurePlaceholderHeight", + "privacy": "private" + }, + { + "kind": "method", + "name": "updateFromScroll", "privacy": "private" }, { @@ -5482,142 +5999,89 @@ ], "attributes": [ { - "name": "options", - "fieldName": "options", - "default": "[]", - "description": "The options to display in the dropdown.", - "type": { - "text": "LeOption[]" - } - }, - { - "name": "value", - "fieldName": "value", - "description": "Current value(s) - single value or array for multiselect.", - "type": { - "text": "LeOptionValue | LeOptionValue[]" - } - }, - { - "name": "multiple", - "fieldName": "multiple", + "name": "static", + "fieldName": "isStatic", "default": "false", - "description": "Whether multiple selection is allowed.", + "description": "Force static positioning (default). Ignored if `sticky` or `fixed` are true.", "type": { "text": "boolean" } }, { - "name": "open", - "fieldName": "open", + "name": "sticky", + "fieldName": "sticky", "default": "false", - "description": "Whether the dropdown is open.", + "description": "Sticky positioning (in-flow). Ignored if `fixed` is true.", "type": { "text": "boolean" } }, { - "name": "disabled", - "fieldName": "disabled", + "name": "fixed", + "fieldName": "fixed", "default": "false", - "description": "Whether the dropdown is disabled.", - "type": { - "text": "boolean" - } - }, - { - "name": "filter-fn", - "fieldName": "filterFn", - "description": "Filter function for options.\nReturn true to include the option.", - "type": { - "text": "(option: LeOption, query: string) => boolean" - } - }, - { - "name": "filter-query", - "fieldName": "filterQuery", - "default": "''", - "description": "Current filter query string.", - "type": { - "text": "string" - } - }, - { - "name": "empty-text", - "fieldName": "emptyText", - "default": "'No options'", - "description": "Placeholder text when no options match filter.", - "type": { - "text": "string" - } - }, - { - "name": "show-checkboxes", - "fieldName": "showCheckboxes", - "default": "true", - "description": "Whether to show checkboxes for multiselect mode.", + "description": "Fixed positioning (out-of-flow). Takes precedence over `sticky`/`static`.", "type": { "text": "boolean" } }, { - "name": "max-height", - "fieldName": "maxHeight", - "default": "'300px'", - "description": "Maximum height of the dropdown list.", + "name": "reveal-on-scroll", + "fieldName": "revealOnScroll", + "description": "Sticky-only reveal behavior (hide on scroll down, show on scroll up).\n- missing/false: disabled\n- true/empty attribute: enabled with default threshold (16)\n- number (as string): enabled and used as threshold", "type": { "text": "string" } }, { - "name": "width", - "fieldName": "width", - "description": "Width of the dropdown. If not set, matches trigger width.", + "name": "shrink-offset", + "fieldName": "shrinkOffset", + "description": "Shrink trigger.\n- missing/0: disabled\n- number (px): shrink when scrollY >= that value (but never before header height)\n- css var name (e.g. --foo): shrink when scrollY >= resolved var value\n- selector (e.g. .page-title): shrink when that element scrolls out of view above the viewport", "type": { "text": "string" } }, { - "name": "full-width", - "fieldName": "fullWidth", + "name": "expand-on-hover", + "fieldName": "expandOnHover", "default": "false", - "description": "Sets the dropdown to full width of the trigger.", + "description": "If true, expand the header when hovered", "type": { "text": "boolean" } }, { - "name": "close-on-click-outside", - "fieldName": "closeOnClickOutside", - "default": "true", - "description": "Whether to close the dropdown when clicking outside.\n(used to support combobox with input focus)", + "name": "layout", + "fieldName": "layout", + "default": "'default'", + "description": "Layout Type\n- default is three cells in the grid: start, title, end\n- space-between will use the start and end slots but put space between them and hide the title slot", "type": { - "text": "boolean" + "text": "'default' | 'space-between'" } } ], - "tagName": "le-dropdown-base", + "tagName": "le-header", "events": [ { - "name": "leOptionSelect", + "name": "leHeaderState", "type": { - "text": "EventEmitter" + "text": "EventEmitter<{\n y: number;\n direction: 'up' | 'down';\n revealed: boolean;\n shrunk: boolean;\n }>" }, - "description": "Emitted when an option is selected." + "description": "Emits whenever scroll-driven state changes." }, { - "name": "leDropdownOpen", + "name": "leHeaderShrinkChange", "type": { - "text": "EventEmitter" + "text": "EventEmitter<{ shrunk: boolean; y: number }>" }, - "description": "Emitted when the dropdown opens." + "description": "Emits when the header shrinks/expands (only on change)." }, { - "name": "leDropdownClose", + "name": "leHeaderVisibilityChange", "type": { - "text": "EventEmitter" + "text": "EventEmitter<{ visible: boolean; y: number }>" }, - "description": "Emitted when the dropdown closes." + "description": "Emits when the header hides/shows (only on change)." } ], "customElement": true @@ -5626,18 +6090,18 @@ "exports": [ { "kind": "js", - "name": "LeDropdownBase", + "name": "LeHeader", "declaration": { - "name": "LeDropdownBase", - "module": "src/components/le-dropdown-base/le-dropdown-base.tsx" + "name": "LeHeader", + "module": "src/components/le-header/le-header.tsx" } }, { "kind": "custom-element-definition", - "name": "le-dropdown-base", + "name": "le-header", "declaration": { - "name": "LeDropdownBase", - "module": "src/components/le-dropdown-base/le-dropdown-base.tsx" + "name": "LeHeader", + "module": "src/components/le-header/le-header.tsx" } } ] @@ -10845,71 +11309,76 @@ }, { "kind": "javascript-module", - "path": "src/components/le-select/le-select.tsx", + "path": "src/components/le-segmented-control/le-segmented-control.tsx", "declarations": [ { "kind": "class", - "description": "A select dropdown component for single selection.", - "name": "LeSelect", - "members": [ + "description": "A segmented control component (iOS-style toggle buttons).\n\nPerfect for toggling between a small set of related options.", + "name": "LeSegmentedControl", + "cssProperties": [ { - "kind": "field", - "name": "el", - "type": { - "text": "HTMLElement" - } + "description": "Background color of the control", + "name": "--le-segmented-bg" }, { - "kind": "field", - "name": "options", - "type": { - "text": "LeOption[] | string" - }, - "default": "[]", - "description": "The options to display in the dropdown." + "description": "Padding around segments", + "name": "--le-segmented-padding" }, { - "kind": "field", - "name": "value", - "type": { - "text": "LeOptionValue | undefined" - }, - "description": "The currently selected value." + "description": "Gap between segments", + "name": "--le-segmented-gap" + }, + { + "description": "Border radius of the control", + "name": "--le-segmented-radius" + } + ], + "cssParts": [ + { + "description": "The main container", + "name": "container" + }, + { + "description": "Individual segment buttons", + "name": "segment" }, + { + "description": "The currently active segment", + "name": "segment-active" + } + ], + "members": [ { "kind": "field", - "name": "placeholder", + "name": "el", "type": { - "text": "string" - }, - "default": "'Select an option'", - "description": "Placeholder text when no option is selected." + "text": "HTMLElement" + } }, { "kind": "field", - "name": "disabled", + "name": "containerRef", "type": { - "text": "boolean" + "text": "HTMLElement | undefined" }, - "default": "false", - "description": "Whether the select is disabled." + "privacy": "private" }, { "kind": "field", - "name": "required", + "name": "options", "type": { - "text": "boolean" + "text": "LeOption[] | string" }, - "default": "false", - "description": "Whether selection is required." + "default": "[]", + "description": "Array of options for the segmented control." }, { "kind": "field", - "name": "name", + "name": "value", "type": { - "text": "string | undefined" + "text": "LeOptionValue | undefined" }, - "description": "Name attribute for form submission." + "description": "The value of the currently selected option." }, { "kind": "field", @@ -10918,42 +11387,54 @@ "text": "'small' | 'medium' | 'large'" }, "default": "'medium'", - "description": "Size variant of the select." + "description": "Size of the control." + }, + { + "kind": "field", + "name": "overflow", + "type": { + "text": "'auto' | 'hidden' | 'visible' | 'scroll'" + }, + "default": "'auto'", + "description": "Scroll behavior for overflowing tabs." }, { "kind": "field", - "name": "variant", + "name": "fullWidth", "type": { - "text": "'default' | 'outlined' | 'solid'" + "text": "boolean" }, - "default": "'default'", - "description": "Visual variant of the select." + "default": "false", + "description": "Whether the control should take full width." }, { "kind": "field", - "name": "open", + "name": "disabled", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the dropdown is currently open." + "description": "Whether the control is disabled." }, { "kind": "field", - "name": "selectedOption", + "name": "segmentConfigs", "type": { - "text": "LeOption | undefined" + "text": "SegmentConfig[]" }, - "privacy": "private" + "privacy": "private", + "default": "[]", + "description": "Internal segment configurations built from declarative items or options prop." }, { "kind": "field", - "name": "declarativeOptions", + "name": "focusedIndex", "type": { - "text": "LeOption[]" + "text": "number" }, "privacy": "private", - "default": "[]" + "default": "0", + "description": "Internal state for focused index (keyboard navigation)" }, { "kind": "field", @@ -10962,15 +11443,17 @@ "text": "boolean" }, "privacy": "private", - "default": "false" + "default": "false", + "description": "Whether we're using declarative mode (le-item children)" }, { "kind": "field", - "name": "dropdownEl", + "name": "declarativeOptions", "type": { - "text": "HTMLLeDropdownBaseElement | undefined" + "text": "LeOption[]" }, - "privacy": "private" + "privacy": "private", + "default": "[]" }, { "kind": "field", @@ -10980,10 +11463,6 @@ }, "privacy": "private" }, - { - "kind": "method", - "name": "handleValueChange" - }, { "kind": "method", "name": "handleOptionsChange" @@ -10994,7 +11473,7 @@ }, { "kind": "method", - "name": "syncDeclarativeOptionsAndSelection", + "name": "syncDeclarativeOptionsAndSegments", "privacy": "private" }, { @@ -11013,53 +11492,97 @@ }, { "kind": "method", - "name": "updateSelectedOption", + "name": "buildSegmentsConfigs", "privacy": "private" }, { - "kind": "field", - "name": "handleOptionSelect", - "privacy": "private" + "kind": "method", + "name": "getOptionValue", + "privacy": "private", + "return": { + "type": { + "text": "LeOptionValue" + } + }, + "parameters": [ + { + "name": "option", + "type": { + "text": "LeOption" + } + } + ] }, { - "kind": "field", - "name": "handleDropdownOpen", - "privacy": "private" + "kind": "method", + "name": "selectOption", + "privacy": "private", + "parameters": [ + { + "name": "option", + "type": { + "text": "LeOption" + } + } + ] }, { "kind": "field", - "name": "handleDropdownClose", + "name": "handleClick", "privacy": "private" }, { "kind": "field", - "name": "handleTriggerClick", + "name": "handleKeyDown", "privacy": "private" }, { - "kind": "field", - "name": "handleTriggerKeyDown", - "privacy": "private" + "kind": "method", + "name": "findNextEnabled", + "privacy": "private", + "return": { + "type": { + "text": "number" + } + }, + "parameters": [ + { + "name": "direction", + "type": { + "text": "1 | -1" + } + } + ] }, { "kind": "method", - "name": "showDropdown", - "description": "Opens the dropdown." + "name": "findFirstEnabled", + "privacy": "private", + "return": { + "type": { + "text": "number" + } + } }, { "kind": "method", - "name": "hideDropdown", - "description": "Closes the dropdown." + "name": "findLastEnabled", + "privacy": "private", + "return": { + "type": { + "text": "number" + } + } }, { "kind": "method", - "name": "renderIcon", + "name": "focusSegment", "privacy": "private", "parameters": [ { - "name": "icon", + "name": "index", "type": { - "text": "string | undefined" + "text": "number" } } ] @@ -11074,7 +11597,7 @@ "name": "options", "fieldName": "options", "default": "[]", - "description": "The options to display in the dropdown.", + "description": "Array of options for the segmented control.", "type": { "text": "LeOption[] | string" } @@ -11082,96 +11605,56 @@ { "name": "value", "fieldName": "value", - "description": "The currently selected value.", + "description": "The value of the currently selected option.", "type": { "text": "LeOptionValue" } }, { - "name": "placeholder", - "fieldName": "placeholder", - "default": "'Select an option'", - "description": "Placeholder text when no option is selected.", + "name": "size", + "fieldName": "size", + "default": "'medium'", + "description": "Size of the control.", "type": { - "text": "string" + "text": "'small' | 'medium' | 'large'" } }, { - "name": "disabled", - "fieldName": "disabled", - "default": "false", - "description": "Whether the select is disabled.", + "name": "overflow", + "fieldName": "overflow", + "default": "'auto'", + "description": "Scroll behavior for overflowing tabs.", "type": { - "text": "boolean" + "text": "'auto' | 'hidden' | 'visible' | 'scroll'" } }, { - "name": "required", - "fieldName": "required", + "name": "full-width", + "fieldName": "fullWidth", "default": "false", - "description": "Whether selection is required.", + "description": "Whether the control should take full width.", "type": { "text": "boolean" } }, { - "name": "name", - "fieldName": "name", - "description": "Name attribute for form submission.", - "type": { - "text": "string" - } - }, - { - "name": "size", - "fieldName": "size", - "default": "'medium'", - "description": "Size variant of the select.", - "type": { - "text": "'small' | 'medium' | 'large'" - } - }, - { - "name": "variant", - "fieldName": "variant", - "default": "'default'", - "description": "Visual variant of the select.", - "type": { - "text": "'default' | 'outlined' | 'solid'" - } - }, - { - "name": "open", - "fieldName": "open", + "name": "disabled", + "fieldName": "disabled", "default": "false", - "description": "Whether the dropdown is currently open.", + "description": "Whether the control is disabled.", "type": { "text": "boolean" } } ], - "tagName": "le-select", + "tagName": "le-segmented-control", "events": [ { "name": "leChange", "type": { "text": "EventEmitter | undefined" }, - "description": "Emitted when the selected value changes." - }, - { - "name": "leOpen", - "type": { - "text": "EventEmitter | undefined" - }, - "description": "Emitted when the dropdown opens." - }, - { - "name": "leClose", - "type": { - "text": "EventEmitter | undefined" - }, - "description": "Emitted when the dropdown closes." + "description": "Emitted when the selection changes." } ], "customElement": true @@ -11180,62 +11663,30 @@ "exports": [ { "kind": "js", - "name": "LeSelect", + "name": "LeSegmentedControl", "declaration": { - "name": "LeSelect", - "module": "src/components/le-select/le-select.tsx" + "name": "LeSegmentedControl", + "module": "src/components/le-segmented-control/le-segmented-control.tsx" } }, { "kind": "custom-element-definition", - "name": "le-select", + "name": "le-segmented-control", "declaration": { - "name": "LeSelect", - "module": "src/components/le-select/le-select.tsx" + "name": "LeSegmentedControl", + "module": "src/components/le-segmented-control/le-segmented-control.tsx" } } ] }, { "kind": "javascript-module", - "path": "src/components/le-segmented-control/le-segmented-control.tsx", - "declarations": [ - { - "kind": "class", - "description": "A segmented control component (iOS-style toggle buttons).\n\nPerfect for toggling between a small set of related options.", - "name": "LeSegmentedControl", - "cssProperties": [ - { - "description": "Background color of the control", - "name": "--le-segmented-bg" - }, - { - "description": "Padding around segments", - "name": "--le-segmented-padding" - }, - { - "description": "Gap between segments", - "name": "--le-segmented-gap" - }, - { - "description": "Border radius of the control", - "name": "--le-segmented-radius" - } - ], - "cssParts": [ - { - "description": "The main container", - "name": "container" - }, - { - "description": "Individual segment buttons", - "name": "segment" - }, - { - "description": "The currently active segment", - "name": "segment-active" - } - ], + "path": "src/components/le-select/le-select.tsx", + "declarations": [ + { + "kind": "class", + "description": "A select dropdown component for single selection.", + "name": "LeSelect", "members": [ { "kind": "field", @@ -11244,14 +11695,6 @@ "text": "HTMLElement" } }, - { - "kind": "field", - "name": "containerRef", - "type": { - "text": "HTMLElement | undefined" - }, - "privacy": "private" - }, { "kind": "field", "name": "options", @@ -11259,7 +11702,7 @@ "text": "LeOption[] | string" }, "default": "[]", - "description": "Array of options for the segmented control." + "description": "The options to display in the dropdown." }, { "kind": "field", @@ -11267,73 +11710,77 @@ "type": { "text": "LeOptionValue | undefined" }, - "description": "The value of the currently selected option." + "description": "The currently selected value." }, { "kind": "field", - "name": "size", + "name": "placeholder", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "string" }, - "default": "'medium'", - "description": "Size of the control." + "default": "'Select an option'", + "description": "Placeholder text when no option is selected." }, { "kind": "field", - "name": "overflow", + "name": "disabled", "type": { - "text": "'auto' | 'hidden' | 'visible' | 'scroll'" + "text": "boolean" }, - "default": "'auto'", - "description": "Scroll behavior for overflowing tabs." + "default": "false", + "description": "Whether the select is disabled." }, { "kind": "field", - "name": "fullWidth", + "name": "required", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the control should take full width." + "description": "Whether selection is required." }, { "kind": "field", - "name": "disabled", + "name": "name", "type": { - "text": "boolean" + "text": "string | undefined" }, - "default": "false", - "description": "Whether the control is disabled." + "description": "Name attribute for form submission." }, { "kind": "field", - "name": "segmentConfigs", + "name": "size", "type": { - "text": "SegmentConfig[]" + "text": "'small' | 'medium' | 'large'" }, - "privacy": "private", - "default": "[]", - "description": "Internal segment configurations built from declarative items or options prop." + "default": "'medium'", + "description": "Size variant of the select." }, { "kind": "field", - "name": "focusedIndex", + "name": "variant", "type": { - "text": "number" + "text": "'default' | 'outlined' | 'solid'" }, - "privacy": "private", - "default": "0", - "description": "Internal state for focused index (keyboard navigation)" + "default": "'default'", + "description": "Visual variant of the select." }, { "kind": "field", - "name": "isDeclarativeMode", + "name": "open", "type": { "text": "boolean" }, - "privacy": "private", "default": "false", - "description": "Whether we're using declarative mode (le-item children)" + "description": "Whether the dropdown is currently open." + }, + { + "kind": "field", + "name": "selectedOption", + "type": { + "text": "LeOption | undefined" + }, + "privacy": "private" }, { "kind": "field", @@ -11344,6 +11791,23 @@ "privacy": "private", "default": "[]" }, + { + "kind": "field", + "name": "isDeclarativeMode", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" + }, + { + "kind": "field", + "name": "dropdownEl", + "type": { + "text": "HTMLLeDropdownBaseElement | undefined" + }, + "privacy": "private" + }, { "kind": "field", "name": "mutationObserver", @@ -11352,6 +11816,10 @@ }, "privacy": "private" }, + { + "kind": "method", + "name": "handleValueChange" + }, { "kind": "method", "name": "handleOptionsChange" @@ -11362,7 +11830,7 @@ }, { "kind": "method", - "name": "syncDeclarativeOptionsAndSegments", + "name": "syncDeclarativeOptionsAndSelection", "privacy": "private" }, { @@ -11381,97 +11849,53 @@ }, { "kind": "method", - "name": "buildSegmentsConfigs", + "name": "updateSelectedOption", "privacy": "private" }, { - "kind": "method", - "name": "getOptionValue", - "privacy": "private", - "return": { - "type": { - "text": "LeOptionValue" - } - }, - "parameters": [ - { - "name": "option", - "type": { - "text": "LeOption" - } - } - ] + "kind": "field", + "name": "handleOptionSelect", + "privacy": "private" }, { - "kind": "method", - "name": "selectOption", - "privacy": "private", - "parameters": [ - { - "name": "option", - "type": { - "text": "LeOption" - } - } - ] + "kind": "field", + "name": "handleDropdownOpen", + "privacy": "private" }, { "kind": "field", - "name": "handleClick", + "name": "handleDropdownClose", "privacy": "private" }, { "kind": "field", - "name": "handleKeyDown", + "name": "handleTriggerClick", "privacy": "private" }, { - "kind": "method", - "name": "findNextEnabled", - "privacy": "private", - "return": { - "type": { - "text": "number" - } - }, - "parameters": [ - { - "name": "direction", - "type": { - "text": "1 | -1" - } - } - ] + "kind": "field", + "name": "handleTriggerKeyDown", + "privacy": "private" }, { "kind": "method", - "name": "findFirstEnabled", - "privacy": "private", - "return": { - "type": { - "text": "number" - } - } + "name": "showDropdown", + "description": "Opens the dropdown." }, { "kind": "method", - "name": "findLastEnabled", - "privacy": "private", - "return": { - "type": { - "text": "number" - } - } + "name": "hideDropdown", + "description": "Closes the dropdown." }, { "kind": "method", - "name": "focusSegment", + "name": "renderIcon", "privacy": "private", "parameters": [ { - "name": "index", + "name": "icon", "type": { - "text": "number" + "text": "string | undefined" } } ] @@ -11486,7 +11910,7 @@ "name": "options", "fieldName": "options", "default": "[]", - "description": "Array of options for the segmented control.", + "description": "The options to display in the dropdown.", "type": { "text": "LeOption[] | string" } @@ -11494,56 +11918,96 @@ { "name": "value", "fieldName": "value", - "description": "The value of the currently selected option.", + "description": "The currently selected value.", "type": { "text": "LeOptionValue" } }, { - "name": "size", - "fieldName": "size", - "default": "'medium'", - "description": "Size of the control.", + "name": "placeholder", + "fieldName": "placeholder", + "default": "'Select an option'", + "description": "Placeholder text when no option is selected.", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "string" } }, { - "name": "overflow", - "fieldName": "overflow", - "default": "'auto'", - "description": "Scroll behavior for overflowing tabs.", + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Whether the select is disabled.", "type": { - "text": "'auto' | 'hidden' | 'visible' | 'scroll'" + "text": "boolean" } }, { - "name": "full-width", - "fieldName": "fullWidth", + "name": "required", + "fieldName": "required", "default": "false", - "description": "Whether the control should take full width.", + "description": "Whether selection is required.", "type": { "text": "boolean" } }, { - "name": "disabled", - "fieldName": "disabled", + "name": "name", + "fieldName": "name", + "description": "Name attribute for form submission.", + "type": { + "text": "string" + } + }, + { + "name": "size", + "fieldName": "size", + "default": "'medium'", + "description": "Size variant of the select.", + "type": { + "text": "'small' | 'medium' | 'large'" + } + }, + { + "name": "variant", + "fieldName": "variant", + "default": "'default'", + "description": "Visual variant of the select.", + "type": { + "text": "'default' | 'outlined' | 'solid'" + } + }, + { + "name": "open", + "fieldName": "open", "default": "false", - "description": "Whether the control is disabled.", + "description": "Whether the dropdown is currently open.", "type": { "text": "boolean" } } ], - "tagName": "le-segmented-control", + "tagName": "le-select", "events": [ { - "name": "leChange", + "name": "leChange", + "type": { + "text": "EventEmitter | undefined" + }, + "description": "Emitted when the selected value changes." + }, + { + "name": "leOpen", + "type": { + "text": "EventEmitter | undefined" + }, + "description": "Emitted when the dropdown opens." + }, + { + "name": "leClose", "type": { - "text": "EventEmitter | undefined" + "text": "EventEmitter | undefined" }, - "description": "Emitted when the selection changes." + "description": "Emitted when the dropdown closes." } ], "customElement": true @@ -11552,18 +12016,18 @@ "exports": [ { "kind": "js", - "name": "LeSegmentedControl", + "name": "LeSelect", "declaration": { - "name": "LeSegmentedControl", - "module": "src/components/le-segmented-control/le-segmented-control.tsx" + "name": "LeSelect", + "module": "src/components/le-select/le-select.tsx" } }, { "kind": "custom-element-definition", - "name": "le-segmented-control", + "name": "le-select", "declaration": { - "name": "LeSegmentedControl", - "module": "src/components/le-segmented-control/le-segmented-control.tsx" + "name": "LeSelect", + "module": "src/components/le-select/le-select.tsx" } } ] @@ -12359,30 +12823,12 @@ }, { "kind": "javascript-module", - "path": "src/components/le-stack/le-stack.tsx", + "path": "src/components/le-side-panel-toggle/le-side-panel-toggle.tsx", "declarations": [ { "kind": "class", - "description": "A flexible stack layout component using CSS flexbox.\n\n`le-stack` arranges its children in a row (horizontal) or column (vertical)\nwith configurable spacing, alignment, and wrapping behavior. Perfect for\ncreating responsive layouts.", - "name": "LeStack", - "cssProperties": [ - { - "description": "Gap between items (defaults to var(--le-space-md))", - "name": "--le-stack-gap" - } - ], - "cssParts": [ - { - "description": "The main stack container", - "name": "stack" - } - ], - "slots": [ - { - "description": "Default slot for stack items (le-box components recommended)", - "name": "" - } - ], + "description": "", + "name": "LeSidePanelToggle", "members": [ { "kind": "field", @@ -12393,73 +12839,77 @@ }, { "kind": "field", - "name": "direction", + "name": "panelId", "type": { - "text": "'horizontal' | 'vertical'" + "text": "string | undefined" }, - "default": "'horizontal'", - "description": "Direction of the stack layout" + "description": "Optional id used to target a specific panel." }, { "kind": "field", - "name": "gap", + "name": "action", + "type": { + "text": "LeSidePanelToggleAction" + }, + "default": "'toggle'", + "description": "Action to emit. Default toggles the panel." + }, + { + "kind": "field", + "name": "shortcut", "type": { "text": "string | undefined" }, - "description": "Gap between items (CSS value like '8px', '1rem', 'var(--le-space-md)')" + "description": "Optional keyboard shortcut like `Mod+B` or `Alt+N`." }, { "kind": "field", - "name": "align", + "name": "disabled", "type": { - "text": "'start' | 'center' | 'end' | 'stretch' | 'baseline'" + "text": "boolean" }, - "default": "'stretch'", - "description": "Alignment of items on the cross axis" + "default": "false", + "description": "Disables the toggle." }, { "kind": "field", - "name": "justify", + "name": "mode", "type": { - "text": "'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'" + "text": "'default' | 'admin'" }, - "default": "'start'", - "description": "Distribution of items on the main axis" + "default": "'default'" }, { "kind": "field", - "name": "wrap", + "name": "variant", "type": { - "text": "boolean" + "text": "'solid' | 'outlined' | 'clear' | 'system'" }, - "default": "false", - "description": "Whether items should wrap to multiple lines" + "default": "'solid'" }, { "kind": "field", - "name": "alignContent", + "name": "color", "type": { - "text": "'start' | 'center' | 'end' | 'stretch' | 'space-between' | 'space-around'" + "text": "'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info'" }, - "default": "'stretch'", - "description": "Alignment of wrapped lines (only applies when wrap is true)" + "default": "'primary'" }, { "kind": "field", - "name": "reverse", + "name": "size", "type": { - "text": "boolean" + "text": "'small' | 'medium' | 'large'" }, - "default": "false", - "description": "Whether to reverse the order of items" + "default": "'medium'" }, { "kind": "field", - "name": "maxItems", + "name": "selected", "type": { - "text": "number | undefined" + "text": "boolean" }, - "description": "Maximum number of items allowed in the stack (for CMS validation)" + "default": "false" }, { "kind": "field", @@ -12467,189 +12917,303 @@ "type": { "text": "boolean" }, - "default": "false", - "description": "Whether the stack should take full width of its container" + "default": "false" }, { "kind": "field", - "name": "fullHeight", + "name": "iconOnly", "type": { - "text": "boolean" + "text": "string | Node | undefined" + } + }, + { + "kind": "field", + "name": "iconStart", + "type": { + "text": "string | Node | undefined" + } + }, + { + "kind": "field", + "name": "iconEnd", + "type": { + "text": "string | Node | undefined" + } + }, + { + "kind": "field", + "name": "type", + "type": { + "text": "'button' | 'submit' | 'reset'" }, - "default": "false", - "description": "Whether the stack should take full height of its container" + "default": "'button'" }, { "kind": "field", - "name": "padding", + "name": "href", + "type": { + "text": "string | undefined" + } + }, + { + "kind": "field", + "name": "target", "type": { "text": "string | undefined" + } + }, + { + "kind": "field", + "name": "align", + "type": { + "text": "'start' | 'center' | 'space-between' | 'end'" }, - "description": "Padding inside the stack container (CSS value)" + "default": "'center'" + }, + { + "kind": "field", + "name": "shortcutSpec", + "type": { + "text": "ShortcutSpec | undefined" + }, + "privacy": "private" + }, + { + "kind": "field", + "name": "expanded", + "type": { + "text": "boolean | undefined" + }, + "privacy": "private" + }, + { + "kind": "field", + "name": "buttonEl", + "type": { + "text": "HTMLElement | undefined" + }, + "privacy": "private" + }, + { + "kind": "field", + "name": "forwardedNodes", + "type": { + "text": "ChildNode[] | undefined" + }, + "privacy": "private" }, { "kind": "method", - "name": "getFlexDirection", + "name": "onShortcutChanged", + "privacy": "protected" + }, + { + "kind": "method", + "name": "syncShortcutListener", + "privacy": "private" + }, + { + "kind": "method", + "name": "getResolvedPanelId", "privacy": "private", "return": { "type": { - "text": "string" + "text": "string | undefined" } } }, { "kind": "method", - "name": "getAlignItems", - "privacy": "private", - "return": { - "type": { - "text": "string" - } + "name": "emitRequest", + "privacy": "private" + }, + { + "kind": "field", + "name": "onDocumentKeyDown", + "privacy": "private" + }, + { + "kind": "field", + "name": "onPanelStateChange", + "privacy": "private" + }, + { + "kind": "method", + "name": "syncForwardedNodesIntoButton", + "privacy": "private" + }, + { + "kind": "method", + "name": "syncAriaExpanded", + "privacy": "private" + }, + { + "kind": "method", + "name": "render" + } + ], + "attributes": [ + { + "name": "panel-id", + "fieldName": "panelId", + "description": "Optional id used to target a specific panel.", + "type": { + "text": "string" + } + }, + { + "name": "action", + "fieldName": "action", + "default": "'toggle'", + "description": "Action to emit. Default toggles the panel.", + "type": { + "text": "LeSidePanelToggleAction" + } + }, + { + "name": "shortcut", + "fieldName": "shortcut", + "description": "Optional keyboard shortcut like `Mod+B` or `Alt+N`.", + "type": { + "text": "string" } }, { - "kind": "method", - "name": "getJustifyContent", - "privacy": "private", - "return": { - "type": { - "text": "string" - } + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Disables the toggle.", + "type": { + "text": "boolean" } }, { - "kind": "method", - "name": "getAlignContent", - "privacy": "private", - "return": { - "type": { - "text": "string" - } + "name": "mode", + "fieldName": "mode", + "default": "'default'", + "type": { + "text": "'default' | 'admin'" } }, { - "kind": "method", - "name": "render" - } - ], - "attributes": [ - { - "name": "direction", - "fieldName": "direction", - "default": "'horizontal'", - "description": "Direction of the stack layout", + "name": "variant", + "fieldName": "variant", + "default": "'solid'", "type": { - "text": "'horizontal' | 'vertical'" + "text": "'solid' | 'outlined' | 'clear' | 'system'" } }, { - "name": "gap", - "fieldName": "gap", - "description": "Gap between items (CSS value like '8px', '1rem', 'var(--le-space-md)')", + "name": "color", + "fieldName": "color", + "default": "'primary'", "type": { - "text": "string" + "text": "'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info'" } }, { - "name": "align", - "fieldName": "align", - "default": "'stretch'", - "description": "Alignment of items on the cross axis", + "name": "size", + "fieldName": "size", + "default": "'medium'", "type": { - "text": "'start' | 'center' | 'end' | 'stretch' | 'baseline'" + "text": "'small' | 'medium' | 'large'" } }, { - "name": "justify", - "fieldName": "justify", - "default": "'start'", - "description": "Distribution of items on the main axis", + "name": "selected", + "fieldName": "selected", + "default": "false", "type": { - "text": "'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'" + "text": "boolean" } }, { - "name": "wrap", - "fieldName": "wrap", + "name": "full-width", + "fieldName": "fullWidth", "default": "false", - "description": "Whether items should wrap to multiple lines", "type": { "text": "boolean" } }, { - "name": "align-content", - "fieldName": "alignContent", - "default": "'stretch'", - "description": "Alignment of wrapped lines (only applies when wrap is true)", + "name": "icon-only", + "fieldName": "iconOnly", "type": { - "text": "'start' | 'center' | 'end' | 'stretch' | 'space-between' | 'space-around'" + "text": "string | Node" } }, { - "name": "reverse", - "fieldName": "reverse", - "default": "false", - "description": "Whether to reverse the order of items", + "name": "icon-start", + "fieldName": "iconStart", "type": { - "text": "boolean" + "text": "string | Node" } }, { - "name": "max-items", - "fieldName": "maxItems", - "description": "Maximum number of items allowed in the stack (for CMS validation)", + "name": "icon-end", + "fieldName": "iconEnd", "type": { - "text": "number" + "text": "string | Node" } }, { - "name": "full-width", - "fieldName": "fullWidth", - "default": "false", - "description": "Whether the stack should take full width of its container", + "name": "type", + "fieldName": "type", + "default": "'button'", "type": { - "text": "boolean" + "text": "'button' | 'submit' | 'reset'" } }, { - "name": "full-height", - "fieldName": "fullHeight", - "default": "false", - "description": "Whether the stack should take full height of its container", + "name": "href", + "fieldName": "href", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "padding", - "fieldName": "padding", - "description": "Padding inside the stack container (CSS value)", + "name": "target", + "fieldName": "target", "type": { "text": "string" } + }, + { + "name": "align", + "fieldName": "align", + "default": "'center'", + "type": { + "text": "'start' | 'center' | 'space-between' | 'end'" + } + } + ], + "tagName": "le-side-panel-toggle", + "events": [ + { + "name": "leSidePanelRequestToggle", + "type": { + "text": "EventEmitter" + } } ], - "tagName": "le-stack", - "events": [], "customElement": true } ], "exports": [ { "kind": "js", - "name": "LeStack", + "name": "LeSidePanelToggle", "declaration": { - "name": "LeStack", - "module": "src/components/le-stack/le-stack.tsx" + "name": "LeSidePanelToggle", + "module": "src/components/le-side-panel-toggle/le-side-panel-toggle.tsx" } }, { "kind": "custom-element-definition", - "name": "le-stack", + "name": "le-side-panel-toggle", "declaration": { - "name": "LeStack", - "module": "src/components/le-stack/le-stack.tsx" + "name": "LeSidePanelToggle", + "module": "src/components/le-side-panel-toggle/le-side-panel-toggle.tsx" } } ] @@ -13057,229 +13621,173 @@ }, { "kind": "javascript-module", - "path": "src/components/le-side-panel-toggle/le-side-panel-toggle.tsx", + "path": "src/components/le-stack/le-stack.tsx", "declarations": [ { "kind": "class", - "description": "", - "name": "LeSidePanelToggle", - "members": [ - { - "kind": "field", - "name": "el", - "type": { - "text": "HTMLElement" - } - }, - { - "kind": "field", - "name": "panelId", - "type": { - "text": "string | undefined" - }, - "description": "Optional id used to target a specific panel." - }, - { - "kind": "field", - "name": "action", - "type": { - "text": "LeSidePanelToggleAction" - }, - "default": "'toggle'", - "description": "Action to emit. Default toggles the panel." - }, - { - "kind": "field", - "name": "shortcut", - "type": { - "text": "string | undefined" - }, - "description": "Optional keyboard shortcut like `Mod+B` or `Alt+N`." - }, - { - "kind": "field", - "name": "disabled", - "type": { - "text": "boolean" - }, - "default": "false", - "description": "Disables the toggle." - }, - { - "kind": "field", - "name": "mode", - "type": { - "text": "'default' | 'admin'" - }, - "default": "'default'" - }, - { - "kind": "field", - "name": "variant", - "type": { - "text": "'solid' | 'outlined' | 'clear' | 'system'" - }, - "default": "'solid'" - }, - { - "kind": "field", - "name": "color", - "type": { - "text": "'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info'" - }, - "default": "'primary'" - }, + "description": "A flexible stack layout component using CSS flexbox.\n\n`le-stack` arranges its children in a row (horizontal) or column (vertical)\nwith configurable spacing, alignment, and wrapping behavior. Perfect for\ncreating responsive layouts.", + "name": "LeStack", + "cssProperties": [ { - "kind": "field", - "name": "size", - "type": { - "text": "'small' | 'medium' | 'large'" - }, - "default": "'medium'" - }, + "description": "Gap between items (defaults to var(--le-space-md))", + "name": "--le-stack-gap" + } + ], + "cssParts": [ { - "kind": "field", - "name": "selected", - "type": { - "text": "boolean" - }, - "default": "false" - }, + "description": "The main stack container", + "name": "stack" + } + ], + "slots": [ { - "kind": "field", - "name": "fullWidth", - "type": { - "text": "boolean" - }, - "default": "false" - }, + "description": "Default slot for stack items (le-box components recommended)", + "name": "" + } + ], + "members": [ { "kind": "field", - "name": "iconOnly", + "name": "el", "type": { - "text": "string | Node | undefined" + "text": "HTMLElement" } }, { "kind": "field", - "name": "iconStart", + "name": "direction", "type": { - "text": "string | Node | undefined" - } + "text": "'horizontal' | 'vertical'" + }, + "default": "'horizontal'", + "description": "Direction of the stack layout" }, { "kind": "field", - "name": "iconEnd", + "name": "gap", "type": { - "text": "string | Node | undefined" - } + "text": "string | undefined" + }, + "description": "Gap between items (CSS value like '8px', '1rem', 'var(--le-space-md)')" }, { "kind": "field", - "name": "type", + "name": "align", "type": { - "text": "'button' | 'submit' | 'reset'" + "text": "'start' | 'center' | 'end' | 'stretch' | 'baseline'" }, - "default": "'button'" + "default": "'stretch'", + "description": "Alignment of items on the cross axis" }, { "kind": "field", - "name": "href", + "name": "justify", "type": { - "text": "string | undefined" - } + "text": "'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'" + }, + "default": "'start'", + "description": "Distribution of items on the main axis" }, { "kind": "field", - "name": "target", + "name": "wrap", "type": { - "text": "string | undefined" - } + "text": "boolean" + }, + "default": "false", + "description": "Whether items should wrap to multiple lines" }, { "kind": "field", - "name": "align", + "name": "alignContent", "type": { - "text": "'start' | 'center' | 'space-between' | 'end'" + "text": "'start' | 'center' | 'end' | 'stretch' | 'space-between' | 'space-around'" }, - "default": "'center'" + "default": "'stretch'", + "description": "Alignment of wrapped lines (only applies when wrap is true)" }, { "kind": "field", - "name": "shortcutSpec", + "name": "reverse", "type": { - "text": "ShortcutSpec | undefined" + "text": "boolean" }, - "privacy": "private" + "default": "false", + "description": "Whether to reverse the order of items" }, { "kind": "field", - "name": "expanded", + "name": "maxItems", "type": { - "text": "boolean | undefined" + "text": "number | undefined" }, - "privacy": "private" + "description": "Maximum number of items allowed in the stack (for CMS validation)" }, { "kind": "field", - "name": "buttonEl", + "name": "fullWidth", "type": { - "text": "HTMLElement | undefined" + "text": "boolean" }, - "privacy": "private" + "default": "false", + "description": "Whether the stack should take full width of its container" }, { "kind": "field", - "name": "forwardedNodes", + "name": "fullHeight", "type": { - "text": "ChildNode[] | undefined" + "text": "boolean" }, - "privacy": "private" - }, - { - "kind": "method", - "name": "onShortcutChanged", - "privacy": "protected" + "default": "false", + "description": "Whether the stack should take full height of its container" }, { - "kind": "method", - "name": "syncShortcutListener", - "privacy": "private" + "kind": "field", + "name": "padding", + "type": { + "text": "string | undefined" + }, + "description": "Padding inside the stack container (CSS value)" }, { "kind": "method", - "name": "getResolvedPanelId", + "name": "getFlexDirection", "privacy": "private", "return": { "type": { - "text": "string | undefined" + "text": "string" } } }, { "kind": "method", - "name": "emitRequest", - "privacy": "private" - }, - { - "kind": "field", - "name": "onDocumentKeyDown", - "privacy": "private" - }, - { - "kind": "field", - "name": "onPanelStateChange", - "privacy": "private" + "name": "getAlignItems", + "privacy": "private", + "return": { + "type": { + "text": "string" + } + } }, { "kind": "method", - "name": "syncForwardedNodesIntoButton", - "privacy": "private" + "name": "getJustifyContent", + "privacy": "private", + "return": { + "type": { + "text": "string" + } + } }, { "kind": "method", - "name": "syncAriaExpanded", - "privacy": "private" + "name": "getAlignContent", + "privacy": "private", + "return": { + "type": { + "text": "string" + } + } }, { "kind": "method", @@ -13288,222 +13796,184 @@ ], "attributes": [ { - "name": "panel-id", - "fieldName": "panelId", - "description": "Optional id used to target a specific panel.", - "type": { - "text": "string" - } - }, - { - "name": "action", - "fieldName": "action", - "default": "'toggle'", - "description": "Action to emit. Default toggles the panel.", + "name": "direction", + "fieldName": "direction", + "default": "'horizontal'", + "description": "Direction of the stack layout", "type": { - "text": "LeSidePanelToggleAction" + "text": "'horizontal' | 'vertical'" } }, { - "name": "shortcut", - "fieldName": "shortcut", - "description": "Optional keyboard shortcut like `Mod+B` or `Alt+N`.", + "name": "gap", + "fieldName": "gap", + "description": "Gap between items (CSS value like '8px', '1rem', 'var(--le-space-md)')", "type": { "text": "string" } }, { - "name": "disabled", - "fieldName": "disabled", - "default": "false", - "description": "Disables the toggle.", + "name": "align", + "fieldName": "align", + "default": "'stretch'", + "description": "Alignment of items on the cross axis", "type": { - "text": "boolean" + "text": "'start' | 'center' | 'end' | 'stretch' | 'baseline'" } }, { - "name": "mode", - "fieldName": "mode", - "default": "'default'", + "name": "justify", + "fieldName": "justify", + "default": "'start'", + "description": "Distribution of items on the main axis", "type": { - "text": "'default' | 'admin'" + "text": "'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'" } }, { - "name": "variant", - "fieldName": "variant", - "default": "'solid'", + "name": "wrap", + "fieldName": "wrap", + "default": "false", + "description": "Whether items should wrap to multiple lines", "type": { - "text": "'solid' | 'outlined' | 'clear' | 'system'" + "text": "boolean" } }, { - "name": "color", - "fieldName": "color", - "default": "'primary'", + "name": "align-content", + "fieldName": "alignContent", + "default": "'stretch'", + "description": "Alignment of wrapped lines (only applies when wrap is true)", "type": { - "text": "'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info'" + "text": "'start' | 'center' | 'end' | 'stretch' | 'space-between' | 'space-around'" } }, { - "name": "size", - "fieldName": "size", - "default": "'medium'", + "name": "reverse", + "fieldName": "reverse", + "default": "false", + "description": "Whether to reverse the order of items", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "boolean" } }, { - "name": "selected", - "fieldName": "selected", - "default": "false", + "name": "max-items", + "fieldName": "maxItems", + "description": "Maximum number of items allowed in the stack (for CMS validation)", "type": { - "text": "boolean" + "text": "number" } }, { "name": "full-width", "fieldName": "fullWidth", "default": "false", + "description": "Whether the stack should take full width of its container", "type": { "text": "boolean" } }, { - "name": "icon-only", - "fieldName": "iconOnly", - "type": { - "text": "string | Node" - } - }, - { - "name": "icon-start", - "fieldName": "iconStart", - "type": { - "text": "string | Node" - } - }, - { - "name": "icon-end", - "fieldName": "iconEnd", - "type": { - "text": "string | Node" - } - }, - { - "name": "type", - "fieldName": "type", - "default": "'button'", - "type": { - "text": "'button' | 'submit' | 'reset'" - } - }, - { - "name": "href", - "fieldName": "href", - "type": { - "text": "string" - } - }, - { - "name": "target", - "fieldName": "target", - "type": { - "text": "string" - } - }, - { - "name": "align", - "fieldName": "align", - "default": "'center'", + "name": "full-height", + "fieldName": "fullHeight", + "default": "false", + "description": "Whether the stack should take full height of its container", "type": { - "text": "'start' | 'center' | 'space-between' | 'end'" + "text": "boolean" } - } - ], - "tagName": "le-side-panel-toggle", - "events": [ + }, { - "name": "leSidePanelRequestToggle", + "name": "padding", + "fieldName": "padding", + "description": "Padding inside the stack container (CSS value)", "type": { - "text": "EventEmitter" + "text": "string" } } ], + "tagName": "le-stack", + "events": [], "customElement": true } ], "exports": [ { "kind": "js", - "name": "LeSidePanelToggle", + "name": "LeStack", "declaration": { - "name": "LeSidePanelToggle", - "module": "src/components/le-side-panel-toggle/le-side-panel-toggle.tsx" + "name": "LeStack", + "module": "src/components/le-stack/le-stack.tsx" } }, { "kind": "custom-element-definition", - "name": "le-side-panel-toggle", + "name": "le-stack", "declaration": { - "name": "LeSidePanelToggle", - "module": "src/components/le-side-panel-toggle/le-side-panel-toggle.tsx" + "name": "LeStack", + "module": "src/components/le-stack/le-stack.tsx" } } ] }, { "kind": "javascript-module", - "path": "src/components/le-string-input/le-string-input.tsx", + "path": "src/components/le-tab/le-tab.tsx", "declarations": [ { "kind": "class", - "description": "A text input component with support for labels, descriptions, icons, and external IDs.", - "name": "LeStringInput", + "description": "A flexible tab component with multiple variants and states.", + "name": "LeTab", "cssProperties": [ { - "description": "Input background color", - "name": "--le-input-bg" - }, - { - "description": "Input text color", - "name": "--le-input-color" + "description": "Tab background color", + "name": "--le-tab-bg" }, { - "description": "Input border style", - "name": "--le-input-border" + "description": "Tab text color", + "name": "--le-tab-color" }, { - "description": "Input border style when focused", - "name": "--le-input-border-focus" + "description": "Tab border radius", + "name": "--le-tab-border-radius" }, { - "description": "Input border radius", - "name": "--le-input-radius" + "description": "Tab horizontal padding", + "name": "--le-tab-padding-x" }, { - "description": "Input padding", - "name": "--le-input-padding" + "description": "Tab vertical padding", + "name": "--le-tab-padding-y" } ], - "slots": [ + "cssParts": [ { - "description": "The label content for the input", - "name": "label" + "description": "The native button element", + "name": "button" }, { - "description": "Additional description text displayed below the input", - "name": "description" + "description": "The tab content wrapper", + "name": "content" }, { - "description": "Icon to display at the start of the input", + "description": "The start icon slot", "name": "icon-start" }, { - "description": "Icon to display at the end of the input", + "description": "The end icon slot", "name": "icon-end" } ], + "slots": [ + { + "description": "Tab text content", + "name": "" + }, + { + "description": "Icon for icon-only tabs", + "name": "icon-only" + } + ], "members": [ { "kind": "field", @@ -13514,203 +13984,163 @@ }, { "kind": "field", - "name": "hasLabelSlot", - "type": { - "text": "boolean" - }, - "privacy": "private", - "default": "false" - }, - { - "kind": "field", - "name": "hasIconStartSlot", - "type": { - "text": "boolean" - }, - "privacy": "private", - "default": "false" - }, - { - "kind": "field", - "name": "hasIconEndSlot", + "name": "mode", "type": { - "text": "boolean" + "text": "'default' | 'admin'" }, - "privacy": "private", - "default": "false" + "default": "'default'", + "description": "Mode of the popover should be 'default' for internal use" }, { "kind": "field", - "name": "hasDescriptionSlot", + "name": "label", "type": { - "text": "boolean" + "text": "string | undefined" }, - "privacy": "private", - "default": "false" + "description": "Label if it is not provided via slot" }, { "kind": "field", - "name": "disconnectSlotObserver", + "name": "value", "type": { - "text": "() => void | undefined" + "text": "string | undefined" }, - "privacy": "private" + "description": "Value of the tab, defaults to label if not provided" }, { "kind": "field", - "name": "inputEl", + "name": "variant", "type": { - "text": "HTMLInputElement | undefined" + "text": "'underlined' | 'solid' | 'pills' | 'enclosed' | 'icon-only'" }, - "privacy": "private" + "default": "'underlined'", + "description": "Tab variant style" }, { "kind": "field", - "name": "inputRef", + "name": "position", "type": { - "text": "(el: HTMLInputElement) => void | undefined" + "text": "'top' | 'bottom' | 'start' | 'end'" }, - "description": "Pass the ref of the input element to the parent component" + "default": "'top'", + "description": "Position of the tabs when used within a le-tabs component" }, { "kind": "field", - "name": "mode", + "name": "size", "type": { - "text": "'default' | 'admin' | undefined" + "text": "'small' | 'medium' | 'large'" }, - "description": "Mode of the popover should be 'default' for internal use" + "default": "'medium'", + "description": "Tab size" }, { "kind": "field", - "name": "value", + "name": "focusable", "type": { - "text": "string | undefined" + "text": "boolean" }, - "description": "The value of the input" + "default": "true", + "description": "Whether the tab can get focus\nneeded for accessibility when used in custom tab implementations" }, { "kind": "field", - "name": "name", + "name": "selected", "type": { - "text": "string | undefined" + "text": "boolean" }, - "description": "The name of the input" + "default": "false", + "description": "Whether the tab is in a selected/active state" }, { "kind": "field", - "name": "type", + "name": "fullWidth", "type": { - "text": "'text' | 'email' | 'password' | 'search' | 'tel' | 'url'" + "text": "boolean" }, - "default": "'text'", - "description": "The type of the input (text, email, password, etc.)" + "default": "false", + "description": "Whether the tab takes full width of its container" }, { "kind": "field", - "name": "label", + "name": "icon", "type": { - "text": "string | undefined" + "text": "string | Node | undefined" }, - "description": "Label for the input" + "description": "Icon only tab image or emoji\nif this prop is set, the tab will render only the icon slot" }, { "kind": "field", - "name": "description", + "name": "showLabel", "type": { - "text": "string | undefined" + "text": "boolean" }, - "description": "Description text displayed below the input" + "default": "false", + "description": "Whether to show the label when in icon-only mode" }, { "kind": "field", "name": "iconStart", "type": { - "text": "string | undefined" + "text": "string | Node | undefined" }, - "description": "Icon for the start icon" + "description": "Start icon image or emoji" }, { "kind": "field", "name": "iconEnd", "type": { - "text": "string | undefined" - }, - "description": "Icon for the end icon" - }, - { - "kind": "field", - "name": "placeholder", - "type": { - "text": "string | undefined" + "text": "string | Node | undefined" }, - "description": "Placeholder text" + "description": "End icon image or emoji" }, { "kind": "field", - "name": "clearable", + "name": "disabled", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the input can be cleared with a built-in clear button" + "description": "Whether the tab is disabled" }, { "kind": "field", - "name": "autocomplete", + "name": "href", "type": { "text": "string | undefined" }, - "description": "Native autocomplete attribute forwarded to the input" + "description": "Optional href to make the tab act as a link" }, { "kind": "field", - "name": "disabled", + "name": "target", "type": { - "text": "boolean" + "text": "string | undefined" }, - "default": "false", - "description": "Whether the input is disabled" + "description": "Link target when href is set" }, { "kind": "field", - "name": "readonly", + "name": "align", "type": { - "text": "boolean" + "text": "'start' | 'center' | 'space-between' | 'end'" }, - "default": "false", - "description": "Whether the input is read-only" + "default": "'center'", + "description": "Alignment of the tab label without the end icon" }, { - "kind": "field", - "name": "externalId", - "type": { - "text": "string | undefined" + "kind": "method", + "name": "getTabConfig", + "return": { + "type": { + "text": "Promise<{\n label?: string;\n value?: string;\n icon?: string | Node;\n iconStart?: string | Node;\n iconEnd?: string | Node;\n disabled: boolean;\n }>" + } }, - "description": "External ID for linking with external systems" - }, - { - "kind": "field", - "name": "handleInput", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleChange", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleClick", - "privacy": "private" - }, - { - "kind": "field", - "name": "handleClear", - "privacy": "private" + "description": "Get tab configuration for parent component" }, { - "kind": "method", - "name": "initSlotObserver", + "kind": "field", + "name": "handleClick", "privacy": "private" }, { @@ -13719,146 +14149,161 @@ } ], "attributes": [ - { - "name": "input-ref", - "fieldName": "inputRef", - "description": "Pass the ref of the input element to the parent component", - "type": { - "text": "(el: HTMLInputElement) => void" - } - }, { "name": "mode", "fieldName": "mode", + "default": "'default'", "description": "Mode of the popover should be 'default' for internal use", "type": { "text": "'default' | 'admin'" } }, + { + "name": "label", + "fieldName": "label", + "description": "Label if it is not provided via slot", + "type": { + "text": "string" + } + }, { "name": "value", "fieldName": "value", - "description": "The value of the input", + "description": "Value of the tab, defaults to label if not provided", "type": { "text": "string" } }, { - "name": "name", - "fieldName": "name", - "description": "The name of the input", + "name": "variant", + "fieldName": "variant", + "default": "'underlined'", + "description": "Tab variant style", "type": { - "text": "string" + "text": "'underlined' | 'solid' | 'pills' | 'enclosed' | 'icon-only'" } }, { - "name": "type", - "fieldName": "type", - "default": "'text'", - "description": "The type of the input (text, email, password, etc.)", + "name": "position", + "fieldName": "position", + "default": "'top'", + "description": "Position of the tabs when used within a le-tabs component", "type": { - "text": "'text' | 'email' | 'password' | 'search' | 'tel' | 'url'" + "text": "'top' | 'bottom' | 'start' | 'end'" } }, { - "name": "label", - "fieldName": "label", - "description": "Label for the input", + "name": "size", + "fieldName": "size", + "default": "'medium'", + "description": "Tab size", "type": { - "text": "string" + "text": "'small' | 'medium' | 'large'" } }, { - "name": "description", - "fieldName": "description", - "description": "Description text displayed below the input", + "name": "focusable", + "fieldName": "focusable", + "default": "true", + "description": "Whether the tab can get focus\nneeded for accessibility when used in custom tab implementations", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "icon-start", - "fieldName": "iconStart", - "description": "Icon for the start icon", + "name": "selected", + "fieldName": "selected", + "default": "false", + "description": "Whether the tab is in a selected/active state", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "icon-end", - "fieldName": "iconEnd", - "description": "Icon for the end icon", + "name": "full-width", + "fieldName": "fullWidth", + "default": "false", + "description": "Whether the tab takes full width of its container", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "placeholder", - "fieldName": "placeholder", - "description": "Placeholder text", + "name": "icon", + "fieldName": "icon", + "description": "Icon only tab image or emoji\nif this prop is set, the tab will render only the icon slot", "type": { - "text": "string" + "text": "string | Node" } }, { - "name": "clearable", - "fieldName": "clearable", + "name": "show-label", + "fieldName": "showLabel", "default": "false", - "description": "Whether the input can be cleared with a built-in clear button", + "description": "Whether to show the label when in icon-only mode", "type": { "text": "boolean" } }, { - "name": "autocomplete", - "fieldName": "autocomplete", - "description": "Native autocomplete attribute forwarded to the input", + "name": "icon-start", + "fieldName": "iconStart", + "description": "Start icon image or emoji", "type": { - "text": "string" + "text": "string | Node" + } + }, + { + "name": "icon-end", + "fieldName": "iconEnd", + "description": "End icon image or emoji", + "type": { + "text": "string | Node" } }, { "name": "disabled", "fieldName": "disabled", "default": "false", - "description": "Whether the input is disabled", + "description": "Whether the tab is disabled", "type": { "text": "boolean" } }, { - "name": "readonly", - "fieldName": "readonly", - "default": "false", - "description": "Whether the input is read-only", + "name": "href", + "fieldName": "href", + "description": "Optional href to make the tab act as a link", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "external-id", - "fieldName": "externalId", - "description": "External ID for linking with external systems", + "name": "target", + "fieldName": "target", + "description": "Link target when href is set", "type": { "text": "string" } + }, + { + "name": "align", + "fieldName": "align", + "default": "'center'", + "description": "Alignment of the tab label without the end icon", + "type": { + "text": "'start' | 'center' | 'space-between' | 'end'" + } } ], - "tagName": "le-string-input", + "tagName": "le-tab", "events": [ { - "name": "leChange", - "type": { - "text": "EventEmitter<{\n value?: string;\n name?: string;\n externalId?: string;\n }> | undefined" - }, - "description": "Emitted when the value changes (on blur or Enter)" - }, - { - "name": "leInput", + "name": "click", "type": { - "text": "EventEmitter<{\n value?: string;\n name?: string;\n externalId?: string;\n }> | undefined" + "text": "EventEmitter | undefined" }, - "description": "Emitted when the input value changes (on keystroke)" + "description": "Emitted when the tab is clicked.\nThis is a custom event that wraps the native click but ensures the target is the le-tab." } ], "customElement": true @@ -13867,18 +14312,18 @@ "exports": [ { "kind": "js", - "name": "LeStringInput", + "name": "LeTab", "declaration": { - "name": "LeStringInput", - "module": "src/components/le-string-input/le-string-input.tsx" + "name": "LeTab", + "module": "src/components/le-tab/le-tab.tsx" } }, { "kind": "custom-element-definition", - "name": "le-string-input", + "name": "le-tab", "declaration": { - "name": "LeStringInput", - "module": "src/components/le-string-input/le-string-input.tsx" + "name": "LeTab", + "module": "src/components/le-tab/le-tab.tsx" } } ] @@ -14260,86 +14705,131 @@ }, { "kind": "javascript-module", - "path": "src/components/le-tab/le-tab.tsx", + "path": "src/components/le-string-input/le-string-input.tsx", "declarations": [ { "kind": "class", - "description": "A flexible tab component with multiple variants and states.", - "name": "LeTab", + "description": "A text input component with support for labels, descriptions, icons, and external IDs.", + "name": "LeStringInput", "cssProperties": [ { - "description": "Tab background color", - "name": "--le-tab-bg" + "description": "Input background color", + "name": "--le-input-bg" }, { - "description": "Tab text color", - "name": "--le-tab-color" + "description": "Input text color", + "name": "--le-input-color" }, { - "description": "Tab border radius", - "name": "--le-tab-border-radius" + "description": "Input border style", + "name": "--le-input-border" }, { - "description": "Tab horizontal padding", - "name": "--le-tab-padding-x" + "description": "Input border style when focused", + "name": "--le-input-border-focus" }, { - "description": "Tab vertical padding", - "name": "--le-tab-padding-y" + "description": "Input border radius", + "name": "--le-input-radius" + }, + { + "description": "Input padding", + "name": "--le-input-padding" } ], - "cssParts": [ + "slots": [ { - "description": "The native button element", - "name": "button" + "description": "The label content for the input", + "name": "label" }, { - "description": "The tab content wrapper", - "name": "content" + "description": "Additional description text displayed below the input", + "name": "description" }, { - "description": "The start icon slot", + "description": "Icon to display at the start of the input", "name": "icon-start" }, { - "description": "The end icon slot", + "description": "Icon to display at the end of the input", "name": "icon-end" } ], - "slots": [ + "members": [ { - "description": "Tab text content", - "name": "" + "kind": "field", + "name": "el", + "type": { + "text": "HTMLElement" + } }, { - "description": "Icon for icon-only tabs", - "name": "icon-only" - } - ], - "members": [ + "kind": "field", + "name": "hasLabelSlot", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" + }, + { + "kind": "field", + "name": "hasIconStartSlot", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" + }, + { + "kind": "field", + "name": "hasIconEndSlot", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" + }, + { + "kind": "field", + "name": "hasDescriptionSlot", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" + }, + { + "kind": "field", + "name": "disconnectSlotObserver", + "type": { + "text": "() => void | undefined" + }, + "privacy": "private" + }, { "kind": "field", - "name": "el", + "name": "inputEl", "type": { - "text": "HTMLElement" - } + "text": "HTMLInputElement | undefined" + }, + "privacy": "private" }, { "kind": "field", - "name": "mode", + "name": "inputRef", "type": { - "text": "'default' | 'admin'" + "text": "(el: HTMLInputElement) => void | undefined" }, - "default": "'default'", - "description": "Mode of the popover should be 'default' for internal use" + "description": "Pass the ref of the input element to the parent component" }, { "kind": "field", - "name": "label", + "name": "mode", "type": { - "text": "string | undefined" + "text": "'default' | 'admin' | undefined" }, - "description": "Label if it is not provided via slot" + "description": "Mode of the popover should be 'default' for internal use" }, { "kind": "field", @@ -14347,142 +14837,131 @@ "type": { "text": "string | undefined" }, - "description": "Value of the tab, defaults to label if not provided" + "description": "The value of the input" }, { "kind": "field", - "name": "variant", + "name": "name", "type": { - "text": "'underlined' | 'solid' | 'pills' | 'enclosed' | 'icon-only'" + "text": "string | undefined" }, - "default": "'underlined'", - "description": "Tab variant style" + "description": "The name of the input" }, { "kind": "field", - "name": "position", + "name": "type", "type": { - "text": "'top' | 'bottom' | 'start' | 'end'" + "text": "'text' | 'email' | 'password' | 'search' | 'tel' | 'url'" }, - "default": "'top'", - "description": "Position of the tabs when used within a le-tabs component" + "default": "'text'", + "description": "The type of the input (text, email, password, etc.)" }, { "kind": "field", - "name": "size", + "name": "label", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "string | undefined" }, - "default": "'medium'", - "description": "Tab size" + "description": "Label for the input" }, { "kind": "field", - "name": "focusable", + "name": "description", "type": { - "text": "boolean" + "text": "string | undefined" }, - "default": "true", - "description": "Whether the tab can get focus\nneeded for accessibility when used in custom tab implementations" + "description": "Description text displayed below the input" }, { "kind": "field", - "name": "selected", + "name": "iconStart", "type": { - "text": "boolean" + "text": "string | undefined" }, - "default": "false", - "description": "Whether the tab is in a selected/active state" + "description": "Icon for the start icon" }, { "kind": "field", - "name": "fullWidth", + "name": "iconEnd", "type": { - "text": "boolean" + "text": "string | undefined" }, - "default": "false", - "description": "Whether the tab takes full width of its container" + "description": "Icon for the end icon" }, { "kind": "field", - "name": "icon", + "name": "placeholder", "type": { - "text": "string | Node | undefined" + "text": "string | undefined" }, - "description": "Icon only tab image or emoji\nif this prop is set, the tab will render only the icon slot" + "description": "Placeholder text" }, { "kind": "field", - "name": "showLabel", + "name": "clearable", "type": { "text": "boolean" }, "default": "false", - "description": "Whether to show the label when in icon-only mode" + "description": "Whether the input can be cleared with a built-in clear button" }, { "kind": "field", - "name": "iconStart", + "name": "autocomplete", "type": { - "text": "string | Node | undefined" + "text": "string | undefined" }, - "description": "Start icon image or emoji" + "description": "Native autocomplete attribute forwarded to the input" }, { "kind": "field", - "name": "iconEnd", + "name": "disabled", "type": { - "text": "string | Node | undefined" + "text": "boolean" }, - "description": "End icon image or emoji" + "default": "false", + "description": "Whether the input is disabled" }, { "kind": "field", - "name": "disabled", + "name": "readonly", "type": { "text": "boolean" }, "default": "false", - "description": "Whether the tab is disabled" + "description": "Whether the input is read-only" }, { "kind": "field", - "name": "href", + "name": "externalId", "type": { "text": "string | undefined" }, - "description": "Optional href to make the tab act as a link" + "description": "External ID for linking with external systems" }, { "kind": "field", - "name": "target", - "type": { - "text": "string | undefined" - }, - "description": "Link target when href is set" + "name": "handleInput", + "privacy": "private" }, { "kind": "field", - "name": "align", - "type": { - "text": "'start' | 'center' | 'space-between' | 'end'" - }, - "default": "'center'", - "description": "Alignment of the tab label without the end icon" + "name": "handleChange", + "privacy": "private" }, { - "kind": "method", - "name": "getTabConfig", - "return": { - "type": { - "text": "Promise<{\n label?: string;\n value?: string;\n icon?: string | Node;\n iconStart?: string | Node;\n iconEnd?: string | Node;\n disabled: boolean;\n }>" - } - }, - "description": "Get tab configuration for parent component" + "kind": "field", + "name": "handleClick", + "privacy": "private" }, { "kind": "field", - "name": "handleClick", + "name": "handleClear", + "privacy": "private" + }, + { + "kind": "method", + "name": "initSlotObserver", "privacy": "private" }, { @@ -14492,160 +14971,145 @@ ], "attributes": [ { - "name": "mode", - "fieldName": "mode", - "default": "'default'", - "description": "Mode of the popover should be 'default' for internal use", + "name": "input-ref", + "fieldName": "inputRef", + "description": "Pass the ref of the input element to the parent component", "type": { - "text": "'default' | 'admin'" + "text": "(el: HTMLInputElement) => void" } }, { - "name": "label", - "fieldName": "label", - "description": "Label if it is not provided via slot", + "name": "mode", + "fieldName": "mode", + "description": "Mode of the popover should be 'default' for internal use", "type": { - "text": "string" + "text": "'default' | 'admin'" } }, { "name": "value", "fieldName": "value", - "description": "Value of the tab, defaults to label if not provided", + "description": "The value of the input", "type": { "text": "string" } }, { - "name": "variant", - "fieldName": "variant", - "default": "'underlined'", - "description": "Tab variant style", + "name": "name", + "fieldName": "name", + "description": "The name of the input", "type": { - "text": "'underlined' | 'solid' | 'pills' | 'enclosed' | 'icon-only'" + "text": "string" } }, { - "name": "position", - "fieldName": "position", - "default": "'top'", - "description": "Position of the tabs when used within a le-tabs component", + "name": "type", + "fieldName": "type", + "default": "'text'", + "description": "The type of the input (text, email, password, etc.)", "type": { - "text": "'top' | 'bottom' | 'start' | 'end'" + "text": "'text' | 'email' | 'password' | 'search' | 'tel' | 'url'" } }, { - "name": "size", - "fieldName": "size", - "default": "'medium'", - "description": "Tab size", + "name": "label", + "fieldName": "label", + "description": "Label for the input", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "string" } }, { - "name": "focusable", - "fieldName": "focusable", - "default": "true", - "description": "Whether the tab can get focus\nneeded for accessibility when used in custom tab implementations", + "name": "description", + "fieldName": "description", + "description": "Description text displayed below the input", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "selected", - "fieldName": "selected", - "default": "false", - "description": "Whether the tab is in a selected/active state", + "name": "icon-start", + "fieldName": "iconStart", + "description": "Icon for the start icon", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "full-width", - "fieldName": "fullWidth", - "default": "false", - "description": "Whether the tab takes full width of its container", + "name": "icon-end", + "fieldName": "iconEnd", + "description": "Icon for the end icon", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "icon", - "fieldName": "icon", - "description": "Icon only tab image or emoji\nif this prop is set, the tab will render only the icon slot", + "name": "placeholder", + "fieldName": "placeholder", + "description": "Placeholder text", "type": { - "text": "string | Node" + "text": "string" } }, { - "name": "show-label", - "fieldName": "showLabel", + "name": "clearable", + "fieldName": "clearable", "default": "false", - "description": "Whether to show the label when in icon-only mode", + "description": "Whether the input can be cleared with a built-in clear button", "type": { "text": "boolean" } }, { - "name": "icon-start", - "fieldName": "iconStart", - "description": "Start icon image or emoji", - "type": { - "text": "string | Node" - } - }, - { - "name": "icon-end", - "fieldName": "iconEnd", - "description": "End icon image or emoji", + "name": "autocomplete", + "fieldName": "autocomplete", + "description": "Native autocomplete attribute forwarded to the input", "type": { - "text": "string | Node" + "text": "string" } }, { "name": "disabled", "fieldName": "disabled", "default": "false", - "description": "Whether the tab is disabled", + "description": "Whether the input is disabled", "type": { "text": "boolean" } }, { - "name": "href", - "fieldName": "href", - "description": "Optional href to make the tab act as a link", - "type": { - "text": "string" - } - }, - { - "name": "target", - "fieldName": "target", - "description": "Link target when href is set", + "name": "readonly", + "fieldName": "readonly", + "default": "false", + "description": "Whether the input is read-only", "type": { - "text": "string" + "text": "boolean" } }, { - "name": "align", - "fieldName": "align", - "default": "'center'", - "description": "Alignment of the tab label without the end icon", + "name": "external-id", + "fieldName": "externalId", + "description": "External ID for linking with external systems", "type": { - "text": "'start' | 'center' | 'space-between' | 'end'" + "text": "string" } } ], - "tagName": "le-tab", + "tagName": "le-string-input", "events": [ { - "name": "click", + "name": "leChange", "type": { - "text": "EventEmitter | undefined" + "text": "EventEmitter<{\n value?: string;\n name?: string;\n externalId?: string;\n }> | undefined" }, - "description": "Emitted when the tab is clicked.\nThis is a custom event that wraps the native click but ensures the target is the le-tab." + "description": "Emitted when the value changes (on blur or Enter)" + }, + { + "name": "leInput", + "type": { + "text": "EventEmitter<{\n value?: string;\n name?: string;\n externalId?: string;\n }> | undefined" + }, + "description": "Emitted when the input value changes (on keystroke)" } ], "customElement": true @@ -14654,18 +15118,18 @@ "exports": [ { "kind": "js", - "name": "LeTab", + "name": "LeStringInput", "declaration": { - "name": "LeTab", - "module": "src/components/le-tab/le-tab.tsx" + "name": "LeStringInput", + "module": "src/components/le-string-input/le-string-input.tsx" } }, { "kind": "custom-element-definition", - "name": "le-tab", + "name": "le-string-input", "declaration": { - "name": "LeTab", - "module": "src/components/le-tab/le-tab.tsx" + "name": "LeStringInput", + "module": "src/components/le-string-input/le-string-input.tsx" } } ] @@ -15310,334 +15774,298 @@ }, { "kind": "javascript-module", - "path": "src/components/le-tag/le-tag.tsx", + "path": "src/components/le-toolbar/le-toolbar.tsx", "declarations": [ { "kind": "class", - "description": "A tag/chip component for displaying labels with optional dismiss functionality.", - "name": "LeTag", + "description": "A priority-aware, overflow-safe toolbar component.\n\nItems are slotted light-DOM children. Each item may carry a\n`priority` attribute (lower = more important). When there\nisn't enough space, lower-priority items move to an overflow menu.\n\nCollapsible `le-button-group` children are asked to reduce their own\nfootprint first before their contents are overflowed entirely.", + "name": "LeToolbar", + "cssParts": [ + { + "description": "The main flex row", + "name": "container" + }, + { + "description": "The \"more\" button wrapper", + "name": "overflow-trigger" + } + ], "slots": [ { - "description": "Default slot for custom content (overrides label prop)", + "description": "Toolbar items", "name": "" + }, + { + "description": "Custom content for the overflow trigger button", + "name": "more" } ], "members": [ { "kind": "field", - "name": "label", + "name": "el", "type": { - "text": "string | undefined" - }, - "description": "The text label to display in the tag." + "text": "HTMLElement" + } }, { "kind": "field", - "name": "mode", + "name": "items", "type": { - "text": "'default' | 'admin' | undefined" + "text": "unknown | undefined" }, - "description": "Mode of the popover should be 'default' for internal use" + "description": "Optional declarative items input.\n\nThe current implementation is slot-driven, but when this prop changes we\nstill invalidate the slotted-items cache and recompute layout." }, { "kind": "field", - "name": "icon", + "name": "alignItems", "type": { - "text": "string | undefined" + "text": "'start' | 'center' | 'end' | 'stretch'" }, - "description": "Icon to display before the label.\nCan be an emoji, URL, or icon name." + "default": "'start'", + "description": "Alignment of items along the main axis." }, { "kind": "field", - "name": "dismissible", + "name": "itemGap", "type": { - "text": "boolean" + "text": "string" }, - "default": "false", - "description": "Whether the tag can be dismissed (shows close button)." + "default": "'var(--le-toolbar-gap, var(--le-spacing-1, 4px))'", + "description": "Spacing between top-level toolbar items.\nAccepts any valid CSS length (e.g. `8px`, `0.5rem`, `var(--le-spacing-2)`)." }, { "kind": "field", - "name": "disabled", + "name": "overflowIcon", "type": { - "text": "boolean" + "text": "string" }, - "default": "false", - "description": "Whether the tag is disabled." + "default": "'ellipsis-horizontal'", + "description": "Icon for the overflow trigger button when no custom slot content is provided." }, { "kind": "field", - "name": "size", + "name": "overflowLabel", "type": { - "text": "'small' | 'medium' | 'large'" + "text": "string" }, - "default": "'medium'", - "description": "The size of the tag." + "default": "'More'", + "description": "Accessible label for the overflow trigger button." }, { "kind": "field", - "name": "color", + "name": "disablePopover", "type": { - "text": "| 'default'\n | 'primary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'secondary'\n | 'info'" + "text": "boolean" }, - "default": "'default'", - "description": "The color of the tag." + "default": "false", + "description": "Disable the built-in overflow popover.\nThe toolbar will still compute overflow state and emit events, but\nwon't render its own menu. Useful for custom overflow handling." }, { "kind": "field", - "name": "handleDismiss", - "privacy": "private" - }, - { - "kind": "method", - "name": "renderIcon", - "privacy": "private" + "name": "debugVirtualToolbar", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Temporary debug mode: render the virtual toolbar visibly above\nthe live toolbar so collapse measurements can be inspected." }, { - "kind": "method", - "name": "render" - } - ], - "attributes": [ - { - "name": "label", - "fieldName": "label", - "description": "The text label to display in the tag.", + "kind": "field", + "name": "debugPauseBeforeMeasure", "type": { - "text": "string" - } + "text": "boolean" + }, + "default": "false", + "description": "Temporary debug mode: stop before measuring virtual widths so the\nvirtual DOM can be inspected before collapse simulation mutates it." }, { - "name": "mode", - "fieldName": "mode", - "description": "Mode of the popover should be 'default' for internal use", + "kind": "field", + "name": "solverOutput", "type": { - "text": "'default' | 'admin'" - } + "text": "SolverOutput" + }, + "privacy": "private", + "default": "{ visibleIds: new Set(), collapsedGroupIds: new Set(), overflowIds: new Set(), showTrigger: false, }" }, { - "name": "icon", - "fieldName": "icon", - "description": "Icon to display before the label.\nCan be an emoji, URL, or icon name.", + "kind": "field", + "name": "overflowMenuItems", "type": { - "text": "string" - } + "text": "LeOption[]" + }, + "privacy": "private", + "default": "[]" }, { - "name": "dismissible", - "fieldName": "dismissible", - "default": "false", - "description": "Whether the tag can be dismissed (shows close button).", + "kind": "field", + "name": "itemSlots", "type": { - "text": "boolean" - } + "text": "Array<{ id: string; slotName: string }>" + }, + "privacy": "private", + "default": "[]" }, { - "name": "disabled", - "fieldName": "disabled", - "default": "false", - "description": "Whether the tag is disabled.", + "kind": "field", + "name": "initializingLayout", "type": { "text": "boolean" - } + }, + "privacy": "private", + "default": "true" }, { - "name": "size", - "fieldName": "size", - "default": "'medium'", - "description": "The size of the tag.", + "kind": "field", + "name": "debugStepIndex", "type": { - "text": "'small' | 'medium' | 'large'" - } + "text": "number" + }, + "privacy": "private", + "default": "0" }, { - "name": "color", - "fieldName": "color", - "default": "'default'", - "description": "The color of the tag.", + "kind": "field", + "name": "debugStepMeasuredWidth", "type": { - "text": "| 'default'\n | 'primary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'secondary'\n | 'info'" - } - } - ], - "tagName": "le-tag", - "events": [ + "text": "number" + }, + "privacy": "private", + "default": "0" + }, { - "name": "leDismiss", + "kind": "field", + "name": "virtualTriggerVisible", "type": { - "text": "EventEmitter | undefined" + "text": "boolean" }, - "description": "Emitted when the dismiss button is clicked." - } - ], - "customElement": true - } - ], - "exports": [ - { - "kind": "js", - "name": "LeTag", - "declaration": { - "name": "LeTag", - "module": "src/components/le-tag/le-tag.tsx" - } - }, - { - "kind": "custom-element-definition", - "name": "le-tag", - "declaration": { - "name": "LeTag", - "module": "src/components/le-tag/le-tag.tsx" - } - } - ] - }, - { - "kind": "javascript-module", - "path": "src/components/le-text/le-text.tsx", - "declarations": [ - { - "kind": "class", - "description": "A text component with rich text editing capabilities in admin mode.\n\n`le-text` renders semantic text elements (headings, paragraphs, code, quotes)\nand provides a Notion-like rich text editor in admin mode with formatting\ntoolbar for bold, italic, links, and paragraph type selection.", - "name": "LeText", - "cssProperties": [ - { - "description": "Text color", - "name": "--le-text-color" + "privacy": "private", + "default": "false" }, { - "description": "Font size", - "name": "--le-text-font-size" + "kind": "field", + "name": "hasPreparedInitialLayout", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false" }, { - "description": "Line height", - "name": "--le-text-line-height" + "kind": "field", + "name": "instanceId", + "type": { + "text": "string" + }, + "privacy": "private" }, - { - "description": "Font weight", - "name": "--le-text-font-weight" - } - ], - "cssParts": [ - { - "description": "The text container element", - "name": "text" - } - ], - "slots": [ - { - "description": "Default slot for text content", - "name": "" - } - ], - "members": [ { "kind": "field", - "name": "el", + "name": "toolbarHostEl", "type": { - "text": "HTMLElement" - } + "text": "HTMLElement | null | undefined" + }, + "privacy": "private", + "description": "Toolbar host element (follows the width of the parent element)." }, { "kind": "field", - "name": "variant", + "name": "toolbarContainerEl", "type": { - "text": "| 'p'\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'code'\n | 'quote'\n | 'label'\n | 'small'" + "text": "HTMLElement | null | undefined" }, - "default": "'p'", - "description": "The semantic variant/type of text element" + "privacy": "private", + "description": "Live toolbar items container." }, { "kind": "field", - "name": "align", + "name": "virtualToolbarEl", "type": { - "text": "'left' | 'center' | 'right' | 'justify'" + "text": "HTMLElement | undefined" }, - "default": "'left'", - "description": "Text alignment" + "privacy": "private", + "description": "Hidden virtual toolbar container used for browser-native overflow checks." }, { "kind": "field", - "name": "color", + "name": "virtualItemsEl", "type": { - "text": "string | undefined" + "text": "HTMLElement | undefined" }, - "description": "Text color (CSS value or theme token)" + "privacy": "private", + "description": "Virtual row where cloned participants are mounted for simulation." }, { "kind": "field", - "name": "truncate", + "name": "virtualTriggerEl", "type": { - "text": "boolean" + "text": "HTMLElement | undefined" }, - "default": "false", - "description": "Whether the text should truncate with ellipsis" + "privacy": "private", + "description": "Virtual trigger element that participates in layout when overflow exists." }, { "kind": "field", - "name": "maxLines", + "name": "resizeObserver", "type": { - "text": "number | undefined" + "text": "ResizeObserver | undefined" }, - "description": "Maximum number of lines before truncating (requires truncate=true)" + "privacy": "private" }, { "kind": "field", - "name": "adminMode", + "name": "mutationObserver", "type": { - "text": "boolean" + "text": "MutationObserver | undefined" }, - "privacy": "private", - "default": "false", - "description": "Internal state to track admin mode" + "privacy": "private" }, { "kind": "field", - "name": "content", + "name": "pendingRecalc", "type": { - "text": "string" + "text": "number | null" }, "privacy": "private", - "default": "''", - "description": "The HTML content being edited" + "default": "null" }, { "kind": "field", - "name": "isFocused", + "name": "debugVirtualWidth", "type": { - "text": "boolean" + "text": "number" }, "privacy": "private", - "default": "false", - "description": "Whether the editor is focused (shows toolbar)" + "default": "0" }, { "kind": "field", - "name": "selectionState", + "name": "observersAttached", "type": { - "text": "SelectionState" + "text": "boolean" }, "privacy": "private", - "default": "{ isBold: false, isItalic: false, isUnderline: false, isStrikethrough: false, isLink: false, blockType: 'p', }", - "description": "Current selection state for toolbar button highlighting" + "default": "false", + "description": "Prevent double-attach on rapid connect/disconnect cycles." }, { "kind": "field", - "name": "editorRef", + "name": "itemMap", "type": { - "text": "HTMLDivElement | undefined" + "text": "Map" }, "privacy": "private", - "description": "Reference to the contenteditable element" + "default": "new Map()", + "description": "Map from ID → original light-DOM element plus virtual clone." }, { "kind": "field", - "name": "slotRef", + "name": "collapseSteps", "type": { - "text": "HTMLSlotElement | undefined" + "text": "CollapseStep[]" }, "privacy": "private", - "description": "Reference to the slot element" + "default": "[]" }, { "kind": "field", @@ -15649,998 +16077,974 @@ }, { "kind": "method", - "name": "onVariantChange" + "name": "handleAlignChange" }, { "kind": "method", - "name": "readSlottedContent", - "privacy": "private", - "description": "Read content from slotted elements" + "name": "handleGapChange", + "parameters": [ + { + "name": "newValue", + "type": { + "text": "string" + } + } + ] }, { "kind": "method", - "name": "syncContentToSlot", - "privacy": "private", - "description": "Sync edited content back to the slot" + "name": "handleItemsChange" }, { - "kind": "field", - "name": "handleInput", - "privacy": "private", - "description": "Handle input in the contenteditable" + "kind": "method", + "name": "handleSlotChange" }, { - "kind": "field", - "name": "handleFocus", + "kind": "method", + "name": "resetToolbar", + "description": "Reset the toolbar's internal layout state and recalculate item visibility from scratch." + }, + { + "kind": "method", + "name": "recalculate", + "description": "Force a layout recalculation." + }, + { + "kind": "method", + "name": "attachObservers", + "privacy": "private" + }, + { + "kind": "method", + "name": "detachObservers", + "privacy": "private" + }, + { + "kind": "method", + "name": "observeContainer", "privacy": "private", - "description": "Handle focus on the editor" + "parameters": [ + { + "name": "el", + "optional": true, + "type": { + "text": "HTMLElement | null" + } + } + ] }, { - "kind": "field", - "name": "handleBlur", + "kind": "method", + "name": "scheduleRecalc", + "privacy": "private" + }, + { + "kind": "method", + "name": "getItemId", "privacy": "private", - "description": "Handle blur on the editor" + "return": { + "type": { + "text": "string" + } + }, + "parameters": [ + { + "name": "el", + "type": { + "text": "HTMLElement" + } + }, + { + "name": "index", + "type": { + "text": "number" + } + } + ] }, { - "kind": "field", - "name": "handleSelectionChange", + "kind": "method", + "name": "getItemPriority", "privacy": "private", - "description": "Handle selection change to update toolbar state" + "return": { + "type": { + "text": "number" + } + }, + "parameters": [ + { + "name": "el", + "type": { + "text": "HTMLElement" + } + }, + { + "name": "index", + "type": { + "text": "number" + } + } + ] }, { "kind": "method", - "name": "updateSelectionState", + "name": "buildOverflowOption", "privacy": "private", - "description": "Update the selection state for toolbar highlighting" + "return": { + "type": { + "text": "Promise" + } + }, + "parameters": [ + { + "name": "item", + "type": { + "text": "HTMLElement" + } + }, + { + "name": "id", + "type": { + "text": "string" + } + } + ] }, { "kind": "method", - "name": "isSelectionInLink", + "name": "setVirtualTriggerVisible", "privacy": "private", - "return": { - "type": { - "text": "boolean" - } - }, "parameters": [ { - "name": "selection", + "name": "visible", "type": { - "text": "Selection" + "text": "boolean" } } - ], - "description": "Check if current selection is within a link" + ] }, { "kind": "method", - "name": "execCommand", + "name": "getAuthoredVirtualCollapse", "privacy": "private", + "return": { + "type": { + "text": "string | undefined" + } + }, "parameters": [ { - "name": "command", - "type": { - "text": "string" - } - }, - { - "name": "value", - "optional": true, + "name": "record", "type": { - "text": "string" + "text": "ToolbarItemRecord" } } - ], - "description": "Execute a formatting command" + ] }, { - "kind": "field", - "name": "toggleBold", - "privacy": "private", - "description": "Toggle bold formatting" + "kind": "method", + "name": "resetVirtualState", + "privacy": "private" }, { - "kind": "field", - "name": "toggleItalic", - "privacy": "private", - "description": "Toggle italic formatting" + "kind": "method", + "name": "initializeDebugMeasurementState", + "privacy": "private" }, { - "kind": "field", - "name": "toggleUnderline", - "privacy": "private", - "description": "Toggle underline formatting" + "kind": "method", + "name": "runDebugMeasurementStep" }, { "kind": "field", - "name": "toggleStrikethrough", - "privacy": "private", - "description": "Toggle strikethrough formatting" + "name": "handleVirtualDebugClick", + "privacy": "private" }, { - "kind": "field", - "name": "toggleLink", - "privacy": "private", - "description": "Add or edit a link" + "kind": "method", + "name": "clearVirtualMeasurements", + "privacy": "private" }, { - "kind": "field", - "name": "changeVariant", + "kind": "method", + "name": "settleVirtualItem", "privacy": "private", - "description": "Change the block type/variant" + "parameters": [ + { + "name": "element", + "optional": true, + "type": { + "text": "HTMLElement" + } + } + ] }, { "kind": "method", - "name": "renderToolbar", + "name": "waitForNestedCustomElements", "privacy": "private", - "description": "Render the formatting toolbar" + "parameters": [ + { + "name": "root", + "type": { + "text": "HTMLElement" + } + } + ] }, { "kind": "method", - "name": "getTag", + "name": "syncVirtualTriggerContent", + "privacy": "private" + }, + { + "kind": "method", + "name": "prepareToolbarItems", + "privacy": "private" + }, + { + "kind": "method", + "name": "getVisibilityState", "privacy": "private", "return": { "type": { - "text": "string" + "text": "'visible' | 'collapsed'" } }, - "description": "Get the semantic tag for the current variant" + "parameters": [ + { + "name": "value", + "type": { + "text": "string | null" + } + } + ] }, { "kind": "method", - "name": "render" - } - ], - "attributes": [ - { - "name": "variant", - "fieldName": "variant", - "default": "'p'", - "description": "The semantic variant/type of text element", - "type": { - "text": "| 'p'\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'code'\n | 'quote'\n | 'label'\n | 'small'" - } - }, - { - "name": "align", - "fieldName": "align", - "default": "'left'", - "description": "Text alignment", - "type": { - "text": "'left' | 'center' | 'right' | 'justify'" - } + "name": "calculateLayoutWidths", + "privacy": "private" }, { - "name": "color", - "fieldName": "color", - "description": "Text color (CSS value or theme token)", - "type": { - "text": "string" - } + "kind": "method", + "name": "computeLayout", + "privacy": "private" }, { - "name": "truncate", - "fieldName": "truncate", - "default": "false", - "description": "Whether the text should truncate with ellipsis", - "type": { - "text": "boolean" - } + "kind": "method", + "name": "applyOutput", + "privacy": "private", + "parameters": [ + { + "name": "output", + "type": { + "text": "SolverOutput" + } + }, + { + "name": "overflowItems", + "type": { + "text": "LeOption[]" + } + } + ] }, { - "name": "max-lines", - "fieldName": "maxLines", - "description": "Maximum number of lines before truncating (requires truncate=true)", - "type": { - "text": "number" - } - } - ], - "tagName": "le-text", - "events": [], - "customElement": true - } - ], - "exports": [ - { - "kind": "js", - "name": "LeText", - "declaration": { - "name": "LeText", - "module": "src/components/le-text/le-text.tsx" - } - }, - { - "kind": "custom-element-definition", - "name": "le-text", - "declaration": { - "name": "LeText", - "module": "src/components/le-text/le-text.tsx" - } - } - ] - }, - { - "kind": "javascript-module", - "path": "src/components/le-toolbar/le-toolbar.tsx", - "declarations": [ - { - "kind": "class", - "description": "A priority-aware, overflow-safe toolbar component.\n\nItems are slotted light-DOM children. Each item may carry a\n`priority` attribute (lower = more important). When there\nisn't enough space, lower-priority items move to an overflow menu.\n\nCollapsible `le-button-group` children are asked to reduce their own\nfootprint first before their contents are overflowed entirely.", - "name": "LeToolbar", - "cssParts": [ - { - "description": "The main flex row", - "name": "container" + "kind": "field", + "name": "handleOverflowSelect", + "privacy": "private" }, { - "description": "The \"more\" button wrapper", - "name": "overflow-trigger" - } - ], - "slots": [ - { - "description": "Toolbar items", - "name": "" + "kind": "method", + "name": "renderVirtualToolbar", + "privacy": "private" }, { - "description": "Custom content for the overflow trigger button", - "name": "more" + "kind": "method", + "name": "render" } ], - "members": [ - { - "kind": "field", - "name": "el", - "type": { - "text": "HTMLElement" - } - }, + "attributes": [ { - "kind": "field", "name": "items", + "fieldName": "items", + "description": "Optional declarative items input.\n\nThe current implementation is slot-driven, but when this prop changes we\nstill invalidate the slotted-items cache and recompute layout.", "type": { - "text": "unknown | undefined" - }, - "description": "Optional declarative items input.\n\nThe current implementation is slot-driven, but when this prop changes we\nstill invalidate the slotted-items cache and recompute layout." + "text": "unknown" + } }, { - "kind": "field", - "name": "alignItems", + "name": "align-items", + "fieldName": "alignItems", + "default": "'start'", + "description": "Alignment of items along the main axis.", "type": { "text": "'start' | 'center' | 'end' | 'stretch'" - }, - "default": "'start'", - "description": "Alignment of items along the main axis." + } }, { - "kind": "field", - "name": "itemGap", + "name": "item-gap", + "fieldName": "itemGap", + "default": "'var(--le-toolbar-gap, var(--le-spacing-1, 4px))'", + "description": "Spacing between top-level toolbar items.\nAccepts any valid CSS length (e.g. `8px`, `0.5rem`, `var(--le-spacing-2)`).", "type": { "text": "string" - }, - "default": "'var(--le-toolbar-gap, var(--le-spacing-1, 4px))'", - "description": "Spacing between top-level toolbar items.\nAccepts any valid CSS length (e.g. `8px`, `0.5rem`, `var(--le-spacing-2)`)." + } }, { - "kind": "field", - "name": "overflowIcon", + "name": "overflow-icon", + "fieldName": "overflowIcon", + "default": "'ellipsis-horizontal'", + "description": "Icon for the overflow trigger button when no custom slot content is provided.", "type": { "text": "string" - }, - "default": "'ellipsis-horizontal'", - "description": "Icon for the overflow trigger button when no custom slot content is provided." + } }, { - "kind": "field", - "name": "overflowLabel", + "name": "overflow-label", + "fieldName": "overflowLabel", + "default": "'More'", + "description": "Accessible label for the overflow trigger button.", "type": { "text": "string" - }, - "default": "'More'", - "description": "Accessible label for the overflow trigger button." + } }, { - "kind": "field", - "name": "disablePopover", + "name": "disable-popover", + "fieldName": "disablePopover", + "default": "false", + "description": "Disable the built-in overflow popover.\nThe toolbar will still compute overflow state and emit events, but\nwon't render its own menu. Useful for custom overflow handling.", "type": { "text": "boolean" - }, - "default": "false", - "description": "Disable the built-in overflow popover.\nThe toolbar will still compute overflow state and emit events, but\nwon't render its own menu. Useful for custom overflow handling." + } }, { - "kind": "field", - "name": "debugVirtualToolbar", + "name": "debug-virtual-toolbar", + "fieldName": "debugVirtualToolbar", + "default": "false", + "description": "Temporary debug mode: render the virtual toolbar visibly above\nthe live toolbar so collapse measurements can be inspected.", "type": { "text": "boolean" - }, - "default": "false", - "description": "Temporary debug mode: render the virtual toolbar visibly above\nthe live toolbar so collapse measurements can be inspected." + } }, { - "kind": "field", - "name": "debugPauseBeforeMeasure", + "name": "debug-pause-before-measure", + "fieldName": "debugPauseBeforeMeasure", + "default": "false", + "description": "Temporary debug mode: stop before measuring virtual widths so the\nvirtual DOM can be inspected before collapse simulation mutates it.", "type": { "text": "boolean" - }, - "default": "false", - "description": "Temporary debug mode: stop before measuring virtual widths so the\nvirtual DOM can be inspected before collapse simulation mutates it." - }, + } + } + ], + "tagName": "le-toolbar", + "events": [ { - "kind": "field", - "name": "solverOutput", + "name": "leToolbarOverflowChange", "type": { - "text": "SolverOutput" + "text": "EventEmitter | undefined" }, - "privacy": "private", - "default": "{ visibleIds: new Set(), collapsedGroupIds: new Set(), overflowIds: new Set(), showTrigger: false, }" - }, + "description": "Emitted when the overflow state changes." + } + ], + "customElement": true + }, + { + "kind": "variable", + "name": "slotName", + "default": "`__le-toolbar-item-${index}`" + }, + { + "kind": "variable", + "name": "priority" + }, + { + "kind": "variable", + "name": "kind", + "type": { + "text": "ToolbarItemRecord['kind']" + } + }, + { + "kind": "variable", + "name": "overflowOption" + } + ], + "exports": [ + { + "kind": "js", + "name": "LeToolbar", + "declaration": { + "name": "LeToolbar", + "module": "src/components/le-toolbar/le-toolbar.tsx" + } + }, + { + "kind": "custom-element-definition", + "name": "le-toolbar", + "declaration": { + "name": "LeToolbar", + "module": "src/components/le-toolbar/le-toolbar.tsx" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/components/le-toolbar-spacer/le-toolbar-spacer.tsx", + "declarations": [ + { + "kind": "class", + "description": "Flexible spacer for le-toolbar layouts.\n\nDefault behavior (no width): occupies available free space and shrinks naturally.\nWith numeric `width`: behaves as a fixed-width spacer that can be collapsed by le-toolbar.", + "name": "LeToolbarSpacer", + "members": [ { "kind": "field", - "name": "overflowMenuItems", + "name": "width", "type": { - "text": "LeOption[]" + "text": "number | string | undefined" }, - "privacy": "private", - "default": "[]" + "description": "Optional fixed width.\nNumeric values (e.g. `24`) are treated as px.\nString values may be any valid CSS width (e.g. `2rem`, `var(--le-spacing-2)`)." }, { - "kind": "field", - "name": "itemSlots", - "type": { - "text": "Array<{ id: string; slotName: string }>" - }, + "kind": "method", + "name": "isValidCssWidth", "privacy": "private", - "default": "[]" - }, - { - "kind": "field", - "name": "initializingLayout", - "type": { - "text": "boolean" + "return": { + "type": { + "text": "boolean" + } }, - "privacy": "private", - "default": "true" + "parameters": [ + { + "name": "value", + "type": { + "text": "string" + } + } + ] }, { - "kind": "field", - "name": "debugStepIndex", - "type": { - "text": "number" - }, + "kind": "method", + "name": "getFixedWidthValue", "privacy": "private", - "default": "0" + "return": { + "type": { + "text": "string | undefined" + } + } }, { - "kind": "field", - "name": "debugStepMeasuredWidth", - "type": { - "text": "number" - }, + "kind": "method", + "name": "isFixedSpacer", "privacy": "private", - "default": "0" + "return": { + "type": { + "text": "boolean" + } + } }, { - "kind": "field", - "name": "virtualTriggerVisible", - "type": { - "text": "boolean" + "kind": "method", + "name": "getCollapseMeta", + "return": { + "type": { + "text": "Promise" + } }, - "privacy": "private", - "default": "false" + "description": "Returns collapse meta for toolbar integration." }, { - "kind": "field", - "name": "hasPreparedInitialLayout", + "kind": "method", + "name": "render" + } + ], + "attributes": [ + { + "name": "width", + "fieldName": "width", + "description": "Optional fixed width.\nNumeric values (e.g. `24`) are treated as px.\nString values may be any valid CSS width (e.g. `2rem`, `var(--le-spacing-2)`).", "type": { - "text": "boolean" - }, - "privacy": "private", - "default": "false" - }, + "text": "number | string" + } + } + ], + "tagName": "le-toolbar-spacer", + "events": [], + "customElement": true + } + ], + "exports": [ + { + "kind": "js", + "name": "LeToolbarSpacer", + "declaration": { + "name": "LeToolbarSpacer", + "module": "src/components/le-toolbar-spacer/le-toolbar-spacer.tsx" + } + }, + { + "kind": "custom-element-definition", + "name": "le-toolbar-spacer", + "declaration": { + "name": "LeToolbarSpacer", + "module": "src/components/le-toolbar-spacer/le-toolbar-spacer.tsx" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/components/le-text/le-text.tsx", + "declarations": [ + { + "kind": "class", + "description": "A text component with rich text editing capabilities in admin mode.\n\n`le-text` renders semantic text elements (headings, paragraphs, code, quotes)\nand provides a Notion-like rich text editor in admin mode with formatting\ntoolbar for bold, italic, links, and paragraph type selection.", + "name": "LeText", + "cssProperties": [ { - "kind": "field", - "name": "instanceId", - "type": { - "text": "string" - }, - "privacy": "private" + "description": "Text color", + "name": "--le-text-color" }, { - "kind": "field", - "name": "toolbarHostEl", - "type": { - "text": "HTMLElement | null | undefined" - }, - "privacy": "private", - "description": "Toolbar host element (follows the width of the parent element)." + "description": "Font size", + "name": "--le-text-font-size" }, { - "kind": "field", - "name": "toolbarContainerEl", - "type": { - "text": "HTMLElement | null | undefined" - }, - "privacy": "private", - "description": "Live toolbar items container." + "description": "Line height", + "name": "--le-text-line-height" }, { - "kind": "field", - "name": "virtualToolbarEl", - "type": { - "text": "HTMLElement | undefined" - }, - "privacy": "private", - "description": "Hidden virtual toolbar container used for browser-native overflow checks." - }, + "description": "Font weight", + "name": "--le-text-font-weight" + } + ], + "cssParts": [ { - "kind": "field", - "name": "virtualItemsEl", - "type": { - "text": "HTMLElement | undefined" - }, - "privacy": "private", - "description": "Virtual row where cloned participants are mounted for simulation." - }, + "description": "The text container element", + "name": "text" + } + ], + "slots": [ { - "kind": "field", - "name": "virtualTriggerEl", - "type": { - "text": "HTMLElement | undefined" - }, - "privacy": "private", - "description": "Virtual trigger element that participates in layout when overflow exists." - }, + "description": "Default slot for text content", + "name": "" + } + ], + "members": [ { "kind": "field", - "name": "resizeObserver", + "name": "el", "type": { - "text": "ResizeObserver | undefined" - }, - "privacy": "private" + "text": "HTMLElement" + } }, { "kind": "field", - "name": "mutationObserver", + "name": "variant", "type": { - "text": "MutationObserver | undefined" + "text": "| 'p'\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'code'\n | 'quote'\n | 'label'\n | 'small'" }, - "privacy": "private" + "default": "'p'", + "description": "The semantic variant/type of text element" }, { "kind": "field", - "name": "pendingRecalc", + "name": "align", "type": { - "text": "number | null" + "text": "'left' | 'center' | 'right' | 'justify'" }, - "privacy": "private", - "default": "null" + "default": "'left'", + "description": "Text alignment" }, { "kind": "field", - "name": "debugVirtualWidth", + "name": "color", "type": { - "text": "number" + "text": "string | undefined" }, - "privacy": "private", - "default": "0" + "description": "Text color (CSS value or theme token)" }, { "kind": "field", - "name": "observersAttached", + "name": "truncate", "type": { "text": "boolean" }, - "privacy": "private", "default": "false", - "description": "Prevent double-attach on rapid connect/disconnect cycles." + "description": "Whether the text should truncate with ellipsis" }, { "kind": "field", - "name": "itemMap", + "name": "maxLines", "type": { - "text": "Map" + "text": "number | undefined" }, - "privacy": "private", - "default": "new Map()", - "description": "Map from ID → original light-DOM element plus virtual clone." + "description": "Maximum number of lines before truncating (requires truncate=true)" }, { "kind": "field", - "name": "collapseSteps", + "name": "adminMode", "type": { - "text": "CollapseStep[]" + "text": "boolean" }, "privacy": "private", - "default": "[]" + "default": "false", + "description": "Internal state to track admin mode" }, { "kind": "field", - "name": "disconnectModeObserver", + "name": "content", "type": { - "text": "() => void | undefined" - }, - "privacy": "private" - }, - { - "kind": "method", - "name": "handleAlignChange" - }, - { - "kind": "method", - "name": "handleGapChange", - "parameters": [ - { - "name": "newValue", - "type": { - "text": "string" - } - } - ] - }, - { - "kind": "method", - "name": "handleItemsChange" - }, - { - "kind": "method", - "name": "handleSlotChange" - }, - { - "kind": "method", - "name": "resetToolbar", - "description": "Reset the toolbar's internal layout state and recalculate item visibility from scratch." - }, - { - "kind": "method", - "name": "recalculate", - "description": "Force a layout recalculation." - }, - { - "kind": "method", - "name": "attachObservers", - "privacy": "private" - }, - { - "kind": "method", - "name": "detachObservers", - "privacy": "private" - }, - { - "kind": "method", - "name": "observeContainer", - "privacy": "private", - "parameters": [ - { - "name": "el", - "optional": true, - "type": { - "text": "HTMLElement | null" - } - } - ] - }, - { - "kind": "method", - "name": "scheduleRecalc", - "privacy": "private" - }, - { - "kind": "method", - "name": "getItemId", - "privacy": "private", - "return": { - "type": { - "text": "string" - } + "text": "string" }, - "parameters": [ - { - "name": "el", - "type": { - "text": "HTMLElement" - } - }, - { - "name": "index", - "type": { - "text": "number" - } - } - ] - }, - { - "kind": "method", - "name": "getItemPriority", "privacy": "private", - "return": { - "type": { - "text": "number" - } - }, - "parameters": [ - { - "name": "el", - "type": { - "text": "HTMLElement" - } - }, - { - "name": "index", - "type": { - "text": "number" - } - } - ] + "default": "''", + "description": "The HTML content being edited" }, { - "kind": "method", - "name": "buildOverflowOption", - "privacy": "private", - "return": { - "type": { - "text": "Promise" - } - }, - "parameters": [ - { - "name": "item", - "type": { - "text": "HTMLElement" - } - }, - { - "name": "id", - "type": { - "text": "string" - } - } - ] + "kind": "field", + "name": "isFocused", + "type": { + "text": "boolean" + }, + "privacy": "private", + "default": "false", + "description": "Whether the editor is focused (shows toolbar)" }, { - "kind": "method", - "name": "setVirtualTriggerVisible", + "kind": "field", + "name": "selectionState", + "type": { + "text": "SelectionState" + }, "privacy": "private", - "parameters": [ - { - "name": "visible", - "type": { - "text": "boolean" - } - } - ] + "default": "{ isBold: false, isItalic: false, isUnderline: false, isStrikethrough: false, isLink: false, blockType: 'p', }", + "description": "Current selection state for toolbar button highlighting" }, { - "kind": "method", - "name": "getAuthoredVirtualCollapse", + "kind": "field", + "name": "editorRef", + "type": { + "text": "HTMLDivElement | undefined" + }, "privacy": "private", - "return": { - "type": { - "text": "string | undefined" - } + "description": "Reference to the contenteditable element" + }, + { + "kind": "field", + "name": "slotRef", + "type": { + "text": "HTMLSlotElement | undefined" }, - "parameters": [ - { - "name": "record", - "type": { - "text": "ToolbarItemRecord" - } - } - ] + "privacy": "private", + "description": "Reference to the slot element" }, { - "kind": "method", - "name": "resetVirtualState", + "kind": "field", + "name": "disconnectModeObserver", + "type": { + "text": "() => void | undefined" + }, "privacy": "private" }, { "kind": "method", - "name": "initializeDebugMeasurementState", - "privacy": "private" + "name": "onVariantChange" }, { "kind": "method", - "name": "runDebugMeasurementStep" + "name": "readSlottedContent", + "privacy": "private", + "description": "Read content from slotted elements" }, { - "kind": "field", - "name": "handleVirtualDebugClick", - "privacy": "private" + "kind": "method", + "name": "syncContentToSlot", + "privacy": "private", + "description": "Sync edited content back to the slot" }, { - "kind": "method", - "name": "clearVirtualMeasurements", - "privacy": "private" + "kind": "field", + "name": "handleInput", + "privacy": "private", + "description": "Handle input in the contenteditable" }, { - "kind": "method", - "name": "settleVirtualItem", + "kind": "field", + "name": "handleFocus", "privacy": "private", - "parameters": [ - { - "name": "element", - "optional": true, - "type": { - "text": "HTMLElement" - } - } - ] + "description": "Handle focus on the editor" }, { - "kind": "method", - "name": "waitForNestedCustomElements", + "kind": "field", + "name": "handleBlur", "privacy": "private", - "parameters": [ - { - "name": "root", - "type": { - "text": "HTMLElement" - } - } - ] + "description": "Handle blur on the editor" }, { - "kind": "method", - "name": "syncVirtualTriggerContent", - "privacy": "private" + "kind": "field", + "name": "handleSelectionChange", + "privacy": "private", + "description": "Handle selection change to update toolbar state" }, { "kind": "method", - "name": "prepareToolbarItems", - "privacy": "private" + "name": "updateSelectionState", + "privacy": "private", + "description": "Update the selection state for toolbar highlighting" }, { "kind": "method", - "name": "getVisibilityState", + "name": "isSelectionInLink", "privacy": "private", "return": { "type": { - "text": "'visible' | 'collapsed'" + "text": "boolean" } }, "parameters": [ { - "name": "value", + "name": "selection", "type": { - "text": "string | null" + "text": "Selection" } } - ] - }, - { - "kind": "method", - "name": "calculateLayoutWidths", - "privacy": "private" - }, - { - "kind": "method", - "name": "computeLayout", - "privacy": "private" + ], + "description": "Check if current selection is within a link" }, { "kind": "method", - "name": "applyOutput", + "name": "execCommand", "privacy": "private", "parameters": [ { - "name": "output", + "name": "command", "type": { - "text": "SolverOutput" + "text": "string" } }, { - "name": "overflowItems", + "name": "value", + "optional": true, "type": { - "text": "LeOption[]" + "text": "string" } } - ] + ], + "description": "Execute a formatting command" }, { "kind": "field", - "name": "handleOverflowSelect", - "privacy": "private" + "name": "toggleBold", + "privacy": "private", + "description": "Toggle bold formatting" }, { - "kind": "method", - "name": "renderVirtualToolbar", - "privacy": "private" + "kind": "field", + "name": "toggleItalic", + "privacy": "private", + "description": "Toggle italic formatting" }, { - "kind": "method", - "name": "render" - } - ], - "attributes": [ + "kind": "field", + "name": "toggleUnderline", + "privacy": "private", + "description": "Toggle underline formatting" + }, { - "name": "items", - "fieldName": "items", - "description": "Optional declarative items input.\n\nThe current implementation is slot-driven, but when this prop changes we\nstill invalidate the slotted-items cache and recompute layout.", - "type": { - "text": "unknown" - } + "kind": "field", + "name": "toggleStrikethrough", + "privacy": "private", + "description": "Toggle strikethrough formatting" }, { - "name": "align-items", - "fieldName": "alignItems", - "default": "'start'", - "description": "Alignment of items along the main axis.", - "type": { - "text": "'start' | 'center' | 'end' | 'stretch'" - } + "kind": "field", + "name": "toggleLink", + "privacy": "private", + "description": "Add or edit a link" }, { - "name": "item-gap", - "fieldName": "itemGap", - "default": "'var(--le-toolbar-gap, var(--le-spacing-1, 4px))'", - "description": "Spacing between top-level toolbar items.\nAccepts any valid CSS length (e.g. `8px`, `0.5rem`, `var(--le-spacing-2)`).", - "type": { - "text": "string" - } + "kind": "field", + "name": "changeVariant", + "privacy": "private", + "description": "Change the block type/variant" }, { - "name": "overflow-icon", - "fieldName": "overflowIcon", - "default": "'ellipsis-horizontal'", - "description": "Icon for the overflow trigger button when no custom slot content is provided.", - "type": { - "text": "string" - } + "kind": "method", + "name": "renderToolbar", + "privacy": "private", + "description": "Render the formatting toolbar" }, { - "name": "overflow-label", - "fieldName": "overflowLabel", - "default": "'More'", - "description": "Accessible label for the overflow trigger button.", + "kind": "method", + "name": "getTag", + "privacy": "private", + "return": { + "type": { + "text": "string" + } + }, + "description": "Get the semantic tag for the current variant" + }, + { + "kind": "method", + "name": "render" + } + ], + "attributes": [ + { + "name": "variant", + "fieldName": "variant", + "default": "'p'", + "description": "The semantic variant/type of text element", "type": { - "text": "string" + "text": "| 'p'\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'code'\n | 'quote'\n | 'label'\n | 'small'" } }, { - "name": "disable-popover", - "fieldName": "disablePopover", - "default": "false", - "description": "Disable the built-in overflow popover.\nThe toolbar will still compute overflow state and emit events, but\nwon't render its own menu. Useful for custom overflow handling.", + "name": "align", + "fieldName": "align", + "default": "'left'", + "description": "Text alignment", "type": { - "text": "boolean" + "text": "'left' | 'center' | 'right' | 'justify'" } }, { - "name": "debug-virtual-toolbar", - "fieldName": "debugVirtualToolbar", - "default": "false", - "description": "Temporary debug mode: render the virtual toolbar visibly above\nthe live toolbar so collapse measurements can be inspected.", + "name": "color", + "fieldName": "color", + "description": "Text color (CSS value or theme token)", "type": { - "text": "boolean" + "text": "string" } }, { - "name": "debug-pause-before-measure", - "fieldName": "debugPauseBeforeMeasure", + "name": "truncate", + "fieldName": "truncate", "default": "false", - "description": "Temporary debug mode: stop before measuring virtual widths so the\nvirtual DOM can be inspected before collapse simulation mutates it.", + "description": "Whether the text should truncate with ellipsis", "type": { "text": "boolean" } - } - ], - "tagName": "le-toolbar", - "events": [ + }, { - "name": "leToolbarOverflowChange", + "name": "max-lines", + "fieldName": "maxLines", + "description": "Maximum number of lines before truncating (requires truncate=true)", "type": { - "text": "EventEmitter | undefined" - }, - "description": "Emitted when the overflow state changes." + "text": "number" + } } ], + "tagName": "le-text", + "events": [], "customElement": true - }, - { - "kind": "variable", - "name": "slotName", - "default": "`__le-toolbar-item-${index}`" - }, - { - "kind": "variable", - "name": "priority" - }, - { - "kind": "variable", - "name": "kind", - "type": { - "text": "ToolbarItemRecord['kind']" - } - }, - { - "kind": "variable", - "name": "overflowOption" } ], "exports": [ { "kind": "js", - "name": "LeToolbar", + "name": "LeText", "declaration": { - "name": "LeToolbar", - "module": "src/components/le-toolbar/le-toolbar.tsx" + "name": "LeText", + "module": "src/components/le-text/le-text.tsx" } }, { "kind": "custom-element-definition", - "name": "le-toolbar", + "name": "le-text", "declaration": { - "name": "LeToolbar", - "module": "src/components/le-toolbar/le-toolbar.tsx" + "name": "LeText", + "module": "src/components/le-text/le-text.tsx" } } ] }, { "kind": "javascript-module", - "path": "src/components/le-toolbar-spacer/le-toolbar-spacer.tsx", + "path": "src/components/le-tag/le-tag.tsx", "declarations": [ { "kind": "class", - "description": "Flexible spacer for le-toolbar layouts.\n\nDefault behavior (no width): occupies available free space and shrinks naturally.\nWith numeric `width`: behaves as a fixed-width spacer that can be collapsed by le-toolbar.", - "name": "LeToolbarSpacer", + "description": "A tag/chip component for displaying labels with optional dismiss functionality.", + "name": "LeTag", + "slots": [ + { + "description": "Default slot for custom content (overrides label prop)", + "name": "" + } + ], "members": [ { "kind": "field", - "name": "width", + "name": "label", "type": { - "text": "number | string | undefined" + "text": "string | undefined" }, - "description": "Optional fixed width.\nNumeric values (e.g. `24`) are treated as px.\nString values may be any valid CSS width (e.g. `2rem`, `var(--le-spacing-2)`)." + "description": "The text label to display in the tag." }, { - "kind": "method", - "name": "isValidCssWidth", - "privacy": "private", - "return": { - "type": { - "text": "boolean" - } + "kind": "field", + "name": "mode", + "type": { + "text": "'default' | 'admin' | undefined" }, - "parameters": [ - { - "name": "value", - "type": { - "text": "string" - } - } - ] + "description": "Mode of the popover should be 'default' for internal use" }, { - "kind": "method", - "name": "getFixedWidthValue", - "privacy": "private", - "return": { - "type": { - "text": "string | undefined" - } - } + "kind": "field", + "name": "icon", + "type": { + "text": "string | undefined" + }, + "description": "Icon to display before the label.\nCan be an emoji, URL, or icon name." }, { - "kind": "method", - "name": "isFixedSpacer", - "privacy": "private", - "return": { - "type": { - "text": "boolean" - } - } + "kind": "field", + "name": "dismissible", + "type": { + "text": "boolean" + }, + "default": "false", + "description": "Whether the tag can be dismissed (shows close button)." }, { - "kind": "method", - "name": "getCollapseMeta", - "return": { - "type": { - "text": "Promise" - } + "kind": "field", + "name": "disabled", + "type": { + "text": "boolean" }, - "description": "Returns collapse meta for toolbar integration." + "default": "false", + "description": "Whether the tag is disabled." + }, + { + "kind": "field", + "name": "size", + "type": { + "text": "'small' | 'medium' | 'large'" + }, + "default": "'medium'", + "description": "The size of the tag." + }, + { + "kind": "field", + "name": "color", + "type": { + "text": "| 'default'\n | 'primary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'secondary'\n | 'info'" + }, + "default": "'default'", + "description": "The color of the tag." + }, + { + "kind": "field", + "name": "handleDismiss", + "privacy": "private" + }, + { + "kind": "method", + "name": "renderIcon", + "privacy": "private" }, { "kind": "method", @@ -16649,34 +17053,94 @@ ], "attributes": [ { - "name": "width", - "fieldName": "width", - "description": "Optional fixed width.\nNumeric values (e.g. `24`) are treated as px.\nString values may be any valid CSS width (e.g. `2rem`, `var(--le-spacing-2)`).", + "name": "label", + "fieldName": "label", + "description": "The text label to display in the tag.", "type": { - "text": "number | string" + "text": "string" + } + }, + { + "name": "mode", + "fieldName": "mode", + "description": "Mode of the popover should be 'default' for internal use", + "type": { + "text": "'default' | 'admin'" + } + }, + { + "name": "icon", + "fieldName": "icon", + "description": "Icon to display before the label.\nCan be an emoji, URL, or icon name.", + "type": { + "text": "string" + } + }, + { + "name": "dismissible", + "fieldName": "dismissible", + "default": "false", + "description": "Whether the tag can be dismissed (shows close button).", + "type": { + "text": "boolean" + } + }, + { + "name": "disabled", + "fieldName": "disabled", + "default": "false", + "description": "Whether the tag is disabled.", + "type": { + "text": "boolean" + } + }, + { + "name": "size", + "fieldName": "size", + "default": "'medium'", + "description": "The size of the tag.", + "type": { + "text": "'small' | 'medium' | 'large'" + } + }, + { + "name": "color", + "fieldName": "color", + "default": "'default'", + "description": "The color of the tag.", + "type": { + "text": "| 'default'\n | 'primary'\n | 'success'\n | 'warning'\n | 'danger'\n | 'secondary'\n | 'info'" } } ], - "tagName": "le-toolbar-spacer", - "events": [], + "tagName": "le-tag", + "events": [ + { + "name": "leDismiss", + "type": { + "text": "EventEmitter | undefined" + }, + "description": "Emitted when the dismiss button is clicked." + } + ], "customElement": true } ], "exports": [ { "kind": "js", - "name": "LeToolbarSpacer", + "name": "LeTag", "declaration": { - "name": "LeToolbarSpacer", - "module": "src/components/le-toolbar-spacer/le-toolbar-spacer.tsx" + "name": "LeTag", + "module": "src/components/le-tag/le-tag.tsx" } }, { "kind": "custom-element-definition", - "name": "le-toolbar-spacer", + "name": "le-tag", "declaration": { - "name": "LeToolbarSpacer", - "module": "src/components/le-toolbar-spacer/le-toolbar-spacer.tsx" + "name": "LeTag", + "module": "src/components/le-tag/le-tag.tsx" } } ] diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index e20722e..c25cb99 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -11,6 +11,7 @@ import { LeBreadcrumbSelectDetail } from "./components/le-breadcrumbs/le-breadcr import { TooltipPlacement } from "."; import { LeCollapseMeta } from "./types/toolbar"; import { LeButtonGroupItemsMeta } from "./components/le-button-group/le-button-group"; +import { LeContextMenuSelectDetail } from "./components/le-context-menu/le-context-menu"; import { LeDragHandleOrientation, LeDragHandlePlacement } from "./components/le-drag-handle/le-drag-handle"; import { LeActiveContext } from "./components/le-kit/le-kit"; import { LeNavigationItemSelectDetail, LeNavigationItemToggleDetail } from "./components/le-navigation/le-navigation"; @@ -28,6 +29,7 @@ export { LeBreadcrumbSelectDetail } from "./components/le-breadcrumbs/le-breadcr export { TooltipPlacement } from "."; export { LeCollapseMeta } from "./types/toolbar"; export { LeButtonGroupItemsMeta } from "./components/le-button-group/le-button-group"; +export { LeContextMenuSelectDetail } from "./components/le-context-menu/le-context-menu"; export { LeDragHandleOrientation, LeDragHandlePlacement } from "./components/le-drag-handle/le-drag-handle"; export { LeActiveContext } from "./components/le-kit/le-kit"; export { LeNavigationItemSelectDetail, LeNavigationItemToggleDetail } from "./components/le-navigation/le-navigation"; @@ -781,6 +783,50 @@ export namespace Components { */ "hostStyle"?: { [key: string]: string }; } + /** + * Context menu component that displays a vertical navigation menu + * when the user right-clicks or long-presses on its children. + */ + interface LeContextMenu { + /** + * Alignment of the menu relative to the trigger. + * @default 'start' + */ + "align": 'start' | 'center' | 'end'; + /** + * Whether to show a backdrop behind the menu, lifting the active item. + * @default false + */ + "backdrop": boolean; + /** + * Disables right-click and touch interactions. + * @default false + */ + "disabled": boolean; + "hide": () => Promise; + /** + * List of menu items represented as options. + * @default [] + */ + "items": LeOption[] | string; + /** + * Whether the context menu is open. + * @default false + */ + "open": boolean; + /** + * Behavior of the menu on page scroll: - 'blocked': blocks page scroll - 'menu-close': closes the menu automatically on scroll (default) - 'fixed-menu': menu scrolls with the page + * @default 'menu-close' + */ + "pageScrollBehavior": 'blocked' | 'menu-close' | 'fixed-menu'; + /** + * Position of the menu relative to the trigger. If 'mouse', positions next to mouse/touch coords. + * @default 'mouse' + */ + "position": 'top' | 'bottom' | 'left' | 'right' | 'mouse'; + "show": (x?: number, y?: number) => Promise; + "toggle": (x?: number, y?: number) => Promise; + } /** * Shows a "smart" header title based on what has scrolled out of view. * When `selector` matches multiple elements, the title becomes the last element @@ -2728,6 +2774,10 @@ export interface LeComboboxCustomEvent extends CustomEvent { detail: T; target: HTMLLeComboboxElement; } +export interface LeContextMenuCustomEvent extends CustomEvent { + detail: T; + target: HTMLLeContextMenuElement; +} export interface LeDropdownBaseCustomEvent extends CustomEvent { detail: T; target: HTMLLeDropdownBaseElement; @@ -3148,6 +3198,28 @@ declare global { prototype: HTMLLeComponentElement; new (): HTMLLeComponentElement; }; + interface HTMLLeContextMenuElementEventMap { + "leContextMenuSelect": LeContextMenuSelectDetail; + "leContextMenuClose": void; + } + /** + * Context menu component that displays a vertical navigation menu + * when the user right-clicks or long-presses on its children. + */ + interface HTMLLeContextMenuElement extends Components.LeContextMenu, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLLeContextMenuElement, ev: LeContextMenuCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLLeContextMenuElement, ev: LeContextMenuCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLLeContextMenuElement: { + prototype: HTMLLeContextMenuElement; + new (): HTMLLeContextMenuElement; + }; /** * Shows a "smart" header title based on what has scrolled out of view. * When `selector` matches multiple elements, the title becomes the last element @@ -4004,6 +4076,7 @@ declare global { "le-collapse": HTMLLeCollapseElement; "le-combobox": HTMLLeComboboxElement; "le-component": HTMLLeComponentElement; + "le-context-menu": HTMLLeContextMenuElement; "le-current-heading": HTMLLeCurrentHeadingElement; "le-drag-handle": HTMLLeDragHandleElement; "le-dropdown-base": HTMLLeDropdownBaseElement; @@ -4810,6 +4883,55 @@ declare namespace LocalJSX { */ "hostStyle"?: { [key: string]: string }; } + /** + * Context menu component that displays a vertical navigation menu + * when the user right-clicks or long-presses on its children. + */ + interface LeContextMenu { + /** + * Alignment of the menu relative to the trigger. + * @default 'start' + */ + "align"?: 'start' | 'center' | 'end'; + /** + * Whether to show a backdrop behind the menu, lifting the active item. + * @default false + */ + "backdrop"?: boolean; + /** + * Disables right-click and touch interactions. + * @default false + */ + "disabled"?: boolean; + /** + * List of menu items represented as options. + * @default [] + */ + "items"?: LeOption[] | string; + /** + * Emitted when the context menu is closed. + */ + "onLeContextMenuClose"?: (event: LeContextMenuCustomEvent) => void; + /** + * Emitted when a menu item is selected. + */ + "onLeContextMenuSelect"?: (event: LeContextMenuCustomEvent) => void; + /** + * Whether the context menu is open. + * @default false + */ + "open"?: boolean; + /** + * Behavior of the menu on page scroll: - 'blocked': blocks page scroll - 'menu-close': closes the menu automatically on scroll (default) - 'fixed-menu': menu scrolls with the page + * @default 'menu-close' + */ + "pageScrollBehavior"?: 'blocked' | 'menu-close' | 'fixed-menu'; + /** + * Position of the menu relative to the trigger. If 'mouse', positions next to mouse/touch coords. + * @default 'mouse' + */ + "position"?: 'top' | 'bottom' | 'left' | 'right' | 'mouse'; + } /** * Shows a "smart" header title based on what has scrolled out of view. * When `selector` matches multiple elements, the title becomes the last element @@ -6909,6 +7031,15 @@ declare namespace LocalJSX { "displayName": string; "hostClass": string; } + interface LeContextMenuAttributes { + "open": boolean; + "disabled": boolean; + "items": LeOption[] | string; + "backdrop": boolean; + "pageScrollBehavior": 'blocked' | 'menu-close' | 'fixed-menu'; + "position": 'top' | 'bottom' | 'left' | 'right' | 'mouse'; + "align": 'start' | 'center' | 'end'; + } interface LeCurrentHeadingAttributes { "selector": string; } @@ -7286,6 +7417,7 @@ declare namespace LocalJSX { "le-collapse": Omit & { [K in keyof LeCollapse & keyof LeCollapseAttributes]?: LeCollapse[K] } & { [K in keyof LeCollapse & keyof LeCollapseAttributes as `attr:${K}`]?: LeCollapseAttributes[K] } & { [K in keyof LeCollapse & keyof LeCollapseAttributes as `prop:${K}`]?: LeCollapse[K] }; "le-combobox": Omit & { [K in keyof LeCombobox & keyof LeComboboxAttributes]?: LeCombobox[K] } & { [K in keyof LeCombobox & keyof LeComboboxAttributes as `attr:${K}`]?: LeComboboxAttributes[K] } & { [K in keyof LeCombobox & keyof LeComboboxAttributes as `prop:${K}`]?: LeCombobox[K] }; "le-component": Omit & { [K in keyof LeComponent & keyof LeComponentAttributes]?: LeComponent[K] } & { [K in keyof LeComponent & keyof LeComponentAttributes as `attr:${K}`]?: LeComponentAttributes[K] } & { [K in keyof LeComponent & keyof LeComponentAttributes as `prop:${K}`]?: LeComponent[K] } & OneOf<"component", LeComponent["component"], LeComponentAttributes["component"]>; + "le-context-menu": Omit & { [K in keyof LeContextMenu & keyof LeContextMenuAttributes]?: LeContextMenu[K] } & { [K in keyof LeContextMenu & keyof LeContextMenuAttributes as `attr:${K}`]?: LeContextMenuAttributes[K] } & { [K in keyof LeContextMenu & keyof LeContextMenuAttributes as `prop:${K}`]?: LeContextMenu[K] }; "le-current-heading": Omit & { [K in keyof LeCurrentHeading & keyof LeCurrentHeadingAttributes]?: LeCurrentHeading[K] } & { [K in keyof LeCurrentHeading & keyof LeCurrentHeadingAttributes as `attr:${K}`]?: LeCurrentHeadingAttributes[K] } & { [K in keyof LeCurrentHeading & keyof LeCurrentHeadingAttributes as `prop:${K}`]?: LeCurrentHeading[K] }; "le-drag-handle": Omit & { [K in keyof LeDragHandle & keyof LeDragHandleAttributes]?: LeDragHandle[K] } & { [K in keyof LeDragHandle & keyof LeDragHandleAttributes as `attr:${K}`]?: LeDragHandleAttributes[K] } & { [K in keyof LeDragHandle & keyof LeDragHandleAttributes as `prop:${K}`]?: LeDragHandle[K] }; "le-dropdown-base": Omit & { [K in keyof LeDropdownBase & keyof LeDropdownBaseAttributes]?: LeDropdownBase[K] } & { [K in keyof LeDropdownBase & keyof LeDropdownBaseAttributes as `attr:${K}`]?: LeDropdownBaseAttributes[K] } & { [K in keyof LeDropdownBase & keyof LeDropdownBaseAttributes as `prop:${K}`]?: LeDropdownBase[K] }; @@ -7509,6 +7641,11 @@ declare module "@stencil/core" { * @cmsCategory System */ "le-component": LocalJSX.IntrinsicElements["le-component"] & JSXBase.HTMLAttributes; + /** + * Context menu component that displays a vertical navigation menu + * when the user right-clicks or long-presses on its children. + */ + "le-context-menu": LocalJSX.IntrinsicElements["le-context-menu"] & JSXBase.HTMLAttributes; /** * Shows a "smart" header title based on what has scrolled out of view. * When `selector` matches multiple elements, the title becomes the last element diff --git a/packages/core/src/components/le-breadcrumbs/readme.md b/packages/core/src/components/le-breadcrumbs/readme.md index 4f2545a..56cce70 100644 --- a/packages/core/src/components/le-breadcrumbs/readme.md +++ b/packages/core/src/components/le-breadcrumbs/readme.md @@ -67,8 +67,8 @@ graph TD; le-popup --> le-button le-popup --> le-component le-tooltip --> le-component - le-navigation --> le-string-input le-navigation --> le-icon + le-navigation --> le-string-input le-navigation --> le-collapse le-navigation --> le-popover le-navigation --> le-bar diff --git a/packages/core/src/components/le-button-group/readme.md b/packages/core/src/components/le-button-group/readme.md index 8e2bdf5..090ffc2 100644 --- a/packages/core/src/components/le-button-group/readme.md +++ b/packages/core/src/components/le-button-group/readme.md @@ -146,8 +146,8 @@ graph TD; le-overflow-menu --> le-popover le-overflow-menu --> le-button le-overflow-menu --> le-icon - le-navigation --> le-string-input le-navigation --> le-icon + le-navigation --> le-string-input le-navigation --> le-collapse le-navigation --> le-popover le-navigation --> le-bar diff --git a/packages/core/src/components/le-context-menu/le-context-menu.css b/packages/core/src/components/le-context-menu/le-context-menu.css new file mode 100644 index 0000000..228a6a9 --- /dev/null +++ b/packages/core/src/components/le-context-menu/le-context-menu.css @@ -0,0 +1,68 @@ +:host { + display: contents; +} + +.context-menu-trigger-zone { + display: contents; +} + +.le-context-menu-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 9990; + opacity: 0; + pointer-events: none; + transition: opacity 0.22s cubic-bezier(0.16, 1, 0.3, 1); +} + +:host([open]) .le-context-menu-backdrop { + opacity: 1; + pointer-events: auto; +} + +/* Shared backdrop style for Shadow DOM */ +.le-backdrop-background { + background: var(--le-color-backdrop, rgba(0, 0, 0, 0.5)); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); +} + +:host([has-backdrop]) ::slotted(.le-context-menu-active-item) { + position: relative !important; + z-index: 9995 !important; + box-shadow: var(--le-shadow-lg, 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.15)); + transition: transform 0.22s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.22s cubic-bezier(0.16, 1, 0.3, 1); +} + +:host([open][has-backdrop]) ::slotted(.le-context-menu-active-item) { + transform: scale(1.02); +} + +le-popover { + position: fixed !important; + pointer-events: none; + z-index: 9999 !important; +} + +le-popover::part(content) { + pointer-events: auto; + padding: var(--le-spacing-1, 4px); +} + +le-popover::part(trigger) { + width: 100%; + height: 100%; + opacity: 0; + pointer-events: none; +} + +le-navigation { + --le-nav-padding: 0; + --le-nav-item-padding-x: var(--le-spacing-2, 8px); + --le-nav-item-indent: var(--le-spacing-3, 12px); + --le-items-gap: 0; + --le-font-size: var(--le-font-size-sm, 0.875rem); +} diff --git a/packages/core/src/components/le-context-menu/le-context-menu.spec.tsx b/packages/core/src/components/le-context-menu/le-context-menu.spec.tsx new file mode 100644 index 0000000..1867400 --- /dev/null +++ b/packages/core/src/components/le-context-menu/le-context-menu.spec.tsx @@ -0,0 +1,330 @@ +import { beforeAll, describe, expect, it } from '@jest/globals'; +import { newSpecPage } from '@stencil/core/testing'; +import { mockMutationObserver } from '../../utils/test-helpers'; +import { LeContextMenu } from './le-context-menu'; +import { LePopover } from '../le-popover/le-popover'; +import { LeNavigation } from '../le-navigation/le-navigation'; + +beforeAll(() => { + mockMutationObserver(); +}); + +describe('le-context-menu', () => { + it('renders default host structure and properties', async () => { + const page = await newSpecPage({ + components: [LeContextMenu, LePopover, LeNavigation], + html: ` + +
Right click me
+
+ `, + }); + + const host = page.root as any; + expect(host).not.toBeNull(); + expect(host.backdrop).toBe(false); + expect(host.pageScrollBehavior).toBe('menu-close'); + expect(host.position).toBe('mouse'); + expect(host.align).toBe('start'); + + const triggerZone = host.shadowRoot?.querySelector('.context-menu-trigger-zone'); + expect(triggerZone).not.toBeNull(); + }); + + it('toggles open state and sets coordinates on right click', async () => { + const page = await newSpecPage({ + components: [LeContextMenu, LePopover, LeNavigation], + html: ` + +
Right click me
+
+ `, + }); + + const host = page.root as any; + const triggerZone = host.shadowRoot?.querySelector('.context-menu-trigger-zone') as HTMLElement; + + expect(host.open).toBe(false); + + // Dispatch contextmenu event + const contextMenuEvent = new MouseEvent('contextmenu', { + bubbles: true, + cancelable: true, + clientX: 100, + clientY: 200, + }); + triggerZone.dispatchEvent(contextMenuEvent); + await page.waitForChanges(); + + expect(host.open).toBe(true); + const popover = host.shadowRoot?.querySelector('le-popover') as HTMLElement; + expect(popover.style.left).toBe('100px'); + expect(popover.style.top).toBe('200px'); + }); + + it('renders backdrop when backdrop prop is set and elevates trigger item', async () => { + const page = await newSpecPage({ + components: [LeContextMenu, LePopover, LeNavigation], + html: ` + +
Right click me
+
+ `, + }); + + const host = page.root as any; + expect(host.backdrop).toBe(true); + expect(host.hasAttribute('has-backdrop')).toBe(true); + + const triggerEl = host.querySelector('.trigger') as HTMLElement; + const triggerZone = host.shadowRoot?.querySelector('.context-menu-trigger-zone') as HTMLElement; + + const component = page.rootInstance as any; + component.activeTriggerEl = triggerEl; + + // Open the menu by clicking the trigger + const contextMenuEvent = new MouseEvent('contextmenu', { + bubbles: true, + cancelable: true, + clientX: 100, + clientY: 200, + }); + triggerZone.dispatchEvent(contextMenuEvent); + await page.waitForChanges(); + + expect(host.open).toBe(true); + + // Verify backdrop element is rendered in shadow DOM + const backdrop = host.shadowRoot?.querySelector('.le-context-menu-backdrop') as HTMLElement; + expect(backdrop).not.toBeNull(); + + // Verify trigger item has the active-item class added to it + expect(triggerEl.classList.contains('le-context-menu-active-item')).toBe(true); + + // Click backdrop to close + backdrop.click(); + await page.waitForChanges(); + + expect(host.open).toBe(false); + }); + + it('does not add class when backdrop prop is false', async () => { + const page = await newSpecPage({ + components: [LeContextMenu, LePopover, LeNavigation], + html: ` + +
Right click me
+
+ `, + }); + + const host = page.root as any; + const triggerEl = host.querySelector('.trigger') as HTMLElement; + const triggerZone = host.shadowRoot?.querySelector('.context-menu-trigger-zone') as HTMLElement; + + const component = page.rootInstance as any; + component.activeTriggerEl = triggerEl; + + const contextMenuEvent = new MouseEvent('contextmenu', { + bubbles: true, + cancelable: true, + clientX: 100, + clientY: 200, + }); + triggerZone.dispatchEvent(contextMenuEvent); + await page.waitForChanges(); + + expect(host.open).toBe(true); + // Since backdrop is false, we should verify the active item class is NOT added, + // and the host does not have has-backdrop attribute. + expect(triggerEl.classList.contains('le-context-menu-active-item')).toBe(false); + expect(host.hasAttribute('has-backdrop')).toBe(false); + }); + + it('closes menu on scroll when pageScrollBehavior is menu-close', async () => { + const page = await newSpecPage({ + components: [LeContextMenu, LePopover, LeNavigation], + html: ` + +
Right click me
+
+ `, + }); + + const host = page.root as any; + const triggerEl = host.querySelector('.trigger') as HTMLElement; + const triggerZone = host.shadowRoot?.querySelector('.context-menu-trigger-zone') as HTMLElement; + + const component = page.rootInstance as any; + component.activeTriggerEl = triggerEl; + + // Open menu + const contextMenuEvent = new MouseEvent('contextmenu', { + bubbles: true, + cancelable: true, + clientX: 100, + clientY: 200, + }); + triggerZone.dispatchEvent(contextMenuEvent); + await page.waitForChanges(); + expect(host.open).toBe(true); + + // Manually trigger popover open callback to set up scroll listener in JSDOM + component.handlePopoverOpen(); + await page.waitForChanges(); + + // Simulate page scroll + const scrollEvent = new Event('scroll', { bubbles: true }); + window.dispatchEvent(scrollEvent); + await page.waitForChanges(); + + expect(host.open).toBe(false); + }); + + it('updates coordinates on scroll when pageScrollBehavior is fixed-menu', async () => { + const page = await newSpecPage({ + components: [LeContextMenu, LePopover, LeNavigation], + html: ` + +
Right click me
+
+ `, + }); + + const host = page.root as any; + const triggerEl = host.querySelector('.trigger') as HTMLElement; + const triggerZone = host.shadowRoot?.querySelector('.context-menu-trigger-zone') as HTMLElement; + + // Mock initial client rects + triggerEl.getBoundingClientRect = () => ({ + top: 100, + left: 100, + width: 100, + height: 50, + bottom: 150, + right: 200, + x: 100, + y: 100, + toJSON: () => {} + } as DOMRect); + + const component = page.rootInstance as any; + component.activeTriggerEl = triggerEl; + + // Open menu + const contextMenuEvent = new MouseEvent('contextmenu', { + bubbles: true, + cancelable: true, + clientX: 150, + clientY: 250, + }); + triggerZone.dispatchEvent(contextMenuEvent); + await page.waitForChanges(); + expect(host.open).toBe(true); + + // Manually trigger popover open callback to set up scroll listener in JSDOM + component.handlePopoverOpen(); + await page.waitForChanges(); + + // Now mock triggerEl moving due to scroll: moves up by 50px (top becomes 50) + triggerEl.getBoundingClientRect = () => ({ + top: 50, + left: 100, + width: 100, + height: 50, + bottom: 100, + right: 200, + x: 100, + y: 50, + toJSON: () => {} + } as DOMRect); + + // Simulate page scroll + const scrollEvent = new Event('scroll', { bubbles: true }); + window.dispatchEvent(scrollEvent); + await page.waitForChanges(); + + // Delta dy = 50 - 100 = -50px. + // New coords-y should be clientY (250) + dy (-50) = 200px. + const popover = host.shadowRoot?.querySelector('le-popover') as HTMLElement; + expect(popover.style.top).toBe('200px'); + expect(popover.style.left).toBe('150px'); + }); + + it('updates coordinates on scroll for touch trigger', async () => { + const page = await newSpecPage({ + components: [LeContextMenu, LePopover, LeNavigation], + html: ` + +
Long press me
+
+ `, + }); + + const host = page.root as any; + const triggerEl = host.querySelector('.trigger') as HTMLElement; + const triggerZone = host.shadowRoot?.querySelector('.context-menu-trigger-zone') as HTMLElement; + + // Mock initial client rects + triggerEl.getBoundingClientRect = () => ({ + top: 100, + left: 100, + width: 100, + height: 50, + bottom: 150, + right: 200, + x: 100, + y: 100, + toJSON: () => {} + } as DOMRect); + + triggerZone.getBoundingClientRect = () => ({ + top: 100, + left: 100, + width: 100, + height: 50, + bottom: 150, + right: 200, + x: 100, + y: 100, + toJSON: () => {} + } as DOMRect); + + const component = page.rootInstance as any; + component.activeTriggerEl = triggerEl; + + // Open menu using touch/show + await component.show(); + await page.waitForChanges(); + expect(host.open).toBe(true); + + // Manually trigger popover open callback to set up scroll listener in JSDOM + component.handlePopoverOpen(); + await page.waitForChanges(); + + // Now mock triggerEl moving due to scroll: moves up by 50px (top becomes 50) + triggerEl.getBoundingClientRect = () => ({ + top: 50, + left: 100, + width: 100, + height: 50, + bottom: 100, + right: 200, + x: 100, + y: 50, + toJSON: () => {} + } as DOMRect); + + // Simulate page scroll + const scrollEvent = new Event('scroll', { bubbles: true }); + window.dispatchEvent(scrollEvent); + await page.waitForChanges(); + + // In touch mode, coordinates should exactly match the moving element box! + const popover = host.shadowRoot?.querySelector('le-popover') as HTMLElement; + expect(popover.style.top).toBe('50px'); + expect(popover.style.left).toBe('100px'); + expect(popover.style.width).toBe('100px'); + expect(popover.style.height).toBe('50px'); + }); +}); diff --git a/packages/core/src/components/le-context-menu/le-context-menu.tsx b/packages/core/src/components/le-context-menu/le-context-menu.tsx new file mode 100644 index 0000000..4488aee --- /dev/null +++ b/packages/core/src/components/le-context-menu/le-context-menu.tsx @@ -0,0 +1,447 @@ +import { + Component, + Prop, + State, + Event, + EventEmitter, + Element, + Method, + Watch, + h, + Host, +} from '@stencil/core'; +import type { LeOption } from '../../types/options'; +import { parseOptionInput } from '../../utils/utils'; +import { LONG_PRESS_DURATION, LONG_PRESS_MOVE_THRESHOLD } from '../../utils/constants'; +import type { LeNavigationItemSelectDetail } from '../le-navigation/le-navigation'; + +export interface LeContextMenuSelectDetail { + id: string; + item: LeOption; + originalEvent: MouseEvent | KeyboardEvent; +} + +/** + * Context menu component that displays a vertical navigation menu + * when the user right-clicks or long-presses on its children. + * + * @slot - Trigger content + */ +@Component({ + tag: 'le-context-menu', + styleUrl: 'le-context-menu.css', + shadow: true, +}) +export class LeContextMenu { + @Element() el!: HTMLElement; + + private popoverEl?: HTMLLePopoverElement; + private navigationEl?: HTMLLeNavigationElement; + private containerEl?: HTMLElement; + private activeTriggerEl?: HTMLElement; + + private touchTimeout?: any; + private startX = 0; + private startY = 0; + private isLongPressActive = false; + + private initialTriggerRect?: DOMRect; + private initialCoords = { x: 0, y: 0 }; + private isMenuOpen = false; + + /** + * Whether the context menu is open. + */ + @Prop({ mutable: true, reflect: true }) open: boolean = false; + + /** + * Disables right-click and touch interactions. + */ + @Prop() disabled: boolean = false; + + /** + * List of menu items represented as options. + */ + @Prop() items: LeOption[] | string = []; + + /** + * Whether to show a backdrop behind the menu, lifting the active item. + */ + @Prop() backdrop: boolean = false; + + /** + * Behavior of the menu on page scroll: + * - 'blocked': blocks page scroll + * - 'menu-close': closes the menu automatically on scroll (default) + * - 'fixed-menu': menu scrolls with the page + */ + @Prop() pageScrollBehavior: 'blocked' | 'menu-close' | 'fixed-menu' = 'menu-close'; + + /** + * Position of the menu relative to the trigger. + * If 'mouse', positions next to mouse/touch coords. + */ + @Prop() position: 'top' | 'bottom' | 'left' | 'right' | 'mouse' = 'mouse'; + + /** + * Alignment of the menu relative to the trigger. + */ + @Prop() align: 'start' | 'center' | 'end' = 'start'; + + @State() private coords = { x: 0, y: 0 }; + @State() private lastTriggerType: 'mouse' | 'touch' = 'mouse'; + + /** + * Emitted when a menu item is selected. + */ + @Event({ cancelable: true }) + leContextMenuSelect!: EventEmitter; + + /** + * Emitted when the context menu is closed. + */ + @Event() leContextMenuClose!: EventEmitter; + + @Watch('open') + handleOpenChange(nextOpen: boolean) { + if (nextOpen) { + const triggerEl = this.activeTriggerEl || this.containerEl; + if (triggerEl) { + this.initialTriggerRect = triggerEl.getBoundingClientRect(); + } + this.initialCoords = { ...this.coords }; + + if (this.backdrop && this.activeTriggerEl) { + this.activeTriggerEl.classList.add('le-context-menu-active-item'); + } + } else { + this.initialTriggerRect = undefined; + const el = this.activeTriggerEl; + if (el) { + if (this.backdrop) { + // Remove class after transition completes + setTimeout(() => { + el.classList.remove('le-context-menu-active-item'); + }, 250); + } + this.activeTriggerEl = undefined; + } + } + } + + componentDidUpdate() { + if (this.isMenuOpen) { + void this.popoverEl?.updatePosition(); + } + } + + disconnectedCallback() { + this.removeScrollListener(); + } + + @Method() + async show(x?: number, y?: number) { + if (this.disabled) return; + if (x !== undefined && y !== undefined) { + this.coords = { x, y }; + this.lastTriggerType = 'mouse'; + } else { + if (this.containerEl) { + const rect = this.containerEl.getBoundingClientRect(); + this.coords = { + x: rect.left + rect.width / 2, + y: rect.top + rect.height / 2, + }; + } + this.lastTriggerType = 'touch'; + } + this.open = true; + + requestAnimationFrame(() => { + void this.popoverEl?.updatePosition(); + void this.navigationEl?.focusActiveItem(); + }); + } + + @Method() + async hide() { + this.open = false; + } + + @Method() + async toggle(x?: number, y?: number) { + if (this.open) { + await this.hide(); + } else { + await this.show(x, y); + } + } + + private parseItems(input: LeOption[] | string): LeOption[] { + return parseOptionInput(input, 'le-context-menu', 'items'); + } + + private handlePopoverOpen = () => { + if (this.isMenuOpen) return; + this.isMenuOpen = true; + + if (this.pageScrollBehavior === 'blocked') { + document.body.style.overflow = 'hidden'; + } + + this.addScrollListener(); + + requestAnimationFrame(() => { + void this.navigationEl?.focusActiveItem(); + }); + }; + + private handlePopoverClose = () => { + if (!this.isMenuOpen) return; + this.isMenuOpen = false; + this.open = false; + + if (this.pageScrollBehavior === 'blocked') { + document.body.style.overflow = ''; + } + + this.removeScrollListener(); + + this.leContextMenuClose.emit(); + }; + + private handleScroll = () => { + if (!this.isMenuOpen) return; + + if (this.pageScrollBehavior === 'menu-close') { + void this.hide(); + } else if (this.pageScrollBehavior === 'fixed-menu') { + this.updateCoordsOnScroll(); + } + }; + + private updateCoordsOnScroll() { + if (!this.isMenuOpen) return; + + const triggerEl = this.activeTriggerEl || this.containerEl; + const isTouch = this.lastTriggerType === 'touch'; + const useElementBox = this.position !== 'mouse' || isTouch; + + let x = this.coords.x; + let y = this.coords.y; + let width = 0; + let height = 0; + + if (useElementBox && triggerEl && this.initialTriggerRect) { + const currentRect = triggerEl.getBoundingClientRect(); + x = currentRect.left; + y = currentRect.top; + width = currentRect.width; + height = currentRect.height; + } else if (this.initialTriggerRect && triggerEl) { + const currentRect = triggerEl.getBoundingClientRect(); + const dx = currentRect.left - this.initialTriggerRect.left; + const dy = currentRect.top - this.initialTriggerRect.top; + x = this.initialCoords.x + dx; + y = this.initialCoords.y + dy; + } + + // Direct DOM manipulation to avoid Stencil render cycle on scroll and ensure synchronous layout updates + if (this.popoverEl) { + this.popoverEl.style.left = `${x}px`; + this.popoverEl.style.top = `${y}px`; + this.popoverEl.style.width = `${width}px`; + this.popoverEl.style.height = `${height}px`; + } + + void this.popoverEl?.updatePosition(); + } + + private addScrollListener() { + window.addEventListener('scroll', this.handleScroll, true); + } + + private removeScrollListener() { + window.removeEventListener('scroll', this.handleScroll, true); + } + + private triggerMenu(x: number, y: number, event: MouseEvent | TouchEvent) { + let target = event.target as HTMLElement; + while (target && target.parentElement !== this.el && target !== this.el) { + target = target.parentElement as HTMLElement; + } + + if (target && target !== this.el) { + this.activeTriggerEl = target; + } + + this.lastTriggerType = event.type.startsWith('touch') ? 'touch' : 'mouse'; + this.coords = { x, y }; + this.open = true; + + requestAnimationFrame(() => { + void this.popoverEl?.updatePosition(); + void this.navigationEl?.focusActiveItem(); + }); + } + + private handleTouchStart = (e: TouchEvent) => { + if (this.disabled) return; + const touch = e.touches[0]; + this.startX = touch.clientX; + this.startY = touch.clientY; + this.isLongPressActive = false; + + this.clearTouchTimeout(); + + this.touchTimeout = setTimeout(() => { + this.isLongPressActive = true; + this.triggerMenu(touch.clientX, touch.clientY, e); + }, LONG_PRESS_DURATION); + }; + + private handleTouchMove = (e: TouchEvent) => { + if (!this.touchTimeout) return; + const touch = e.touches[0]; + const dx = touch.clientX - this.startX; + const dy = touch.clientY - this.startY; + + if (Math.hypot(dx, dy) > LONG_PRESS_MOVE_THRESHOLD) { + this.clearTouchTimeout(); + } + }; + + private handleTouchEnd = (e: TouchEvent) => { + this.clearTouchTimeout(); + if (this.isLongPressActive) { + e.preventDefault(); + setTimeout(() => { + this.isLongPressActive = false; + }, 100); + } + }; + + private clearTouchTimeout() { + if (this.touchTimeout) { + clearTimeout(this.touchTimeout); + this.touchTimeout = undefined; + } + } + + private handleContextMenu = (e: MouseEvent) => { + if (this.disabled) return; + e.preventDefault(); + + if (this.isLongPressActive) { + return; + } + + this.triggerMenu(e.clientX, e.clientY, e); + }; + + private handleNavigationSelect = (event: CustomEvent) => { + const { item, id, originalEvent } = event.detail; + + originalEvent.stopPropagation(); + + if (item.disabled) return; + + if (!item.href && !item.action && (item.children?.length || 0) > 0) { + return; + } + + const emitted = this.leContextMenuSelect.emit({ + id: item.id || id, + item, + originalEvent, + }); + + if (!emitted.defaultPrevented) { + this.open = false; + } + }; + + render() { + const parsedItems = this.parseItems(this.items); + + const triggerEl = this.activeTriggerEl || this.containerEl; + const isTouch = this.lastTriggerType === 'touch'; + const useElementBox = this.position !== 'mouse' || isTouch; + + let x = this.coords.x; + let y = this.coords.y; + let width = 0; + let height = 0; + + if (useElementBox && triggerEl && this.initialTriggerRect) { + const currentRect = triggerEl.getBoundingClientRect(); + x = currentRect.left; + y = currentRect.top; + width = currentRect.width; + height = currentRect.height; + } else if (this.initialTriggerRect && triggerEl) { + const currentRect = triggerEl.getBoundingClientRect(); + const dx = currentRect.left - this.initialTriggerRect.left; + const dy = currentRect.top - this.initialTriggerRect.top; + x = this.initialCoords.x + dx; + y = this.initialCoords.y + dy; + } + + const popoverPosition = this.position === 'mouse' ? (isTouch ? 'bottom' : 'auto') : this.position; + const popoverAlign = this.position === 'mouse' ? (isTouch ? 'center' : 'start') : this.align; + + return ( + +
(this.containerEl = el)} + class="context-menu-trigger-zone" + onContextMenu={this.handleContextMenu} + onTouchStart={this.handleTouchStart} + onTouchMove={this.handleTouchMove} + onTouchEnd={this.handleTouchEnd} + > + +
+ + {this.backdrop && ( +
this.hide()} + /> + )} + + (this.popoverEl = el)} + style={{ + left: `${x}px`, + top: `${y}px`, + width: `${width}px`, + height: `${height}px`, + }} + open={this.open} + position={popoverPosition} + align={popoverAlign} + showClose={false} + closeOnClickOutside={true} + closeOnEscape={true} + minWidth="160px" + onLePopoverOpen={this.handlePopoverOpen} + onLePopoverClose={this.handlePopoverClose} + > +
+ {parsedItems.length > 0 && ( + (this.navigationEl = el)} + orientation="vertical" + toggle-position="end" + items={parsedItems} + onLeNavItemSelect={this.handleNavigationSelect} + /> + )} + + + ); + } +} diff --git a/packages/core/src/components/le-context-menu/readme.md b/packages/core/src/components/le-context-menu/readme.md new file mode 100644 index 0000000..027deba --- /dev/null +++ b/packages/core/src/components/le-context-menu/readme.md @@ -0,0 +1,146 @@ +# le-context-menu + + + + + + +## Overview + +Context menu component that displays a vertical navigation menu +when the user right-clicks or long-presses on its children. + +## Properties + +| Property | Attribute | Description | Type | Default | +| -------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | -------------- | +| `align` | `align` | Alignment of the menu relative to the trigger. | `"center" \| "end" \| "start"` | `'start'` | +| `backdrop` | `backdrop` | Whether to show a backdrop behind the menu, lifting the active item. | `boolean` | `false` | +| `disabled` | `disabled` | Disables right-click and touch interactions. | `boolean` | `false` | +| `items` | `items` | List of menu items represented as options. | `LeOption[] \| string` | `[]` | +| `open` | `open` | Whether the context menu is open. | `boolean` | `false` | +| `pageScrollBehavior` | `page-scroll-behavior` | Behavior of the menu on page scroll: - 'blocked': blocks page scroll - 'menu-close': closes the menu automatically on scroll (default) - 'fixed-menu': menu scrolls with the page | `"blocked" \| "fixed-menu" \| "menu-close"` | `'menu-close'` | +| `position` | `position` | Position of the menu relative to the trigger. If 'mouse', positions next to mouse/touch coords. | `"bottom" \| "left" \| "mouse" \| "right" \| "top"` | `'mouse'` | + + +## Events + +| Event | Description | Type | +| --------------------- | ---------------------------------------- | ---------------------------------------- | +| `leContextMenuClose` | Emitted when the context menu is closed. | `CustomEvent` | +| `leContextMenuSelect` | Emitted when a menu item is selected. | `CustomEvent` | + + +## Methods + +### `hide() => Promise` + + + +#### Returns + +Type: `Promise` + + + +### `show(x?: number, y?: number) => Promise` + + + +#### Parameters + +| Name | Type | Description | +| ---- | --------------------- | ----------- | +| `x` | `number \| undefined` | | +| `y` | `number \| undefined` | | + +#### Returns + +Type: `Promise` + + + +### `toggle(x?: number, y?: number) => Promise` + + + +#### Parameters + +| Name | Type | Description | +| ---- | --------------------- | ----------- | +| `x` | `number \| undefined` | | +| `y` | `number \| undefined` | | + +#### Returns + +Type: `Promise` + + + + +## Slots + +| Slot | Description | +| ---- | --------------- | +| | Trigger content | + + +## Dependencies + +### Depends on + +- [le-popover](../le-popover) +- [le-navigation](../le-navigation) + +### Graph +```mermaid +graph TD; + le-context-menu --> le-popover + le-context-menu --> le-navigation + le-navigation --> le-icon + le-navigation --> le-string-input + le-navigation --> le-collapse + le-navigation --> le-popover + le-navigation --> le-bar + le-navigation --> le-component + le-string-input --> le-component + le-string-input --> le-button + le-string-input --> le-icon + le-string-input --> le-slot + le-component --> le-button + le-component --> le-select + le-component --> le-checkbox + le-component --> le-string-input + le-component --> le-popover + le-component --> le-popup + le-button --> le-icon + le-button --> le-visibility + le-button --> le-slot + le-button --> le-component + le-button --> le-tooltip + le-slot --> le-popover + le-slot --> le-button + le-slot --> le-string-input + le-tooltip --> le-component + le-select --> le-component + le-select --> le-dropdown-base + le-select --> le-button + le-dropdown-base --> le-popover + le-checkbox --> le-component + le-checkbox --> le-slot + le-popup --> le-slot + le-popup --> le-button + le-popup --> le-component + le-collapse --> le-component + le-bar --> le-icon + le-bar --> le-overflow-menu + le-overflow-menu --> le-navigation + le-overflow-menu --> le-popover + le-overflow-menu --> le-button + le-overflow-menu --> le-icon + style le-context-menu fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/core/src/components/le-navigation/le-navigation.css b/packages/core/src/components/le-navigation/le-navigation.css index 9ee768a..dd9cd02 100644 --- a/packages/core/src/components/le-navigation/le-navigation.css +++ b/packages/core/src/components/le-navigation/le-navigation.css @@ -84,7 +84,7 @@ padding: 0; display: flex; flex-direction: column; - gap: var(--le-spacing-1); + gap: var(--le-items-gap, var(--le-spacing-1)); } .nav-row { @@ -469,3 +469,20 @@ le-popover::part(content) { padding: var(--le-spacing-1); } + +.nav-check-icon { + flex-shrink: 0; + width: 16px; + height: 16px; + display: inline-flex; + align-items: center; + justify-content: center; + color: currentColor; +} + +.nav-check-spacer { + flex-shrink: 0; + width: 16px; + height: 16px; + display: inline-block; +} diff --git a/packages/core/src/components/le-navigation/le-navigation.tsx b/packages/core/src/components/le-navigation/le-navigation.tsx index ee4cb00..821660f 100644 --- a/packages/core/src/components/le-navigation/le-navigation.tsx +++ b/packages/core/src/components/le-navigation/le-navigation.tsx @@ -209,7 +209,7 @@ export class LeNavigation { private pendingInitialScrollFrame?: number; private renderLabel(label: string | HTMLCollection) { - if (label instanceof HTMLCollection) { + if (typeof HTMLCollection !== 'undefined' && label instanceof HTMLCollection) { const div = document.createElement('div'); Array.from(label).forEach(n => div.appendChild(n.cloneNode(true))); label = div.innerHTML; @@ -224,6 +224,9 @@ export class LeNavigation { if (icon.includes('<')) { return ; } + if (icon.length > 3) { + return ; + } return icon; } @@ -1150,6 +1153,7 @@ export class LeNavigation { const filtered = query ? this.filterTree(items, query, pathPrefix, openFromSearch) : items; const ancestorLeadingSlots = leadingToggleAncestors ?? 0; const levelHasChildren = filtered.some(item => this.getChildItems(item).length > 0); + const hasCheckableItems = filtered.some(item => item.checked !== undefined); const topLevelEndToggles = depth === 0 && this.togglePosition === 'end'; const useLeadingToggleSlot = !topLevelEndToggles && levelHasChildren; const firstEnabledId = filtered.find(item => !item.disabled) @@ -1275,6 +1279,12 @@ export class LeNavigation { {useLeadingToggleSlot && !hasChildren && (
+
+

Context Menu Component

+

Right-click on desktop or long-press on mobile to show context menus. Supports checkmarks and backdrop elevation.

+
+
+ + +
+ Right-Click / Long-Press Here +
+
+
+ +
+ + +
+ Right-Click / Long-Press (Backdrop) +
+
+
+
+
+

Tooltip Component

Hover on desktop or long-press on touch devices. Uses top-layer popover behavior with viewport-aware positioning.

diff --git a/packages/core/src/themes/index.css b/packages/core/src/themes/index.css index 0699902..e86355c 100644 --- a/packages/core/src/themes/index.css +++ b/packages/core/src/themes/index.css @@ -21,3 +21,10 @@ .header-is-shrunk .hidden-on-shrink { display: none; } + +/* Shared backdrop utility class */ +.le-backdrop-background { + background: var(--le-color-backdrop, rgba(0, 0, 0, 0.5)); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); +} diff --git a/packages/core/src/utils/constants.ts b/packages/core/src/utils/constants.ts new file mode 100644 index 0000000..4ecc873 --- /dev/null +++ b/packages/core/src/utils/constants.ts @@ -0,0 +1,13 @@ +/** + * Global constants for touch and pointer interactions. + */ + +/** + * Duration in milliseconds a user must hold down a touch pointer before it is recognized as a long-press. + */ +export const LONG_PRESS_DURATION = 500; + +/** + * Distance in pixels a user's touch pointer can move before cancelling a long-press. + */ +export const LONG_PRESS_MOVE_THRESHOLD = 10;