luxen-ui 0.17.0 → 0.17.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.
@@ -3750,6 +3750,17 @@
3750
3750
  "description": "The type of item: `normal` or `checkbox`.",
3751
3751
  "attribute": "type"
3752
3752
  },
3753
+ {
3754
+ "kind": "field",
3755
+ "name": "variant",
3756
+ "type": {
3757
+ "text": "'destructive' | undefined"
3758
+ },
3759
+ "default": "undefined",
3760
+ "description": "Visual variant. `destructive` marks a delete-style action in red.",
3761
+ "attribute": "variant",
3762
+ "reflects": true
3763
+ },
3753
3764
  {
3754
3765
  "kind": "field",
3755
3766
  "name": "checked",
@@ -3813,6 +3824,15 @@
3813
3824
  }
3814
3825
  ],
3815
3826
  "attributes": [
3827
+ {
3828
+ "description": "Visual variant. `destructive` marks a delete-style action in red.",
3829
+ "name": "variant",
3830
+ "type": {
3831
+ "text": "'destructive' | undefined"
3832
+ },
3833
+ "default": "undefined",
3834
+ "fieldName": "variant"
3835
+ },
3816
3836
  {
3817
3837
  "name": "value",
3818
3838
  "type": {
@@ -3864,7 +3884,13 @@
3864
3884
  "module": "/src/html/shared/luxen-element.js"
3865
3885
  },
3866
3886
  "tagName": "l-dropdown-item",
3867
- "customElement": true
3887
+ "customElement": true,
3888
+ "_attributes": [
3889
+ {
3890
+ "name": "variant",
3891
+ "description": "destructive — Renders the item as a destructive action (red), for delete-style entries."
3892
+ }
3893
+ ]
3868
3894
  }
3869
3895
  ],
3870
3896
  "exports": [
@@ -12,6 +12,8 @@ import { LuxenElement } from '../../shared/luxen-element.js';
12
12
  *
13
13
  * @cssproperty --color - Text color.
14
14
  *
15
+ * @attribute variant - destructive — Renders the item as a destructive action (red), for delete-style entries.
16
+ *
15
17
  * @customElement l-dropdown-item
16
18
  */
17
19
  export declare class DropdownItem extends LuxenElement {
@@ -23,6 +25,8 @@ export declare class DropdownItem extends LuxenElement {
23
25
  accessor disabled: boolean;
24
26
  /** The type of item: `normal` or `checkbox`. */
25
27
  accessor type: 'normal' | 'checkbox';
28
+ /** Visual variant. `destructive` marks a delete-style action in red. */
29
+ accessor variant: 'destructive' | undefined;
26
30
  /** Whether the checkbox item is checked. */
27
31
  accessor checked: boolean;
28
32
  /** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,gBAAgB,CAIrB;IAEH,2CAA2C;IAE3C,QAAQ,CAAC,KAAK,SAAM;IAEpB,yBAAyB;IAEzB,QAAQ,CAAC,QAAQ,UAAS;IAE1B,gDAAgD;IAEhD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEhD,4CAA4C;IAE5C,QAAQ,CAAC,OAAO,UAAS;IAEzB,+EAA+E;IAE/E,QAAQ,CAAC,WAAW,UAAS;IAG7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,2DAA2D;IAC3D,IAAI,UAAU,IAAI,OAAO,CAExB;IAEQ,iBAAiB;IAU1B,OAAO,KAAK,UAAU,GAErB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM;IAUtB,uFAAuF;IACvF,eAAe,IAAI,YAAY,EAAE;IAQjC,oEAAoE;IACpE,WAAW;IAKX,4EAA4E;IAC5E,YAAY;IAKH,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9C,OAAO,CAAC,YAAY;YAKN,wBAAwB;IA2BtC,OAAO,CAAC,oBAAoB,CAK1B;IAEO,MAAM;CAiEhB"}
1
+ {"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,gBAAgB,CAIrB;IAEH,2CAA2C;IAE3C,QAAQ,CAAC,KAAK,SAAM;IAEpB,yBAAyB;IAEzB,QAAQ,CAAC,QAAQ,UAAS;IAE1B,gDAAgD;IAEhD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEhD,wEAAwE;IAExE,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAa;IAExD,4CAA4C;IAE5C,QAAQ,CAAC,OAAO,UAAS;IAEzB,+EAA+E;IAE/E,QAAQ,CAAC,WAAW,UAAS;IAG7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,2DAA2D;IAC3D,IAAI,UAAU,IAAI,OAAO,CAExB;IAEQ,iBAAiB;IAU1B,OAAO,KAAK,UAAU,GAErB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM;IAUtB,uFAAuF;IACvF,eAAe,IAAI,YAAY,EAAE;IAQjC,oEAAoE;IACpE,WAAW;IAKX,4EAA4E;IAC5E,YAAY;IAKH,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9C,OAAO,CAAC,YAAY;YAKN,wBAAwB;IA2BtC,OAAO,CAAC,oBAAoB,CAK1B;IAEO,MAAM;CAiEhB"}
@@ -1,4 +1,4 @@
1
- import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,r,t as i}from"../../chunks/decorate.js";import{a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{PopoverController as l}from"../../shared/controllers/popover.js";var u=t(`: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);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);outline:2px solid var(--l-focus-ring);outline-offset:-2px}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}:host([submenu-open]) .item{background:var(--l-color-bg-state-hover)}.check{flex-shrink:0;width:16px;display:flex}.submenu-indicator{background-color:currentColor;flex-shrink:0;block-size:16px;inline-size:16px;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat}:host(:dir(rtl)) .submenu-indicator{scale:-1 1}.submenu{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding,.25rem);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius,6px);background:var(--background,var(--l-color-surface-overlay));color:var(--l-color-text-primary);box-shadow:var(--shadow,0 4px 6px -1px #00000014, 0 2px 4px -2px #0000000f);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}slot[name=submenu]::slotted(hr){height:1px;margin-block:var(--padding,.25rem);margin-inline:calc(var(--padding,.25rem) * -1);background:var(--l-color-divider);border:0;display:block}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),d=class extends s{constructor(...t){super(...t),this._submenuFloating=new l(this,{getTriggerElement:()=>this,getFloatingElement:()=>this._submenuEl,getArrowElement:()=>null}),this.#e=``,this.#t=!1,this.#n=`normal`,this.#r=!1,this.#i=!1,this.#a=!1,this._onSubmenuSlotChange=t=>{let n=t.target;this._hasSubmenu=n.assignedElements().some(t=>t.tagName===e(`dropdown-item`).toUpperCase())}}static{this.styles=[c,u]}#e;get value(){return this.#e}set value(e){this.#e=e}#t;get disabled(){return this.#t}set disabled(e){this.#t=e}#n;get type(){return this.#n}set type(e){this.#n=e}#r;get checked(){return this.#r}set checked(e){this.#r=e}#i;get submenuOpen(){return this.#i}set submenuOpen(e){this.#i=e}#a;get _hasSubmenu(){return this.#a}set _hasSubmenu(e){this.#a=e}get hasSubmenu(){return this._hasSubmenu}connectedCallback(){super.connectedCallback(),this.hasAttribute(`role`)||this.setAttribute(`role`,`none`)}get _submenuEl(){return this.shadowRoot.querySelector(`.submenu`)}getTextLabel(){let e=this.shadowRoot?.querySelector(`slot:not([name])`);return e?e.assignedNodes({flatten:!0}).map(e=>e.textContent??``).join(``).trim():(this.textContent??``).trim()}getSubmenuItems(){let t=this.shadowRoot?.querySelector(`slot[name="submenu"]`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()&&!t.disabled):[]}openSubmenu(){!this._hasSubmenu||this.disabled||this.submenuOpen||(this.submenuOpen=!0)}closeSubmenu(){for(let e of this.getSubmenuItems())e.closeSubmenu();this.submenuOpen&&=!1}updated(e){e.has(`submenuOpen`)&&this._handleSubmenuOpenChange()}_getDuration(e){let t=parseFloat(getComputedStyle(this).getPropertyValue(e));return Number.isNaN(t)?150:t}async _handleSubmenuOpenChange(){let e=this._submenuEl;if(!e)return;let t={placement:getComputedStyle(this).direction===`rtl`?`left-start`:`right-start`,distance:0,skidding:-parseFloat(getComputedStyle(e).paddingTop||`0`)};if(this.submenuOpen){if(!this.isConnected||(e.showPopover(),await this._submenuFloating.updatePosition(t),!this.submenuOpen))return;await this._submenuFloating.animateShow(e,this._getDuration(`--show-duration`)),this._submenuFloating.startPositioning(t)}else{if(!e.matches(`:popover-open`))return;this._submenuFloating.stopPositioning(),await this._submenuFloating.animateHide(e,this._getDuration(`--hide-duration`)),e.matches(`:popover-open`)&&e.hidePopover()}}render(){let e=this.type===`checkbox`;return a`
1
+ import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,r,t as i}from"../../chunks/decorate.js";import{a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{PopoverController as l}from"../../shared/controllers/popover.js";var u=t(`: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);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);outline:2px solid var(--l-focus-ring);outline-offset:-2px}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}:host([submenu-open]) .item{background:var(--l-color-bg-state-hover)}:host([variant=destructive]) .item{color:var(--l-color-text-danger)}:host([variant=destructive]) .item:focus-visible{background:var(--l-color-bg-fill-danger-soft);outline-color:var(--l-color-text-danger)}@media (hover:hover){:host([variant=destructive]) .item:hover{background:var(--l-color-bg-fill-danger-soft)}}.check{flex-shrink:0;width:16px;display:flex}.submenu-indicator{background-color:currentColor;flex-shrink:0;block-size:16px;inline-size:16px;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat}:host(:dir(rtl)) .submenu-indicator{scale:-1 1}.submenu{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding,.25rem);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius,6px);background:var(--background,var(--l-color-surface-overlay));color:var(--l-color-text-primary);box-shadow:var(--shadow,0 4px 6px -1px #00000014, 0 2px 4px -2px #0000000f);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}slot[name=submenu]::slotted(hr){height:1px;margin-block:var(--padding,.25rem);margin-inline:calc(var(--padding,.25rem) * -1);background:var(--l-color-divider);border:0;display:block}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),d=class extends s{constructor(...t){super(...t),this._submenuFloating=new l(this,{getTriggerElement:()=>this,getFloatingElement:()=>this._submenuEl,getArrowElement:()=>null}),this.#e=``,this.#t=!1,this.#n=`normal`,this.#r=void 0,this.#i=!1,this.#a=!1,this.#o=!1,this._onSubmenuSlotChange=t=>{let n=t.target;this._hasSubmenu=n.assignedElements().some(t=>t.tagName===e(`dropdown-item`).toUpperCase())}}static{this.styles=[c,u]}#e;get value(){return this.#e}set value(e){this.#e=e}#t;get disabled(){return this.#t}set disabled(e){this.#t=e}#n;get type(){return this.#n}set type(e){this.#n=e}#r;get variant(){return this.#r}set variant(e){this.#r=e}#i;get checked(){return this.#i}set checked(e){this.#i=e}#a;get submenuOpen(){return this.#a}set submenuOpen(e){this.#a=e}#o;get _hasSubmenu(){return this.#o}set _hasSubmenu(e){this.#o=e}get hasSubmenu(){return this._hasSubmenu}connectedCallback(){super.connectedCallback(),this.hasAttribute(`role`)||this.setAttribute(`role`,`none`)}get _submenuEl(){return this.shadowRoot.querySelector(`.submenu`)}getTextLabel(){let e=this.shadowRoot?.querySelector(`slot:not([name])`);return e?e.assignedNodes({flatten:!0}).map(e=>e.textContent??``).join(``).trim():(this.textContent??``).trim()}getSubmenuItems(){let t=this.shadowRoot?.querySelector(`slot[name="submenu"]`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()&&!t.disabled):[]}openSubmenu(){!this._hasSubmenu||this.disabled||this.submenuOpen||(this.submenuOpen=!0)}closeSubmenu(){for(let e of this.getSubmenuItems())e.closeSubmenu();this.submenuOpen&&=!1}updated(e){e.has(`submenuOpen`)&&this._handleSubmenuOpenChange()}_getDuration(e){let t=parseFloat(getComputedStyle(this).getPropertyValue(e));return Number.isNaN(t)?150:t}async _handleSubmenuOpenChange(){let e=this._submenuEl;if(!e)return;let t={placement:getComputedStyle(this).direction===`rtl`?`left-start`:`right-start`,distance:0,skidding:-parseFloat(getComputedStyle(e).paddingTop||`0`)};if(this.submenuOpen){if(!this.isConnected||(e.showPopover(),await this._submenuFloating.updatePosition(t),!this.submenuOpen))return;await this._submenuFloating.animateShow(e,this._getDuration(`--show-duration`)),this._submenuFloating.startPositioning(t)}else{if(!e.matches(`:popover-open`))return;this._submenuFloating.stopPositioning(),await this._submenuFloating.animateHide(e,this._getDuration(`--hide-duration`)),e.matches(`:popover-open`)&&e.hidePopover()}}render(){let e=this.type===`checkbox`;return a`
2
2
  <div
3
3
  class="item"
4
4
  role=${e?`menuitemcheckbox`:`menuitem`}
@@ -54,5 +54,5 @@ import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.
54
54
  @slotchange=${this._onSubmenuSlotChange}
55
55
  ></slot>
56
56
  </div>
57
- `}};i([n()],d.prototype,`value`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`disabled`,null),i([n()],d.prototype,`type`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`checked`,null),i([n({type:Boolean,reflect:!0,attribute:`submenu-open`})],d.prototype,`submenuOpen`,null),i([r()],d.prototype,`_hasSubmenu`,null);export{d as DropdownItem};
57
+ `}};i([n()],d.prototype,`value`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`disabled`,null),i([n()],d.prototype,`type`,null),i([n({reflect:!0})],d.prototype,`variant`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`checked`,null),i([n({type:Boolean,reflect:!0,attribute:`submenu-open`})],d.prototype,`submenuOpen`,null),i([r()],d.prototype,`_hasSubmenu`,null);export{d as DropdownItem};
58
58
  //# sourceMappingURL=dropdown-item.js.map
@@ -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 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);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n /* A ring distinct from the hover tint (which is the same low-contrast fill).\n Negative offset keeps the 2px ring inside the row so it never collides with\n the panel edge or a neighboring item. */\n background: var(--l-color-bg-state-hover);\n outline: 2px solid var(--l-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n/* Parent item stays highlighted while its submenu is open. */\n:host([submenu-open]) .item {\n background: var(--l-color-bg-state-hover);\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n/* Inline-end chevron marking an item that opens a submenu. */\n.submenu-indicator {\n inline-size: 16px;\n block-size: 16px;\n flex-shrink: 0;\n background-color: currentColor;\n mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")\n no-repeat center / contain;\n}\n\n:host(:dir(rtl)) .submenu-indicator {\n scale: -1 1;\n}\n\n/* Submenu panel — mirrors the l-dropdown panel. The public custom properties\n (--padding, --background, --border-radius, --shadow) inherit from the\n ancestor l-dropdown through the light DOM, so a themed dropdown themes its\n submenus too; their fallbacks repeat the dropdown defaults for standalone use. */\n.submenu {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n padding: var(--padding, 0.25rem);\n margin: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius, 6px);\n background: var(--background, var(--l-color-surface-overlay));\n color: var(--l-color-text-primary);\n box-shadow: var(--shadow, 0 4px 6px -1px rgb(0 0 0 / 8%), 0 2px 4px -2px rgb(0 0 0 / 6%));\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n/* Lightweight separator alternative to <l-divider> — same dimensions, no extra import. */\nslot[name='submenu']::slotted(hr) {\n display: block;\n height: 1px;\n margin-block: var(--padding, 0.25rem);\n margin-inline: calc(var(--padding, 0.25rem) * -1);\n border: 0;\n background: var(--l-color-divider);\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, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property, state } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { tagName } from '../../registry.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 * @slot submenu - Nested `l-dropdown-item` elements rendered in a submenu panel anchored to this item. Drop an `<hr>` between them for a separator.\n *\n * @csspart submenu - The floating submenu panel.\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 private _submenuFloating = new PopoverController(this, {\n getTriggerElement: () => this,\n getFloatingElement: () => this._submenuEl,\n getArrowElement: () => null,\n });\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 /** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */\n @property({ type: Boolean, reflect: true, attribute: 'submenu-open' })\n accessor submenuOpen = false;\n\n @state()\n private accessor _hasSubmenu = false;\n\n /** Whether this item has nested `slot=\"submenu\"` items. */\n get hasSubmenu(): boolean {\n return this._hasSubmenu;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n // The host is a generic wrapper between the `role=\"menu\"` panel and the\n // `role=\"menuitem\"` row in its shadow root; `none` removes it from the\n // accessibility tree so the menu owns its menuitems directly (ARIA\n // required-owned-elements). The inner row keeps its menuitem role.\n // Guard preserves a consumer-set role.\n if (!this.hasAttribute('role')) this.setAttribute('role', 'none');\n }\n\n private get _submenuEl(): HTMLElement | null {\n return this.shadowRoot!.querySelector<HTMLElement>('.submenu');\n }\n\n /** Returns the text label of this item (excludes nested submenu items). */\n getTextLabel(): string {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return (this.textContent ?? '').trim();\n return slot\n .assignedNodes({ flatten: true })\n .map((node) => node.textContent ?? '')\n .join('')\n .trim();\n }\n\n /** Returns the enabled `l-dropdown-item` elements slotted into this item's submenu. */\n getSubmenuItems(): DropdownItem[] {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"submenu\"]');\n if (!slot) return [];\n return (slot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase() && !el.disabled,\n );\n }\n\n /** Opens this item's submenu panel. No-op without submenu items. */\n openSubmenu() {\n if (!this._hasSubmenu || this.disabled || this.submenuOpen) return;\n this.submenuOpen = true;\n }\n\n /** Closes this item's submenu panel, including any open nested submenus. */\n closeSubmenu() {\n for (const child of this.getSubmenuItems()) child.closeSubmenu();\n if (this.submenuOpen) this.submenuOpen = false;\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('submenuOpen')) {\n void this._handleSubmenuOpenChange();\n }\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 private async _handleSubmenuOpenChange() {\n const panel = this._submenuEl;\n if (!panel) return;\n\n const isRTL = getComputedStyle(this).direction === 'rtl';\n const posOpts = {\n placement: (isRTL ? 'left-start' : 'right-start') as Placement,\n distance: 0,\n // Pull the panel up by its own padding so its first item lines up with this item.\n skidding: -parseFloat(getComputedStyle(panel).paddingTop || '0'),\n };\n\n if (this.submenuOpen) {\n if (!this.isConnected) return;\n panel.showPopover();\n await this._submenuFloating.updatePosition(posOpts);\n if (!this.submenuOpen) return;\n await this._submenuFloating.animateShow(panel, this._getDuration('--show-duration'));\n this._submenuFloating.startPositioning(posOpts);\n } else {\n if (!panel.matches(':popover-open')) return;\n this._submenuFloating.stopPositioning();\n await this._submenuFloating.animateHide(panel, this._getDuration('--hide-duration'));\n if (panel.matches(':popover-open')) panel.hidePopover();\n }\n }\n\n private _onSubmenuSlotChange = (e: Event) => {\n const slot = e.target as HTMLSlotElement;\n this._hasSubmenu = slot\n .assignedElements()\n .some((el) => el.tagName === tagName('dropdown-item').toUpperCase());\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 aria-haspopup=${this._hasSubmenu ? 'menu' : nothing}\n aria-expanded=${this._hasSubmenu ? String(this.submenuOpen) : nothing}\n aria-controls=${this._hasSubmenu ? 'submenu' : 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\n class=\"label\"\n id=\"label\"\n ><slot></slot\n ></span>\n <slot name=\"suffix\"></slot>\n ${this._hasSubmenu\n ? html`<span\n class=\"submenu-indicator\"\n aria-hidden=\"true\"\n ></span>`\n : nothing}\n </div>\n <div\n class=\"submenu\"\n part=\"submenu\"\n id=\"submenu\"\n popover=\"manual\"\n role=\"menu\"\n aria-labelledby=\"label\"\n >\n <slot\n name=\"submenu\"\n @slotchange=${this._onSubmenuSlotChange}\n ></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
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);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n /* A ring distinct from the hover tint (which is the same low-contrast fill).\n Negative offset keeps the 2px ring inside the row so it never collides with\n the panel edge or a neighboring item. */\n background: var(--l-color-bg-state-hover);\n outline: 2px solid var(--l-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n/* Parent item stays highlighted while its submenu is open. */\n:host([submenu-open]) .item {\n background: var(--l-color-bg-state-hover);\n}\n\n/* Destructive variant — a delete-style action. Red label at rest, danger-tinted\n fill and focus ring on hover/focus so the highlight reads as \"danger\" rather\n than the neutral hover tint. */\n:host([variant='destructive']) .item {\n color: var(--l-color-text-danger);\n}\n\n:host([variant='destructive']) .item:focus-visible {\n background: var(--l-color-bg-fill-danger-soft);\n outline-color: var(--l-color-text-danger);\n}\n\n@media (hover: hover) {\n :host([variant='destructive']) .item:hover {\n background: var(--l-color-bg-fill-danger-soft);\n }\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n/* Inline-end chevron marking an item that opens a submenu. */\n.submenu-indicator {\n inline-size: 16px;\n block-size: 16px;\n flex-shrink: 0;\n background-color: currentColor;\n mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")\n no-repeat center / contain;\n}\n\n:host(:dir(rtl)) .submenu-indicator {\n scale: -1 1;\n}\n\n/* Submenu panel — mirrors the l-dropdown panel. The public custom properties\n (--padding, --background, --border-radius, --shadow) inherit from the\n ancestor l-dropdown through the light DOM, so a themed dropdown themes its\n submenus too; their fallbacks repeat the dropdown defaults for standalone use. */\n.submenu {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n padding: var(--padding, 0.25rem);\n margin: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius, 6px);\n background: var(--background, var(--l-color-surface-overlay));\n color: var(--l-color-text-primary);\n box-shadow: var(--shadow, 0 4px 6px -1px rgb(0 0 0 / 8%), 0 2px 4px -2px rgb(0 0 0 / 6%));\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n/* Lightweight separator alternative to <l-divider> — same dimensions, no extra import. */\nslot[name='submenu']::slotted(hr) {\n display: block;\n height: 1px;\n margin-block: var(--padding, 0.25rem);\n margin-inline: calc(var(--padding, 0.25rem) * -1);\n border: 0;\n background: var(--l-color-divider);\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, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property, state } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { tagName } from '../../registry.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 * @slot submenu - Nested `l-dropdown-item` elements rendered in a submenu panel anchored to this item. Drop an `<hr>` between them for a separator.\n *\n * @csspart submenu - The floating submenu panel.\n *\n * @cssproperty --color - Text color.\n *\n * @attribute variant - destructive — Renders the item as a destructive action (red), for delete-style entries.\n *\n * @customElement l-dropdown-item\n */\nexport class DropdownItem extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _submenuFloating = new PopoverController(this, {\n getTriggerElement: () => this,\n getFloatingElement: () => this._submenuEl,\n getArrowElement: () => null,\n });\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 /** Visual variant. `destructive` marks a delete-style action in red. */\n @property({ reflect: true })\n accessor variant: 'destructive' | undefined = undefined;\n\n /** Whether the checkbox item is checked. */\n @property({ type: Boolean, reflect: true })\n accessor checked = false;\n\n /** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */\n @property({ type: Boolean, reflect: true, attribute: 'submenu-open' })\n accessor submenuOpen = false;\n\n @state()\n private accessor _hasSubmenu = false;\n\n /** Whether this item has nested `slot=\"submenu\"` items. */\n get hasSubmenu(): boolean {\n return this._hasSubmenu;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n // The host is a generic wrapper between the `role=\"menu\"` panel and the\n // `role=\"menuitem\"` row in its shadow root; `none` removes it from the\n // accessibility tree so the menu owns its menuitems directly (ARIA\n // required-owned-elements). The inner row keeps its menuitem role.\n // Guard preserves a consumer-set role.\n if (!this.hasAttribute('role')) this.setAttribute('role', 'none');\n }\n\n private get _submenuEl(): HTMLElement | null {\n return this.shadowRoot!.querySelector<HTMLElement>('.submenu');\n }\n\n /** Returns the text label of this item (excludes nested submenu items). */\n getTextLabel(): string {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return (this.textContent ?? '').trim();\n return slot\n .assignedNodes({ flatten: true })\n .map((node) => node.textContent ?? '')\n .join('')\n .trim();\n }\n\n /** Returns the enabled `l-dropdown-item` elements slotted into this item's submenu. */\n getSubmenuItems(): DropdownItem[] {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"submenu\"]');\n if (!slot) return [];\n return (slot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase() && !el.disabled,\n );\n }\n\n /** Opens this item's submenu panel. No-op without submenu items. */\n openSubmenu() {\n if (!this._hasSubmenu || this.disabled || this.submenuOpen) return;\n this.submenuOpen = true;\n }\n\n /** Closes this item's submenu panel, including any open nested submenus. */\n closeSubmenu() {\n for (const child of this.getSubmenuItems()) child.closeSubmenu();\n if (this.submenuOpen) this.submenuOpen = false;\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('submenuOpen')) {\n void this._handleSubmenuOpenChange();\n }\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 private async _handleSubmenuOpenChange() {\n const panel = this._submenuEl;\n if (!panel) return;\n\n const isRTL = getComputedStyle(this).direction === 'rtl';\n const posOpts = {\n placement: (isRTL ? 'left-start' : 'right-start') as Placement,\n distance: 0,\n // Pull the panel up by its own padding so its first item lines up with this item.\n skidding: -parseFloat(getComputedStyle(panel).paddingTop || '0'),\n };\n\n if (this.submenuOpen) {\n if (!this.isConnected) return;\n panel.showPopover();\n await this._submenuFloating.updatePosition(posOpts);\n if (!this.submenuOpen) return;\n await this._submenuFloating.animateShow(panel, this._getDuration('--show-duration'));\n this._submenuFloating.startPositioning(posOpts);\n } else {\n if (!panel.matches(':popover-open')) return;\n this._submenuFloating.stopPositioning();\n await this._submenuFloating.animateHide(panel, this._getDuration('--hide-duration'));\n if (panel.matches(':popover-open')) panel.hidePopover();\n }\n }\n\n private _onSubmenuSlotChange = (e: Event) => {\n const slot = e.target as HTMLSlotElement;\n this._hasSubmenu = slot\n .assignedElements()\n .some((el) => el.tagName === tagName('dropdown-item').toUpperCase());\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 aria-haspopup=${this._hasSubmenu ? 'menu' : nothing}\n aria-expanded=${this._hasSubmenu ? String(this.submenuOpen) : nothing}\n aria-controls=${this._hasSubmenu ? 'submenu' : 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\n class=\"label\"\n id=\"label\"\n ><slot></slot\n ></span>\n <slot name=\"suffix\"></slot>\n ${this._hasSubmenu\n ? html`<span\n class=\"submenu-indicator\"\n aria-hidden=\"true\"\n ></span>`\n : nothing}\n </div>\n <div\n class=\"submenu\"\n part=\"submenu\"\n id=\"submenu\"\n popover=\"manual\"\n role=\"menu\"\n aria-labelledby=\"label\"\n >\n <slot\n name=\"submenu\"\n @slotchange=${this._onSubmenuSlotChange}\n ></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
@@ -3750,6 +3750,17 @@
3750
3750
  "description": "The type of item: `normal` or `checkbox`.",
3751
3751
  "attribute": "type"
3752
3752
  },
3753
+ {
3754
+ "kind": "field",
3755
+ "name": "variant",
3756
+ "type": {
3757
+ "text": "'destructive' | undefined"
3758
+ },
3759
+ "default": "undefined",
3760
+ "description": "Visual variant. `destructive` marks a delete-style action in red.",
3761
+ "attribute": "variant",
3762
+ "reflects": true
3763
+ },
3753
3764
  {
3754
3765
  "kind": "field",
3755
3766
  "name": "checked",
@@ -3813,6 +3824,15 @@
3813
3824
  }
3814
3825
  ],
3815
3826
  "attributes": [
3827
+ {
3828
+ "description": "Visual variant. `destructive` marks a delete-style action in red.",
3829
+ "name": "variant",
3830
+ "type": {
3831
+ "text": "'destructive' | undefined"
3832
+ },
3833
+ "default": "undefined",
3834
+ "fieldName": "variant"
3835
+ },
3816
3836
  {
3817
3837
  "name": "value",
3818
3838
  "type": {
@@ -3864,7 +3884,13 @@
3864
3884
  "module": "/src/html/shared/luxen-element.js"
3865
3885
  },
3866
3886
  "tagName": "l-dropdown-item",
3867
- "customElement": true
3887
+ "customElement": true,
3888
+ "_attributes": [
3889
+ {
3890
+ "name": "variant",
3891
+ "description": "destructive — Renders the item as a destructive action (red), for delete-style entries."
3892
+ }
3893
+ ]
3868
3894
  }
3869
3895
  ],
3870
3896
  "exports": [
@@ -44,6 +44,24 @@
44
44
  background: var(--l-color-bg-state-hover);
45
45
  }
46
46
 
47
+ /* Destructive variant — a delete-style action. Red label at rest, danger-tinted
48
+ fill and focus ring on hover/focus so the highlight reads as "danger" rather
49
+ than the neutral hover tint. */
50
+ :host([variant='destructive']) .item {
51
+ color: var(--l-color-text-danger);
52
+ }
53
+
54
+ :host([variant='destructive']) .item:focus-visible {
55
+ background: var(--l-color-bg-fill-danger-soft);
56
+ outline-color: var(--l-color-text-danger);
57
+ }
58
+
59
+ @media (hover: hover) {
60
+ :host([variant='destructive']) .item:hover {
61
+ background: var(--l-color-bg-fill-danger-soft);
62
+ }
63
+ }
64
+
47
65
  .check {
48
66
  display: flex;
49
67
  width: 16px;
@@ -12,6 +12,8 @@ import { LuxenElement } from '../../shared/luxen-element.js';
12
12
  *
13
13
  * @cssproperty --color - Text color.
14
14
  *
15
+ * @attribute variant - destructive — Renders the item as a destructive action (red), for delete-style entries.
16
+ *
15
17
  * @customElement l-dropdown-item
16
18
  */
17
19
  export declare class DropdownItem extends LuxenElement {
@@ -23,6 +25,8 @@ export declare class DropdownItem extends LuxenElement {
23
25
  accessor disabled: boolean;
24
26
  /** The type of item: `normal` or `checkbox`. */
25
27
  accessor type: 'normal' | 'checkbox';
28
+ /** Visual variant. `destructive` marks a delete-style action in red. */
29
+ accessor variant: 'destructive' | undefined;
26
30
  /** Whether the checkbox item is checked. */
27
31
  accessor checked: boolean;
28
32
  /** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,gBAAgB,CAIrB;IAEH,2CAA2C;IAE3C,QAAQ,CAAC,KAAK,SAAM;IAEpB,yBAAyB;IAEzB,QAAQ,CAAC,QAAQ,UAAS;IAE1B,gDAAgD;IAEhD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEhD,4CAA4C;IAE5C,QAAQ,CAAC,OAAO,UAAS;IAEzB,+EAA+E;IAE/E,QAAQ,CAAC,WAAW,UAAS;IAG7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,2DAA2D;IAC3D,IAAI,UAAU,IAAI,OAAO,CAExB;IAEQ,iBAAiB;IAU1B,OAAO,KAAK,UAAU,GAErB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM;IAUtB,uFAAuF;IACvF,eAAe,IAAI,YAAY,EAAE;IAQjC,oEAAoE;IACpE,WAAW;IAKX,4EAA4E;IAC5E,YAAY;IAKH,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9C,OAAO,CAAC,YAAY;YAKN,wBAAwB;IA2BtC,OAAO,CAAC,oBAAoB,CAK1B;IAEO,MAAM;CAiEhB"}
1
+ {"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,gBAAgB,CAIrB;IAEH,2CAA2C;IAE3C,QAAQ,CAAC,KAAK,SAAM;IAEpB,yBAAyB;IAEzB,QAAQ,CAAC,QAAQ,UAAS;IAE1B,gDAAgD;IAEhD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEhD,wEAAwE;IAExE,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAa;IAExD,4CAA4C;IAE5C,QAAQ,CAAC,OAAO,UAAS;IAEzB,+EAA+E;IAE/E,QAAQ,CAAC,WAAW,UAAS;IAG7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,2DAA2D;IAC3D,IAAI,UAAU,IAAI,OAAO,CAExB;IAEQ,iBAAiB;IAU1B,OAAO,KAAK,UAAU,GAErB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM;IAUtB,uFAAuF;IACvF,eAAe,IAAI,YAAY,EAAE;IAQjC,oEAAoE;IACpE,WAAW;IAKX,4EAA4E;IAC5E,YAAY;IAKH,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9C,OAAO,CAAC,YAAY;YAKN,wBAAwB;IA2BtC,OAAO,CAAC,oBAAoB,CAK1B;IAEO,MAAM;CAiEhB"}
@@ -15,7 +15,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
15
15
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
16
16
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
17
17
  };
18
- var _DropdownItem_value_accessor_storage, _DropdownItem_disabled_accessor_storage, _DropdownItem_type_accessor_storage, _DropdownItem_checked_accessor_storage, _DropdownItem_submenuOpen_accessor_storage, _DropdownItem__hasSubmenu_accessor_storage;
18
+ var _DropdownItem_value_accessor_storage, _DropdownItem_disabled_accessor_storage, _DropdownItem_type_accessor_storage, _DropdownItem_variant_accessor_storage, _DropdownItem_checked_accessor_storage, _DropdownItem_submenuOpen_accessor_storage, _DropdownItem__hasSubmenu_accessor_storage;
19
19
  import { html, nothing, unsafeCSS } from 'lit';
20
20
  import { LuxenElement } from '../../shared/luxen-element.js';
21
21
  import { property, state } from 'lit/decorators.js';
@@ -36,6 +36,8 @@ const styles = unsafeCSS(rawStyles);
36
36
  *
37
37
  * @cssproperty --color - Text color.
38
38
  *
39
+ * @attribute variant - destructive — Renders the item as a destructive action (red), for delete-style entries.
40
+ *
39
41
  * @customElement l-dropdown-item
40
42
  */
41
43
  export class DropdownItem extends LuxenElement {
@@ -49,6 +51,7 @@ export class DropdownItem extends LuxenElement {
49
51
  _DropdownItem_value_accessor_storage.set(this, '');
50
52
  _DropdownItem_disabled_accessor_storage.set(this, false);
51
53
  _DropdownItem_type_accessor_storage.set(this, 'normal');
54
+ _DropdownItem_variant_accessor_storage.set(this, undefined);
52
55
  _DropdownItem_checked_accessor_storage.set(this, false);
53
56
  _DropdownItem_submenuOpen_accessor_storage.set(this, false);
54
57
  _DropdownItem__hasSubmenu_accessor_storage.set(this, false);
@@ -68,6 +71,9 @@ export class DropdownItem extends LuxenElement {
68
71
  /** The type of item: `normal` or `checkbox`. */
69
72
  get type() { return __classPrivateFieldGet(this, _DropdownItem_type_accessor_storage, "f"); }
70
73
  set type(value) { __classPrivateFieldSet(this, _DropdownItem_type_accessor_storage, value, "f"); }
74
+ /** Visual variant. `destructive` marks a delete-style action in red. */
75
+ get variant() { return __classPrivateFieldGet(this, _DropdownItem_variant_accessor_storage, "f"); }
76
+ set variant(value) { __classPrivateFieldSet(this, _DropdownItem_variant_accessor_storage, value, "f"); }
71
77
  /** Whether the checkbox item is checked. */
72
78
  get checked() { return __classPrivateFieldGet(this, _DropdownItem_checked_accessor_storage, "f"); }
73
79
  set checked(value) { __classPrivateFieldSet(this, _DropdownItem_checked_accessor_storage, value, "f"); }
@@ -228,7 +234,7 @@ export class DropdownItem extends LuxenElement {
228
234
  `;
229
235
  }
230
236
  }
231
- _DropdownItem_value_accessor_storage = new WeakMap(), _DropdownItem_disabled_accessor_storage = new WeakMap(), _DropdownItem_type_accessor_storage = new WeakMap(), _DropdownItem_checked_accessor_storage = new WeakMap(), _DropdownItem_submenuOpen_accessor_storage = new WeakMap(), _DropdownItem__hasSubmenu_accessor_storage = new WeakMap();
237
+ _DropdownItem_value_accessor_storage = new WeakMap(), _DropdownItem_disabled_accessor_storage = new WeakMap(), _DropdownItem_type_accessor_storage = new WeakMap(), _DropdownItem_variant_accessor_storage = new WeakMap(), _DropdownItem_checked_accessor_storage = new WeakMap(), _DropdownItem_submenuOpen_accessor_storage = new WeakMap(), _DropdownItem__hasSubmenu_accessor_storage = new WeakMap();
232
238
  DropdownItem.styles = [hostStyles, styles];
233
239
  __decorate([
234
240
  property()
@@ -239,6 +245,9 @@ __decorate([
239
245
  __decorate([
240
246
  property()
241
247
  ], DropdownItem.prototype, "type", null);
248
+ __decorate([
249
+ property({ reflect: true })
250
+ ], DropdownItem.prototype, "variant", null);
242
251
  __decorate([
243
252
  property({ type: Boolean, reflect: true })
244
253
  ], DropdownItem.prototype, "checked", null);
@@ -39,6 +39,14 @@
39
39
  "reflects": false,
40
40
  "description": "The type of item: `normal` or `checkbox`."
41
41
  },
42
+ {
43
+ "name": "variant",
44
+ "attribute": "variant",
45
+ "type": "'destructive' | undefined",
46
+ "default": "undefined",
47
+ "reflects": true,
48
+ "description": "Visual variant. `destructive` marks a delete-style action in red."
49
+ },
42
50
  {
43
51
  "name": "checked",
44
52
  "attribute": "checked",
@@ -64,7 +72,14 @@
64
72
  "description": "Whether this item has nested `slot=\"submenu\"` items."
65
73
  }
66
74
  ],
67
- "attributes": [],
75
+ "attributes": [
76
+ {
77
+ "name": "variant",
78
+ "data": false,
79
+ "values": [],
80
+ "description": "destructive — Renders the item as a destructive action (red), for delete-style entries."
81
+ }
82
+ ],
68
83
  "events": [],
69
84
  "methods": [
70
85
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.17.0",
2
+ "version": "0.17.1",
3
3
  "elements": [
4
4
  {
5
5
  "name": "alert",
@@ -2289,6 +2289,14 @@
2289
2289
  "reflects": false,
2290
2290
  "description": "The type of item: `normal` or `checkbox`."
2291
2291
  },
2292
+ {
2293
+ "name": "variant",
2294
+ "attribute": "variant",
2295
+ "type": "'destructive' | undefined",
2296
+ "default": "undefined",
2297
+ "reflects": true,
2298
+ "description": "Visual variant. `destructive` marks a delete-style action in red."
2299
+ },
2292
2300
  {
2293
2301
  "name": "checked",
2294
2302
  "attribute": "checked",
@@ -2314,7 +2322,14 @@
2314
2322
  "description": "Whether this item has nested `slot=\"submenu\"` items."
2315
2323
  }
2316
2324
  ],
2317
- "attributes": [],
2325
+ "attributes": [
2326
+ {
2327
+ "name": "variant",
2328
+ "data": false,
2329
+ "values": [],
2330
+ "description": "destructive — Renders the item as a destructive action (red), for delete-style entries."
2331
+ }
2332
+ ],
2318
2333
  "events": [],
2319
2334
  "methods": [
2320
2335
  {
@@ -74,6 +74,57 @@ Set `type="checkbox"` for toggleable items. The dropdown stays open when checkin
74
74
  </l-dropdown>
75
75
  ```
76
76
 
77
+ ### Destructive items
78
+
79
+ Set `variant="destructive"` to mark a delete-style action in red.
80
+
81
+ ```html
82
+ <l-dropdown>
83
+ <button
84
+ slot="trigger"
85
+ class="l-button"
86
+ >
87
+ Project
88
+ </button>
89
+
90
+ <l-dropdown-item value="share">
91
+ <iconify-icon
92
+ slot="prefix"
93
+ icon="lucide:share"
94
+ ></iconify-icon>
95
+ Share
96
+ </l-dropdown-item>
97
+ <l-dropdown-item value="preferences">
98
+ <iconify-icon
99
+ slot="prefix"
100
+ icon="lucide:settings"
101
+ ></iconify-icon>
102
+ Preferences
103
+ </l-dropdown-item>
104
+
105
+ <l-divider></l-divider>
106
+
107
+ <l-dropdown-label>Danger zone</l-dropdown-label>
108
+ <l-dropdown-item value="archive">
109
+ <iconify-icon
110
+ slot="prefix"
111
+ icon="lucide:archive"
112
+ ></iconify-icon>
113
+ Archive
114
+ </l-dropdown-item>
115
+ <l-dropdown-item
116
+ value="delete"
117
+ variant="destructive"
118
+ >
119
+ <iconify-icon
120
+ slot="prefix"
121
+ icon="lucide:trash-2"
122
+ ></iconify-icon>
123
+ Delete
124
+ </l-dropdown-item>
125
+ </l-dropdown>
126
+ ```
127
+
77
128
  ### Section labels
78
129
 
79
130
  Add `<l-dropdown-label>` to caption a group of items. It is non-interactive — keyboard navigation and typeahead skip it. Use `<l-divider>` between sections.
@@ -433,6 +484,7 @@ while open.
433
484
  - **value**: `string` — The value associated with this item.
434
485
  - **disabled**: `boolean` (default: `false`) — Disables the item.
435
486
  - **type**: `'normal' | 'checkbox'` (default: `'normal'`) — The type of item: `normal` or `checkbox`.
487
+ - **variant**: `'destructive' | undefined` (default: `undefined`) — Visual variant. `destructive` marks a delete-style action in red.
436
488
  - **checked**: `boolean` (default: `false`) — Whether the checkbox item is checked.
437
489
  - **submenu-open**: `boolean` (default: `false`) — Whether this item's submenu is open. Managed by the parent `l-dropdown`.
438
490
  - **hasSubmenu**: `boolean` — Whether this item has nested `slot="submenu"` items.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luxen-ui",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "Modern web components and CSS-first UI library built with Lit. Framework-agnostic, customizable prefix, design tokens.",
5
5
  "keywords": [
6
6
  "custom-elements",