@oslokommune/punkt-elements 13.7.0 → 13.9.0

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.
@@ -0,0 +1,332 @@
1
+ import { P as $, x as p, n as h, a as y } from "./element-CRDRygXu.js";
2
+ import { P as C } from "./pkt-slot-controller-BPGj-LC5.js";
3
+ import { r as w } from "./state-DS_kr2Fy.js";
4
+ import { e as x, n as f } from "./ref-Xa5dbh--.js";
5
+ import { o as u } from "./if-defined-BWZGb3bh.js";
6
+ /**
7
+ * @license
8
+ * Copyright 2021 Google LLC
9
+ * SPDX-License-Identifier: BSD-3-Clause
10
+ */
11
+ let P = class extends Event {
12
+ constructor(t, s, i, e) {
13
+ super("context-request", { bubbles: !0, composed: !0 }), this.context = t, this.contextTarget = s, this.callback = i, this.subscribe = e ?? !1;
14
+ }
15
+ };
16
+ /**
17
+ * @license
18
+ * Copyright 2021 Google LLC
19
+ * SPDX-License-Identifier: BSD-3-Clause
20
+ */
21
+ /**
22
+ * @license
23
+ * Copyright 2021 Google LLC
24
+ * SPDX-License-Identifier: BSD-3-Clause
25
+ */
26
+ let m = class {
27
+ constructor(t, s, i, e) {
28
+ if (this.subscribe = !1, this.provided = !1, this.value = void 0, this.t = (a, r) => {
29
+ this.unsubscribe && (this.unsubscribe !== r && (this.provided = !1, this.unsubscribe()), this.subscribe || this.unsubscribe()), this.value = a, this.host.requestUpdate(), this.provided && !this.subscribe || (this.provided = !0, this.callback && this.callback(a, r)), this.unsubscribe = r;
30
+ }, this.host = t, s.context !== void 0) {
31
+ const a = s;
32
+ this.context = a.context, this.callback = a.callback, this.subscribe = a.subscribe ?? !1;
33
+ } else this.context = s, this.callback = i, this.subscribe = e ?? !1;
34
+ this.host.addController(this);
35
+ }
36
+ hostConnected() {
37
+ this.dispatchRequest();
38
+ }
39
+ hostDisconnected() {
40
+ this.unsubscribe && (this.unsubscribe(), this.unsubscribe = void 0);
41
+ }
42
+ dispatchRequest() {
43
+ this.host.dispatchEvent(new P(this.context, this.host, this.t, this.subscribe));
44
+ }
45
+ };
46
+ /**
47
+ * @license
48
+ * Copyright 2021 Google LLC
49
+ * SPDX-License-Identifier: BSD-3-Clause
50
+ */
51
+ class N {
52
+ get value() {
53
+ return this.o;
54
+ }
55
+ set value(t) {
56
+ this.setValue(t);
57
+ }
58
+ setValue(t, s = !1) {
59
+ const i = s || !Object.is(t, this.o);
60
+ this.o = t, i && this.updateObservers();
61
+ }
62
+ constructor(t) {
63
+ this.subscriptions = /* @__PURE__ */ new Map(), this.updateObservers = () => {
64
+ for (const [s, { disposer: i }] of this.subscriptions) s(this.o, i);
65
+ }, t !== void 0 && (this.value = t);
66
+ }
67
+ addCallback(t, s, i) {
68
+ if (!i) return void t(this.value);
69
+ this.subscriptions.has(t) || this.subscriptions.set(t, { disposer: () => {
70
+ this.subscriptions.delete(t);
71
+ }, consumerHost: s });
72
+ const { disposer: e } = this.subscriptions.get(t);
73
+ t(this.value, e);
74
+ }
75
+ clearCallbacks() {
76
+ this.subscriptions.clear();
77
+ }
78
+ }
79
+ /**
80
+ * @license
81
+ * Copyright 2021 Google LLC
82
+ * SPDX-License-Identifier: BSD-3-Clause
83
+ */
84
+ let _ = class extends Event {
85
+ constructor(t, s) {
86
+ super("context-provider", { bubbles: !0, composed: !0 }), this.context = t, this.contextTarget = s;
87
+ }
88
+ };
89
+ class g extends N {
90
+ constructor(t, s, i) {
91
+ var e, a;
92
+ super(s.context !== void 0 ? s.initialValue : i), this.onContextRequest = (r) => {
93
+ if (r.context !== this.context) return;
94
+ const n = r.contextTarget ?? r.composedPath()[0];
95
+ n !== this.host && (r.stopPropagation(), this.addCallback(r.callback, n, r.subscribe));
96
+ }, this.onProviderRequest = (r) => {
97
+ if (r.context !== this.context || (r.contextTarget ?? r.composedPath()[0]) === this.host) return;
98
+ const n = /* @__PURE__ */ new Set();
99
+ for (const [v, { consumerHost: k }] of this.subscriptions) n.has(v) || (n.add(v), k.dispatchEvent(new P(this.context, k, v, !0)));
100
+ r.stopPropagation();
101
+ }, this.host = t, s.context !== void 0 ? this.context = s.context : this.context = s, this.attachListeners(), (a = (e = this.host).addController) == null || a.call(e, this);
102
+ }
103
+ attachListeners() {
104
+ this.host.addEventListener("context-request", this.onContextRequest), this.host.addEventListener("context-provider", this.onProviderRequest);
105
+ }
106
+ hostConnected() {
107
+ this.host.dispatchEvent(new _(this.context, this.host));
108
+ }
109
+ }
110
+ /**
111
+ * @license
112
+ * Copyright 2017 Google LLC
113
+ * SPDX-License-Identifier: BSD-3-Clause
114
+ */
115
+ function R({ context: o }) {
116
+ return (t, s) => {
117
+ const i = /* @__PURE__ */ new WeakMap();
118
+ if (typeof s == "object") return { get() {
119
+ return t.get.call(this);
120
+ }, set(e) {
121
+ return i.get(this).setValue(e), t.set.call(this, e);
122
+ }, init(e) {
123
+ return i.set(this, new g(this, { context: o, initialValue: e })), e;
124
+ } };
125
+ {
126
+ t.constructor.addInitializer(((r) => {
127
+ i.set(r, new g(r, { context: o }));
128
+ }));
129
+ const e = Object.getOwnPropertyDescriptor(t, s);
130
+ let a;
131
+ if (e === void 0) {
132
+ const r = /* @__PURE__ */ new WeakMap();
133
+ a = { get() {
134
+ return r.get(this);
135
+ }, set(n) {
136
+ i.get(this).setValue(n), r.set(this, n);
137
+ }, configurable: !0, enumerable: !0 };
138
+ } else {
139
+ const r = e.set;
140
+ a = { ...e, set(n) {
141
+ i.get(this).setValue(n), r == null || r.call(this, n);
142
+ } };
143
+ }
144
+ return void Object.defineProperty(t, s, a);
145
+ }
146
+ };
147
+ }
148
+ /**
149
+ * @license
150
+ * Copyright 2022 Google LLC
151
+ * SPDX-License-Identifier: BSD-3-Clause
152
+ */
153
+ function A({ context: o, subscribe: t }) {
154
+ return (s, i) => {
155
+ typeof i == "object" ? i.addInitializer((function() {
156
+ new m(this, { context: o, callback: (e) => {
157
+ s.set.call(this, e);
158
+ }, subscribe: t });
159
+ })) : s.constructor.addInitializer(((e) => {
160
+ new m(e, { context: o, callback: (a) => {
161
+ e[i] = a;
162
+ }, subscribe: t });
163
+ }));
164
+ };
165
+ }
166
+ const S = Symbol("pkt-tabs-context");
167
+ var O = Object.defineProperty, T = Object.getOwnPropertyDescriptor, d = (o, t, s, i) => {
168
+ for (var e = i > 1 ? void 0 : i ? T(t, s) : t, a = o.length - 1, r; a >= 0; a--)
169
+ (r = o[a]) && (e = (i ? r(t, s, e) : r(e)) || e);
170
+ return i && e && O(t, s, e), e;
171
+ };
172
+ let b = class extends $ {
173
+ constructor() {
174
+ super(), this.arrowNav = !0, this.disableArrowNav = !1, this.tabRefs = [], this.tabCount = 0, this.defaultSlot = x(), this.context = {
175
+ useArrowNav: this.useArrowNav,
176
+ registerTab: this.registerTab.bind(this),
177
+ handleClick: this.handleClick.bind(this),
178
+ handleKeyUp: this.handleKeyUp.bind(this)
179
+ }, this.slotController = new C(this, this.defaultSlot);
180
+ }
181
+ get useArrowNav() {
182
+ return this.arrowNav && !this.disableArrowNav;
183
+ }
184
+ // Update context when properties change
185
+ updated(o) {
186
+ (o.has("arrowNav") || o.has("disableArrowNav")) && (this.context = {
187
+ ...this.context,
188
+ useArrowNav: this.useArrowNav
189
+ });
190
+ }
191
+ registerTab(o, t) {
192
+ this.tabRefs[t] = o, this.tabCount = Math.max(this.tabCount, t + 1);
193
+ }
194
+ handleClick(o) {
195
+ this.dispatchEvent(
196
+ new CustomEvent("tab-selected", {
197
+ detail: { index: o },
198
+ bubbles: !0,
199
+ composed: !0
200
+ })
201
+ );
202
+ }
203
+ handleKeyUp(o, t) {
204
+ var s, i;
205
+ this.useArrowNav && (o.code === "ArrowLeft" && t !== 0 && ((s = this.tabRefs[t - 1]) == null || s.focus()), o.code === "ArrowRight" && t < this.tabCount - 1 && ((i = this.tabRefs[t + 1]) == null || i.focus()), (o.code === "ArrowDown" || o.code === "Space") && this.dispatchEvent(
206
+ new CustomEvent("tab-selected", {
207
+ detail: { index: t },
208
+ bubbles: !0,
209
+ composed: !0
210
+ })
211
+ ));
212
+ }
213
+ render() {
214
+ const o = this.useArrowNav ? "tablist" : "navigation";
215
+ return p`
216
+ <div class="pkt-tabs">
217
+ <div class="pkt-tabs__list" role=${o} ${f(this.defaultSlot)}></div>
218
+ </div>
219
+ `;
220
+ }
221
+ };
222
+ d([
223
+ h({ type: Boolean, reflect: !0, attribute: "arrow-nav" })
224
+ ], b.prototype, "arrowNav", 2);
225
+ d([
226
+ h({ type: Boolean, reflect: !0, attribute: "disable-arrow-nav" })
227
+ ], b.prototype, "disableArrowNav", 2);
228
+ d([
229
+ w()
230
+ ], b.prototype, "tabRefs", 2);
231
+ d([
232
+ w()
233
+ ], b.prototype, "tabCount", 2);
234
+ d([
235
+ R({ context: S }),
236
+ w()
237
+ ], b.prototype, "context", 2);
238
+ b = d([
239
+ y("pkt-tabs")
240
+ ], b);
241
+ const M = b;
242
+ var j = Object.defineProperty, q = Object.getOwnPropertyDescriptor, l = (o, t, s, i) => {
243
+ for (var e = i > 1 ? void 0 : i ? q(t, s) : t, a = o.length - 1, r; a >= 0; a--)
244
+ (r = o[a]) && (e = (i ? r(t, s, e) : r(e)) || e);
245
+ return i && e && j(t, s, e), e;
246
+ };
247
+ let c = class extends $ {
248
+ constructor() {
249
+ super(), this.active = !1, this.href = "", this.icon = "", this.controls = "", this.tag = "", this.tagSkin = "blue", this.index = 0, this.elementRef = x(), this.defaultSlot = x(), this.slotController = new C(this, this.defaultSlot);
250
+ }
251
+ connectedCallback() {
252
+ super.connectedCallback(), this.updateComplete.then(() => {
253
+ this.elementRef.value && this.context && this.context.registerTab(this.elementRef.value, this.index);
254
+ });
255
+ }
256
+ handleClick() {
257
+ this.context && this.context.handleClick(this.index);
258
+ }
259
+ handleKeyUp(o) {
260
+ this.context && this.context.handleKeyUp(o, this.index);
261
+ }
262
+ render() {
263
+ var r;
264
+ const o = ((r = this.context) == null ? void 0 : r.useArrowNav) ?? !0, t = this.active ? "active" : "", s = o ? "tab" : void 0, i = o ? this.active : void 0, e = this.active || !o ? void 0 : -1, a = p`
265
+ ${this.icon ? p`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>` : ""}
266
+ <span ${f(this.defaultSlot)}></span>
267
+ ${this.tag ? p`<pkt-tag skin=${this.tagSkin} size="small">${this.tag}</pkt-tag>` : ""}
268
+ `;
269
+ return this.href ? p`
270
+ <a
271
+ ${f(this.elementRef)}
272
+ href=${this.href}
273
+ class="pkt-tabs__link ${t}"
274
+ role=${u(s)}
275
+ aria-selected=${u(i)}
276
+ aria-controls=${u(this.controls || void 0)}
277
+ tabindex=${u(e)}
278
+ @click=${this.handleClick}
279
+ @keyup=${this.handleKeyUp}
280
+ >
281
+ ${a}
282
+ </a>
283
+ ` : p`
284
+ <button
285
+ ${f(this.elementRef)}
286
+ type="button"
287
+ class="pkt-tabs__button pkt-link-button ${t}"
288
+ role=${u(s)}
289
+ aria-selected=${u(i)}
290
+ aria-controls=${u(this.controls || void 0)}
291
+ tabindex=${u(e)}
292
+ @click=${this.handleClick}
293
+ @keyup=${this.handleKeyUp}
294
+ >
295
+ ${a}
296
+ </button>
297
+ `;
298
+ }
299
+ };
300
+ l([
301
+ h({ type: Boolean, reflect: !0 })
302
+ ], c.prototype, "active", 2);
303
+ l([
304
+ h({ type: String, reflect: !0 })
305
+ ], c.prototype, "href", 2);
306
+ l([
307
+ h({ type: String, reflect: !0 })
308
+ ], c.prototype, "icon", 2);
309
+ l([
310
+ h({ type: String, reflect: !0 })
311
+ ], c.prototype, "controls", 2);
312
+ l([
313
+ h({ type: String, reflect: !0 })
314
+ ], c.prototype, "tag", 2);
315
+ l([
316
+ h({ type: String, reflect: !0, attribute: "tag-skin" })
317
+ ], c.prototype, "tagSkin", 2);
318
+ l([
319
+ h({ type: Number, reflect: !0 })
320
+ ], c.prototype, "index", 2);
321
+ l([
322
+ A({ context: S, subscribe: !0 }),
323
+ h({ attribute: !1 })
324
+ ], c.prototype, "context", 2);
325
+ c = l([
326
+ y("pkt-tab-item")
327
+ ], c);
328
+ const B = c;
329
+ export {
330
+ M as P,
331
+ B as a
332
+ };
package/dist/tabs.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-elements",
3
- "version": "13.7.0",
3
+ "version": "13.9.0",
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",
@@ -29,6 +29,7 @@
29
29
  "dependencies": {
30
30
  "@date-fns/tz": "^1.2.0",
31
31
  "@lit-labs/router": "^0.1.3",
32
+ "@lit/context": "^1.1.6",
32
33
  "@types/node": "^20.17.30",
33
34
  "date-fns": "^4.1.0",
34
35
  "dialog-polyfill": "^0.5.6",
@@ -43,7 +44,7 @@
43
44
  "@babel/preset-env": "^7.28.3",
44
45
  "@babel/preset-typescript": "^7.25.9",
45
46
  "@oslokommune/punkt-assets": "^13.6.3",
46
- "@oslokommune/punkt-css": "^13.6.15",
47
+ "@oslokommune/punkt-css": "^13.9.0",
47
48
  "@testing-library/jest-dom": "^6.6.3",
48
49
  "@typescript-eslint/eslint-plugin": "^8.46.0",
49
50
  "@typescript-eslint/parser": "^8.46.0",
@@ -80,5 +81,5 @@
80
81
  "url": "https://github.com/oslokommune/punkt/issues"
81
82
  },
82
83
  "license": "MIT",
83
- "gitHead": "cfe27c04046718ff79f60e3babba60062296f05b"
84
+ "gitHead": "8bafc95c82ea1e49d1d1811b573baf02de2ef3d5"
84
85
  }
@@ -71,7 +71,7 @@ export class PktIcon extends PktElement<Props> {
71
71
  name: PktIconName = ''
72
72
 
73
73
  @property({ type: SVGElement })
74
- private icon: any = unsafeSVG(errorSvg)
74
+ private icon: ReturnType<typeof unsafeSVG> = unsafeSVG(errorSvg)
75
75
 
76
76
  @property({ type: Array, noAccessor: true })
77
77
  private _updatedProps: string[] = []
@@ -25,6 +25,7 @@ export { PktRadioButton } from '@/components/radiobutton'
25
25
  // TODO: Avklar om RadioButton kan eksporteres som *kun* PktRadiobutton
26
26
  export { PktRadioButton as PktRadiobutton } from '@/components/radiobutton'
27
27
  export { PktTag } from '@/components/tag'
28
+ export { PktTabs, PktTabItem } from '@/components/tabs'
28
29
  export { PktTextarea } from '@/components/textarea'
29
30
  export { PktTextinput } from '@/components/textinput'
30
31
  export { PktSelect } from '@/components/select'
@@ -62,6 +63,8 @@ export type { IPktHeading, TPktHeadingSize, TPktHeadingLevel } from '@/component
62
63
 
63
64
  export type { TTagSkin, TTagType } from '@/components/tag'
64
65
 
66
+ export type { IPktTabs, IPktTabItem, TSkin as TTabItemSkin } from '@/components/tabs'
67
+
65
68
  export type { TSelectOption } from '@/components/select'
66
69
 
67
70
  export type { IPktBackLink } from '@/components/backlink'
@@ -0,0 +1,8 @@
1
+ import PktTabs from './tabs'
2
+ import type { IPktTabs } from './tabs'
3
+ import PktTabItem from './tabitem'
4
+ import type { IPktTabItem, TSkin } from './tabitem'
5
+
6
+ export { PktTabs, PktTabItem }
7
+ export type { IPktTabs, IPktTabItem, TSkin }
8
+ export default PktTabs
@@ -0,0 +1,117 @@
1
+ import { PktElement } from '@/base-elements/element'
2
+ import { PktSlotController } from '@/controllers/pkt-slot-controller'
3
+ import { html } from 'lit'
4
+ import { customElement, property } from 'lit/decorators.js'
5
+ import { consume } from '@lit/context'
6
+ import { createRef, Ref, ref } from 'lit/directives/ref.js'
7
+ import { ifDefined } from 'lit/directives/if-defined.js'
8
+ import { tabsContext, type TabsContext } from './tabs-context'
9
+
10
+ export type TSkin = 'blue' | 'green' | 'red' | 'beige' | 'yellow' | 'grey' | 'gray' | 'blue-light'
11
+
12
+ export interface IPktTabItem {
13
+ active?: boolean
14
+ href?: string
15
+ icon?: string
16
+ controls?: string
17
+ tag?: string
18
+ tagSkin?: TSkin
19
+ index?: number
20
+ }
21
+
22
+ @customElement('pkt-tab-item')
23
+ export class PktTabItem extends PktElement<IPktTabItem> implements IPktTabItem {
24
+ @property({ type: Boolean, reflect: true }) active: boolean = false
25
+ @property({ type: String, reflect: true }) href: string = ''
26
+ @property({ type: String, reflect: true }) icon: string = ''
27
+ @property({ type: String, reflect: true }) controls: string = ''
28
+ @property({ type: String, reflect: true }) tag: string = ''
29
+ @property({ type: String, reflect: true, attribute: 'tag-skin' }) tagSkin: TSkin = 'blue'
30
+ @property({ type: Number, reflect: true }) index: number = 0
31
+
32
+ // Consume context from parent pkt-tabs
33
+ @consume({ context: tabsContext, subscribe: true })
34
+ @property({ attribute: false })
35
+ context?: TabsContext
36
+
37
+ elementRef: Ref<HTMLAnchorElement | HTMLButtonElement> = createRef()
38
+ defaultSlot: Ref<HTMLElement> = createRef()
39
+ slotController!: PktSlotController
40
+
41
+ constructor() {
42
+ super()
43
+ this.slotController = new PktSlotController(this, this.defaultSlot)
44
+ }
45
+
46
+ connectedCallback() {
47
+ super.connectedCallback()
48
+ // Wait for element to be fully initialized
49
+ this.updateComplete.then(() => {
50
+ if (this.elementRef.value && this.context) {
51
+ this.context.registerTab(this.elementRef.value, this.index)
52
+ }
53
+ })
54
+ }
55
+
56
+ private handleClick() {
57
+ if (this.context) {
58
+ this.context.handleClick(this.index)
59
+ }
60
+ }
61
+
62
+ private handleKeyUp(event: KeyboardEvent) {
63
+ if (this.context) {
64
+ this.context.handleKeyUp(event, this.index)
65
+ }
66
+ }
67
+
68
+ render() {
69
+ const useArrowNav = this.context?.useArrowNav ?? true
70
+ const commonClasses = this.active ? 'active' : ''
71
+ const role = useArrowNav ? 'tab' : undefined
72
+ const ariaSelected = useArrowNav ? this.active : undefined
73
+ const tabIndex = this.active || !useArrowNav ? undefined : -1
74
+
75
+ const content = html`
76
+ ${this.icon ? html`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>` : ''}
77
+ <span ${ref(this.defaultSlot)}></span>
78
+ ${this.tag ? html`<pkt-tag skin=${this.tagSkin} size="small">${this.tag}</pkt-tag>` : ''}
79
+ `
80
+
81
+ if (this.href) {
82
+ return html`
83
+ <a
84
+ ${ref(this.elementRef)}
85
+ href=${this.href}
86
+ class="pkt-tabs__link ${commonClasses}"
87
+ role=${ifDefined(role)}
88
+ aria-selected=${ifDefined(ariaSelected)}
89
+ aria-controls=${ifDefined(this.controls || undefined)}
90
+ tabindex=${ifDefined(tabIndex)}
91
+ @click=${this.handleClick}
92
+ @keyup=${this.handleKeyUp}
93
+ >
94
+ ${content}
95
+ </a>
96
+ `
97
+ }
98
+
99
+ return html`
100
+ <button
101
+ ${ref(this.elementRef)}
102
+ type="button"
103
+ class="pkt-tabs__button pkt-link-button ${commonClasses}"
104
+ role=${ifDefined(role)}
105
+ aria-selected=${ifDefined(ariaSelected)}
106
+ aria-controls=${ifDefined(this.controls || undefined)}
107
+ tabindex=${ifDefined(tabIndex)}
108
+ @click=${this.handleClick}
109
+ @keyup=${this.handleKeyUp}
110
+ >
111
+ ${content}
112
+ </button>
113
+ `
114
+ }
115
+ }
116
+
117
+ export default PktTabItem
@@ -0,0 +1,25 @@
1
+ import { createContext } from '@lit/context'
2
+
3
+ export interface TabsContext {
4
+ /**
5
+ * Whether arrow navigation is enabled (computed from arrowNav && !disableArrowNav)
6
+ */
7
+ useArrowNav: boolean
8
+
9
+ /**
10
+ * Register a tab item with the parent tabs container
11
+ */
12
+ registerTab: (element: HTMLElement, index: number) => void
13
+
14
+ /**
15
+ * Handle click event from a tab item
16
+ */
17
+ handleClick: (index: number) => void
18
+
19
+ /**
20
+ * Handle keyboard event from a tab item
21
+ */
22
+ handleKeyUp: (event: KeyboardEvent, index: number) => void
23
+ }
24
+
25
+ export const tabsContext = createContext<TabsContext>(Symbol('pkt-tabs-context'))