@oslokommune/punkt-elements 15.4.5 → 16.0.2

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/{card-CnPjrdre.js → card-CmfUyl_s.js} +1 -1
  3. package/dist/{card-5S2r9UD1.cjs → card-Db9QSEqh.cjs} +1 -1
  4. package/dist/{checkbox-D98_NjcU.cjs → checkbox-Cpyay9_l.cjs} +1 -1
  5. package/dist/{checkbox-BSz71IeT.js → checkbox-D6nltMuc.js} +1 -1
  6. package/dist/combobox-Bv37b6cI.cjs +135 -0
  7. package/dist/combobox-CoO8T-F-.js +818 -0
  8. package/dist/{datepicker-SEKblnRR.cjs → datepicker-CrvQ5Y5w.cjs} +1 -1
  9. package/dist/{datepicker-nnyTW0vf.js → datepicker-DbsIuC5Z.js} +2 -2
  10. package/dist/index.d.ts +157 -90
  11. package/dist/{input-element-Bkv6Yxld.js → input-element-BGNbdzy2.js} +1 -1
  12. package/dist/{input-element-DM0tY799.cjs → input-element-CSDVA3Y6.cjs} +1 -1
  13. package/dist/listbox-Dm2mKp6_.cjs +101 -0
  14. package/dist/listbox-OdkIn9_A.js +431 -0
  15. package/dist/pkt-card.cjs +1 -1
  16. package/dist/pkt-card.js +1 -1
  17. package/dist/pkt-checkbox.cjs +1 -1
  18. package/dist/pkt-checkbox.js +1 -1
  19. package/dist/pkt-combobox.cjs +1 -1
  20. package/dist/pkt-combobox.js +1 -1
  21. package/dist/pkt-datepicker.cjs +1 -1
  22. package/dist/pkt-datepicker.js +2 -2
  23. package/dist/pkt-header.cjs +1 -1
  24. package/dist/pkt-header.js +1 -1
  25. package/dist/pkt-index.cjs +1 -1
  26. package/dist/pkt-index.js +9 -9
  27. package/dist/pkt-listbox.cjs +1 -1
  28. package/dist/pkt-listbox.js +1 -1
  29. package/dist/pkt-options-controller-BogGk-6J.cjs +1 -0
  30. package/dist/{pkt-options-controller-BcGywCmf.js → pkt-options-controller-Z-bPox7n.js} +2 -2
  31. package/dist/pkt-radiobutton.cjs +1 -1
  32. package/dist/pkt-radiobutton.js +1 -1
  33. package/dist/pkt-select.cjs +1 -1
  34. package/dist/pkt-select.js +1 -1
  35. package/dist/pkt-tag.cjs +1 -1
  36. package/dist/pkt-tag.js +1 -1
  37. package/dist/pkt-textarea.cjs +1 -1
  38. package/dist/pkt-textarea.js +1 -1
  39. package/dist/pkt-textinput.cjs +1 -1
  40. package/dist/pkt-textinput.js +1 -1
  41. package/dist/{radiobutton-95wp024h.cjs → radiobutton-CNHCpKn0.cjs} +1 -1
  42. package/dist/{radiobutton-CTFAV5GU.js → radiobutton-DgC27mb0.js} +1 -1
  43. package/dist/{select-YLvYAQX6.js → select-7VuYtPZv.js} +2 -2
  44. package/dist/{select-CZ_Lx5W6.cjs → select-PWPy5gTB.cjs} +1 -1
  45. package/dist/{tag-68q0_Sn0.js → tag-DZPqFiem.js} +37 -33
  46. package/dist/tag-DmbgBCKu.cjs +27 -0
  47. package/dist/{textarea-CuTsE1WX.cjs → textarea-CO7Ikug5.cjs} +1 -1
  48. package/dist/{textarea-DhWH99qN.js → textarea-VpCEjVFx.js} +1 -1
  49. package/dist/{textinput-BCi9p0Du.js → textinput-C2AZ9ss2.js} +1 -1
  50. package/dist/{textinput-st4Vml5J.cjs → textinput-DRFZU3dA.cjs} +1 -1
  51. package/package.json +4 -4
  52. package/src/components/card/card.ts +1 -0
  53. package/src/components/combobox/combobox-base.ts +158 -0
  54. package/src/components/combobox/combobox-handlers.ts +419 -0
  55. package/src/components/combobox/combobox-types.ts +10 -0
  56. package/src/components/combobox/combobox-utils.ts +135 -0
  57. package/src/components/combobox/combobox-value.ts +248 -0
  58. package/src/components/combobox/combobox.accessibility.test.ts +243 -0
  59. package/src/components/combobox/{combobox.test.ts → combobox.core.test.ts} +104 -46
  60. package/src/components/combobox/combobox.interaction.test.ts +436 -0
  61. package/src/components/combobox/combobox.selection.test.ts +543 -0
  62. package/src/components/combobox/combobox.ts +260 -734
  63. package/src/components/listbox/index.ts +2 -0
  64. package/src/components/listbox/listbox.interaction.test.ts +580 -0
  65. package/src/components/listbox/listbox.test.ts +32 -6
  66. package/src/components/listbox/listbox.ts +109 -126
  67. package/src/components/tag/tag.ts +3 -0
  68. package/dist/combobox-C5YcNVSZ.cjs +0 -128
  69. package/dist/combobox-cer7PLSE.js +0 -533
  70. package/dist/listbox-C7NEa9SU.cjs +0 -96
  71. package/dist/listbox-Cykec1bj.js +0 -361
  72. package/dist/pkt-options-controller-BnTmkl3g.cjs +0 -1
  73. package/dist/tag-BnT5onW2.cjs +0 -26
@@ -1,4 +1,4 @@
1
- import { P as t } from "./textinput-BCi9p0Du.js";
1
+ import { P as t } from "./textinput-C2AZ9ss2.js";
2
2
  const p = t;
3
3
  export {
4
4
  t as PktTextinput,
@@ -1,4 +1,4 @@
1
- "use strict";const t=require("./element-CMTfByxQ.cjs"),r=require("./state-BNgpvY-A.cjs"),u=require("./input-element-DM0tY799.cjs"),h=require("./ref-CxLwrCxt.cjs"),c=require("./class-map-Bokp1SoS.cjs");var d=Object.defineProperty,k=Object.getOwnPropertyDescriptor,i=(p,e,n,a)=>{for(var s=a>1?void 0:a?k(e,n):e,o=p.length-1,l;o>=0;o--)(l=p[o])&&(s=(a?l(e,n,s):l(s))||s);return a&&s&&d(e,n,s),s};exports.PktRadioButton=class extends u.PktInputElement{constructor(){super(...arguments),this.inputRef=h.e(),this.value="",this.checkHelptext=null,this.defaultChecked=!1,this.hasTile=!1,this.checked=null,this.type="radio",this.tagText=null,this.optionalTag=!1,this.optionalText="Valgfritt",this.requiredTag=!1,this.requiredText="Må fylles ut",this._checked=!1}connectedCallback(){super.connectedCallback()}attributeChangedCallback(e,n,a){e==="defaultChecked"&&(this._checked=this.defaultChecked),e==="checked"&&(this._checked=this.checked===""||this.checked==="true"||this.checked===!0),super.attributeChangedCallback(e,n,a)}render(){const e=c.e({"pkt-input-check__input":!0,"pkt-input-check__input--tile":this.hasTile,"pkt-input-check__input--tile-disabled":this.disabled&&this.hasTile}),n=c.e({"pkt-input-check__input-checkbox":!0,"pkt-input-check__input-checkbox--error":this.hasError}),a=c.e({"pkt-input-check__input-label":!0,"pkt-input-check__input-label--disabled":this.disabled,"pkt-sr-only":this.hideLabel}),s="pkt-tag pkt-tag--small pkt-tag--thin-text",o=()=>t.b`
1
+ "use strict";const t=require("./element-CMTfByxQ.cjs"),r=require("./state-BNgpvY-A.cjs"),u=require("./input-element-CSDVA3Y6.cjs"),h=require("./ref-CxLwrCxt.cjs"),c=require("./class-map-Bokp1SoS.cjs");var d=Object.defineProperty,k=Object.getOwnPropertyDescriptor,i=(p,e,n,a)=>{for(var s=a>1?void 0:a?k(e,n):e,o=p.length-1,l;o>=0;o--)(l=p[o])&&(s=(a?l(e,n,s):l(s))||s);return a&&s&&d(e,n,s),s};exports.PktRadioButton=class extends u.PktInputElement{constructor(){super(...arguments),this.inputRef=h.e(),this.value="",this.checkHelptext=null,this.defaultChecked=!1,this.hasTile=!1,this.checked=null,this.type="radio",this.tagText=null,this.optionalTag=!1,this.optionalText="Valgfritt",this.requiredTag=!1,this.requiredText="Må fylles ut",this._checked=!1}connectedCallback(){super.connectedCallback()}attributeChangedCallback(e,n,a){e==="defaultChecked"&&(this._checked=this.defaultChecked),e==="checked"&&(this._checked=this.checked===""||this.checked==="true"||this.checked===!0),super.attributeChangedCallback(e,n,a)}render(){const e=c.e({"pkt-input-check__input":!0,"pkt-input-check__input--tile":this.hasTile,"pkt-input-check__input--tile-disabled":this.disabled&&this.hasTile}),n=c.e({"pkt-input-check__input-checkbox":!0,"pkt-input-check__input-checkbox--error":this.hasError}),a=c.e({"pkt-input-check__input-label":!0,"pkt-input-check__input-label--disabled":this.disabled,"pkt-sr-only":this.hideLabel}),s="pkt-tag pkt-tag--small pkt-tag--thin-text",o=()=>t.b`
2
2
  ${this.tagText?t.b`<span class=${s+" pkt-tag--gray"}>${this.tagText}</span>`:t.A}
3
3
  ${this.optionalTag?t.b`<span class=${s+" pkt-tag--blue-light"}>${this.optionalText}</span>`:t.A}
4
4
  ${this.requiredTag?t.b`<span class=${s+" pkt-tag--beige"}>${this.requiredText}</span>`:t.A}
@@ -1,6 +1,6 @@
1
1
  import { b as h, A as n, n as s, t as d } from "./element-CV9utnHJ.js";
2
2
  import { r as k } from "./state-l4hGZdFJ.js";
3
- import { P as f } from "./input-element-Bkv6Yxld.js";
3
+ import { P as f } from "./input-element-BGNbdzy2.js";
4
4
  import { e as g, n as b } from "./ref-Dma3n3i8.js";
5
5
  import { e as u } from "./class-map-3ADKve8g.js";
6
6
  var y = Object.defineProperty, $ = Object.getOwnPropertyDescriptor, i = (e, a, c, l) => {
@@ -2,8 +2,8 @@ import { n as c, b as a, t as _ } from "./element-CV9utnHJ.js";
2
2
  import { e as u, n as d } from "./ref-Dma3n3i8.js";
3
3
  import { o as n } from "./if-defined-rXcLNTzN.js";
4
4
  import { r as $ } from "./state-l4hGZdFJ.js";
5
- import { P as x } from "./input-element-Bkv6Yxld.js";
6
- import { P as m } from "./pkt-options-controller-BcGywCmf.js";
5
+ import { P as x } from "./input-element-BGNbdzy2.js";
6
+ import { P as m } from "./pkt-options-controller-Z-bPox7n.js";
7
7
  import { P as b } from "./pkt-slot-controller-D7CrjM52.js";
8
8
  import "./input-wrapper-CaUY90qz.js";
9
9
  var P = Object.defineProperty, v = (e, t, s, o) => {
@@ -1,4 +1,4 @@
1
- "use strict";const p=require("./element-CMTfByxQ.cjs"),h=require("./ref-CxLwrCxt.cjs"),l=require("./if-defined-hKKmbsI8.cjs"),c=require("./state-BNgpvY-A.cjs"),v=require("./input-element-DM0tY799.cjs"),f=require("./pkt-options-controller-BnTmkl3g.cjs"),_=require("./pkt-slot-controller-D4nKlom5.cjs");require("./input-wrapper-JU4D2TGu.cjs");var $=Object.defineProperty,u=(r,t,e,i)=>{for(var s=void 0,o=r.length-1,n;o>=0;o--)(n=r[o])&&(s=n(t,e,s)||s);return s&&$(t,e,s),s};class a extends v.PktInputElement{constructor(){super(...arguments),this._optionsProp=[],this._options=[]}get options(){return this._options.map(t=>({...t,selected:this.isOptionSelected(t)}))}set options(t){this._optionsProp=t,this.requestUpdate("_optionsProp",this._options)}isOptionSelected(t){return Array.isArray(this.value)?this.value.includes(t.value):t.value===this.value}findOptionByValue(t){return this._options.find(e=>e.value===t)}getSelectedOptions(){return this._options.filter(t=>this.isOptionSelected(t))}parseOptions(){var i,s;const t=this._optionsProp.length>0,e=((s=(i=this.optionsController)==null?void 0:i.nodes)==null?void 0:s.length)&&this.optionsController.nodes.length>0;t?this._options=this._optionsProp:e&&(this._options=this.optionsController.options)}willUpdate(t){super.willUpdate(t),this.parseOptions()}}u([p.n({type:Array,attribute:"options"})],a.prototype,"_optionsProp");u([c.r()],a.prototype,"_options");var x=Object.defineProperty,b=Object.getOwnPropertyDescriptor,d=(r,t,e,i)=>{for(var s=i>1?void 0:i?b(t,e):t,o=r.length-1,n;o>=0;o--)(n=r[o])&&(s=(i?n(t,e,s):n(s))||s);return i&&s&&x(t,e,s),s};exports.PktSelect=class extends a{constructor(){super(),this.inputRef=h.e(),this.helptextSlot=h.e(),this.value="",this.selectedIndex=-1,this.selectedOptions=void 0,this.optionsController=new f.PktOptionsSlotController(this),this.slotController=new _.PktSlotController(this,this.helptextSlot),this.slotController.skipOptions=!0}connectedCallback(){super.connectedCallback(),this.parseOptions(),this._options.forEach(t=>{t.selected&&!this.value&&(this.value=t.value)})}add(t,e){const i={value:t.value||t.text,label:t.text||t.value,selected:t.selected,disabled:t.disabled};if(e===void 0)this._options.push(i);else if(typeof e=="number")this._options.splice(e,0,i);else{const s=e.value||e.text,o=this._options.findIndex(n=>n.value===s);o>=0?this._options.splice(o,0,i):this._options.push(i)}t.selected&&(this.value=t.value||t.text,this.selectedIndex=this._options.findIndex(s=>s.value===this.value)),this.requestUpdate()}remove(t){var e;typeof t=="number"&&(this.selectedIndex===t&&(this.value=((e=this._options[0])==null?void 0:e.value)||""),this._options.splice(t,1),this.requestUpdate())}item(t){var e;return(e=this.inputRef.value)==null?void 0:e.item(t)}namedItem(t){var e;return(e=this.inputRef.value)==null?void 0:e.namedItem(t)}showPicker(){this.inputRef.value&&"showPicker"in this.inputRef.value&&this.inputRef.value.showPicker()}attributeChangedCallback(t,e,i){var s,o;t==="options"&&(this._options=i?JSON.parse(i):[]),t==="value"&&this.value!==e&&(this.selectedIndex=this.touched?this.returnNumberOrNull((s=this.inputRef.value)==null?void 0:s.selectedIndex):this._options.findIndex(n=>n.value===i),this.selectedOptions=(o=this.inputRef.value)==null?void 0:o.selectedOptions,this.valueChanged(i,e)),super.attributeChangedCallback(t,e,i)}update(t){var e,i;super.update(t),t.has("_optionsProp")&&this._optionsProp.length>0&&(this._options=this._optionsProp,this.requestUpdate("_options"),!this.value&&this._options.length>0&&(this.value=this._options[0].value,this.selectedIndex=0)),t.has("value")&&this.value!==t.get("value")&&(this.selectedIndex=this.touched?this.returnNumberOrNull((e=this.inputRef.value)==null?void 0:e.selectedIndex):this._options.findIndex(s=>s.value===this.value),this.selectedOptions=(i=this.inputRef.value)==null?void 0:i.selectedOptions,this.valueChanged(this.value,t.get("value"))),t.has("id")&&!this.name&&this.id&&(this.name=this.id)}firstUpdated(t){var e;super.firstUpdated(t),this._optionsProp.length&&(this._options=this._optionsProp),!this.value&&this._options.length>0?(this.value=this._options[0].value,this.selectedIndex=0):this.selectedIndex=this._options.findIndex(i=>i.value===this.value),this.selectedOptions=(e=this.inputRef.value)==null?void 0:e.selectedOptions}render(){const t=`pkt-input ${this.fullwidth?"pkt-input--fullwidth":""}`;return p.b`
1
+ "use strict";const p=require("./element-CMTfByxQ.cjs"),h=require("./ref-CxLwrCxt.cjs"),l=require("./if-defined-hKKmbsI8.cjs"),c=require("./state-BNgpvY-A.cjs"),v=require("./input-element-CSDVA3Y6.cjs"),f=require("./pkt-options-controller-BogGk-6J.cjs"),_=require("./pkt-slot-controller-D4nKlom5.cjs");require("./input-wrapper-JU4D2TGu.cjs");var $=Object.defineProperty,u=(r,t,e,i)=>{for(var s=void 0,o=r.length-1,n;o>=0;o--)(n=r[o])&&(s=n(t,e,s)||s);return s&&$(t,e,s),s};class a extends v.PktInputElement{constructor(){super(...arguments),this._optionsProp=[],this._options=[]}get options(){return this._options.map(t=>({...t,selected:this.isOptionSelected(t)}))}set options(t){this._optionsProp=t,this.requestUpdate("_optionsProp",this._options)}isOptionSelected(t){return Array.isArray(this.value)?this.value.includes(t.value):t.value===this.value}findOptionByValue(t){return this._options.find(e=>e.value===t)}getSelectedOptions(){return this._options.filter(t=>this.isOptionSelected(t))}parseOptions(){var i,s;const t=this._optionsProp.length>0,e=((s=(i=this.optionsController)==null?void 0:i.nodes)==null?void 0:s.length)&&this.optionsController.nodes.length>0;t?this._options=this._optionsProp:e&&(this._options=this.optionsController.options)}willUpdate(t){super.willUpdate(t),this.parseOptions()}}u([p.n({type:Array,attribute:"options"})],a.prototype,"_optionsProp");u([c.r()],a.prototype,"_options");var x=Object.defineProperty,b=Object.getOwnPropertyDescriptor,d=(r,t,e,i)=>{for(var s=i>1?void 0:i?b(t,e):t,o=r.length-1,n;o>=0;o--)(n=r[o])&&(s=(i?n(t,e,s):n(s))||s);return i&&s&&x(t,e,s),s};exports.PktSelect=class extends a{constructor(){super(),this.inputRef=h.e(),this.helptextSlot=h.e(),this.value="",this.selectedIndex=-1,this.selectedOptions=void 0,this.optionsController=new f.PktOptionsSlotController(this),this.slotController=new _.PktSlotController(this,this.helptextSlot),this.slotController.skipOptions=!0}connectedCallback(){super.connectedCallback(),this.parseOptions(),this._options.forEach(t=>{t.selected&&!this.value&&(this.value=t.value)})}add(t,e){const i={value:t.value||t.text,label:t.text||t.value,selected:t.selected,disabled:t.disabled};if(e===void 0)this._options.push(i);else if(typeof e=="number")this._options.splice(e,0,i);else{const s=e.value||e.text,o=this._options.findIndex(n=>n.value===s);o>=0?this._options.splice(o,0,i):this._options.push(i)}t.selected&&(this.value=t.value||t.text,this.selectedIndex=this._options.findIndex(s=>s.value===this.value)),this.requestUpdate()}remove(t){var e;typeof t=="number"&&(this.selectedIndex===t&&(this.value=((e=this._options[0])==null?void 0:e.value)||""),this._options.splice(t,1),this.requestUpdate())}item(t){var e;return(e=this.inputRef.value)==null?void 0:e.item(t)}namedItem(t){var e;return(e=this.inputRef.value)==null?void 0:e.namedItem(t)}showPicker(){this.inputRef.value&&"showPicker"in this.inputRef.value&&this.inputRef.value.showPicker()}attributeChangedCallback(t,e,i){var s,o;t==="options"&&(this._options=i?JSON.parse(i):[]),t==="value"&&this.value!==e&&(this.selectedIndex=this.touched?this.returnNumberOrNull((s=this.inputRef.value)==null?void 0:s.selectedIndex):this._options.findIndex(n=>n.value===i),this.selectedOptions=(o=this.inputRef.value)==null?void 0:o.selectedOptions,this.valueChanged(i,e)),super.attributeChangedCallback(t,e,i)}update(t){var e,i;super.update(t),t.has("_optionsProp")&&this._optionsProp.length>0&&(this._options=this._optionsProp,this.requestUpdate("_options"),!this.value&&this._options.length>0&&(this.value=this._options[0].value,this.selectedIndex=0)),t.has("value")&&this.value!==t.get("value")&&(this.selectedIndex=this.touched?this.returnNumberOrNull((e=this.inputRef.value)==null?void 0:e.selectedIndex):this._options.findIndex(s=>s.value===this.value),this.selectedOptions=(i=this.inputRef.value)==null?void 0:i.selectedOptions,this.valueChanged(this.value,t.get("value"))),t.has("id")&&!this.name&&this.id&&(this.name=this.id)}firstUpdated(t){var e;super.firstUpdated(t),this._optionsProp.length&&(this._options=this._optionsProp),!this.value&&this._options.length>0?(this.value=this._options[0].value,this.selectedIndex=0):this.selectedIndex=this._options.findIndex(i=>i.value===this.value),this.selectedOptions=(e=this.inputRef.value)==null?void 0:e.selectedOptions}render(){const t=`pkt-input ${this.fullwidth?"pkt-input--fullwidth":""}`;return p.b`
2
2
  <pkt-input-wrapper
3
3
  ?counter=${this.counter}
4
4
  ?disabled=${this.disabled}
@@ -1,21 +1,21 @@
1
- import { e as u } from "./class-map-3ADKve8g.js";
2
- import { P as m, b as p, n as r, t as g } from "./element-CV9utnHJ.js";
3
- import { r as k } from "./state-l4hGZdFJ.js";
1
+ import { e as d } from "./class-map-3ADKve8g.js";
2
+ import { P as m, b as n, n as o, t as k } from "./element-CV9utnHJ.js";
3
+ import { r as b } from "./state-l4hGZdFJ.js";
4
4
  import { P as y } from "./pkt-slot-controller-D7CrjM52.js";
5
- import { e as b, n as f } from "./ref-Dma3n3i8.js";
5
+ import { e as g, n as f } from "./ref-Dma3n3i8.js";
6
6
  import "./icon-D0IQAVwS.js";
7
- import { o as d } from "./if-defined-rXcLNTzN.js";
8
- const _ = { size: { default: "medium" }, skin: { default: "blue" }, closeTag: { default: !1 }, type: { default: "button" } }, n = {
9
- props: _
7
+ import { o as u } from "./if-defined-rXcLNTzN.js";
8
+ const $ = { size: { default: "medium" }, skin: { default: "blue" }, closeTag: { default: !1 }, type: { default: "button" } }, l = {
9
+ props: $
10
10
  };
11
- var $ = Object.defineProperty, S = Object.getOwnPropertyDescriptor, e = (s, i, l, o) => {
12
- for (var a = o > 1 ? void 0 : o ? S(i, l) : i, c = s.length - 1, h; c >= 0; c--)
13
- (h = s[c]) && (a = (o ? h(i, l, a) : h(a)) || a);
14
- return o && a && $(i, l, a), a;
11
+ var _ = Object.defineProperty, S = Object.getOwnPropertyDescriptor, e = (s, i, p, a) => {
12
+ for (var r = a > 1 ? void 0 : a ? S(i, p) : i, c = s.length - 1, h; c >= 0; c--)
13
+ (h = s[c]) && (r = (a ? h(i, p, r) : h(r)) || r);
14
+ return a && r && _(i, p, r), r;
15
15
  };
16
16
  let t = class extends m {
17
17
  constructor() {
18
- super(), this.defaultSlot = b(), this.closeTag = n.props.closeTag.default, this.size = n.props.size.default, this.skin = n.props.skin.default, this.textStyle = null, this.iconName = void 0, this.type = n.props.type.default, this.ariaLabel = null, this._isClosed = !1, this._ariaDescription = null, this.close = (s) => {
18
+ super(), this.defaultSlot = g(), this.closeTag = l.props.closeTag.default, this.size = l.props.size.default, this.skin = l.props.skin.default, this.textStyle = null, this.iconName = void 0, this.type = l.props.type.default, this.ariaLabel = null, this.buttonTabindex = void 0, this._isClosed = !1, this._ariaDescription = null, this.close = (s) => {
19
19
  this._isClosed = !0, this.dispatchEvent(
20
20
  new CustomEvent("close", { detail: { origin: s }, bubbles: !1, composed: !0 })
21
21
  ), this.dispatchEvent(
@@ -27,10 +27,10 @@ let t = class extends m {
27
27
  * Lifecycle
28
28
  */
29
29
  firstUpdated(s) {
30
- var i, l;
30
+ var i, p;
31
31
  if (super.firstUpdated(s), this.closeTag && !this.ariaLabel) {
32
- const o = (l = (i = this.defaultSlot.value) == null ? void 0 : i.textContent) == null ? void 0 : l.trim();
33
- o && (this._ariaDescription = `Klikk for å fjerne ${o}`);
32
+ const a = (p = (i = this.defaultSlot.value) == null ? void 0 : i.textContent) == null ? void 0 : p.trim();
33
+ a && (this._ariaDescription = `Klikk for å fjerne ${a}`);
34
34
  }
35
35
  }
36
36
  render() {
@@ -49,15 +49,16 @@ let t = class extends m {
49
49
  "pkt-btn--icons-right-and-left": this.closeTag && !!this.iconName,
50
50
  "pkt-hide": this._isClosed
51
51
  };
52
- return this.closeTag ? p`
52
+ return this.closeTag ? n`
53
53
  <button
54
- class=${u(i)}
54
+ class=${d(i)}
55
55
  type=${this.type}
56
+ tabindex=${u(this.buttonTabindex)}
56
57
  @click=${this.close}
57
- aria-label=${d(this.ariaLabel || void 0)}
58
- aria-description=${d(this._ariaDescription || void 0)}
58
+ aria-label=${u(this.ariaLabel || void 0)}
59
+ aria-description=${u(this._ariaDescription || void 0)}
59
60
  >
60
- ${this.iconName && p`<pkt-icon
61
+ ${this.iconName && n`<pkt-icon
61
62
  class="pkt-tag__icon"
62
63
  name=${this.iconName}
63
64
  aria-hidden="true"
@@ -65,9 +66,9 @@ let t = class extends m {
65
66
  <span ${f(this.defaultSlot)}></span>
66
67
  <pkt-icon class="pkt-tag__close-btn" name="close"></pkt-icon>
67
68
  </button>
68
- ` : p`
69
- <span class=${u(s)}>
70
- ${this.iconName && p`<pkt-icon
69
+ ` : n`
70
+ <span class=${d(s)}>
71
+ ${this.iconName && n`<pkt-icon
71
72
  class="pkt-tag__icon"
72
73
  name=${this.iconName}
73
74
  aria-hidden="true"
@@ -78,34 +79,37 @@ let t = class extends m {
78
79
  }
79
80
  };
80
81
  e([
81
- r({ type: Boolean, reflect: !0 })
82
+ o({ type: Boolean, reflect: !0 })
82
83
  ], t.prototype, "closeTag", 2);
83
84
  e([
84
- r({ type: String, reflect: !0 })
85
+ o({ type: String, reflect: !0 })
85
86
  ], t.prototype, "size", 2);
86
87
  e([
87
- r({ type: String, reflect: !0 })
88
+ o({ type: String, reflect: !0 })
88
89
  ], t.prototype, "skin", 2);
89
90
  e([
90
- r({ type: String, reflect: !0 })
91
+ o({ type: String, reflect: !0 })
91
92
  ], t.prototype, "textStyle", 2);
92
93
  e([
93
- r({ type: String, reflect: !0 })
94
+ o({ type: String, reflect: !0 })
94
95
  ], t.prototype, "iconName", 2);
95
96
  e([
96
- r({ type: String })
97
+ o({ type: String })
97
98
  ], t.prototype, "type", 2);
98
99
  e([
99
- r({ type: String })
100
+ o({ type: String })
100
101
  ], t.prototype, "ariaLabel", 2);
101
102
  e([
102
- k()
103
+ o({ type: Number, attribute: "button-tabindex" })
104
+ ], t.prototype, "buttonTabindex", 2);
105
+ e([
106
+ b()
103
107
  ], t.prototype, "_isClosed", 2);
104
108
  e([
105
- k()
109
+ b()
106
110
  ], t.prototype, "_ariaDescription", 2);
107
111
  t = e([
108
- g("pkt-tag")
112
+ k("pkt-tag")
109
113
  ], t);
110
114
  export {
111
115
  t as P
@@ -0,0 +1,27 @@
1
+ "use strict";const h=require("./class-map-Bokp1SoS.cjs"),t=require("./element-CMTfByxQ.cjs"),d=require("./state-BNgpvY-A.cjs"),k=require("./pkt-slot-controller-D4nKlom5.cjs"),c=require("./ref-CxLwrCxt.cjs");require("./icon-Dj0oZZSa.cjs");const u=require("./if-defined-hKKmbsI8.cjs"),g={size:{default:"medium"},skin:{default:"blue"},closeTag:{default:!1},type:{default:"button"}},n={props:g};var f=Object.defineProperty,b=Object.getOwnPropertyDescriptor,s=(r,e,a,o)=>{for(var i=o>1?void 0:o?b(e,a):e,l=r.length-1,p;l>=0;l--)(p=r[l])&&(i=(o?p(e,a,i):p(i))||i);return o&&i&&f(e,a,i),i};exports.PktTag=class extends t.PktElement{constructor(){super(),this.defaultSlot=c.e(),this.closeTag=n.props.closeTag.default,this.size=n.props.size.default,this.skin=n.props.skin.default,this.textStyle=null,this.iconName=void 0,this.type=n.props.type.default,this.ariaLabel=null,this.buttonTabindex=void 0,this._isClosed=!1,this._ariaDescription=null,this.close=e=>{this._isClosed=!0,this.dispatchEvent(new CustomEvent("close",{detail:{origin:e},bubbles:!1,composed:!0})),this.dispatchEvent(new CustomEvent("on-close",{detail:{origin:e},bubbles:!1,composed:!0}))},this.slotController=new k.PktSlotController(this,this.defaultSlot),this._isClosed=!1}firstUpdated(e){var a,o;if(super.firstUpdated(e),this.closeTag&&!this.ariaLabel){const i=(o=(a=this.defaultSlot.value)==null?void 0:a.textContent)==null?void 0:o.trim();i&&(this._ariaDescription=`Klikk for å fjerne ${i}`)}}render(){const e={"pkt-tag":!0,[`pkt-tag--${this.size}`]:!!this.size,[`pkt-tag--${this.skin}`]:!!this.skin,[`pkt-tag--${this.textStyle}`]:!!this.textStyle},a={"pkt-tag":!0,"pkt-btn":!0,"pkt-btn--tertiary":!0,[`pkt-tag--${this.textStyle}`]:!!this.textStyle,[`pkt-tag--${this.size}`]:!!this.size,[`pkt-tag--${this.skin}`]:!!this.skin,"pkt-btn--icons-right-and-left":this.closeTag&&!!this.iconName,"pkt-hide":this._isClosed};return this.closeTag?t.b`
2
+ <button
3
+ class=${h.e(a)}
4
+ type=${this.type}
5
+ tabindex=${u.o(this.buttonTabindex)}
6
+ @click=${this.close}
7
+ aria-label=${u.o(this.ariaLabel||void 0)}
8
+ aria-description=${u.o(this._ariaDescription||void 0)}
9
+ >
10
+ ${this.iconName&&t.b`<pkt-icon
11
+ class="pkt-tag__icon"
12
+ name=${this.iconName}
13
+ aria-hidden="true"
14
+ ></pkt-icon>`}
15
+ <span ${c.n(this.defaultSlot)}></span>
16
+ <pkt-icon class="pkt-tag__close-btn" name="close"></pkt-icon>
17
+ </button>
18
+ `:t.b`
19
+ <span class=${h.e(e)}>
20
+ ${this.iconName&&t.b`<pkt-icon
21
+ class="pkt-tag__icon"
22
+ name=${this.iconName}
23
+ aria-hidden="true"
24
+ ></pkt-icon>`}
25
+ <span ${c.n(this.defaultSlot)}></span>
26
+ </span>
27
+ `}};s([t.n({type:Boolean,reflect:!0})],exports.PktTag.prototype,"closeTag",2);s([t.n({type:String,reflect:!0})],exports.PktTag.prototype,"size",2);s([t.n({type:String,reflect:!0})],exports.PktTag.prototype,"skin",2);s([t.n({type:String,reflect:!0})],exports.PktTag.prototype,"textStyle",2);s([t.n({type:String,reflect:!0})],exports.PktTag.prototype,"iconName",2);s([t.n({type:String})],exports.PktTag.prototype,"type",2);s([t.n({type:String})],exports.PktTag.prototype,"ariaLabel",2);s([t.n({type:Number,attribute:"button-tabindex"})],exports.PktTag.prototype,"buttonTabindex",2);s([d.r()],exports.PktTag.prototype,"_isClosed",2);s([d.r()],exports.PktTag.prototype,"_ariaDescription",2);exports.PktTag=s([t.t("pkt-tag")],exports.PktTag);
@@ -1,4 +1,4 @@
1
- "use strict";const s=require("./element-CMTfByxQ.cjs"),p=require("./if-defined-hKKmbsI8.cjs"),c=require("./state-BNgpvY-A.cjs"),l=require("./ref-CxLwrCxt.cjs"),$=require("./class-map-Bokp1SoS.cjs"),a=require("./directive-C7oCP5Bh.cjs"),d=require("./directive-helpers-B3Vpl3xV.cjs"),x=require("./input-element-DM0tY799.cjs"),g=require("./pkt-slot-controller-D4nKlom5.cjs");require("./input-wrapper-JU4D2TGu.cjs");require("./icon-Dj0oZZSa.cjs");/**
1
+ "use strict";const s=require("./element-CMTfByxQ.cjs"),p=require("./if-defined-hKKmbsI8.cjs"),c=require("./state-BNgpvY-A.cjs"),l=require("./ref-CxLwrCxt.cjs"),$=require("./class-map-Bokp1SoS.cjs"),a=require("./directive-C7oCP5Bh.cjs"),d=require("./directive-helpers-B3Vpl3xV.cjs"),x=require("./input-element-CSDVA3Y6.cjs"),g=require("./pkt-slot-controller-D4nKlom5.cjs");require("./input-wrapper-JU4D2TGu.cjs");require("./icon-Dj0oZZSa.cjs");/**
2
2
  * @license
3
3
  * Copyright 2020 Google LLC
4
4
  * SPDX-License-Identifier: BSD-3-Clause
@@ -5,7 +5,7 @@ import { e as c, n as m } from "./ref-Dma3n3i8.js";
5
5
  import { e as v } from "./class-map-3ADKve8g.js";
6
6
  import { e as b, i as T, t as a } from "./directive-oAbCiebi.js";
7
7
  import { r as y, p as w } from "./directive-helpers-CyPtA1_i.js";
8
- import { P as C } from "./input-element-Bkv6Yxld.js";
8
+ import { P as C } from "./input-element-BGNbdzy2.js";
9
9
  import { P as E } from "./pkt-slot-controller-D7CrjM52.js";
10
10
  import "./input-wrapper-CaUY90qz.js";
11
11
  import "./icon-D0IQAVwS.js";
@@ -3,7 +3,7 @@ import { o as u } from "./if-defined-rXcLNTzN.js";
3
3
  import { r as f } from "./state-l4hGZdFJ.js";
4
4
  import { e as d, n as m } from "./ref-Dma3n3i8.js";
5
5
  import { e as g } from "./class-map-3ADKve8g.js";
6
- import { P as x } from "./input-element-Bkv6Yxld.js";
6
+ import { P as x } from "./input-element-BGNbdzy2.js";
7
7
  import { P as v } from "./pkt-slot-controller-D7CrjM52.js";
8
8
  import "./input-wrapper-CaUY90qz.js";
9
9
  import "./icon-D0IQAVwS.js";
@@ -1,4 +1,4 @@
1
- "use strict";const t=require("./element-CMTfByxQ.cjs"),a=require("./if-defined-hKKmbsI8.cjs"),l=require("./state-BNgpvY-A.cjs"),h=require("./ref-CxLwrCxt.cjs"),c=require("./class-map-Bokp1SoS.cjs"),d=require("./input-element-DM0tY799.cjs"),$=require("./pkt-slot-controller-D4nKlom5.cjs");require("./input-wrapper-JU4D2TGu.cjs");require("./icon-Dj0oZZSa.cjs");var x=Object.defineProperty,f=Object.getOwnPropertyDescriptor,r=(u,i,s,n)=>{for(var e=n>1?void 0:n?f(i,s):i,o=u.length-1,p;o>=0;o--)(p=u[o])&&(e=(n?p(i,s,e):p(e))||e);return n&&e&&x(i,s,e),e};exports.PktTextinput=class extends d.PktInputElement{constructor(){super(),this.inputRef=h.e(),this.helptextSlot=h.e(),this.value="",this.type="text",this.size=null,this.autocomplete=null,this.iconNameRight=null,this.prefix=null,this.suffix=null,this.omitSearchIcon=!1,this.counterCurrent=0,this.slotController=new $.PktSlotController(this,this.helptextSlot)}attributeChangedCallback(i,s,n){i==="value"&&this.value!==s&&(this.counterCurrent=n?n.length:0,this.valueChanged(n,s)),super.attributeChangedCallback(i,s,n)}updated(i){var s;super.updated(i),i.has("value")&&(this.counterCurrent=((s=this.value)==null?void 0:s.length)||0,this.valueChanged(this.value,i.get("value"))),i.has("id")&&!this.name&&this.id&&(this.name=this.id)}render(){const i=this.type==="search"&&!this.iconNameRight&&!this.omitSearchIcon,s=c.e({"pkt-input":!0,"pkt-input--fullwidth":this.fullwidth,"pkt-input--counter-error":this.counter&&this.counterMaxLength&&this.value.length&&this.value.length>this.counterMaxLength}),n=this.ariaLabelledby||`${this.id}-input-label`;return t.b`
1
+ "use strict";const t=require("./element-CMTfByxQ.cjs"),a=require("./if-defined-hKKmbsI8.cjs"),l=require("./state-BNgpvY-A.cjs"),h=require("./ref-CxLwrCxt.cjs"),c=require("./class-map-Bokp1SoS.cjs"),d=require("./input-element-CSDVA3Y6.cjs"),$=require("./pkt-slot-controller-D4nKlom5.cjs");require("./input-wrapper-JU4D2TGu.cjs");require("./icon-Dj0oZZSa.cjs");var x=Object.defineProperty,f=Object.getOwnPropertyDescriptor,r=(u,i,s,n)=>{for(var e=n>1?void 0:n?f(i,s):i,o=u.length-1,p;o>=0;o--)(p=u[o])&&(e=(n?p(i,s,e):p(e))||e);return n&&e&&x(i,s,e),e};exports.PktTextinput=class extends d.PktInputElement{constructor(){super(),this.inputRef=h.e(),this.helptextSlot=h.e(),this.value="",this.type="text",this.size=null,this.autocomplete=null,this.iconNameRight=null,this.prefix=null,this.suffix=null,this.omitSearchIcon=!1,this.counterCurrent=0,this.slotController=new $.PktSlotController(this,this.helptextSlot)}attributeChangedCallback(i,s,n){i==="value"&&this.value!==s&&(this.counterCurrent=n?n.length:0,this.valueChanged(n,s)),super.attributeChangedCallback(i,s,n)}updated(i){var s;super.updated(i),i.has("value")&&(this.counterCurrent=((s=this.value)==null?void 0:s.length)||0,this.valueChanged(this.value,i.get("value"))),i.has("id")&&!this.name&&this.id&&(this.name=this.id)}render(){const i=this.type==="search"&&!this.iconNameRight&&!this.omitSearchIcon,s=c.e({"pkt-input":!0,"pkt-input--fullwidth":this.fullwidth,"pkt-input--counter-error":this.counter&&this.counterMaxLength&&this.value.length&&this.value.length>this.counterMaxLength}),n=this.ariaLabelledby||`${this.id}-input-label`;return t.b`
2
2
  <pkt-input-wrapper
3
3
  label="${this.label}"
4
4
  ?counter=${this.counter}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-elements",
3
- "version": "15.4.5",
3
+ "version": "16.0.2",
4
4
  "description": "Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo",
5
5
  "homepage": "https://punkt.oslo.kommune.no",
6
6
  "author": "Team Designsystem, Oslo Origo",
@@ -41,8 +41,8 @@
41
41
  "@babel/plugin-transform-private-property-in-object": "^7.25.9",
42
42
  "@babel/preset-env": "^7.28.3",
43
43
  "@babel/preset-typescript": "^7.25.9",
44
- "@oslokommune/punkt-assets": "^15.0.0",
45
- "@oslokommune/punkt-css": "^15.4.4",
44
+ "@oslokommune/punkt-assets": "^16.0.0",
45
+ "@oslokommune/punkt-css": "^16.0.0",
46
46
  "@testing-library/jest-dom": "^6.6.3",
47
47
  "@typescript-eslint/eslint-plugin": "^8.46.0",
48
48
  "@typescript-eslint/parser": "^8.46.0",
@@ -79,5 +79,5 @@
79
79
  "url": "https://github.com/oslokommune/punkt/issues"
80
80
  },
81
81
  "license": "MIT",
82
- "gitHead": "64154bb897bebc4a10bddd741816ca5943b1f687"
82
+ "gitHead": "78107e738894fed2f40d8baad0273c29d58544f0"
83
83
  }
@@ -78,6 +78,7 @@ export class PktCard extends PktElement implements IPktCard {
78
78
  return value as TCardSkin
79
79
  } else {
80
80
  if (value && !validSkins.includes(value as TCardSkin)) {
81
+ // eslint-disable-next-line no-console -- Acceptable to log a warning for invalid skin values
81
82
  console.warn(
82
83
  `Invalid skin value "${value}". Using default skin "${specs.props.skin.default}".`,
83
84
  )
@@ -0,0 +1,158 @@
1
+ import { property, state } from 'lit/decorators.js'
2
+ import { Ref, createRef } from 'lit/directives/ref.js'
3
+ import { PktInputElement } from '@/base-elements/input-element'
4
+ import { PktOptionsSlotController } from '@/controllers/pkt-options-controller'
5
+ import { PktSlotController } from '@/controllers/pkt-slot-controller'
6
+ import { isMaxSelectionReached } from 'shared-utils/combobox/option-utils'
7
+ import specs from 'componentSpecs/combobox.json'
8
+
9
+ import type { IPktComboboxOption, TPktComboboxTagPlacement } from './combobox-types'
10
+ import PktListbox from '../listbox'
11
+
12
+ /**
13
+ * Base class for PktCombobox.
14
+ * Declares all reactive properties, state, refs, and simple helpers.
15
+ */
16
+ export class ComboboxBase extends PktInputElement {
17
+ protected readonly helptextSlot: Ref<HTMLElement> = createRef()
18
+
19
+ constructor() {
20
+ super()
21
+ this.optionsController = new PktOptionsSlotController(this)
22
+ this.slotController = new PktSlotController(this, this.helptextSlot)
23
+ this.slotController.skipOptions = true
24
+ }
25
+
26
+ // Props / Attributes
27
+ @property({ type: String, reflect: true }) value: string | string[] = ''
28
+ @property({ type: Array }) options: IPktComboboxOption[] = []
29
+ @property({ type: Array, attribute: 'default-options' }) defaultOptions: IPktComboboxOption[] = []
30
+ @property({ type: Boolean, attribute: 'allow-user-input' }) allowUserInput: boolean = false
31
+ @property({ type: Boolean }) typeahead: boolean = false
32
+ @property({ type: Boolean, attribute: 'include-search' }) includeSearch: boolean = false
33
+ @property({ type: String, attribute: 'search-placeholder' }) searchPlaceholder: string = ''
34
+ @property({ type: Boolean }) multiple: boolean = false
35
+ @property({ type: Number }) maxlength: number | null = null
36
+ @property({ type: String, attribute: 'display-value-as' }) displayValueAs: string =
37
+ specs.props.displayValueAs.default
38
+ @property({ type: String, attribute: 'tag-placement' })
39
+ tagPlacement: TPktComboboxTagPlacement | null = null
40
+ // Internal use only — syncs to _isOptionsOpen in updated(), but does not
41
+ // reliably open the listbox as a declarative attribute (requires focus state
42
+ // and populated _options). Not part of the public API / component spec.
43
+ // Used in tests to set initial open state without relying on focus or options population.
44
+ @property({ type: Boolean, attribute: 'open' }) isOpen: boolean = false
45
+
46
+ // Internal state
47
+ @state() override _options: IPktComboboxOption[] = []
48
+ @state() protected _value: string[] = []
49
+ @state() protected _isOptionsOpen = false
50
+ @state() protected _userInfoMessage: string = ''
51
+ @state() protected _addValueText: string | null = null
52
+ @state() protected _maxIsReached: boolean = false
53
+ @state() protected _search: string = ''
54
+ @state() protected _inputFocus: boolean = false
55
+ protected _internalValueSync = false
56
+ protected _optionsFromSlot = false
57
+ protected _lastSlotGeneration = 0
58
+ /** When true, the next handleFocus call will not reopen the dropdown. */
59
+ protected _suppressNextOpen = false
60
+
61
+ // Refs
62
+ protected readonly inputRef: Ref<HTMLInputElement> = createRef()
63
+ protected readonly triggerRef: Ref<HTMLDivElement> = createRef()
64
+ protected readonly listboxRef: Ref<PktListbox> = createRef()
65
+
66
+ protected get _hasTextInput(): boolean {
67
+ return this.typeahead || this.allowUserInput
68
+ }
69
+
70
+ protected get _selectionDescription(): string | undefined {
71
+ if (!this.multiple || this._value.length === 0) return undefined
72
+ return `${this._value.length} valgt`
73
+ }
74
+
75
+ /**
76
+ * Focuses the appropriate trigger element after closing the listbox.
77
+ * Select-only: the combobox input div. Editable: the text input.
78
+ */
79
+ protected focusTrigger(): void {
80
+ if (this._hasTextInput) {
81
+ this.inputRef.value?.focus()
82
+ } else {
83
+ this.triggerRef.value?.focus()
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Parses the value prop into an internal string array.
89
+ */
90
+ protected parseValue(): string[] {
91
+ if (Array.isArray(this.value)) {
92
+ return this.multiple ? this.value : this.value.length > 0 ? [this.value[0]] : []
93
+ }
94
+ if (this.value && this.multiple) {
95
+ return this.value.split(',')
96
+ }
97
+ if (this.value) {
98
+ return [this.value]
99
+ }
100
+ return []
101
+ }
102
+
103
+ /**
104
+ * Updates the _maxIsReached state flag.
105
+ */
106
+ protected updateMaxReached(): void {
107
+ this._maxIsReached = isMaxSelectionReached(this._value.length, this.maxlength)
108
+ }
109
+
110
+ /**
111
+ * Syncs the public value property from internal _value state and dispatches
112
+ * events if the value content changed. Always sets this.value as a string
113
+ * to prevent array→string reflect cascades.
114
+ */
115
+ protected syncValueAndDispatch(oldInternal: string[]): void {
116
+ const newInternal = this._value
117
+
118
+ // Sync public value as a string (avoids array→string attribute reflect cascade)
119
+ const newPublicStr = this.multiple ? newInternal.join(',') : newInternal[0] || ''
120
+ const currentPublicStr = Array.isArray(this.value)
121
+ ? this.value.join(',')
122
+ : String(this.value || '')
123
+
124
+ if (newPublicStr !== currentPublicStr) {
125
+ this._internalValueSync = true
126
+ this.value = newPublicStr
127
+ }
128
+
129
+ // Dispatch events if value content changed
130
+ if (oldInternal?.join(',') !== newInternal.join(',')) {
131
+ const eventValue = this.multiple ? [...newInternal] : newInternal[0] || ''
132
+ this.onChange(eventValue)
133
+ } else if (newInternal.length === 0 && oldInternal && oldInternal.length > 0) {
134
+ this.clearInputValue()
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Override onChange to skip the base class touched guard.
140
+ * The base class returns early on the first call (setting touched = true but not
141
+ * dispatching events). Combobox needs consistent event dispatch regardless of
142
+ * touched state.
143
+ */
144
+ protected override onChange(value: string | string[]): void {
145
+ this.touched = true
146
+ super.onChange(value)
147
+ }
148
+
149
+ /**
150
+ * No-op override of the base class valueChanged.
151
+ * The base class version sets both this.value AND this._value, which creates
152
+ * an infinite _value → valueChanged → value → parseValue → _value loop.
153
+ * Combobox handles value sync and event dispatch in syncValueAndDispatch() instead.
154
+ */
155
+ protected override valueChanged(): void {
156
+ // Intentionally empty — combobox manages value sync in updated()
157
+ }
158
+ }