@oslokommune/punkt-elements 16.7.7 → 16.8.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ og skriver commits ca etter [Conventional Commits](https://conventionalcommits.o
5
5
 
6
6
  ---
7
7
 
8
+ ## [16.8.2](https://github.com/oslokommune/punkt/compare/16.8.1...16.8.2) (2026-04-21)
9
+
10
+ ### ⚠ BREAKING CHANGES
11
+ Ingen
12
+
13
+ ### Features
14
+ Ingen
15
+
16
+ ### Bug Fixes
17
+ Ingen
18
+
19
+ ### Chores
20
+ Ingen
21
+
22
+ ---
23
+
24
+
8
25
  ## [16.7.7](https://github.com/oslokommune/punkt/compare/16.7.6...16.7.7) (2026-04-16)
9
26
 
10
27
  ### ⚠ BREAKING CHANGES
package/dist/index.d.ts CHANGED
@@ -471,6 +471,7 @@ declare interface IPktSelect {
471
471
 
472
472
  export declare interface IPktTabItem {
473
473
  active?: boolean;
474
+ disabled?: boolean;
474
475
  href?: string;
475
476
  icon?: string;
476
477
  controls?: string;
@@ -1685,6 +1686,7 @@ declare class PktShadowElement<T = {}> extends LitElement {
1685
1686
 
1686
1687
  export declare class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktTabItem {
1687
1688
  active: boolean;
1689
+ disabled: boolean;
1688
1690
  href: string;
1689
1691
  icon: string;
1690
1692
  controls: string;
@@ -1695,8 +1697,9 @@ export declare class PktTabItem extends PktElementWithSlot<IPktTabItem> implemen
1695
1697
  elementRef: Ref<HTMLAnchorElement | HTMLButtonElement>;
1696
1698
  constructor();
1697
1699
  connectedCallback(): void;
1700
+ updated(): void;
1698
1701
  private handleClick;
1699
- private handleKeyUp;
1702
+ private handleKeyDown;
1700
1703
  render(): TemplateResult<1>;
1701
1704
  }
1702
1705
 
@@ -1704,11 +1707,14 @@ export declare class PktTabs extends PktElementWithSlot<IPktTabs> implements IPk
1704
1707
  arrowNav: boolean;
1705
1708
  disableArrowNav: boolean;
1706
1709
  private tabRefs;
1710
+ private disabledMap;
1707
1711
  private tabCount;
1708
1712
  private get useArrowNav();
1709
1713
  private context;
1710
1714
  updated(changedProperties: PropertyValues): void;
1711
1715
  private registerTab;
1716
+ private isTabDisabled;
1717
+ private findEnabledIndex;
1712
1718
  private handleClick;
1713
1719
  private handleKeyUp;
1714
1720
  render(): TemplateResult<1>;
@@ -1864,7 +1870,7 @@ declare interface TabsContext {
1864
1870
  /**
1865
1871
  * Register a tab item with the parent tabs container
1866
1872
  */
1867
- registerTab: (element: HTMLElement, index: number) => void;
1873
+ registerTab: (element: HTMLElement, index: number, disabled?: boolean) => void;
1868
1874
  /**
1869
1875
  * Handle click event from a tab item
1870
1876
  */
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./alert-BEEnTiCb.cjs`),t=require(`./icon-Dr8sfT2X.cjs`),n=require(`./button-D-TOBaJB.cjs`),r=require(`./accordion-BB1JrONI.cjs`),i=require(`./backlink-Ba03LlbB.cjs`),a=require(`./breadcrumbs-CD1zib3s.cjs`),o=require(`./calendar-E5jJLt8D.cjs`),s=require(`./card-CUFhMVim.cjs`),c=require(`./tag-DLthw5b4.cjs`),l=require(`./heading-BgSWIRii.cjs`),u=require(`./combobox-CGbFTWq_.cjs`),d=require(`./header-DWgPgGfS.cjs`),f=require(`./datepicker-DpmlsnH_.cjs`),p=require(`./helptext-D-3QfZv5.cjs`),m=require(`./input-wrapper-BRuVELpK.cjs`),h=require(`./consent-hwu7pp3S.cjs`),g=require(`./checkbox-BARDb9iV.cjs`),_=require(`./link-CyTCpI0k.cjs`),v=require(`./textinput-DFh8_9cx.cjs`),y=require(`./linkcard-BE4-FewK.cjs`),b=require(`./loader-CX2rsC01.cjs`),x=require(`./messagebox-Din9X53b.cjs`),S=require(`./modal-DS_wUOIv.cjs`),C=require(`./progressbar-CdXtmOBb.cjs`),w=require(`./radiobutton-GOnpUbCW.cjs`),T=require(`./tabs-D8nL9rWx.cjs`),E=require(`./textarea-D7aGOyDf.cjs`),D=require(`./select-C2i8sOk5.cjs`),O=require(`./timepicker-BIfE7KKJ.cjs`);exports.PktAccordion=r.r,exports.PktAccordionItem=r.n,exports.PktAlert=e.n,exports.PktBackLink=i.n,exports.PktBreadcrumbs=a.t,exports.PktButton=n.n,exports.PktCalendar=o.n,exports.PktCard=s.n,exports.PktCheckbox=g.n,exports.PktCombobox=u.n,exports.PktConsent=h.n,exports.PktDateTags=f.a,exports.PktDatepicker=f.t,exports.PktHeader=d.t,exports.PktHeaderService=d.n,exports.PktHeaderUserMenu=d.r,exports.PktHeading=l.n,exports.PktHelptext=p.n,exports.PktIcon=t.n,exports.PktInputWrapper=m.n,exports.PktLink=_.n,exports.PktLinkCard=y.n,exports.PktLoader=b.n,exports.PktMessagebox=x.n,exports.PktModal=S.n,exports.PktProgressbar=C.n,exports.PktRadioButton=w.n,exports.PktRadiobutton=w.n,exports.PktSelect=D.n,exports.PktTabItem=T.n,exports.PktTabs=T.r,exports.PktTag=c.n,exports.PktTextarea=E.n,exports.PktTextinput=v.n,exports.PktTimepicker=O.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./alert-BEEnTiCb.cjs`),t=require(`./icon-Dr8sfT2X.cjs`),n=require(`./button-D-TOBaJB.cjs`),r=require(`./accordion-BB1JrONI.cjs`),i=require(`./backlink-Ba03LlbB.cjs`),a=require(`./breadcrumbs-CD1zib3s.cjs`),o=require(`./calendar-E5jJLt8D.cjs`),s=require(`./card-CUFhMVim.cjs`),c=require(`./tag-DLthw5b4.cjs`),l=require(`./heading-BgSWIRii.cjs`),u=require(`./combobox-CGbFTWq_.cjs`),d=require(`./header-DWgPgGfS.cjs`),f=require(`./datepicker-DpmlsnH_.cjs`),p=require(`./helptext-D-3QfZv5.cjs`),m=require(`./input-wrapper-BRuVELpK.cjs`),h=require(`./consent-hwu7pp3S.cjs`),g=require(`./checkbox-BARDb9iV.cjs`),_=require(`./link-CyTCpI0k.cjs`),v=require(`./textinput-DFh8_9cx.cjs`),y=require(`./linkcard-BE4-FewK.cjs`),b=require(`./loader-CX2rsC01.cjs`),x=require(`./messagebox-Din9X53b.cjs`),S=require(`./modal-DS_wUOIv.cjs`),C=require(`./progressbar-CdXtmOBb.cjs`),w=require(`./radiobutton-GOnpUbCW.cjs`),T=require(`./tabs-COwm9b3n.cjs`),E=require(`./textarea-D7aGOyDf.cjs`),D=require(`./select-C2i8sOk5.cjs`),O=require(`./timepicker-BIfE7KKJ.cjs`);exports.PktAccordion=r.r,exports.PktAccordionItem=r.n,exports.PktAlert=e.n,exports.PktBackLink=i.n,exports.PktBreadcrumbs=a.t,exports.PktButton=n.n,exports.PktCalendar=o.n,exports.PktCard=s.n,exports.PktCheckbox=g.n,exports.PktCombobox=u.n,exports.PktConsent=h.n,exports.PktDateTags=f.a,exports.PktDatepicker=f.t,exports.PktHeader=d.t,exports.PktHeaderService=d.n,exports.PktHeaderUserMenu=d.r,exports.PktHeading=l.n,exports.PktHelptext=p.n,exports.PktIcon=t.n,exports.PktInputWrapper=m.n,exports.PktLink=_.n,exports.PktLinkCard=y.n,exports.PktLoader=b.n,exports.PktMessagebox=x.n,exports.PktModal=S.n,exports.PktProgressbar=C.n,exports.PktRadioButton=w.n,exports.PktRadiobutton=w.n,exports.PktSelect=D.n,exports.PktTabItem=T.n,exports.PktTabs=T.r,exports.PktTag=c.n,exports.PktTextarea=E.n,exports.PktTextinput=v.n,exports.PktTimepicker=O.t;
package/dist/pkt-index.js CHANGED
@@ -23,7 +23,7 @@ import { n as T } from "./messagebox-Cwlg5sOr.js";
23
23
  import { n as E } from "./modal-C_wtsh9m.js";
24
24
  import { n as D } from "./progressbar-Dy9AiYQz.js";
25
25
  import { n as O } from "./radiobutton-V_KPbup_.js";
26
- import { n as k, r as A } from "./tabs-Y7utmJi9.js";
26
+ import { n as k, r as A } from "./tabs-C0DWPmn2.js";
27
27
  import { n as j } from "./textarea-DYOBhJrN.js";
28
28
  import { n as M } from "./select-BSAFd8Wa.js";
29
29
  import { t as N } from "./timepicker-DaSwn1FA.js";
package/dist/pkt-tabs.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require(`./tabs-D8nL9rWx.cjs`);exports.PktTabItem=e.n,exports.PktTabs=e.r,exports.default=e.t;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require(`./tabs-COwm9b3n.cjs`);exports.PktTabItem=e.n,exports.PktTabs=e.r,exports.default=e.t;
package/dist/pkt-tabs.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as e, r as t, t as n } from "./tabs-Y7utmJi9.js";
1
+ import { n as e, r as t, t as n } from "./tabs-C0DWPmn2.js";
2
2
  export { e as PktTabItem, t as PktTabs, n as default };
@@ -171,7 +171,7 @@ function _({ context: e, subscribe: t }) {
171
171
  //#region src/components/tabs/tabs-context.ts
172
172
  var v = d(Symbol("pkt-tabs-context")), y = class extends o {
173
173
  constructor(...e) {
174
- super(...e), this.arrowNav = !0, this.disableArrowNav = !1, this.tabRefs = [], this.tabCount = 0, this.context = {
174
+ super(...e), this.arrowNav = !0, this.disableArrowNav = !1, this.tabRefs = [], this.disabledMap = {}, this.tabCount = 0, this.context = {
175
175
  useArrowNav: this.useArrowNav,
176
176
  registerTab: this.registerTab.bind(this),
177
177
  handleClick: this.handleClick.bind(this),
@@ -187,22 +187,41 @@ var v = d(Symbol("pkt-tabs-context")), y = class extends o {
187
187
  useArrowNav: this.useArrowNav
188
188
  });
189
189
  }
190
- registerTab(e, t) {
191
- this.tabRefs[t] = e, this.tabCount = Math.max(this.tabCount, t + 1);
190
+ registerTab(e, t, n = !1) {
191
+ this.tabRefs[t] = e, this.disabledMap[t] = n, this.tabCount = Math.max(this.tabCount, t + 1);
192
+ }
193
+ isTabDisabled(e) {
194
+ return !!this.disabledMap[e];
195
+ }
196
+ findEnabledIndex(e, t) {
197
+ let n = e + t;
198
+ for (; n >= 0 && n < this.tabCount;) {
199
+ if (!this.isTabDisabled(n)) return n;
200
+ n += t;
201
+ }
202
+ return null;
192
203
  }
193
204
  handleClick(e) {
194
- this.dispatchEvent(new CustomEvent("tab-selected", {
205
+ this.isTabDisabled(e) || this.dispatchEvent(new CustomEvent("tab-selected", {
195
206
  detail: { index: e },
196
207
  bubbles: !0,
197
208
  composed: !0
198
209
  }));
199
210
  }
200
211
  handleKeyUp(e, t) {
201
- this.useArrowNav && (e.code === "ArrowLeft" && t !== 0 && this.tabRefs[t - 1]?.focus(), e.code === "ArrowRight" && t < this.tabCount - 1 && this.tabRefs[t + 1]?.focus(), (e.code === "ArrowDown" || e.code === "Space") && this.dispatchEvent(new CustomEvent("tab-selected", {
202
- detail: { index: t },
203
- bubbles: !0,
204
- composed: !0
205
- })));
212
+ if (this.useArrowNav) {
213
+ if (e.key === "ArrowLeft") {
214
+ e.preventDefault();
215
+ let n = this.findEnabledIndex(t, -1);
216
+ n !== null && this.tabRefs[n]?.focus();
217
+ }
218
+ if (e.key === "ArrowRight") {
219
+ e.preventDefault();
220
+ let n = this.findEnabledIndex(t, 1);
221
+ n !== null && this.tabRefs[n]?.focus();
222
+ }
223
+ (e.key === "Enter" || e.key === " " || e.key === "Spacebar" || e.key === "ArrowDown") && (e.preventDefault(), this.handleClick(t));
224
+ }
206
225
  }
207
226
  render() {
208
227
  return t`
@@ -220,7 +239,7 @@ r([i({
220
239
  type: Boolean,
221
240
  reflect: !0,
222
241
  attribute: "disable-arrow-nav"
223
- })], y.prototype, "disableArrowNav", void 0), r([n()], y.prototype, "tabRefs", void 0), r([n()], y.prototype, "tabCount", void 0), r([g({ context: v }), n()], y.prototype, "context", void 0);
242
+ })], y.prototype, "disableArrowNav", void 0), r([n()], y.prototype, "tabRefs", void 0), r([n()], y.prototype, "disabledMap", void 0), r([n()], y.prototype, "tabCount", void 0), r([g({ context: v }), n()], y.prototype, "context", void 0);
224
243
  try {
225
244
  e("pkt-tabs")(y);
226
245
  } catch {
@@ -230,21 +249,32 @@ try {
230
249
  //#region src/components/tabs/tabitem.ts
231
250
  var b = class extends o {
232
251
  constructor() {
233
- super(), this.active = !1, this.href = "", this.icon = "", this.controls = "", this.tag = "", this.tagSkin = "blue", this.index = 0, this.elementRef = l();
252
+ super(), this.active = !1, this.disabled = !1, this.href = "", this.icon = "", this.controls = "", this.tag = "", this.tagSkin = "blue", this.index = 0, this.elementRef = l();
234
253
  }
235
254
  connectedCallback() {
236
255
  super.connectedCallback(), this.updateComplete.then(() => {
237
- this.elementRef.value && this.context && this.context.registerTab(this.elementRef.value, this.index);
256
+ this.elementRef.value && this.context && this.context.registerTab(this.elementRef.value, this.index, this.disabled);
238
257
  });
239
258
  }
240
- handleClick() {
259
+ updated() {
260
+ this.elementRef.value && this.context && this.context.registerTab(this.elementRef.value, this.index, this.disabled);
261
+ }
262
+ handleClick(e) {
263
+ if (this.disabled) {
264
+ e.preventDefault(), e.stopPropagation();
265
+ return;
266
+ }
241
267
  this.context && this.context.handleClick(this.index);
242
268
  }
243
- handleKeyUp(e) {
269
+ handleKeyDown(e) {
270
+ if (this.disabled && (e.key === "Enter" || e.key === " " || e.key === "Spacebar")) {
271
+ e.preventDefault(), e.stopPropagation();
272
+ return;
273
+ }
244
274
  this.context && this.context.handleKeyUp(e, this.index);
245
275
  }
246
276
  render() {
247
- let e = this.context?.useArrowNav ?? !0, n = this.active ? "active" : "", r = e ? "tab" : void 0, i = e ? this.active : void 0, o = this.active || !e ? void 0 : -1, l = t`
277
+ let e = this.context?.useArrowNav ?? !0, n = this.active && !this.disabled, r = [n ? "active" : "", this.disabled ? "pkt-tabs__item--disabled" : ""].filter(Boolean).join(" "), i = e ? "tab" : void 0, o = e ? n : void 0, l = this.disabled ? -1 : n || !e ? void 0 : -1, u = t`
248
278
  ${this.icon ? t`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>` : ""}
249
279
  <span>${a(this)}</span>
250
280
  ${this.tag ? t`<pkt-tag skin=${this.tagSkin} size="small">${this.tag}</pkt-tag>` : ""}
@@ -252,30 +282,33 @@ var b = class extends o {
252
282
  return this.href ? t`
253
283
  <a
254
284
  ${c(this.elementRef)}
255
- href=${this.href}
256
- class="pkt-tabs__link ${n}"
257
- role=${s(r)}
258
- aria-selected=${s(i)}
285
+ href=${s(this.disabled ? void 0 : this.href)}
286
+ class="pkt-tabs__link ${r}"
287
+ role=${s(i)}
288
+ aria-selected=${s(o)}
289
+ aria-disabled=${s(this.disabled ? "true" : void 0)}
259
290
  aria-controls=${s(this.controls || void 0)}
260
- tabindex=${s(o)}
291
+ tabindex=${s(l)}
261
292
  @click=${this.handleClick}
262
- @keyup=${this.handleKeyUp}
293
+ @keydown=${this.handleKeyDown}
263
294
  >
264
- ${l}
295
+ ${u}
265
296
  </a>
266
297
  ` : t`
267
298
  <button
268
299
  ${c(this.elementRef)}
269
300
  type="button"
270
- class="pkt-tabs__button pkt-link-button ${n}"
271
- role=${s(r)}
272
- aria-selected=${s(i)}
301
+ class="pkt-tabs__button pkt-link-button ${r}"
302
+ ?disabled=${this.disabled}
303
+ aria-disabled=${s(this.disabled ? "true" : void 0)}
304
+ role=${s(i)}
305
+ aria-selected=${s(o)}
273
306
  aria-controls=${s(this.controls || void 0)}
274
- tabindex=${s(o)}
307
+ tabindex=${s(l)}
275
308
  @click=${this.handleClick}
276
- @keyup=${this.handleKeyUp}
309
+ @keydown=${this.handleKeyDown}
277
310
  >
278
- ${l}
311
+ ${u}
279
312
  </button>
280
313
  `;
281
314
  }
@@ -284,6 +317,9 @@ r([i({
284
317
  type: Boolean,
285
318
  reflect: !0
286
319
  })], b.prototype, "active", void 0), r([i({
320
+ type: Boolean,
321
+ reflect: !0
322
+ })], b.prototype, "disabled", void 0), r([i({
287
323
  type: String,
288
324
  reflect: !0
289
325
  })], b.prototype, "href", void 0), r([i({
@@ -1,37 +1,40 @@
1
- const e=require(`./element-DjtxO-1r.cjs`),t=require(`./element-with-slot-CWoTGA1B.cjs`),n=require(`./if-defined-XKOD_t_V.cjs`),r=require(`./ref-BaJ0mBT_.cjs`);var i=class extends Event{constructor(e,t,n,r){super(`context-request`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t,this.callback=n,this.subscribe=r??!1}};function a(e){return e}var o=class{constructor(e,t,n,r){if(this.subscribe=!1,this.provided=!1,this.value=void 0,this.t=(e,t)=>{this.unsubscribe&&(this.unsubscribe!==t&&(this.provided=!1,this.unsubscribe()),this.subscribe||this.unsubscribe()),this.value=e,this.host.requestUpdate(),this.provided&&!this.subscribe||(this.provided=!0,this.callback&&this.callback(e,t)),this.unsubscribe=t},this.host=e,t.context!==void 0){let e=t;this.context=e.context,this.callback=e.callback,this.subscribe=e.subscribe??!1}else this.context=t,this.callback=n,this.subscribe=r??!1;this.host.addController(this)}hostConnected(){this.dispatchRequest()}hostDisconnected(){this.unsubscribe&&=(this.unsubscribe(),void 0)}dispatchRequest(){this.host.dispatchEvent(new i(this.context,this.host,this.t,this.subscribe))}},s=class{get value(){return this.o}set value(e){this.setValue(e)}setValue(e,t=!1){let n=t||!Object.is(e,this.o);this.o=e,n&&this.updateObservers()}constructor(e){this.subscriptions=new Map,this.updateObservers=()=>{for(let[e,{disposer:t}]of this.subscriptions)e(this.o,t)},e!==void 0&&(this.value=e)}addCallback(e,t,n){if(!n)return void e(this.value);this.subscriptions.has(e)||this.subscriptions.set(e,{disposer:()=>{this.subscriptions.delete(e)},consumerHost:t});let{disposer:r}=this.subscriptions.get(e);e(this.value,r)}clearCallbacks(){this.subscriptions.clear()}},c=class extends Event{constructor(e,t){super(`context-provider`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t}},l=class extends s{constructor(e,t,n){super(t.context===void 0?n:t.initialValue),this.onContextRequest=e=>{if(e.context!==this.context)return;let t=e.contextTarget??e.composedPath()[0];t!==this.host&&(e.stopPropagation(),this.addCallback(e.callback,t,e.subscribe))},this.onProviderRequest=e=>{if(e.context!==this.context||(e.contextTarget??e.composedPath()[0])===this.host)return;let t=new Set;for(let[e,{consumerHost:n}]of this.subscriptions)t.has(e)||(t.add(e),n.dispatchEvent(new i(this.context,n,e,!0)));e.stopPropagation()},this.host=e,t.context===void 0?this.context=t:this.context=t.context,this.attachListeners(),this.host.addController?.(this)}attachListeners(){this.host.addEventListener(`context-request`,this.onContextRequest),this.host.addEventListener(`context-provider`,this.onProviderRequest)}hostConnected(){this.host.dispatchEvent(new c(this.context,this.host))}};function u({context:e}){return(t,n)=>{let r=new WeakMap;if(typeof n==`object`)return{get(){return t.get.call(this)},set(e){return r.get(this).setValue(e),t.set.call(this,e)},init(t){return r.set(this,new l(this,{context:e,initialValue:t})),t}};{t.constructor.addInitializer((t=>{r.set(t,new l(t,{context:e}))}));let i=Object.getOwnPropertyDescriptor(t,n),a;if(i===void 0){let e=new WeakMap;a={get(){return e.get(this)},set(t){r.get(this).setValue(t),e.set(this,t)},configurable:!0,enumerable:!0}}else{let e=i.set;a={...i,set(t){r.get(this).setValue(t),e?.call(this,t)}}}Object.defineProperty(t,n,a);return}}}function d({context:e,subscribe:t}){return(n,r)=>{typeof r==`object`?r.addInitializer((function(){new o(this,{context:e,callback:e=>{n.set.call(this,e)},subscribe:t})})):n.constructor.addInitializer((n=>{new o(n,{context:e,callback:e=>{n[r]=e},subscribe:t})}))}}var f=a(Symbol(`pkt-tabs-context`)),p=class extends t.t{constructor(...e){super(...e),this.arrowNav=!0,this.disableArrowNav=!1,this.tabRefs=[],this.tabCount=0,this.context={useArrowNav:this.useArrowNav,registerTab:this.registerTab.bind(this),handleClick:this.handleClick.bind(this),handleKeyUp:this.handleKeyUp.bind(this)}}get useArrowNav(){return this.arrowNav&&!this.disableArrowNav}updated(e){(e.has(`arrowNav`)||e.has(`disableArrowNav`))&&(this.context={...this.context,useArrowNav:this.useArrowNav})}registerTab(e,t){this.tabRefs[t]=e,this.tabCount=Math.max(this.tabCount,t+1)}handleClick(e){this.dispatchEvent(new CustomEvent(`tab-selected`,{detail:{index:e},bubbles:!0,composed:!0}))}handleKeyUp(e,t){this.useArrowNav&&(e.code===`ArrowLeft`&&t!==0&&this.tabRefs[t-1]?.focus(),e.code===`ArrowRight`&&t<this.tabCount-1&&this.tabRefs[t+1]?.focus(),(e.code===`ArrowDown`||e.code===`Space`)&&this.dispatchEvent(new CustomEvent(`tab-selected`,{detail:{index:t},bubbles:!0,composed:!0})))}render(){return e.d`
1
+ const e=require(`./element-DjtxO-1r.cjs`),t=require(`./element-with-slot-CWoTGA1B.cjs`),n=require(`./if-defined-XKOD_t_V.cjs`),r=require(`./ref-BaJ0mBT_.cjs`);var i=class extends Event{constructor(e,t,n,r){super(`context-request`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t,this.callback=n,this.subscribe=r??!1}};function a(e){return e}var o=class{constructor(e,t,n,r){if(this.subscribe=!1,this.provided=!1,this.value=void 0,this.t=(e,t)=>{this.unsubscribe&&(this.unsubscribe!==t&&(this.provided=!1,this.unsubscribe()),this.subscribe||this.unsubscribe()),this.value=e,this.host.requestUpdate(),this.provided&&!this.subscribe||(this.provided=!0,this.callback&&this.callback(e,t)),this.unsubscribe=t},this.host=e,t.context!==void 0){let e=t;this.context=e.context,this.callback=e.callback,this.subscribe=e.subscribe??!1}else this.context=t,this.callback=n,this.subscribe=r??!1;this.host.addController(this)}hostConnected(){this.dispatchRequest()}hostDisconnected(){this.unsubscribe&&=(this.unsubscribe(),void 0)}dispatchRequest(){this.host.dispatchEvent(new i(this.context,this.host,this.t,this.subscribe))}},s=class{get value(){return this.o}set value(e){this.setValue(e)}setValue(e,t=!1){let n=t||!Object.is(e,this.o);this.o=e,n&&this.updateObservers()}constructor(e){this.subscriptions=new Map,this.updateObservers=()=>{for(let[e,{disposer:t}]of this.subscriptions)e(this.o,t)},e!==void 0&&(this.value=e)}addCallback(e,t,n){if(!n)return void e(this.value);this.subscriptions.has(e)||this.subscriptions.set(e,{disposer:()=>{this.subscriptions.delete(e)},consumerHost:t});let{disposer:r}=this.subscriptions.get(e);e(this.value,r)}clearCallbacks(){this.subscriptions.clear()}},c=class extends Event{constructor(e,t){super(`context-provider`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t}},l=class extends s{constructor(e,t,n){super(t.context===void 0?n:t.initialValue),this.onContextRequest=e=>{if(e.context!==this.context)return;let t=e.contextTarget??e.composedPath()[0];t!==this.host&&(e.stopPropagation(),this.addCallback(e.callback,t,e.subscribe))},this.onProviderRequest=e=>{if(e.context!==this.context||(e.contextTarget??e.composedPath()[0])===this.host)return;let t=new Set;for(let[e,{consumerHost:n}]of this.subscriptions)t.has(e)||(t.add(e),n.dispatchEvent(new i(this.context,n,e,!0)));e.stopPropagation()},this.host=e,t.context===void 0?this.context=t:this.context=t.context,this.attachListeners(),this.host.addController?.(this)}attachListeners(){this.host.addEventListener(`context-request`,this.onContextRequest),this.host.addEventListener(`context-provider`,this.onProviderRequest)}hostConnected(){this.host.dispatchEvent(new c(this.context,this.host))}};function u({context:e}){return(t,n)=>{let r=new WeakMap;if(typeof n==`object`)return{get(){return t.get.call(this)},set(e){return r.get(this).setValue(e),t.set.call(this,e)},init(t){return r.set(this,new l(this,{context:e,initialValue:t})),t}};{t.constructor.addInitializer((t=>{r.set(t,new l(t,{context:e}))}));let i=Object.getOwnPropertyDescriptor(t,n),a;if(i===void 0){let e=new WeakMap;a={get(){return e.get(this)},set(t){r.get(this).setValue(t),e.set(this,t)},configurable:!0,enumerable:!0}}else{let e=i.set;a={...i,set(t){r.get(this).setValue(t),e?.call(this,t)}}}Object.defineProperty(t,n,a);return}}}function d({context:e,subscribe:t}){return(n,r)=>{typeof r==`object`?r.addInitializer((function(){new o(this,{context:e,callback:e=>{n.set.call(this,e)},subscribe:t})})):n.constructor.addInitializer((n=>{new o(n,{context:e,callback:e=>{n[r]=e},subscribe:t})}))}}var f=a(Symbol(`pkt-tabs-context`)),p=class extends t.t{constructor(...e){super(...e),this.arrowNav=!0,this.disableArrowNav=!1,this.tabRefs=[],this.disabledMap={},this.tabCount=0,this.context={useArrowNav:this.useArrowNav,registerTab:this.registerTab.bind(this),handleClick:this.handleClick.bind(this),handleKeyUp:this.handleKeyUp.bind(this)}}get useArrowNav(){return this.arrowNav&&!this.disableArrowNav}updated(e){(e.has(`arrowNav`)||e.has(`disableArrowNav`))&&(this.context={...this.context,useArrowNav:this.useArrowNav})}registerTab(e,t,n=!1){this.tabRefs[t]=e,this.disabledMap[t]=n,this.tabCount=Math.max(this.tabCount,t+1)}isTabDisabled(e){return!!this.disabledMap[e]}findEnabledIndex(e,t){let n=e+t;for(;n>=0&&n<this.tabCount;){if(!this.isTabDisabled(n))return n;n+=t}return null}handleClick(e){this.isTabDisabled(e)||this.dispatchEvent(new CustomEvent(`tab-selected`,{detail:{index:e},bubbles:!0,composed:!0}))}handleKeyUp(e,t){if(this.useArrowNav){if(e.key===`ArrowLeft`){e.preventDefault();let n=this.findEnabledIndex(t,-1);n!==null&&this.tabRefs[n]?.focus()}if(e.key===`ArrowRight`){e.preventDefault();let n=this.findEnabledIndex(t,1);n!==null&&this.tabRefs[n]?.focus()}(e.key===`Enter`||e.key===` `||e.key===`Spacebar`||e.key===`ArrowDown`)&&(e.preventDefault(),this.handleClick(t))}}render(){return e.d`
2
2
  <div class="pkt-tabs">
3
3
  <div class="pkt-tabs__list" role=${this.useArrowNav?`tablist`:`navigation`}>${t.n(this)}</div>
4
4
  </div>
5
- `}};e.r([e.s({type:Boolean,reflect:!0,attribute:`arrow-nav`})],p.prototype,`arrowNav`,void 0),e.r([e.s({type:Boolean,reflect:!0,attribute:`disable-arrow-nav`})],p.prototype,`disableArrowNav`,void 0),e.r([e.o()],p.prototype,`tabRefs`,void 0),e.r([e.o()],p.prototype,`tabCount`,void 0),e.r([u({context:f}),e.o()],p.prototype,`context`,void 0);try{e.c(`pkt-tabs`)(p)}catch{console.warn(`Forsøker å definere <pkt-tabs>, men den er allerede definert`)}var m=class extends t.t{constructor(){super(),this.active=!1,this.href=``,this.icon=``,this.controls=``,this.tag=``,this.tagSkin=`blue`,this.index=0,this.elementRef=r.t()}connectedCallback(){super.connectedCallback(),this.updateComplete.then(()=>{this.elementRef.value&&this.context&&this.context.registerTab(this.elementRef.value,this.index)})}handleClick(){this.context&&this.context.handleClick(this.index)}handleKeyUp(e){this.context&&this.context.handleKeyUp(e,this.index)}render(){let i=this.context?.useArrowNav??!0,a=this.active?`active`:``,o=i?`tab`:void 0,s=i?this.active:void 0,c=this.active||!i?void 0:-1,l=e.d`
5
+ `}};e.r([e.s({type:Boolean,reflect:!0,attribute:`arrow-nav`})],p.prototype,`arrowNav`,void 0),e.r([e.s({type:Boolean,reflect:!0,attribute:`disable-arrow-nav`})],p.prototype,`disableArrowNav`,void 0),e.r([e.o()],p.prototype,`tabRefs`,void 0),e.r([e.o()],p.prototype,`disabledMap`,void 0),e.r([e.o()],p.prototype,`tabCount`,void 0),e.r([u({context:f}),e.o()],p.prototype,`context`,void 0);try{e.c(`pkt-tabs`)(p)}catch{console.warn(`Forsøker å definere <pkt-tabs>, men den er allerede definert`)}var m=class extends t.t{constructor(){super(),this.active=!1,this.disabled=!1,this.href=``,this.icon=``,this.controls=``,this.tag=``,this.tagSkin=`blue`,this.index=0,this.elementRef=r.t()}connectedCallback(){super.connectedCallback(),this.updateComplete.then(()=>{this.elementRef.value&&this.context&&this.context.registerTab(this.elementRef.value,this.index,this.disabled)})}updated(){this.elementRef.value&&this.context&&this.context.registerTab(this.elementRef.value,this.index,this.disabled)}handleClick(e){if(this.disabled){e.preventDefault(),e.stopPropagation();return}this.context&&this.context.handleClick(this.index)}handleKeyDown(e){if(this.disabled&&(e.key===`Enter`||e.key===` `||e.key===`Spacebar`)){e.preventDefault(),e.stopPropagation();return}this.context&&this.context.handleKeyUp(e,this.index)}render(){let i=this.context?.useArrowNav??!0,a=this.active&&!this.disabled,o=[a?`active`:``,this.disabled?`pkt-tabs__item--disabled`:``].filter(Boolean).join(` `),s=i?`tab`:void 0,c=i?a:void 0,l=this.disabled?-1:a||!i?void 0:-1,u=e.d`
6
6
  ${this.icon?e.d`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>`:``}
7
7
  <span>${t.n(this)}</span>
8
8
  ${this.tag?e.d`<pkt-tag skin=${this.tagSkin} size="small">${this.tag}</pkt-tag>`:``}
9
9
  `;return this.href?e.d`
10
10
  <a
11
11
  ${r.n(this.elementRef)}
12
- href=${this.href}
13
- class="pkt-tabs__link ${a}"
14
- role=${n.t(o)}
15
- aria-selected=${n.t(s)}
12
+ href=${n.t(this.disabled?void 0:this.href)}
13
+ class="pkt-tabs__link ${o}"
14
+ role=${n.t(s)}
15
+ aria-selected=${n.t(c)}
16
+ aria-disabled=${n.t(this.disabled?`true`:void 0)}
16
17
  aria-controls=${n.t(this.controls||void 0)}
17
- tabindex=${n.t(c)}
18
+ tabindex=${n.t(l)}
18
19
  @click=${this.handleClick}
19
- @keyup=${this.handleKeyUp}
20
+ @keydown=${this.handleKeyDown}
20
21
  >
21
- ${l}
22
+ ${u}
22
23
  </a>
23
24
  `:e.d`
24
25
  <button
25
26
  ${r.n(this.elementRef)}
26
27
  type="button"
27
- class="pkt-tabs__button pkt-link-button ${a}"
28
- role=${n.t(o)}
29
- aria-selected=${n.t(s)}
28
+ class="pkt-tabs__button pkt-link-button ${o}"
29
+ ?disabled=${this.disabled}
30
+ aria-disabled=${n.t(this.disabled?`true`:void 0)}
31
+ role=${n.t(s)}
32
+ aria-selected=${n.t(c)}
30
33
  aria-controls=${n.t(this.controls||void 0)}
31
- tabindex=${n.t(c)}
34
+ tabindex=${n.t(l)}
32
35
  @click=${this.handleClick}
33
- @keyup=${this.handleKeyUp}
36
+ @keydown=${this.handleKeyDown}
34
37
  >
35
- ${l}
38
+ ${u}
36
39
  </button>
37
- `}};e.r([e.s({type:Boolean,reflect:!0})],m.prototype,`active`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`href`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`icon`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`controls`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`tag`,void 0),e.r([e.s({type:String,reflect:!0,attribute:`tag-skin`})],m.prototype,`tagSkin`,void 0),e.r([e.s({type:Number,reflect:!0})],m.prototype,`index`,void 0),e.r([d({context:f,subscribe:!0}),e.s({attribute:!1})],m.prototype,`context`,void 0);try{e.c(`pkt-tab-item`)(m)}catch{console.warn(`Forsøker å definere <pkt-tab-item>, men den er allerede definert`)}var h=p;Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return h}});
40
+ `}};e.r([e.s({type:Boolean,reflect:!0})],m.prototype,`active`,void 0),e.r([e.s({type:Boolean,reflect:!0})],m.prototype,`disabled`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`href`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`icon`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`controls`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`tag`,void 0),e.r([e.s({type:String,reflect:!0,attribute:`tag-skin`})],m.prototype,`tagSkin`,void 0),e.r([e.s({type:Number,reflect:!0})],m.prototype,`index`,void 0),e.r([d({context:f,subscribe:!0}),e.s({attribute:!1})],m.prototype,`context`,void 0);try{e.c(`pkt-tab-item`)(m)}catch{console.warn(`Forsøker å definere <pkt-tab-item>, men den er allerede definert`)}var h=p;Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return h}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-elements",
3
- "version": "16.7.7",
3
+ "version": "16.8.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",
@@ -78,5 +78,5 @@
78
78
  "url": "https://github.com/oslokommune/punkt/issues"
79
79
  },
80
80
  "license": "MIT",
81
- "gitHead": "691a4f7a2b9bc006ed99257fdfea1dbc02ef9fb9"
81
+ "gitHead": "1ff7d19b9d19c3b3e810dcee4bc3bd1d3fcc3610"
82
82
  }
@@ -11,6 +11,7 @@ export type TSkin = 'blue' | 'green' | 'red' | 'beige' | 'yellow' | 'grey' | 'gr
11
11
 
12
12
  export interface IPktTabItem {
13
13
  active?: boolean
14
+ disabled?: boolean
14
15
  href?: string
15
16
  icon?: string
16
17
  controls?: string
@@ -21,6 +22,7 @@ export interface IPktTabItem {
21
22
 
22
23
  export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktTabItem {
23
24
  @property({ type: Boolean, reflect: true }) active: boolean = false
25
+ @property({ type: Boolean, reflect: true }) disabled: boolean = false
24
26
  @property({ type: String, reflect: true }) href: string = ''
25
27
  @property({ type: String, reflect: true }) icon: string = ''
26
28
  @property({ type: String, reflect: true }) controls: string = ''
@@ -44,18 +46,36 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
44
46
  // Wait for element to be fully initialized
45
47
  this.updateComplete.then(() => {
46
48
  if (this.elementRef.value && this.context) {
47
- this.context.registerTab(this.elementRef.value, this.index)
49
+ this.context.registerTab(this.elementRef.value, this.index, this.disabled)
48
50
  }
49
51
  })
50
52
  }
51
53
 
52
- private handleClick() {
54
+ updated() {
55
+ if (this.elementRef.value && this.context) {
56
+ this.context.registerTab(this.elementRef.value, this.index, this.disabled)
57
+ }
58
+ }
59
+
60
+ private handleClick(event: Event) {
61
+ if (this.disabled) {
62
+ event.preventDefault()
63
+ event.stopPropagation()
64
+ return
65
+ }
66
+
53
67
  if (this.context) {
54
68
  this.context.handleClick(this.index)
55
69
  }
56
70
  }
57
71
 
58
- private handleKeyUp(event: KeyboardEvent) {
72
+ private handleKeyDown(event: KeyboardEvent) {
73
+ if (this.disabled && (event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar')) {
74
+ event.preventDefault()
75
+ event.stopPropagation()
76
+ return
77
+ }
78
+
59
79
  if (this.context) {
60
80
  this.context.handleKeyUp(event, this.index)
61
81
  }
@@ -63,10 +83,13 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
63
83
 
64
84
  render() {
65
85
  const useArrowNav = this.context?.useArrowNav ?? true
66
- const commonClasses = this.active ? 'active' : ''
86
+ const isActive = this.active && !this.disabled
87
+ const commonClasses = [isActive ? 'active' : '', this.disabled ? 'pkt-tabs__item--disabled' : '']
88
+ .filter(Boolean)
89
+ .join(' ')
67
90
  const role = useArrowNav ? 'tab' : undefined
68
- const ariaSelected = useArrowNav ? this.active : undefined
69
- const tabIndex = this.active || !useArrowNav ? undefined : -1
91
+ const ariaSelected = useArrowNav ? isActive : undefined
92
+ const tabIndex = this.disabled ? -1 : isActive || !useArrowNav ? undefined : -1
70
93
 
71
94
  const content = html`
72
95
  ${this.icon ? html`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>` : ''}
@@ -78,14 +101,15 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
78
101
  return html`
79
102
  <a
80
103
  ${ref(this.elementRef)}
81
- href=${this.href}
104
+ href=${ifDefined(this.disabled ? undefined : this.href)}
82
105
  class="pkt-tabs__link ${commonClasses}"
83
106
  role=${ifDefined(role)}
84
107
  aria-selected=${ifDefined(ariaSelected)}
108
+ aria-disabled=${ifDefined(this.disabled ? 'true' : undefined)}
85
109
  aria-controls=${ifDefined(this.controls || undefined)}
86
110
  tabindex=${ifDefined(tabIndex)}
87
111
  @click=${this.handleClick}
88
- @keyup=${this.handleKeyUp}
112
+ @keydown=${this.handleKeyDown}
89
113
  >
90
114
  ${content}
91
115
  </a>
@@ -97,12 +121,14 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
97
121
  ${ref(this.elementRef)}
98
122
  type="button"
99
123
  class="pkt-tabs__button pkt-link-button ${commonClasses}"
124
+ ?disabled=${this.disabled}
125
+ aria-disabled=${ifDefined(this.disabled ? 'true' : undefined)}
100
126
  role=${ifDefined(role)}
101
127
  aria-selected=${ifDefined(ariaSelected)}
102
128
  aria-controls=${ifDefined(this.controls || undefined)}
103
129
  tabindex=${ifDefined(tabIndex)}
104
130
  @click=${this.handleClick}
105
- @keyup=${this.handleKeyUp}
131
+ @keydown=${this.handleKeyDown}
106
132
  >
107
133
  ${content}
108
134
  </button>
@@ -9,7 +9,7 @@ export interface TabsContext {
9
9
  /**
10
10
  * Register a tab item with the parent tabs container
11
11
  */
12
- registerTab: (element: HTMLElement, index: number) => void
12
+ registerTab: (element: HTMLElement, index: number, disabled?: boolean) => void
13
13
 
14
14
  /**
15
15
  * Handle click event from a tab item
@@ -216,7 +216,7 @@ describe('PktTabs', () => {
216
216
  expect(document.activeElement).toBe(firstButton)
217
217
 
218
218
  // Simulate ArrowRight key
219
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowRight', bubbles: true })
219
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
220
220
  firstButton.dispatchEvent(keyEvent)
221
221
 
222
222
  // Wait for focus to change
@@ -236,7 +236,7 @@ describe('PktTabs', () => {
236
236
  expect(document.activeElement).toBe(secondButton)
237
237
 
238
238
  // Simulate ArrowLeft key
239
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowLeft', bubbles: true })
239
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true })
240
240
  secondButton.dispatchEvent(keyEvent)
241
241
 
242
242
  // Wait for focus to change
@@ -255,7 +255,7 @@ describe('PktTabs', () => {
255
255
  expect(document.activeElement).toBe(thirdButton)
256
256
 
257
257
  // Simulate ArrowRight key
258
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowRight', bubbles: true })
258
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
259
259
  thirdButton.dispatchEvent(keyEvent)
260
260
 
261
261
  // Wait
@@ -275,7 +275,7 @@ describe('PktTabs', () => {
275
275
  expect(document.activeElement).toBe(firstButton)
276
276
 
277
277
  // Simulate ArrowLeft key
278
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowLeft', bubbles: true })
278
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true })
279
279
  firstButton.dispatchEvent(keyEvent)
280
280
 
281
281
  // Wait
@@ -296,7 +296,7 @@ describe('PktTabs', () => {
296
296
  const button = secondTabItem.querySelector('button') as HTMLButtonElement
297
297
 
298
298
  // Simulate Space key
299
- const keyEvent = new KeyboardEvent('keyup', { code: 'Space', bubbles: true })
299
+ const keyEvent = new KeyboardEvent('keydown', { key: ' ', bubbles: true })
300
300
  button.dispatchEvent(keyEvent)
301
301
 
302
302
  // Wait
@@ -317,7 +317,7 @@ describe('PktTabs', () => {
317
317
  const button = secondTabItem.querySelector('button') as HTMLButtonElement
318
318
 
319
319
  // Simulate ArrowDown key
320
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowDown', bubbles: true })
320
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
321
321
  button.dispatchEvent(keyEvent)
322
322
 
323
323
  // Wait
@@ -340,7 +340,7 @@ describe('PktTabs', () => {
340
340
  expect(document.activeElement).toBe(firstButton)
341
341
 
342
342
  // Simulate ArrowRight key
343
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowRight', bubbles: true })
343
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
344
344
  firstButton.dispatchEvent(keyEvent)
345
345
 
346
346
  // Wait
@@ -360,7 +360,7 @@ describe('PktTabs', () => {
360
360
  expect(document.activeElement).toBe(firstButton)
361
361
 
362
362
  // Simulate ArrowRight key
363
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowRight', bubbles: true })
363
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
364
364
  firstButton.dispatchEvent(keyEvent)
365
365
 
366
366
  // Wait
@@ -379,7 +379,7 @@ describe('PktTabs', () => {
379
379
  firstButton.focus()
380
380
 
381
381
  // Simulate ArrowRight key
382
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowRight', bubbles: true })
382
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
383
383
  firstButton.dispatchEvent(keyEvent)
384
384
 
385
385
  // Wait
@@ -388,6 +388,83 @@ describe('PktTabs', () => {
388
388
  // Should NOT move to second tab (disableArrowNav overrides arrowNav)
389
389
  expect(document.activeElement).toBe(firstButton)
390
390
  })
391
+
392
+ test('skips disabled tabs during ArrowRight navigation', async () => {
393
+ const container = await createTabs(
394
+ '',
395
+ `
396
+ <pkt-tab-item index="0" active>Tab 1</pkt-tab-item>
397
+ <pkt-tab-item index="1" disabled>Tab 2</pkt-tab-item>
398
+ <pkt-tab-item index="2">Tab 3</pkt-tab-item>
399
+ `,
400
+ )
401
+ const tabItems = container.querySelectorAll('pkt-tab-item')
402
+ const firstButton = tabItems[0].querySelector('button') as HTMLButtonElement
403
+ const thirdButton = tabItems[2].querySelector('button') as HTMLButtonElement
404
+
405
+ firstButton.focus()
406
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
407
+ firstButton.dispatchEvent(keyEvent)
408
+ await new Promise((resolve) => setTimeout(resolve, 50))
409
+
410
+ expect(document.activeElement).toBe(thirdButton)
411
+ })
412
+ })
413
+
414
+ describe('Disabled tabs', () => {
415
+ test('does not dispatch tab-selected for disabled button tab', async () => {
416
+ const container = await createTabs(
417
+ '',
418
+ `
419
+ <pkt-tab-item index="0" active>Tab 1</pkt-tab-item>
420
+ <pkt-tab-item index="1" disabled>Tab 2</pkt-tab-item>
421
+ `,
422
+ )
423
+ const tabs = container.querySelector('pkt-tabs') as PktTabs
424
+ const eventListener = vi.fn()
425
+ tabs.addEventListener('tab-selected', eventListener)
426
+
427
+ const disabledButton = container.querySelectorAll('pkt-tab-item')[1].querySelector('button')
428
+ disabledButton?.click()
429
+
430
+ expect(disabledButton).toHaveAttribute('disabled')
431
+ expect(eventListener).not.toHaveBeenCalled()
432
+ })
433
+
434
+ test('disabled link tab has aria-disabled and does not dispatch event', async () => {
435
+ const container = await createTabs(
436
+ '',
437
+ `
438
+ <pkt-tab-item index="0" href="/enabled" active>Enabled</pkt-tab-item>
439
+ <pkt-tab-item index="1" href="/disabled" disabled>Disabled</pkt-tab-item>
440
+ `,
441
+ )
442
+ const tabs = container.querySelector('pkt-tabs') as PktTabs
443
+ const eventListener = vi.fn()
444
+ tabs.addEventListener('tab-selected', eventListener)
445
+
446
+ const disabledLink = container.querySelectorAll('pkt-tab-item')[1].querySelector('a') as HTMLAnchorElement
447
+ disabledLink.click()
448
+ disabledLink.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
449
+
450
+ expect(disabledLink).toHaveAttribute('aria-disabled', 'true')
451
+ expect(disabledLink).toHaveAttribute('tabindex', '-1')
452
+ expect(disabledLink).not.toHaveAttribute('href')
453
+ expect(eventListener).not.toHaveBeenCalled()
454
+ })
455
+
456
+ test('disabled active tab is not exposed as selected', async () => {
457
+ const container = await createTabs(
458
+ '',
459
+ `
460
+ <pkt-tab-item index="0" active disabled>Disabled active</pkt-tab-item>
461
+ <pkt-tab-item index="1">Enabled</pkt-tab-item>
462
+ `,
463
+ )
464
+ const disabledButton = container.querySelector('pkt-tab-item button')
465
+
466
+ expect(disabledButton).toHaveAttribute('aria-selected', 'false')
467
+ })
391
468
  })
392
469
 
393
470
  describe('Accessibility', () => {
@@ -483,7 +560,7 @@ describe('PktTabs', () => {
483
560
 
484
561
  // Navigate through all tabs
485
562
  for (let i = 0; i < 4; i++) {
486
- const keyEvent = new KeyboardEvent('keyup', { code: 'ArrowRight', bubbles: true })
563
+ const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
487
564
  ;(document.activeElement as HTMLElement).dispatchEvent(keyEvent)
488
565
  await new Promise((resolve) => setTimeout(resolve, 50))
489
566
  }
@@ -16,6 +16,7 @@ export class PktTabs extends PktElementWithSlot<IPktTabs> implements IPktTabs {
16
16
  disableArrowNav: boolean = false
17
17
 
18
18
  @state() private tabRefs: Array<HTMLAnchorElement | HTMLButtonElement | null> = []
19
+ @state() private disabledMap: Record<number, boolean> = {}
19
20
  @state() private tabCount: number = 0
20
21
 
21
22
  private get useArrowNav(): boolean {
@@ -42,12 +43,30 @@ export class PktTabs extends PktElementWithSlot<IPktTabs> implements IPktTabs {
42
43
  }
43
44
  }
44
45
 
45
- private registerTab(element: HTMLElement, index: number) {
46
+ private registerTab(element: HTMLElement, index: number, disabled = false) {
46
47
  this.tabRefs[index] = element as HTMLAnchorElement | HTMLButtonElement
48
+ this.disabledMap[index] = disabled
47
49
  this.tabCount = Math.max(this.tabCount, index + 1)
48
50
  }
49
51
 
52
+ private isTabDisabled(index: number): boolean {
53
+ return !!this.disabledMap[index]
54
+ }
55
+
56
+ private findEnabledIndex(startIndex: number, direction: -1 | 1): number | null {
57
+ let current = startIndex + direction
58
+
59
+ while (current >= 0 && current < this.tabCount) {
60
+ if (!this.isTabDisabled(current)) return current
61
+ current += direction
62
+ }
63
+
64
+ return null
65
+ }
66
+
50
67
  private handleClick(index: number) {
68
+ if (this.isTabDisabled(index)) return
69
+
51
70
  this.dispatchEvent(
52
71
  new CustomEvent('tab-selected', {
53
72
  detail: { index },
@@ -60,22 +79,26 @@ export class PktTabs extends PktElementWithSlot<IPktTabs> implements IPktTabs {
60
79
  private handleKeyUp(keyEvent: KeyboardEvent, index: number) {
61
80
  if (!this.useArrowNav) return
62
81
 
63
- if (keyEvent.code === 'ArrowLeft' && index !== 0) {
64
- this.tabRefs[index - 1]?.focus()
82
+ if (keyEvent.key === 'ArrowLeft') {
83
+ keyEvent.preventDefault()
84
+ const previousEnabled = this.findEnabledIndex(index, -1)
85
+ if (previousEnabled !== null) this.tabRefs[previousEnabled]?.focus()
65
86
  }
66
87
 
67
- if (keyEvent.code === 'ArrowRight' && index < this.tabCount - 1) {
68
- this.tabRefs[index + 1]?.focus()
88
+ if (keyEvent.key === 'ArrowRight') {
89
+ keyEvent.preventDefault()
90
+ const nextEnabled = this.findEnabledIndex(index, 1)
91
+ if (nextEnabled !== null) this.tabRefs[nextEnabled]?.focus()
69
92
  }
70
93
 
71
- if (keyEvent.code === 'ArrowDown' || keyEvent.code === 'Space') {
72
- this.dispatchEvent(
73
- new CustomEvent('tab-selected', {
74
- detail: { index },
75
- bubbles: true,
76
- composed: true,
77
- }),
78
- )
94
+ if (
95
+ keyEvent.key === 'Enter' ||
96
+ keyEvent.key === ' ' ||
97
+ keyEvent.key === 'Spacebar' ||
98
+ keyEvent.key === 'ArrowDown'
99
+ ) {
100
+ keyEvent.preventDefault()
101
+ this.handleClick(index)
79
102
  }
80
103
  }
81
104