luxen-ui 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cdn/custom-elements.json +23 -1
- package/cdn/elements/dropdown-item/dropdown-item.js +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
- package/cdn/elements/tabs/tabs.d.ts +5 -0
- package/cdn/elements/tabs/tabs.d.ts.map +1 -1
- package/cdn/elements/tabs/tabs.js.map +1 -1
- package/cdn/elements/tooltip/tooltip.d.ts +1 -1
- package/cdn/elements/tooltip/tooltip.js +1 -1
- package/cdn/elements/tooltip/tooltip.js.map +1 -1
- package/cdn/standalone.css +16 -4
- package/cdn/standalone.js +8 -3
- package/cdn/standalone.js.map +1 -1
- package/cdn/styles/elements/tabs/line.css +4 -4
- package/cdn/styles/preset.css +12 -0
- package/cdn/styles/tokens.css +12 -0
- package/dist/css/elements/tabs/line.css +4 -4
- package/dist/css/preset.css +12 -0
- package/dist/css/tokens.css +12 -0
- package/dist/custom-elements.json +23 -1
- package/dist/elements/dropdown-item/dropdown-item.css +2 -0
- package/dist/elements/tabs/tabs.d.ts +5 -0
- package/dist/elements/tabs/tabs.d.ts.map +1 -1
- package/dist/elements/tabs/tabs.js +5 -0
- package/dist/elements/tooltip/tooltip.css +1 -1
- package/dist/elements/tooltip/tooltip.d.ts +1 -1
- package/dist/elements/tooltip/tooltip.js +1 -1
- package/dist/metadata/index.json +24 -3
- package/dist/metadata/tabs.json +22 -1
- package/dist/metadata/tooltip.json +1 -1
- package/dist/templates/elements/carousel.md +653 -0
- package/dist/templates/elements/disclosure.md +171 -0
- package/dist/templates/elements/divider.md +95 -0
- package/dist/templates/elements/dropdown.md +375 -0
- package/dist/templates/elements/icon.md +104 -0
- package/dist/templates/elements/input-otp.md +191 -0
- package/dist/templates/elements/input-stepper.md +214 -0
- package/dist/templates/elements/kbd.md +55 -0
- package/dist/templates/elements/popover.md +391 -0
- package/dist/templates/elements/rating.md +262 -0
- package/dist/templates/elements/skeleton.md +112 -0
- package/dist/templates/elements/spinner.md +63 -0
- package/dist/templates/elements/stories-viewer.md +102 -0
- package/dist/templates/elements/stories.md +281 -0
- package/dist/templates/elements/story.md +59 -0
- package/dist/templates/elements/tabs.md +168 -0
- package/dist/templates/elements/tooltip.md +397 -0
- package/dist/templates/elements/tree-item.md +71 -0
- package/elements.json +18 -18
- package/package.json +1 -1
package/cdn/custom-elements.json
CHANGED
|
@@ -5477,6 +5477,28 @@
|
|
|
5477
5477
|
"kind": "class",
|
|
5478
5478
|
"description": "",
|
|
5479
5479
|
"name": "Tabs",
|
|
5480
|
+
"cssProperties": [
|
|
5481
|
+
{
|
|
5482
|
+
"description": "`line` variant: color of the active underline that slides under the selected tab.",
|
|
5483
|
+
"name": "--indicator-color",
|
|
5484
|
+
"default": "var(--l-color-text-primary)"
|
|
5485
|
+
},
|
|
5486
|
+
{
|
|
5487
|
+
"description": "`line` variant: thickness of the active underline.",
|
|
5488
|
+
"name": "--indicator-thickness",
|
|
5489
|
+
"default": "2px"
|
|
5490
|
+
},
|
|
5491
|
+
{
|
|
5492
|
+
"description": "`line` variant: color of the static bottom border the tabs sit on.",
|
|
5493
|
+
"name": "--track-color",
|
|
5494
|
+
"default": "var(--l-color-border)"
|
|
5495
|
+
},
|
|
5496
|
+
{
|
|
5497
|
+
"description": "`line` variant: thickness of the static bottom border.",
|
|
5498
|
+
"name": "--track-thickness",
|
|
5499
|
+
"default": "1px"
|
|
5500
|
+
}
|
|
5501
|
+
],
|
|
5480
5502
|
"members": [
|
|
5481
5503
|
{
|
|
5482
5504
|
"kind": "field",
|
|
@@ -5847,7 +5869,7 @@
|
|
|
5847
5869
|
"name": "Tooltip",
|
|
5848
5870
|
"cssProperties": [
|
|
5849
5871
|
{
|
|
5850
|
-
"description": "Background color.
|
|
5872
|
+
"description": "Background color for this tooltip instance. Defaults to the global `--l-tooltip-background-color` token (a neutral inverse surface, dark in light mode / light in dark mode) — override that token to re-skin every tooltip at once.",
|
|
5851
5873
|
"name": "--background-color"
|
|
5852
5874
|
},
|
|
5853
5875
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e}from"../../chunks/lit.js";import{a as t,t as n}from"../../chunks/lit-html.js";import{LuxenElement as r}from"../../shared/luxen-element.js";import{i,t as a}from"../../chunks/decorate.js";import o from"../../shared/styles/host.styles.js";var s=e(`:host{display:block}:host([disabled]){pointer-events:none;opacity:.5}.item{cursor:pointer;color:var(--l-color-text-primary,CanvasText);white-space:nowrap;text-align:start;border-radius:4px;outline:none;align-items:center;gap:8px;padding:.375rem .5rem;font-size:.875rem;line-height:1.5;display:flex}.item:focus-visible{background:var(--l-color-bg-state-hover)}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}.check{flex-shrink:0;width:16px;display:flex}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),c=class extends r{static{this.styles=[o,s]}#e=``;get value(){return this.#e}set value(e){this.#e=e}#t=!1;get disabled(){return this.#t}set disabled(e){this.#t=e}#n=`normal`;get type(){return this.#n}set type(e){this.#n=e}#r=!1;get checked(){return this.#r}set checked(e){this.#r=e}getTextLabel(){return(this.textContent??``).trim()}render(){let e=this.type===`checkbox`;return t`
|
|
1
|
+
import{i as e}from"../../chunks/lit.js";import{a as t,t as n}from"../../chunks/lit-html.js";import{LuxenElement as r}from"../../shared/luxen-element.js";import{i,t as a}from"../../chunks/decorate.js";import o from"../../shared/styles/host.styles.js";var s=e(`:host{display:block}:host([disabled]){pointer-events:none;opacity:.5}.item{box-sizing:border-box;cursor:pointer;min-block-size:36px;color:var(--l-color-text-primary,CanvasText);white-space:nowrap;text-align:start;border-radius:4px;outline:none;align-items:center;gap:8px;padding:.375rem .5rem;font-size:.875rem;line-height:1.5;display:flex}.item:focus-visible{background:var(--l-color-bg-state-hover)}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}.check{flex-shrink:0;width:16px;display:flex}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),c=class extends r{static{this.styles=[o,s]}#e=``;get value(){return this.#e}set value(e){this.#e=e}#t=!1;get disabled(){return this.#t}set disabled(e){this.#t=e}#n=`normal`;get type(){return this.#n}set type(e){this.#n=e}#r=!1;get checked(){return this.#r}set checked(e){this.#r=e}getTextLabel(){return(this.textContent??``).trim()}render(){let e=this.type===`checkbox`;return t`
|
|
2
2
|
<div
|
|
3
3
|
class="item"
|
|
4
4
|
role=${e?`menuitemcheckbox`:`menuitem`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-item.js","names":[],"sources":["../../../src/html/elements/dropdown-item/dropdown-item.css?inline","../../../src/html/elements/dropdown-item/dropdown-item.ts"],"sourcesContent":[":host {\n display: block;\n}\n\n:host([disabled]) {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0.375rem 0.5rem;\n cursor: pointer;\n outline: none;\n border-radius: 4px;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--l-color-text-primary, CanvasText);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n background: var(--l-color-bg-state-hover);\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n:host(:not([checked])) .check svg {\n visibility: hidden;\n}\n\n::slotted([slot='prefix']),\n::slotted([slot='suffix']) {\n display: flex;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1;\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property } from 'lit/decorators.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './dropdown-item.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * A menu item for use inside `<l-dropdown>`.\n *\n * @slot - Label text.\n * @slot prefix - Leading content (e.g. icon).\n * @slot suffix - Trailing content.\n *\n * @cssproperty --color - Text color.\n *\n * @customElement l-dropdown-item\n */\nexport class DropdownItem extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n /** The value associated with this item. */\n @property()\n accessor value = '';\n\n /** Disables the item. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /** The type of item: `normal` or `checkbox`. */\n @property()\n accessor type: 'normal' | 'checkbox' = 'normal';\n\n /** Whether the checkbox item is checked. */\n @property({ type: Boolean, reflect: true })\n accessor checked = false;\n\n /** Returns the text label of this item. */\n getTextLabel(): string {\n return (this.textContent ?? '').trim();\n }\n\n override render() {\n const isCheckbox = this.type === 'checkbox';\n\n return html`\n <div\n class=\"item\"\n role=${isCheckbox ? 'menuitemcheckbox' : 'menuitem'}\n aria-checked=${isCheckbox ? String(this.checked) : nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n tabindex=\"-1\"\n >\n ${isCheckbox\n ? html`\n <span\n class=\"check\"\n aria-hidden=\"true\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n `\n : html` <slot name=\"prefix\"></slot> `}\n <span class=\"label\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n </div>\n `;\n }\n}\n"],"mappings":"0PCMA,IAAM,EAAS,EAAU,
|
|
1
|
+
{"version":3,"file":"dropdown-item.js","names":[],"sources":["../../../src/html/elements/dropdown-item/dropdown-item.css?inline","../../../src/html/elements/dropdown-item/dropdown-item.ts"],"sourcesContent":[":host {\n display: block;\n}\n\n:host([disabled]) {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: 8px;\n box-sizing: border-box;\n min-block-size: 36px;\n padding: 0.375rem 0.5rem;\n cursor: pointer;\n outline: none;\n border-radius: 4px;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--l-color-text-primary, CanvasText);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n background: var(--l-color-bg-state-hover);\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n:host(:not([checked])) .check svg {\n visibility: hidden;\n}\n\n::slotted([slot='prefix']),\n::slotted([slot='suffix']) {\n display: flex;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1;\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property } from 'lit/decorators.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './dropdown-item.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * A menu item for use inside `<l-dropdown>`.\n *\n * @slot - Label text.\n * @slot prefix - Leading content (e.g. icon).\n * @slot suffix - Trailing content.\n *\n * @cssproperty --color - Text color.\n *\n * @customElement l-dropdown-item\n */\nexport class DropdownItem extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n /** The value associated with this item. */\n @property()\n accessor value = '';\n\n /** Disables the item. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /** The type of item: `normal` or `checkbox`. */\n @property()\n accessor type: 'normal' | 'checkbox' = 'normal';\n\n /** Whether the checkbox item is checked. */\n @property({ type: Boolean, reflect: true })\n accessor checked = false;\n\n /** Returns the text label of this item. */\n getTextLabel(): string {\n return (this.textContent ?? '').trim();\n }\n\n override render() {\n const isCheckbox = this.type === 'checkbox';\n\n return html`\n <div\n class=\"item\"\n role=${isCheckbox ? 'menuitemcheckbox' : 'menuitem'}\n aria-checked=${isCheckbox ? String(this.checked) : nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n tabindex=\"-1\"\n >\n ${isCheckbox\n ? html`\n <span\n class=\"check\"\n aria-hidden=\"true\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n `\n : html` <slot name=\"prefix\"></slot> `}\n <span class=\"label\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n </div>\n `;\n }\n}\n"],"mappings":"0PCMA,IAAM,EAAS,EAAU,2pBAAU,CAatB,EAAb,cAAkC,CAAa,oBACpB,CAAC,EAAY,EAAO,IAI5B,OAAR,OAAA,0CAIW,OAAX,UAAA,6CAI8B,aAA9B,MAAA,yCAIU,OAAV,SAAA,yCAGT,cAAuB,CACrB,OAAQ,KAAK,aAAe,IAAI,MAAM,CAGxC,QAAkB,CAChB,IAAM,EAAa,KAAK,OAAS,WAEjC,MAAO,EAAI;;;eAGA,EAAa,mBAAqB,WAAW;uBACrC,EAAa,OAAO,KAAK,QAAQ,CAAG,EAAQ;wBAC3C,KAAK,SAAW,OAAS,EAAQ;;;UAG/C,EACE,CAAI;;;;;;;;;;;;;;;;;;;;cAqBJ,CAAI,gCAAgC;;;;WArD7C,GAAU,CAAA,CAAA,EAAA,UAAA,QAAA,KAAA,IAIV,EAAS,CAAE,KAAM,QAAS,QAAS,GAAM,CAAC,CAAA,CAAA,EAAA,UAAA,WAAA,KAAA,IAI1C,GAAU,CAAA,CAAA,EAAA,UAAA,OAAA,KAAA,IAIV,EAAS,CAAE,KAAM,QAAS,QAAS,GAAM,CAAC,CAAA,CAAA,EAAA,UAAA,UAAA,KAAA"}
|
|
@@ -19,6 +19,11 @@ export type TabsOrientation = 'horizontal' | 'vertical';
|
|
|
19
19
|
*
|
|
20
20
|
* @event change - Fired when the active tab changes. Detail: `{ index: number, name: string | null }`.
|
|
21
21
|
*
|
|
22
|
+
* @cssproperty [--indicator-color=var(--l-color-text-primary)] - `line` variant: color of the active underline that slides under the selected tab.
|
|
23
|
+
* @cssproperty [--indicator-thickness=2px] - `line` variant: thickness of the active underline.
|
|
24
|
+
* @cssproperty [--track-color=var(--l-color-border)] - `line` variant: color of the static bottom border the tabs sit on.
|
|
25
|
+
* @cssproperty [--track-thickness=1px] - `line` variant: thickness of the static bottom border.
|
|
26
|
+
*
|
|
22
27
|
* @customElement l-tabs
|
|
23
28
|
*/
|
|
24
29
|
export declare class Tabs extends LuxenElement {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../src/html/elements/tabs/tabs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,UAAU,CAAC;AAExD
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../src/html/elements/tabs/tabs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,UAAU,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,IAAK,SAAQ,YAAY;IAC3B,gBAAgB;IAIzB,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,OAAO,CAAqB;IAEpC,sBAAsB;IAEtB,OAAO,EAAE,WAAW,CAAU;IAE9B,yCAAyC;IAEzC,KAAK,SAAO;IAEZ,4CAA4C;IAE5C,SAAS,UAAS;IAElB,uBAAuB;IAEvB,WAAW,EAAE,eAAe,CAAgB;IAInC,iBAAiB;IAMjB,oBAAoB;IAKpB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAW9C,OAAO,CAAC,MAAM;IAoDd,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IA2BlB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,QAAQ,CAQd;IAEF,OAAO,CAAC,UAAU,CAkChB;CACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.js","names":[],"sources":["../../../src/html/elements/tabs/tabs.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { uniqueId } from '../../registry.js';\n\nexport type TabsVariant = 'enclosed' | 'line';\nexport type TabsOrientation = 'horizontal' | 'vertical';\n\n/**\n * @summary A tabs component that progressively enhances light DOM markup\n * with ARIA roles, keyboard navigation, and animated indicators.\n *\n * @example\n * ```html\n * <l-tabs variant=\"enclosed\">\n * <div>\n * <button>Tab 1</button>\n * <button>Tab 2</button>\n * </div>\n * <div>Content 1</div>\n * <div>Content 2</div>\n * </l-tabs>\n * ```\n *\n * @event change - Fired when the active tab changes. Detail: `{ index: number, name: string | null }`.\n *\n * @customElement l-tabs\n */\nexport class Tabs extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n private _instanceId = uniqueId('tabs');\n private _tablistEl: HTMLElement | null = null;\n private _tabs: HTMLButtonElement[] = [];\n private _panels: HTMLElement[] = [];\n\n /** Visual variant. */\n @property({ reflect: true })\n variant: TabsVariant = 'line';\n\n /** Index of the active tab (0-based). */\n @property({ reflect: true })\n value = '0';\n\n /** Stretch tabs to fill container width. */\n @property({ type: Boolean, reflect: true, attribute: 'full-width' })\n fullWidth = false;\n\n /** Tab orientation. */\n @property({ reflect: true })\n orientation: TabsOrientation = 'horizontal';\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n // Defer setup to let light DOM children parse\n requestAnimationFrame(() => this._setup());\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._teardown();\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('value') && this._tabs.length) {\n this._selectTab(Number(this.value), false);\n }\n if (changed.has('orientation') && this._tablistEl) {\n this._tablistEl.setAttribute('aria-orientation', this.orientation);\n }\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n const children = Array.from(this.children) as HTMLElement[];\n if (children.length < 2) return;\n\n // First child is the tablist container\n this._tablistEl = children[0];\n this._tablistEl.setAttribute('role', 'tablist');\n this._tablistEl.setAttribute('aria-orientation', this.orientation);\n\n // Buttons inside tablist become tabs\n this._tabs = Array.from(this._tablistEl.querySelectorAll('button'));\n\n // Remaining children become panels\n this._panels = children.slice(1);\n\n const activeIndex = Number(this.value) || 0;\n\n // Enhance tabs\n for (let i = 0; i < this._tabs.length; i++) {\n const tab = this._tabs[i];\n const panel = this._panels[i];\n const name = tab.getAttribute('name') ?? String(i);\n const tabId = `${this._instanceId}-tab-${name}`;\n const panelId = `${this._instanceId}-panel-${name}`;\n\n tab.setAttribute('role', 'tab');\n tab.setAttribute('id', tabId);\n tab.setAttribute('aria-selected', String(i === activeIndex));\n tab.setAttribute('tabindex', i === activeIndex ? '0' : '-1');\n\n if (panel) {\n tab.setAttribute('aria-controls', panelId);\n panel.setAttribute('role', 'tabpanel');\n panel.setAttribute('id', panelId);\n panel.setAttribute('aria-labelledby', tabId);\n panel.setAttribute('tabindex', '0');\n if (i !== activeIndex) {\n panel.hidden = true;\n } else {\n panel.hidden = false;\n }\n }\n }\n\n // Attach listeners\n this._tablistEl.addEventListener('click', this._onClick);\n this._tablistEl.addEventListener('keydown', this._onKeyDown);\n\n // Initial indicator position\n requestAnimationFrame(() => this._updateIndicator());\n }\n\n private _teardown() {\n this._tablistEl?.removeEventListener('click', this._onClick);\n this._tablistEl?.removeEventListener('keydown', this._onKeyDown);\n }\n\n // --- Tab selection ---\n\n private _selectTab(index: number, emitEvent = true) {\n if (index < 0 || index >= this._tabs.length) return;\n\n for (let i = 0; i < this._tabs.length; i++) {\n const tab = this._tabs[i];\n const panel = this._panels[i];\n const isActive = i === index;\n\n tab.setAttribute('aria-selected', String(isActive));\n tab.setAttribute('tabindex', isActive ? '0' : '-1');\n\n if (panel) {\n panel.hidden = !isActive;\n }\n }\n\n this.value = String(index);\n this._updateIndicator();\n\n if (emitEvent) {\n const name = this._tabs[index]?.getAttribute('name') ?? null;\n this.emit('change', { detail: { index, name } });\n }\n }\n\n // --- Indicator ---\n\n private _updateIndicator() {\n if (!this._tablistEl) return;\n const activeTab = this._tabs[Number(this.value)];\n if (!activeTab) return;\n\n const isVertical = this.orientation === 'vertical';\n\n if (isVertical) {\n this._tablistEl.style.setProperty('--_indicator-top', `${activeTab.offsetTop}px`);\n this._tablistEl.style.setProperty('--_indicator-height', `${activeTab.offsetHeight}px`);\n } else {\n this._tablistEl.style.setProperty('--_indicator-left', `${activeTab.offsetLeft}px`);\n this._tablistEl.style.setProperty('--_indicator-width', `${activeTab.offsetWidth}px`);\n }\n }\n\n // --- Event handlers ---\n\n private _onClick = (e: Event) => {\n const tab = (e.target as HTMLElement).closest<HTMLButtonElement>('[role=\"tab\"]');\n if (!tab) return;\n const index = this._tabs.indexOf(tab);\n if (index >= 0) {\n this._selectTab(index);\n tab.focus();\n }\n };\n\n private _onKeyDown = (e: KeyboardEvent) => {\n const target = e.target as HTMLElement;\n if (target.getAttribute('role') !== 'tab') return;\n\n const isHorizontal = this.orientation === 'horizontal';\n const prevKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';\n const nextKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';\n\n let index = this._tabs.indexOf(target as HTMLButtonElement);\n\n switch (e.key) {\n case nextKey:\n e.preventDefault();\n index = (index + 1) % this._tabs.length;\n this._selectTab(index);\n this._tabs[index].focus();\n break;\n case prevKey:\n e.preventDefault();\n index = (index - 1 + this._tabs.length) % this._tabs.length;\n this._selectTab(index);\n this._tabs[index].focus();\n break;\n case 'Home':\n e.preventDefault();\n this._selectTab(0);\n this._tabs[0].focus();\n break;\n case 'End':\n e.preventDefault();\n this._selectTab(this._tabs.length - 1);\n this._tabs[this._tabs.length - 1].focus();\n break;\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tabs.js","names":[],"sources":["../../../src/html/elements/tabs/tabs.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { uniqueId } from '../../registry.js';\n\nexport type TabsVariant = 'enclosed' | 'line';\nexport type TabsOrientation = 'horizontal' | 'vertical';\n\n/**\n * @summary A tabs component that progressively enhances light DOM markup\n * with ARIA roles, keyboard navigation, and animated indicators.\n *\n * @example\n * ```html\n * <l-tabs variant=\"enclosed\">\n * <div>\n * <button>Tab 1</button>\n * <button>Tab 2</button>\n * </div>\n * <div>Content 1</div>\n * <div>Content 2</div>\n * </l-tabs>\n * ```\n *\n * @event change - Fired when the active tab changes. Detail: `{ index: number, name: string | null }`.\n *\n * @cssproperty [--indicator-color=var(--l-color-text-primary)] - `line` variant: color of the active underline that slides under the selected tab.\n * @cssproperty [--indicator-thickness=2px] - `line` variant: thickness of the active underline.\n * @cssproperty [--track-color=var(--l-color-border)] - `line` variant: color of the static bottom border the tabs sit on.\n * @cssproperty [--track-thickness=1px] - `line` variant: thickness of the static bottom border.\n *\n * @customElement l-tabs\n */\nexport class Tabs extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n private _instanceId = uniqueId('tabs');\n private _tablistEl: HTMLElement | null = null;\n private _tabs: HTMLButtonElement[] = [];\n private _panels: HTMLElement[] = [];\n\n /** Visual variant. */\n @property({ reflect: true })\n variant: TabsVariant = 'line';\n\n /** Index of the active tab (0-based). */\n @property({ reflect: true })\n value = '0';\n\n /** Stretch tabs to fill container width. */\n @property({ type: Boolean, reflect: true, attribute: 'full-width' })\n fullWidth = false;\n\n /** Tab orientation. */\n @property({ reflect: true })\n orientation: TabsOrientation = 'horizontal';\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n // Defer setup to let light DOM children parse\n requestAnimationFrame(() => this._setup());\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._teardown();\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('value') && this._tabs.length) {\n this._selectTab(Number(this.value), false);\n }\n if (changed.has('orientation') && this._tablistEl) {\n this._tablistEl.setAttribute('aria-orientation', this.orientation);\n }\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n const children = Array.from(this.children) as HTMLElement[];\n if (children.length < 2) return;\n\n // First child is the tablist container\n this._tablistEl = children[0];\n this._tablistEl.setAttribute('role', 'tablist');\n this._tablistEl.setAttribute('aria-orientation', this.orientation);\n\n // Buttons inside tablist become tabs\n this._tabs = Array.from(this._tablistEl.querySelectorAll('button'));\n\n // Remaining children become panels\n this._panels = children.slice(1);\n\n const activeIndex = Number(this.value) || 0;\n\n // Enhance tabs\n for (let i = 0; i < this._tabs.length; i++) {\n const tab = this._tabs[i];\n const panel = this._panels[i];\n const name = tab.getAttribute('name') ?? String(i);\n const tabId = `${this._instanceId}-tab-${name}`;\n const panelId = `${this._instanceId}-panel-${name}`;\n\n tab.setAttribute('role', 'tab');\n tab.setAttribute('id', tabId);\n tab.setAttribute('aria-selected', String(i === activeIndex));\n tab.setAttribute('tabindex', i === activeIndex ? '0' : '-1');\n\n if (panel) {\n tab.setAttribute('aria-controls', panelId);\n panel.setAttribute('role', 'tabpanel');\n panel.setAttribute('id', panelId);\n panel.setAttribute('aria-labelledby', tabId);\n panel.setAttribute('tabindex', '0');\n if (i !== activeIndex) {\n panel.hidden = true;\n } else {\n panel.hidden = false;\n }\n }\n }\n\n // Attach listeners\n this._tablistEl.addEventListener('click', this._onClick);\n this._tablistEl.addEventListener('keydown', this._onKeyDown);\n\n // Initial indicator position\n requestAnimationFrame(() => this._updateIndicator());\n }\n\n private _teardown() {\n this._tablistEl?.removeEventListener('click', this._onClick);\n this._tablistEl?.removeEventListener('keydown', this._onKeyDown);\n }\n\n // --- Tab selection ---\n\n private _selectTab(index: number, emitEvent = true) {\n if (index < 0 || index >= this._tabs.length) return;\n\n for (let i = 0; i < this._tabs.length; i++) {\n const tab = this._tabs[i];\n const panel = this._panels[i];\n const isActive = i === index;\n\n tab.setAttribute('aria-selected', String(isActive));\n tab.setAttribute('tabindex', isActive ? '0' : '-1');\n\n if (panel) {\n panel.hidden = !isActive;\n }\n }\n\n this.value = String(index);\n this._updateIndicator();\n\n if (emitEvent) {\n const name = this._tabs[index]?.getAttribute('name') ?? null;\n this.emit('change', { detail: { index, name } });\n }\n }\n\n // --- Indicator ---\n\n private _updateIndicator() {\n if (!this._tablistEl) return;\n const activeTab = this._tabs[Number(this.value)];\n if (!activeTab) return;\n\n const isVertical = this.orientation === 'vertical';\n\n if (isVertical) {\n this._tablistEl.style.setProperty('--_indicator-top', `${activeTab.offsetTop}px`);\n this._tablistEl.style.setProperty('--_indicator-height', `${activeTab.offsetHeight}px`);\n } else {\n this._tablistEl.style.setProperty('--_indicator-left', `${activeTab.offsetLeft}px`);\n this._tablistEl.style.setProperty('--_indicator-width', `${activeTab.offsetWidth}px`);\n }\n }\n\n // --- Event handlers ---\n\n private _onClick = (e: Event) => {\n const tab = (e.target as HTMLElement).closest<HTMLButtonElement>('[role=\"tab\"]');\n if (!tab) return;\n const index = this._tabs.indexOf(tab);\n if (index >= 0) {\n this._selectTab(index);\n tab.focus();\n }\n };\n\n private _onKeyDown = (e: KeyboardEvent) => {\n const target = e.target as HTMLElement;\n if (target.getAttribute('role') !== 'tab') return;\n\n const isHorizontal = this.orientation === 'horizontal';\n const prevKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';\n const nextKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';\n\n let index = this._tabs.indexOf(target as HTMLButtonElement);\n\n switch (e.key) {\n case nextKey:\n e.preventDefault();\n index = (index + 1) % this._tabs.length;\n this._selectTab(index);\n this._tabs[index].focus();\n break;\n case prevKey:\n e.preventDefault();\n index = (index - 1 + this._tabs.length) % this._tabs.length;\n this._selectTab(index);\n this._tabs[index].focus();\n break;\n case 'Home':\n e.preventDefault();\n this._selectTab(0);\n this._tabs[0].focus();\n break;\n case 'End':\n e.preventDefault();\n this._selectTab(this._tabs.length - 1);\n this._tabs[this._tabs.length - 1].focus();\n break;\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ import { Placement } from '@floating-ui/dom';
|
|
|
10
10
|
* @csspart body - The tooltip popover container.
|
|
11
11
|
* @csspart arrow - The directional arrow element.
|
|
12
12
|
*
|
|
13
|
-
* @cssproperty --background-color - Background color.
|
|
13
|
+
* @cssproperty --background-color - Background color for this tooltip instance. Defaults to the global `--l-tooltip-background-color` token (a neutral inverse surface, dark in light mode / light in dark mode) — override that token to re-skin every tooltip at once.
|
|
14
14
|
* @cssproperty --text-color - Text color. If unset, auto-derived from `--background-color` luminance.
|
|
15
15
|
* @cssproperty --border-radius - Border radius. Default `4px`.
|
|
16
16
|
* @cssproperty --max-width - Maximum width. Default `180px`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{uniqueId as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{a as n,t as r}from"../../chunks/lit-html.js";import{LuxenElement as i}from"../../shared/luxen-element.js";import{i as a,t as o}from"../../chunks/decorate.js";import s from"../../shared/styles/host.styles.js";import{PopoverController as c}from"../../shared/controllers/popover.js";var l=t(`:host{--background-color:var(--l-
|
|
1
|
+
import{uniqueId as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{a as n,t as r}from"../../chunks/lit-html.js";import{LuxenElement as i}from"../../shared/luxen-element.js";import{i as a,t as o}from"../../chunks/decorate.js";import s from"../../shared/styles/host.styles.js";import{PopoverController as c}from"../../shared/controllers/popover.js";var l=t(`:host{--background-color:var(--l-tooltip-background-color,var(--lightningcss-light,#1f2937)var(--lightningcss-dark,#f9fafb));--border-radius:4px;--max-width:180px;--arrow-size:6px;--show-duration:.15s;--hide-duration:.15s;display:contents}[popover]{inset:unset;box-sizing:border-box;width:max-content;max-width:var(--max-width);border-radius:var(--border-radius);background:var(--background-color);color:var(--text-color,oklch(from var(--background-color) calc(.59 - .41 * sign(l - .5)) 0 0));filter:drop-shadow(0 1px 2px #00000029);pointer-events:none;border:0;padding:4px 8px;font-size:.8125rem;line-height:1.4;overflow:visible}@supports (color:contrast-color(red vs black, white)){[popover]{color:var(--text-color,contrast-color(var(--background-color) vs #111827, #fff to AA))}}i{width:var(--arrow-size);height:var(--arrow-size);background:var(--background-color);display:block;position:absolute;transform:rotate(45deg)}`),u=class extends i{constructor(...t){super(...t),this._tooltipId=e(`tooltip`),this._floating=new c(this,{getTriggerElement:()=>this._trigger,getFloatingElement:()=>this._popover,getArrowElement:()=>this._arrowEl}),this.#e=``,this.#t=`top`,this.#n=8,this.#r=!1,this.#i=!1,this.#a=`hover focus`,this._onPointerEnter=()=>{this._hasTrigger(`hover`)&&(this._floating.cleanupSafePolygon(),this.show())},this._onPointerLeave=e=>{!this._hasTrigger(`hover`)||!this.open||this._floating.handlePointerLeave(e,()=>this.hide())},this._onFocusIn=()=>{this._hasTrigger(`focus`)&&this.show()},this._onFocusOut=()=>{this._hasTrigger(`focus`)&&this.hide()},this._onClick=()=>{this._hasTrigger(`click`)&&this.toggle()},this._onKeyDown=e=>{this.open&&e.key===`Escape`&&(e.stopPropagation(),this.hide())}}static{this.styles=[s,l]}#e;get for(){return this.#e}set for(e){this.#e=e}#t;get placement(){return this.#t}set placement(e){this.#t=e}#n;get distance(){return this.#n}set distance(e){this.#n=e}#r;get open(){return this.#r}set open(e){this.#r=e}#i;get withoutArrow(){return this.#i}set withoutArrow(e){this.#i=e}#a;get trigger(){return this.#a}set trigger(e){this.#a=e}_hasTrigger(e){return this.trigger.split(` `).includes(e)}get _trigger(){return this.for?this.getRootNode().getElementById(this.for):null}get _popover(){return this.shadowRoot.querySelector(`[popover]`)}get _arrowEl(){return this.withoutArrow?null:this.shadowRoot.querySelector(`i`)}_getDuration(e){let t=parseFloat(getComputedStyle(this).getPropertyValue(e));return Number.isNaN(t)?150:t}connectedCallback(){super.connectedCallback(),requestAnimationFrame(()=>this._addTriggerListeners())}disconnectedCallback(){super.disconnectedCallback(),this._removeTriggerListeners()}updated(e){e.has(`open`)&&this._handleOpenChange(),e.has(`for`)&&(this._removeTriggerListeners(e.get(`for`)),this._addTriggerListeners())}show(){this.open||=!0}hide(){this.open&&=!1}toggle(){this.open=!this.open}async _handleOpenChange(){let e=this._popover;if(!e)return;let t={placement:this.placement,distance:this.distance};if(this.open){if(e.showPopover(),await this._floating.updatePosition(t),!this.open)return;await this._floating.animateShow(e,this._getDuration(`--show-duration`)),this._floating.startPositioning(t),this._trigger?.setAttribute(`aria-describedby`,this._tooltipId)}else this._floating.stopPositioning(),this._floating.cleanupSafePolygon(),this._trigger?.removeAttribute(`aria-describedby`),await this._floating.animateHide(e,this._getDuration(`--hide-duration`)),e.matches(`:popover-open`)&&e.hidePopover()}_addTriggerListeners(){this._floating.addTriggerListeners({onPointerEnter:this._onPointerEnter,onPointerLeave:this._onPointerLeave,onFocusIn:this._onFocusIn,onFocusOut:this._onFocusOut,onClick:this._onClick,onKeyDown:this._onKeyDown})}_removeTriggerListeners(e){let t=e?this.getRootNode().getElementById(e):void 0;this._floating.removeTriggerListeners(t)}render(){return n`
|
|
2
2
|
<div
|
|
3
3
|
id=${this._tooltipId}
|
|
4
4
|
popover="manual"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.js","names":[],"sources":["../../../src/html/elements/tooltip/tooltip.css?inline","../../../src/html/elements/tooltip/tooltip.ts"],"sourcesContent":[":host {\n --background-color: var(--l-color-bg-fill-brand, light-dark(#1f2937, #f9fafb));\n --border-radius: 4px;\n --max-width: 180px;\n --arrow-size: 6px;\n --show-duration: 150ms;\n --hide-duration: 150ms;\n\n display: contents;\n}\n\n[popover] {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n max-width: var(--max-width);\n padding: 4px 8px;\n border: 0;\n border-radius: var(--border-radius);\n background: var(--background-color);\n color: var(\n --text-color,\n oklch(from var(--background-color) calc(0.59 - 0.41 * sign(l - 0.5)) 0 0)\n );\n font-size: 0.8125rem;\n line-height: 1.4;\n filter: drop-shadow(0 1px 2px rgb(0 0 0 / 16%));\n pointer-events: none;\n}\n\n@supports (color: contrast-color(red vs black, white)) {\n [popover] {\n color: var(--text-color, contrast-color(var(--background-color) vs #111827, #fff to AA));\n }\n}\n\ni {\n position: absolute;\n display: block;\n width: var(--arrow-size);\n height: var(--arrow-size);\n background: var(--background-color);\n transform: rotate(45deg);\n}\n","import { html, nothing, unsafeCSS, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { uniqueId } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './tooltip.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * @summary A tooltip that displays contextual text on hover or focus.\n * @customElement l-tooltip\n *\n * @slot - Tooltip content (text or rich HTML).\n *\n * @csspart body - The tooltip popover container.\n * @csspart arrow - The directional arrow element.\n *\n * @cssproperty --background-color - Background color. Default: dark in light mode, light in dark mode.\n * @cssproperty --text-color - Text color. If unset, auto-derived from `--background-color` luminance.\n * @cssproperty --border-radius - Border radius. Default `4px`.\n * @cssproperty --max-width - Maximum width. Default `180px`.\n * @cssproperty --arrow-size - Arrow size. Default `6px`.\n * @cssproperty --show-duration - Show animation duration. Default `150ms`.\n * @cssproperty --hide-duration - Hide animation duration. Default `150ms`.\n */\nexport class Tooltip extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _tooltipId = uniqueId('tooltip');\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._trigger,\n getFloatingElement: () => this._popover,\n getArrowElement: () => this._arrowEl,\n });\n\n /** The HTML id of the element triggering the tooltip. */\n @property()\n accessor for = '';\n\n /** The preferred placement of the tooltip. */\n @property()\n accessor placement: Placement = 'top';\n\n /** The distance in pixels from the target element. */\n @property({ type: Number })\n accessor distance = 8;\n\n /** Whether or not the tooltip is visible. */\n @property({ type: Boolean, reflect: true })\n accessor open = false;\n\n /** Hide the directional arrow. */\n @property({ type: Boolean, reflect: true, attribute: 'without-arrow' })\n accessor withoutArrow = false;\n\n /** Space-separated list of trigger modes: `hover`, `focus`, `click`, `manual`. */\n @property()\n accessor trigger = 'hover focus';\n\n private _hasTrigger(type: string) {\n return this.trigger.split(' ').includes(type);\n }\n\n private get _trigger(): HTMLElement | null {\n return this.for ? (this.getRootNode() as Document | ShadowRoot).getElementById(this.for) : null;\n }\n\n private get _popover(): HTMLElement {\n return this.shadowRoot!.querySelector('[popover]')!;\n }\n\n private get _arrowEl(): HTMLElement | null {\n return this.withoutArrow ? null : this.shadowRoot!.querySelector('i');\n }\n\n private _getDuration(prop: '--show-duration' | '--hide-duration'): number {\n const parsed = parseFloat(getComputedStyle(this).getPropertyValue(prop));\n return Number.isNaN(parsed) ? 150 : parsed;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n requestAnimationFrame(() => this._addTriggerListeners());\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._removeTriggerListeners();\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n void this._handleOpenChange();\n }\n if (changed.has('for')) {\n this._removeTriggerListeners(changed.get('for') as string);\n this._addTriggerListeners();\n }\n }\n\n show() {\n if (!this.open) this.open = true;\n }\n\n hide() {\n if (this.open) this.open = false;\n }\n\n toggle() {\n this.open = !this.open;\n }\n\n private async _handleOpenChange() {\n const popover = this._popover;\n if (!popover) return;\n\n const posOpts = { placement: this.placement, distance: this.distance };\n\n if (this.open) {\n popover.showPopover();\n await this._floating.updatePosition(posOpts);\n if (!this.open) return;\n await this._floating.animateShow(popover, this._getDuration('--show-duration'));\n this._floating.startPositioning(posOpts);\n this._trigger?.setAttribute('aria-describedby', this._tooltipId);\n } else {\n this._floating.stopPositioning();\n this._floating.cleanupSafePolygon();\n this._trigger?.removeAttribute('aria-describedby');\n await this._floating.animateHide(popover, this._getDuration('--hide-duration'));\n if (popover.matches(':popover-open')) popover.hidePopover();\n }\n }\n\n // --- Trigger event handlers ---\n\n private _onPointerEnter = () => {\n if (!this._hasTrigger('hover')) return;\n this._floating.cleanupSafePolygon();\n this.show();\n };\n\n private _onPointerLeave = (e: PointerEvent) => {\n if (!this._hasTrigger('hover') || !this.open) return;\n this._floating.handlePointerLeave(e, () => this.hide());\n };\n\n private _onFocusIn = () => {\n if (this._hasTrigger('focus')) this.show();\n };\n private _onFocusOut = () => {\n if (this._hasTrigger('focus')) this.hide();\n };\n private _onClick = () => {\n if (this._hasTrigger('click')) this.toggle();\n };\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (this.open && e.key === 'Escape') {\n e.stopPropagation();\n this.hide();\n }\n };\n\n private _addTriggerListeners() {\n this._floating.addTriggerListeners({\n onPointerEnter: this._onPointerEnter,\n onPointerLeave: this._onPointerLeave,\n onFocusIn: this._onFocusIn,\n onFocusOut: this._onFocusOut,\n onClick: this._onClick,\n onKeyDown: this._onKeyDown,\n });\n }\n\n private _removeTriggerListeners(forId?: string) {\n const trigger = forId\n ? (this.getRootNode() as Document | ShadowRoot).getElementById(forId)\n : undefined;\n this._floating.removeTriggerListeners(trigger);\n }\n\n override render() {\n return html`\n <div\n id=${this._tooltipId}\n popover=\"manual\"\n role=\"tooltip\"\n part=\"body\"\n >\n ${this.withoutArrow\n ? nothing\n : html`\n <i\n part=\"arrow\"\n role=\"presentation\"\n ></i>\n `}\n <slot></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tooltip.js","names":[],"sources":["../../../src/html/elements/tooltip/tooltip.css?inline","../../../src/html/elements/tooltip/tooltip.ts"],"sourcesContent":[":host {\n --background-color: var(--l-tooltip-background-color, light-dark(#1f2937, #f9fafb));\n --border-radius: 4px;\n --max-width: 180px;\n --arrow-size: 6px;\n --show-duration: 150ms;\n --hide-duration: 150ms;\n\n display: contents;\n}\n\n[popover] {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n max-width: var(--max-width);\n padding: 4px 8px;\n border: 0;\n border-radius: var(--border-radius);\n background: var(--background-color);\n color: var(\n --text-color,\n oklch(from var(--background-color) calc(0.59 - 0.41 * sign(l - 0.5)) 0 0)\n );\n font-size: 0.8125rem;\n line-height: 1.4;\n filter: drop-shadow(0 1px 2px rgb(0 0 0 / 16%));\n pointer-events: none;\n}\n\n@supports (color: contrast-color(red vs black, white)) {\n [popover] {\n color: var(--text-color, contrast-color(var(--background-color) vs #111827, #fff to AA));\n }\n}\n\ni {\n position: absolute;\n display: block;\n width: var(--arrow-size);\n height: var(--arrow-size);\n background: var(--background-color);\n transform: rotate(45deg);\n}\n","import { html, nothing, unsafeCSS, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { uniqueId } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './tooltip.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * @summary A tooltip that displays contextual text on hover or focus.\n * @customElement l-tooltip\n *\n * @slot - Tooltip content (text or rich HTML).\n *\n * @csspart body - The tooltip popover container.\n * @csspart arrow - The directional arrow element.\n *\n * @cssproperty --background-color - Background color for this tooltip instance. Defaults to the global `--l-tooltip-background-color` token (a neutral inverse surface, dark in light mode / light in dark mode) — override that token to re-skin every tooltip at once.\n * @cssproperty --text-color - Text color. If unset, auto-derived from `--background-color` luminance.\n * @cssproperty --border-radius - Border radius. Default `4px`.\n * @cssproperty --max-width - Maximum width. Default `180px`.\n * @cssproperty --arrow-size - Arrow size. Default `6px`.\n * @cssproperty --show-duration - Show animation duration. Default `150ms`.\n * @cssproperty --hide-duration - Hide animation duration. Default `150ms`.\n */\nexport class Tooltip extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _tooltipId = uniqueId('tooltip');\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._trigger,\n getFloatingElement: () => this._popover,\n getArrowElement: () => this._arrowEl,\n });\n\n /** The HTML id of the element triggering the tooltip. */\n @property()\n accessor for = '';\n\n /** The preferred placement of the tooltip. */\n @property()\n accessor placement: Placement = 'top';\n\n /** The distance in pixels from the target element. */\n @property({ type: Number })\n accessor distance = 8;\n\n /** Whether or not the tooltip is visible. */\n @property({ type: Boolean, reflect: true })\n accessor open = false;\n\n /** Hide the directional arrow. */\n @property({ type: Boolean, reflect: true, attribute: 'without-arrow' })\n accessor withoutArrow = false;\n\n /** Space-separated list of trigger modes: `hover`, `focus`, `click`, `manual`. */\n @property()\n accessor trigger = 'hover focus';\n\n private _hasTrigger(type: string) {\n return this.trigger.split(' ').includes(type);\n }\n\n private get _trigger(): HTMLElement | null {\n return this.for ? (this.getRootNode() as Document | ShadowRoot).getElementById(this.for) : null;\n }\n\n private get _popover(): HTMLElement {\n return this.shadowRoot!.querySelector('[popover]')!;\n }\n\n private get _arrowEl(): HTMLElement | null {\n return this.withoutArrow ? null : this.shadowRoot!.querySelector('i');\n }\n\n private _getDuration(prop: '--show-duration' | '--hide-duration'): number {\n const parsed = parseFloat(getComputedStyle(this).getPropertyValue(prop));\n return Number.isNaN(parsed) ? 150 : parsed;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n requestAnimationFrame(() => this._addTriggerListeners());\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._removeTriggerListeners();\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n void this._handleOpenChange();\n }\n if (changed.has('for')) {\n this._removeTriggerListeners(changed.get('for') as string);\n this._addTriggerListeners();\n }\n }\n\n show() {\n if (!this.open) this.open = true;\n }\n\n hide() {\n if (this.open) this.open = false;\n }\n\n toggle() {\n this.open = !this.open;\n }\n\n private async _handleOpenChange() {\n const popover = this._popover;\n if (!popover) return;\n\n const posOpts = { placement: this.placement, distance: this.distance };\n\n if (this.open) {\n popover.showPopover();\n await this._floating.updatePosition(posOpts);\n if (!this.open) return;\n await this._floating.animateShow(popover, this._getDuration('--show-duration'));\n this._floating.startPositioning(posOpts);\n this._trigger?.setAttribute('aria-describedby', this._tooltipId);\n } else {\n this._floating.stopPositioning();\n this._floating.cleanupSafePolygon();\n this._trigger?.removeAttribute('aria-describedby');\n await this._floating.animateHide(popover, this._getDuration('--hide-duration'));\n if (popover.matches(':popover-open')) popover.hidePopover();\n }\n }\n\n // --- Trigger event handlers ---\n\n private _onPointerEnter = () => {\n if (!this._hasTrigger('hover')) return;\n this._floating.cleanupSafePolygon();\n this.show();\n };\n\n private _onPointerLeave = (e: PointerEvent) => {\n if (!this._hasTrigger('hover') || !this.open) return;\n this._floating.handlePointerLeave(e, () => this.hide());\n };\n\n private _onFocusIn = () => {\n if (this._hasTrigger('focus')) this.show();\n };\n private _onFocusOut = () => {\n if (this._hasTrigger('focus')) this.hide();\n };\n private _onClick = () => {\n if (this._hasTrigger('click')) this.toggle();\n };\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (this.open && e.key === 'Escape') {\n e.stopPropagation();\n this.hide();\n }\n };\n\n private _addTriggerListeners() {\n this._floating.addTriggerListeners({\n onPointerEnter: this._onPointerEnter,\n onPointerLeave: this._onPointerLeave,\n onFocusIn: this._onFocusIn,\n onFocusOut: this._onFocusOut,\n onClick: this._onClick,\n onKeyDown: this._onKeyDown,\n });\n }\n\n private _removeTriggerListeners(forId?: string) {\n const trigger = forId\n ? (this.getRootNode() as Document | ShadowRoot).getElementById(forId)\n : undefined;\n this._floating.removeTriggerListeners(trigger);\n }\n\n override render() {\n return html`\n <div\n id=${this._tooltipId}\n popover=\"manual\"\n role=\"tooltip\"\n part=\"body\"\n >\n ${this.withoutArrow\n ? nothing\n : html`\n <i\n part=\"arrow\"\n role=\"presentation\"\n ></i>\n `}\n <slot></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
|
package/cdn/standalone.css
CHANGED
|
@@ -1030,6 +1030,18 @@ In dark mode, mixes the base color with black (default 15% black).
|
|
|
1030
1030
|
--l-form-field-choice-gap: var(--l-spacing-2);
|
|
1031
1031
|
}
|
|
1032
1032
|
|
|
1033
|
+
/* Tooltip component token (--l-tooltip-background-color). */
|
|
1034
|
+
|
|
1035
|
+
/* Luxen design-tokens — tooltip component token.
|
|
1036
|
+
Bundled in the Luxen preset via `luxen-ui/css/tokens`. A neutral inverse
|
|
1037
|
+
surface, decoupled from the brand fill so re-theming primary does not
|
|
1038
|
+
recolor tooltips. */
|
|
1039
|
+
|
|
1040
|
+
:root {
|
|
1041
|
+
/* Tooltip surface color — dark in light mode, light in dark mode. Neutral by design; not tied to --l-color-bg-fill-brand, so changing the brand color does not recolor tooltips. Override globally to re-skin every tooltip, or use the per-instance --background-color custom property on a single `<l-tooltip>`. */
|
|
1042
|
+
--l-tooltip-background-color: light-dark(var(--l-color-gray-900), var(--l-color-gray-50));
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1033
1045
|
/* avatar.css */
|
|
1034
1046
|
@layer components {
|
|
1035
1047
|
/*
|
|
@@ -3759,13 +3771,13 @@ In dark mode, mixes the base color with black (default 15% black).
|
|
|
3759
3771
|
|
|
3760
3772
|
l-tabs[variant='line'] {
|
|
3761
3773
|
& [role='tablist'] {
|
|
3762
|
-
border-bottom: 1px solid var(--l-color-border);
|
|
3774
|
+
border-bottom: var(--track-thickness, 1px) solid var(--track-color, var(--l-color-border));
|
|
3763
3775
|
gap: 0;
|
|
3764
3776
|
|
|
3765
3777
|
&::before {
|
|
3766
|
-
bottom: -1px;
|
|
3767
|
-
height: 2px;
|
|
3768
|
-
background: var(--l-color-text-primary);
|
|
3778
|
+
bottom: calc(-1 * var(--track-thickness, 1px));
|
|
3779
|
+
height: var(--indicator-thickness, 2px);
|
|
3780
|
+
background: var(--indicator-color, var(--l-color-text-primary));
|
|
3769
3781
|
border-radius: var(--l-radius-full);
|
|
3770
3782
|
}
|
|
3771
3783
|
}
|
package/cdn/standalone.js
CHANGED
|
@@ -5082,7 +5082,7 @@ __decorate([n$1({ attribute: "min-width" })], Dropdown.prototype, "minWidth", nu
|
|
|
5082
5082
|
define("dropdown", Dropdown);
|
|
5083
5083
|
//#endregion
|
|
5084
5084
|
//#region src/html/elements/dropdown-item/dropdown-item.ts
|
|
5085
|
-
var styles$10 = r$6(":host {\n display: block;\n}\n\n:host([disabled]) {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0.375rem 0.5rem;\n cursor: pointer;\n outline: none;\n border-radius: 4px;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--l-color-text-primary, CanvasText);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n background: var(--l-color-bg-state-hover);\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n:host(:not([checked])) .check svg {\n visibility: hidden;\n}\n\n::slotted([slot='prefix']),\n::slotted([slot='suffix']) {\n display: flex;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1;\n}\n");
|
|
5085
|
+
var styles$10 = r$6(":host {\n display: block;\n}\n\n:host([disabled]) {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: 8px;\n box-sizing: border-box;\n min-block-size: 36px;\n padding: 0.375rem 0.5rem;\n cursor: pointer;\n outline: none;\n border-radius: 4px;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--l-color-text-primary, CanvasText);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n background: var(--l-color-bg-state-hover);\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n:host(:not([checked])) .check svg {\n visibility: hidden;\n}\n\n::slotted([slot='prefix']),\n::slotted([slot='suffix']) {\n display: flex;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1;\n}\n");
|
|
5086
5086
|
/**
|
|
5087
5087
|
* A menu item for use inside `<l-dropdown>`.
|
|
5088
5088
|
*
|
|
@@ -31486,6 +31486,11 @@ define("sticky-bar", StickyBar);
|
|
|
31486
31486
|
*
|
|
31487
31487
|
* @event change - Fired when the active tab changes. Detail: `{ index: number, name: string | null }`.
|
|
31488
31488
|
*
|
|
31489
|
+
* @cssproperty [--indicator-color=var(--l-color-text-primary)] - `line` variant: color of the active underline that slides under the selected tab.
|
|
31490
|
+
* @cssproperty [--indicator-thickness=2px] - `line` variant: thickness of the active underline.
|
|
31491
|
+
* @cssproperty [--track-color=var(--l-color-border)] - `line` variant: color of the static bottom border the tabs sit on.
|
|
31492
|
+
* @cssproperty [--track-thickness=1px] - `line` variant: thickness of the static bottom border.
|
|
31493
|
+
*
|
|
31489
31494
|
* @customElement l-tabs
|
|
31490
31495
|
*/
|
|
31491
31496
|
var Tabs = class extends LuxenElement {
|
|
@@ -31920,7 +31925,7 @@ define("toast", Toast);
|
|
|
31920
31925
|
define("toast-item", ToastItem);
|
|
31921
31926
|
//#endregion
|
|
31922
31927
|
//#region src/html/elements/tooltip/tooltip.ts
|
|
31923
|
-
var styles$2 = r$6(":host {\n --background-color: var(--l-
|
|
31928
|
+
var styles$2 = r$6(":host {\n --background-color: var(--l-tooltip-background-color, light-dark(#1f2937, #f9fafb));\n --border-radius: 4px;\n --max-width: 180px;\n --arrow-size: 6px;\n --show-duration: 150ms;\n --hide-duration: 150ms;\n\n display: contents;\n}\n\n[popover] {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n max-width: var(--max-width);\n padding: 4px 8px;\n border: 0;\n border-radius: var(--border-radius);\n background: var(--background-color);\n color: var(\n --text-color,\n oklch(from var(--background-color) calc(0.59 - 0.41 * sign(l - 0.5)) 0 0)\n );\n font-size: 0.8125rem;\n line-height: 1.4;\n filter: drop-shadow(0 1px 2px rgb(0 0 0 / 16%));\n pointer-events: none;\n}\n\n@supports (color: contrast-color(red vs black, white)) {\n [popover] {\n color: var(--text-color, contrast-color(var(--background-color) vs #111827, #fff to AA));\n }\n}\n\ni {\n position: absolute;\n display: block;\n width: var(--arrow-size);\n height: var(--arrow-size);\n background: var(--background-color);\n transform: rotate(45deg);\n}\n");
|
|
31924
31929
|
/**
|
|
31925
31930
|
* @summary A tooltip that displays contextual text on hover or focus.
|
|
31926
31931
|
* @customElement l-tooltip
|
|
@@ -31930,7 +31935,7 @@ var styles$2 = r$6(":host {\n --background-color: var(--l-color-bg-fill-brand,
|
|
|
31930
31935
|
* @csspart body - The tooltip popover container.
|
|
31931
31936
|
* @csspart arrow - The directional arrow element.
|
|
31932
31937
|
*
|
|
31933
|
-
* @cssproperty --background-color - Background color.
|
|
31938
|
+
* @cssproperty --background-color - Background color for this tooltip instance. Defaults to the global `--l-tooltip-background-color` token (a neutral inverse surface, dark in light mode / light in dark mode) — override that token to re-skin every tooltip at once.
|
|
31934
31939
|
* @cssproperty --text-color - Text color. If unset, auto-derived from `--background-color` luminance.
|
|
31935
31940
|
* @cssproperty --border-radius - Border radius. Default `4px`.
|
|
31936
31941
|
* @cssproperty --max-width - Maximum width. Default `180px`.
|