@oslokommune/punkt-elements 13.8.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,103 @@
1
+ import { PktElement } from '@/base-elements/element'
2
+ import { PktSlotController } from '@/controllers/pkt-slot-controller'
3
+ import { html, PropertyValues } from 'lit'
4
+ import { customElement, property, state } from 'lit/decorators.js'
5
+ import { provide } from '@lit/context'
6
+ import { createRef, Ref, ref } from 'lit/directives/ref.js'
7
+ import { tabsContext, type TabsContext } from './tabs-context'
8
+
9
+ export interface IPktTabs {
10
+ arrowNav?: boolean
11
+ disableArrowNav?: boolean
12
+ }
13
+
14
+ @customElement('pkt-tabs')
15
+ export class PktTabs extends PktElement<IPktTabs> implements IPktTabs {
16
+ @property({ type: Boolean, reflect: true, attribute: 'arrow-nav' }) arrowNav: boolean = true
17
+ @property({ type: Boolean, reflect: true, attribute: 'disable-arrow-nav' })
18
+ disableArrowNav: boolean = false
19
+
20
+ @state() private tabRefs: Array<HTMLAnchorElement | HTMLButtonElement | null> = []
21
+ @state() private tabCount: number = 0
22
+
23
+ private get useArrowNav(): boolean {
24
+ return this.arrowNav && !this.disableArrowNav
25
+ }
26
+
27
+ defaultSlot: Ref<HTMLElement> = createRef()
28
+ slotController!: PktSlotController
29
+
30
+ constructor() {
31
+ super()
32
+ this.slotController = new PktSlotController(this, this.defaultSlot)
33
+ }
34
+
35
+ // Provide context to child tab items
36
+ @provide({ context: tabsContext })
37
+ @state()
38
+ private context: TabsContext = {
39
+ useArrowNav: this.useArrowNav,
40
+ registerTab: this.registerTab.bind(this),
41
+ handleClick: this.handleClick.bind(this),
42
+ handleKeyUp: this.handleKeyUp.bind(this),
43
+ }
44
+
45
+ // Update context when properties change
46
+ updated(changedProperties: PropertyValues) {
47
+ if (changedProperties.has('arrowNav') || changedProperties.has('disableArrowNav')) {
48
+ this.context = {
49
+ ...this.context,
50
+ useArrowNav: this.useArrowNav,
51
+ }
52
+ }
53
+ }
54
+
55
+ private registerTab(element: HTMLElement, index: number) {
56
+ this.tabRefs[index] = element as HTMLAnchorElement | HTMLButtonElement
57
+ this.tabCount = Math.max(this.tabCount, index + 1)
58
+ }
59
+
60
+ private handleClick(index: number) {
61
+ this.dispatchEvent(
62
+ new CustomEvent('tab-selected', {
63
+ detail: { index },
64
+ bubbles: true,
65
+ composed: true,
66
+ }),
67
+ )
68
+ }
69
+
70
+ private handleKeyUp(keyEvent: KeyboardEvent, index: number) {
71
+ if (!this.useArrowNav) return
72
+
73
+ if (keyEvent.code === 'ArrowLeft' && index !== 0) {
74
+ this.tabRefs[index - 1]?.focus()
75
+ }
76
+
77
+ if (keyEvent.code === 'ArrowRight' && index < this.tabCount - 1) {
78
+ this.tabRefs[index + 1]?.focus()
79
+ }
80
+
81
+ if (keyEvent.code === 'ArrowDown' || keyEvent.code === 'Space') {
82
+ this.dispatchEvent(
83
+ new CustomEvent('tab-selected', {
84
+ detail: { index },
85
+ bubbles: true,
86
+ composed: true,
87
+ }),
88
+ )
89
+ }
90
+ }
91
+
92
+ render() {
93
+ const role = this.useArrowNav ? 'tablist' : 'navigation'
94
+
95
+ return html`
96
+ <div class="pkt-tabs">
97
+ <div class="pkt-tabs__list" role=${role} ${ref(this.defaultSlot)}></div>
98
+ </div>
99
+ `
100
+ }
101
+ }
102
+
103
+ export default PktTabs