@oslokommune/punkt-elements 16.16.1 → 16.17.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.
- package/CHANGELOG.md +18 -0
- package/dist/{accordion-nNH87Y60.js → accordion-CU2tJ5vn.js} +23 -9
- package/dist/{accordion-BB1JrONI.cjs → accordion-DDZkjXFb.cjs} +20 -7
- package/dist/{checkbox-Bjp43NxA.js → checkbox-BxNrOGUf.js} +1 -1
- package/dist/{checkbox-nTbihMxW.cjs → checkbox-rmSCYDgR.cjs} +1 -1
- package/dist/{combobox-C4Q6kQEs.cjs → combobox-C8Iwfalo.cjs} +1 -1
- package/dist/{combobox-CL9BLuxt.js → combobox-haqD3OH4.js} +3 -3
- package/dist/components/header-menu/header-menu.d.ts +41 -0
- package/dist/components/header-menu/header-menu.test.d.ts +0 -0
- package/dist/components/header-menu/index.d.ts +5 -0
- package/dist/components/header-menu/types.d.ts +6 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/{datepicker-BUK1Qanw.js → datepicker-DbkWEh8e.js} +2 -2
- package/dist/{datepicker-DYd3JCs_.cjs → datepicker-xZFsUaXB.cjs} +1 -1
- package/dist/docs/components/header-full.d.ts +13 -0
- package/dist/docs/components/header-menu.d.ts +8 -0
- package/dist/{fileupload-DbmqRkz3.js → fileupload-CTnjLOnj.js} +3 -3
- package/dist/{fileupload-DjUAvBcP.cjs → fileupload-D2z1Ngrt.cjs} +1 -1
- package/dist/{header-Dps8o-ZY.cjs → header-BHU7qpA_.cjs} +2 -2
- package/dist/{header-T5O6sfCN.js → header-Zqs7F1pI.js} +4 -3
- package/dist/header-menu-CPI1_5oZ.cjs +126 -0
- package/dist/header-menu-zhGYdnvz.js +319 -0
- package/dist/header-menu.d.ts +1 -0
- package/dist/{helptext-C5rG4VkA.js → helptext-BdqoW19r.js} +1 -1
- package/dist/{helptext-DQgStO4D.cjs → helptext-KWXjKljV.cjs} +1 -1
- package/dist/{input-element-CjebcQd1.js → input-element-B1ciW1N2.js} +1 -1
- package/dist/{input-element-FGcr5hGd.cjs → input-element-D0egtEnr.cjs} +1 -1
- package/dist/{input-wrapper-B_XFi211.cjs → input-wrapper-BJfspAt7.cjs} +1 -1
- package/dist/{input-wrapper-DBpMVSxt.js → input-wrapper-CGSrvrlb.js} +2 -2
- package/dist/{modal-DyIC3uhs.js → modal-DYc5M-Pn.js} +1 -1
- package/dist/{modal-B6591sWa.cjs → modal-J5uWdD1M.cjs} +1 -1
- package/dist/pkt-accordion.cjs +1 -1
- package/dist/pkt-accordion.js +1 -1
- package/dist/pkt-checkbox.cjs +1 -1
- package/dist/pkt-checkbox.js +1 -1
- package/dist/pkt-combobox.cjs +1 -1
- package/dist/pkt-combobox.js +1 -1
- package/dist/pkt-consent.cjs +1 -1
- package/dist/pkt-consent.js +1 -1
- package/dist/pkt-datepicker.cjs +1 -1
- package/dist/pkt-datepicker.js +1 -1
- package/dist/pkt-fileupload.cjs +1 -1
- package/dist/pkt-fileupload.js +1 -1
- package/dist/pkt-header-menu.cjs +1 -0
- package/dist/pkt-header-menu.js +2 -0
- package/dist/pkt-header.cjs +1 -1
- package/dist/pkt-header.js +1 -1
- package/dist/pkt-helptext.cjs +1 -1
- package/dist/pkt-helptext.js +1 -1
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +23 -22
- package/dist/pkt-input-wrapper.cjs +1 -1
- package/dist/pkt-input-wrapper.js +1 -1
- package/dist/pkt-link.cjs +1 -1
- package/dist/pkt-link.js +1 -1
- package/dist/pkt-linkcard.cjs +1 -1
- package/dist/pkt-linkcard.js +1 -1
- package/dist/pkt-loader.cjs +1 -1
- package/dist/pkt-loader.js +1 -1
- package/dist/pkt-messagebox.cjs +1 -1
- package/dist/pkt-messagebox.js +1 -1
- package/dist/pkt-modal.cjs +1 -1
- package/dist/pkt-modal.js +1 -1
- package/dist/pkt-progressbar.cjs +1 -1
- package/dist/pkt-progressbar.js +1 -1
- package/dist/pkt-radiobutton.cjs +1 -1
- package/dist/pkt-radiobutton.js +1 -1
- package/dist/pkt-select.cjs +1 -1
- package/dist/pkt-select.js +1 -1
- package/dist/pkt-tabs.cjs +1 -1
- package/dist/pkt-tabs.js +1 -1
- package/dist/pkt-textarea.cjs +1 -1
- package/dist/pkt-textarea.js +1 -1
- package/dist/pkt-textinput.cjs +1 -1
- package/dist/pkt-textinput.js +1 -1
- package/dist/pkt-timepicker.cjs +1 -1
- package/dist/pkt-timepicker.js +1 -1
- package/dist/{radiobutton-DvXr7tDk.cjs → radiobutton-Crl2w27S.cjs} +1 -1
- package/dist/{radiobutton-B-8-wWFf.js → radiobutton-CzNQ4OLz.js} +1 -1
- package/dist/{select-B75wzvyz.cjs → select-BvlbaxYf.cjs} +1 -1
- package/dist/{select-D9B8Ighz.js → select-DpxXnNje.js} +3 -3
- package/dist/shared-types/header-menu.d.ts +108 -0
- package/dist/shared-types/index.d.ts +2 -0
- package/dist/shared-types/skin.d.ts +1 -1
- package/dist/shared-utils/header-menu/example-data.d.ts +11 -0
- package/dist/shared-utils/header-menu/fetch-header-footer-data.d.ts +12 -0
- package/dist/shared-utils/header-menu/icon-map.d.ts +34 -0
- package/dist/shared-utils/header-menu/index.d.ts +11 -0
- package/dist/shared-utils/header-menu/select-megamenu.d.ts +16 -0
- package/dist/{textarea-CIWXMaAD.js → textarea-BgsX_PHm.js} +2 -2
- package/dist/{textarea-CjqprL6Y.cjs → textarea-BjQ_37ji.cjs} +1 -1
- package/dist/{textinput-CxzAxM8E.js → textinput-BmuQREJY.js} +2 -2
- package/dist/{textinput-B0VWmVIF.cjs → textinput-mjmYIwxr.cjs} +1 -1
- package/dist/{timepicker-DXrpg5C7.js → timepicker-Bj_esO1U.js} +2 -2
- package/dist/{timepicker-Cv9juQeB.cjs → timepicker-C7vU_hTg.cjs} +1 -1
- package/package.json +2 -2
- package/src/components/accordion/accordionitem.ts +22 -5
- package/src/components/header/header-service.ts +1 -0
- package/src/components/header-menu/header-menu.test.ts +314 -0
- package/src/components/header-menu/header-menu.ts +401 -0
- package/src/components/header-menu/index.ts +6 -0
- package/src/components/header-menu/types.ts +31 -0
- package/src/components/index.ts +3 -0
- /package/dist/{consent-f_IGyHss.js → consent-CsX3DABo.js} +0 -0
- /package/dist/{consent-zh2npq2K.cjs → consent-DjXqLn2K.cjs} +0 -0
- /package/dist/{dialog-polyfill.esm-CXDsb3l-.cjs → dialog-polyfill.esm-C6RtqFbz.cjs} +0 -0
- /package/dist/{dialog-polyfill.esm-BmVHGRTX.js → dialog-polyfill.esm-DrODM08x.js} +0 -0
- /package/dist/{input-wrapper-CYCCx8MP.cjs → input-wrapper-CQGoyLdZ.cjs} +0 -0
- /package/dist/{input-wrapper-kw2y6Jp_.js → input-wrapper-DwSsP39J.js} +0 -0
- /package/dist/{link-B8zYc5WK.cjs → link-H7BhqdWV.cjs} +0 -0
- /package/dist/{link-CjJcwcxR.js → link-X9H6zi7W.js} +0 -0
- /package/dist/{linkcard-Ddh5R_L-.js → linkcard-CSWzphQK.js} +0 -0
- /package/dist/{linkcard-C4x-NJMi.cjs → linkcard-zS3WuhAY.cjs} +0 -0
- /package/dist/{loader-DeizpX45.js → loader-B4EGLEoa.js} +0 -0
- /package/dist/{loader-CGQXAuyM.cjs → loader-BWcSsK7i.cjs} +0 -0
- /package/dist/{messagebox-Ccu29rDv.cjs → messagebox-BSvU998N.cjs} +0 -0
- /package/dist/{messagebox-DqoPqo4K.js → messagebox-DsrTKauA.js} +0 -0
- /package/dist/{pkt-options-controller-DbLrdRwH.js → pkt-options-controller-CCB15L_G.js} +0 -0
- /package/dist/{pkt-options-controller-BPBb_O2K.cjs → pkt-options-controller-CeAQ-D-i.cjs} +0 -0
- /package/dist/{progressbar-CrwpnIkv.cjs → progressbar-8RYeaOQe.cjs} +0 -0
- /package/dist/{progressbar-uC-0W6o4.js → progressbar-hQSIzdof.js} +0 -0
- /package/dist/{tabs-RnDtIFv3.cjs → tabs-CDLdEDGH.cjs} +0 -0
- /package/dist/{tabs-4eWA7CsK.js → tabs-DDBYA5c8.js} +0 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { PktElement } from '@/base-elements/element'
|
|
2
|
+
import { html, nothing, type PropertyValues, type TemplateResult } from 'lit'
|
|
3
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
|
4
|
+
import { classMap } from 'lit/directives/class-map.js'
|
|
5
|
+
import { booleanishConverter, type Booleanish } from 'shared-types'
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_HEADER_FOOTER_URL,
|
|
8
|
+
deriveSocialIcon,
|
|
9
|
+
fetchHeaderFooterData,
|
|
10
|
+
mapOdsIcon,
|
|
11
|
+
selectLocaleData,
|
|
12
|
+
} from 'shared-utils/header-menu'
|
|
13
|
+
import type {
|
|
14
|
+
IHeaderFooterLocaleData,
|
|
15
|
+
IHeaderMenuButton,
|
|
16
|
+
IHeaderMenuLink,
|
|
17
|
+
IHeaderMenuSection,
|
|
18
|
+
IHeaderMenuServices,
|
|
19
|
+
IPktHeaderMenu,
|
|
20
|
+
THeaderFooterApi,
|
|
21
|
+
THeaderMenuLocale,
|
|
22
|
+
} from './types'
|
|
23
|
+
|
|
24
|
+
import '@/components/accordion'
|
|
25
|
+
import '@/components/icon'
|
|
26
|
+
|
|
27
|
+
type LoadState = 'idle' | 'loading' | 'ready' | 'error'
|
|
28
|
+
|
|
29
|
+
export class PktHeaderMenu extends PktElement<IPktHeaderMenu> implements IPktHeaderMenu {
|
|
30
|
+
@property({ type: String, attribute: 'data-url' })
|
|
31
|
+
dataUrl: string = DEFAULT_HEADER_FOOTER_URL
|
|
32
|
+
|
|
33
|
+
@property({ type: Object, attribute: false })
|
|
34
|
+
data?: THeaderFooterApi
|
|
35
|
+
|
|
36
|
+
@property({ type: String })
|
|
37
|
+
locale: THeaderMenuLocale = 'nb-NO'
|
|
38
|
+
|
|
39
|
+
@property({ type: Boolean, reflect: true, converter: booleanishConverter })
|
|
40
|
+
open: Booleanish = false
|
|
41
|
+
|
|
42
|
+
@property({ type: String, attribute: 'aria-labelledby', reflect: true })
|
|
43
|
+
ariaLabelledBy: string = ''
|
|
44
|
+
|
|
45
|
+
@property({ type: Number, attribute: 'mobile-breakpoint' })
|
|
46
|
+
mobileBreakpoint: number = 768
|
|
47
|
+
|
|
48
|
+
@state() private loadState: LoadState = 'idle'
|
|
49
|
+
@state() private fetchedData?: THeaderFooterApi
|
|
50
|
+
@state() private isMobile = false
|
|
51
|
+
|
|
52
|
+
private abortController?: AbortController
|
|
53
|
+
private mediaQuery?: MediaQueryList
|
|
54
|
+
|
|
55
|
+
override connectedCallback(): void {
|
|
56
|
+
super.connectedCallback()
|
|
57
|
+
this.classList.add('pkt-header-menu')
|
|
58
|
+
this.updateOpenClass()
|
|
59
|
+
this.setupMediaQuery()
|
|
60
|
+
if (!this.data) {
|
|
61
|
+
void this.loadData()
|
|
62
|
+
} else {
|
|
63
|
+
this.loadState = 'ready'
|
|
64
|
+
this.dispatchEvent(
|
|
65
|
+
new CustomEvent('data-loaded', {
|
|
66
|
+
detail: { data: this.data },
|
|
67
|
+
bubbles: true,
|
|
68
|
+
composed: true,
|
|
69
|
+
}),
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private updateOpenClass() {
|
|
75
|
+
this.classList.toggle('pkt-header-menu--open', Boolean(this.open))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override disconnectedCallback(): void {
|
|
79
|
+
super.disconnectedCallback()
|
|
80
|
+
this.abortController?.abort()
|
|
81
|
+
this.teardownMediaQuery()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
override updated(changedProperties: PropertyValues): void {
|
|
85
|
+
super.updated(changedProperties)
|
|
86
|
+
|
|
87
|
+
if (changedProperties.has('open')) {
|
|
88
|
+
this.updateOpenClass()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
changedProperties.has('mobileBreakpoint') &&
|
|
93
|
+
changedProperties.get('mobileBreakpoint') !== undefined
|
|
94
|
+
) {
|
|
95
|
+
this.setupMediaQuery()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Re-fetch only when dataUrl genuinely changes after first load.
|
|
99
|
+
// The initial fetch is kicked off from connectedCallback, so we
|
|
100
|
+
// skip the noop "field default" change that fires on first update.
|
|
101
|
+
if (
|
|
102
|
+
changedProperties.has('dataUrl') &&
|
|
103
|
+
changedProperties.get('dataUrl') !== undefined &&
|
|
104
|
+
!this.data
|
|
105
|
+
) {
|
|
106
|
+
void this.loadData()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (changedProperties.has('data') && changedProperties.get('data') !== undefined && this.data) {
|
|
110
|
+
this.loadState = 'ready'
|
|
111
|
+
this.dispatchEvent(
|
|
112
|
+
new CustomEvent('data-loaded', {
|
|
113
|
+
detail: { data: this.data },
|
|
114
|
+
bubbles: true,
|
|
115
|
+
composed: true,
|
|
116
|
+
}),
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private setupMediaQuery() {
|
|
122
|
+
this.teardownMediaQuery()
|
|
123
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return
|
|
124
|
+
this.mediaQuery = window.matchMedia(`(max-width: ${this.mobileBreakpoint - 1}px)`)
|
|
125
|
+
this.isMobile = this.mediaQuery.matches
|
|
126
|
+
this.mediaQuery.addEventListener('change', this.handleMediaChange)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private teardownMediaQuery() {
|
|
130
|
+
this.mediaQuery?.removeEventListener('change', this.handleMediaChange)
|
|
131
|
+
this.mediaQuery = undefined
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private handleMediaChange = (event: MediaQueryListEvent) => {
|
|
135
|
+
this.isMobile = event.matches
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private async loadData() {
|
|
139
|
+
this.abortController?.abort()
|
|
140
|
+
this.abortController = new AbortController()
|
|
141
|
+
this.loadState = 'loading'
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const data = await fetchHeaderFooterData<string>(this.dataUrl, this.abortController.signal)
|
|
145
|
+
this.fetchedData = data
|
|
146
|
+
this.loadState = 'ready'
|
|
147
|
+
this.dispatchEvent(
|
|
148
|
+
new CustomEvent('data-loaded', {
|
|
149
|
+
detail: { data },
|
|
150
|
+
bubbles: true,
|
|
151
|
+
composed: true,
|
|
152
|
+
}),
|
|
153
|
+
)
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if ((error as Error).name === 'AbortError') return
|
|
156
|
+
this.loadState = 'error'
|
|
157
|
+
this.dispatchEvent(
|
|
158
|
+
new CustomEvent('data-error', {
|
|
159
|
+
detail: { error },
|
|
160
|
+
bubbles: true,
|
|
161
|
+
composed: true,
|
|
162
|
+
}),
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private get effectiveData(): THeaderFooterApi | undefined {
|
|
168
|
+
return this.data ?? this.fetchedData
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private get localeData(): IHeaderFooterLocaleData | undefined {
|
|
172
|
+
return selectLocaleData(this.effectiveData, this.locale)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
render() {
|
|
176
|
+
if (this.loadState === 'loading') {
|
|
177
|
+
return html`
|
|
178
|
+
<nav aria-busy="true">
|
|
179
|
+
<p class="pkt-header-menu__loading">Laster meny…</p>
|
|
180
|
+
</nav>
|
|
181
|
+
`
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (this.loadState === 'error' || !this.localeData) {
|
|
185
|
+
return html`
|
|
186
|
+
<nav aria-hidden=${!this.open}>
|
|
187
|
+
<p class="pkt-header-menu__error">Kunne ikke laste meny.</p>
|
|
188
|
+
</nav>
|
|
189
|
+
`
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const { megamenu, i18n } = this.localeData
|
|
193
|
+
const navAriaLabel = i18n?.navAriaLabel || 'Hovedmeny'
|
|
194
|
+
|
|
195
|
+
if (this.isMobile) {
|
|
196
|
+
return html`
|
|
197
|
+
<nav aria-label=${navAriaLabel}>
|
|
198
|
+
${this.renderMobileAccordion(megamenu.services, megamenu.sections)}
|
|
199
|
+
${this.renderButtons(megamenu.buttons, true)}
|
|
200
|
+
${this.renderFooter(megamenu.links, megamenu.some)}
|
|
201
|
+
</nav>
|
|
202
|
+
`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return html`
|
|
206
|
+
<nav aria-label=${navAriaLabel}>
|
|
207
|
+
${this.renderServices(megamenu.services)} ${this.renderButtons(megamenu.buttons, false)}
|
|
208
|
+
${this.renderSections(megamenu.sections)}
|
|
209
|
+
${this.renderFooter(megamenu.links, megamenu.some)}
|
|
210
|
+
</nav>
|
|
211
|
+
`
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private renderMobileAccordion(
|
|
215
|
+
services: IHeaderMenuServices,
|
|
216
|
+
sections: IHeaderMenuSection[],
|
|
217
|
+
): TemplateResult {
|
|
218
|
+
return html`
|
|
219
|
+
<div class="pkt-header-menu__sections">
|
|
220
|
+
<pkt-accordion
|
|
221
|
+
class="pkt-header-menu__sections-inner"
|
|
222
|
+
skin="plus-minus"
|
|
223
|
+
name="header-menu-accordion"
|
|
224
|
+
>
|
|
225
|
+
<pkt-accordion-item
|
|
226
|
+
class="pkt-header-menu__section"
|
|
227
|
+
skin="plus-minus"
|
|
228
|
+
id="pkt-header-menu-services"
|
|
229
|
+
title=${services.title}
|
|
230
|
+
>
|
|
231
|
+
${this.renderServicesList(services)}
|
|
232
|
+
</pkt-accordion-item>
|
|
233
|
+
${sections.map(
|
|
234
|
+
(section, index) => html`
|
|
235
|
+
<pkt-accordion-item
|
|
236
|
+
class="pkt-header-menu__section"
|
|
237
|
+
skin="plus-minus"
|
|
238
|
+
id=${`pkt-header-menu-section-${index}`}
|
|
239
|
+
title=${section.title}
|
|
240
|
+
>
|
|
241
|
+
${this.renderSectionList(section.links)}
|
|
242
|
+
</pkt-accordion-item>
|
|
243
|
+
`,
|
|
244
|
+
)}
|
|
245
|
+
</pkt-accordion>
|
|
246
|
+
</div>
|
|
247
|
+
`
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private renderServicesList(services: IHeaderMenuServices): TemplateResult {
|
|
251
|
+
return html`
|
|
252
|
+
<ul class="pkt-header-menu__services-list">
|
|
253
|
+
${services.links.map(
|
|
254
|
+
(link) => html`
|
|
255
|
+
<li class="pkt-header-menu__service">
|
|
256
|
+
<a class="pkt-header-menu__service-link" href=${link.url}>
|
|
257
|
+
<pkt-icon
|
|
258
|
+
class="pkt-header-menu__service-icon"
|
|
259
|
+
name=${mapOdsIcon(link.icon)}
|
|
260
|
+
aria-hidden="true"
|
|
261
|
+
></pkt-icon>
|
|
262
|
+
<span class="pkt-header-menu__service-text">${link.text}</span>
|
|
263
|
+
</a>
|
|
264
|
+
</li>
|
|
265
|
+
`,
|
|
266
|
+
)}
|
|
267
|
+
</ul>
|
|
268
|
+
`
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private renderServices(services: IHeaderMenuServices): TemplateResult {
|
|
272
|
+
return html`
|
|
273
|
+
<div class="pkt-header-menu__services">
|
|
274
|
+
<h2 class="pkt-header-menu__services-title">${services.title}</h2>
|
|
275
|
+
${this.renderServicesList(services)}
|
|
276
|
+
</div>
|
|
277
|
+
`
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private renderButtons(
|
|
281
|
+
buttons: IHeaderMenuButton[] | undefined,
|
|
282
|
+
isMobilePlacement: boolean,
|
|
283
|
+
): TemplateResult | typeof nothing {
|
|
284
|
+
if (!buttons || buttons.length === 0) return nothing
|
|
285
|
+
|
|
286
|
+
const classes = classMap({
|
|
287
|
+
'pkt-header-menu__buttons': true,
|
|
288
|
+
'pkt-header-menu__buttons--mobile': isMobilePlacement,
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
return html`
|
|
292
|
+
<div class=${classes}>
|
|
293
|
+
${buttons.map(
|
|
294
|
+
(button) => html`
|
|
295
|
+
<a
|
|
296
|
+
class="pkt-btn pkt-btn--secondary pkt-btn--icon-right pkt-btn--small"
|
|
297
|
+
href=${button.url}
|
|
298
|
+
>
|
|
299
|
+
<pkt-icon
|
|
300
|
+
class="pkt-btn__icon"
|
|
301
|
+
name=${button.iconName ? mapOdsIcon(button.iconName) : 'user'}
|
|
302
|
+
aria-hidden="true"
|
|
303
|
+
></pkt-icon>
|
|
304
|
+
<span class="pkt-btn__text">${button.text}</span>
|
|
305
|
+
</a>
|
|
306
|
+
`,
|
|
307
|
+
)}
|
|
308
|
+
</div>
|
|
309
|
+
`
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private renderSections(sections: IHeaderMenuSection[]): TemplateResult | typeof nothing {
|
|
313
|
+
if (!sections || sections.length === 0) return nothing
|
|
314
|
+
|
|
315
|
+
return html`
|
|
316
|
+
<div class="pkt-header-menu__sections">
|
|
317
|
+
<div class="pkt-header-menu__sections-inner">
|
|
318
|
+
${sections.map(
|
|
319
|
+
(section) => html`
|
|
320
|
+
<div class="pkt-header-menu__section">
|
|
321
|
+
<h2 class="pkt-header-menu__section-title">${section.title}</h2>
|
|
322
|
+
${this.renderSectionList(section.links)}
|
|
323
|
+
</div>
|
|
324
|
+
`,
|
|
325
|
+
)}
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
`
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private renderSectionList(links: IHeaderMenuLink[]): TemplateResult {
|
|
332
|
+
return html`
|
|
333
|
+
<ul class="pkt-header-menu__section-list">
|
|
334
|
+
${links.map(
|
|
335
|
+
(link) => html`
|
|
336
|
+
<li>
|
|
337
|
+
<a class="pkt-header-menu__section-link" href=${link.url}>${link.text}</a>
|
|
338
|
+
</li>
|
|
339
|
+
`,
|
|
340
|
+
)}
|
|
341
|
+
</ul>
|
|
342
|
+
`
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private renderFooter(
|
|
346
|
+
links: IHeaderMenuLink[],
|
|
347
|
+
some: IHeaderMenuLink[],
|
|
348
|
+
): TemplateResult | typeof nothing {
|
|
349
|
+
if ((!links || links.length === 0) && (!some || some.length === 0)) return nothing
|
|
350
|
+
|
|
351
|
+
return html`
|
|
352
|
+
<div class="pkt-header-menu__footer">
|
|
353
|
+
${links && links.length > 0
|
|
354
|
+
? html`
|
|
355
|
+
<ul class="pkt-header-menu__footer-list">
|
|
356
|
+
${links.map(
|
|
357
|
+
(link) => html`
|
|
358
|
+
<li>
|
|
359
|
+
<a class="pkt-header-menu__footer-link" href=${link.url}>${link.text}</a>
|
|
360
|
+
</li>
|
|
361
|
+
`,
|
|
362
|
+
)}
|
|
363
|
+
</ul>
|
|
364
|
+
`
|
|
365
|
+
: nothing}
|
|
366
|
+
${some && some.length > 0
|
|
367
|
+
? html`
|
|
368
|
+
<ul class="pkt-header-menu__footer-list pkt-header-menu__footer-list--social">
|
|
369
|
+
${some.map((entry) => this.renderSocialLink(entry))}
|
|
370
|
+
</ul>
|
|
371
|
+
`
|
|
372
|
+
: nothing}
|
|
373
|
+
</div>
|
|
374
|
+
`
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private renderSocialLink(entry: IHeaderMenuLink): TemplateResult {
|
|
378
|
+
const iconName = deriveSocialIcon(entry.url, entry.text)
|
|
379
|
+
return html`
|
|
380
|
+
<li>
|
|
381
|
+
<a class="pkt-header-menu__social-link" href=${entry.url} aria-label=${entry.text}>
|
|
382
|
+
${iconName
|
|
383
|
+
? html`<pkt-icon name=${iconName} aria-hidden="true"></pkt-icon>`
|
|
384
|
+
: html`<span>${entry.text}</span>`}
|
|
385
|
+
</a>
|
|
386
|
+
</li>
|
|
387
|
+
`
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
declare global {
|
|
392
|
+
interface HTMLElementTagNameMap {
|
|
393
|
+
'pkt-header-menu': PktHeaderMenu
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
try {
|
|
398
|
+
customElement('pkt-header-menu')(PktHeaderMenu)
|
|
399
|
+
} catch (e) {
|
|
400
|
+
console.warn('Forsøker å definere <pkt-header-menu>, men den er allerede definert')
|
|
401
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IHeaderMenuLink,
|
|
3
|
+
IHeaderMenuIconLink,
|
|
4
|
+
IHeaderMenuButton,
|
|
5
|
+
IHeaderMenuServices,
|
|
6
|
+
IHeaderMenuSection,
|
|
7
|
+
IMegaMenu,
|
|
8
|
+
IHeaderFooter,
|
|
9
|
+
IHeaderFooterLocaleData,
|
|
10
|
+
IPktHeaderMenu as ISharedPktHeaderMenu,
|
|
11
|
+
THeaderFooterApi,
|
|
12
|
+
THeaderMenuLocale,
|
|
13
|
+
} from 'shared-types/header-menu'
|
|
14
|
+
import type { Booleanish } from 'shared-types'
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
IHeaderMenuLink,
|
|
18
|
+
IHeaderMenuIconLink,
|
|
19
|
+
IHeaderMenuButton,
|
|
20
|
+
IHeaderMenuServices,
|
|
21
|
+
IHeaderMenuSection,
|
|
22
|
+
IMegaMenu,
|
|
23
|
+
IHeaderFooter,
|
|
24
|
+
IHeaderFooterLocaleData,
|
|
25
|
+
THeaderFooterApi,
|
|
26
|
+
THeaderMenuLocale,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IPktHeaderMenu extends Omit<ISharedPktHeaderMenu, 'open'> {
|
|
30
|
+
open?: Booleanish
|
|
31
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { PktFileUpload } from '@/components/fileupload'
|
|
|
13
13
|
export { PktDateTags } from '@/components/datepicker/date-tags.js'
|
|
14
14
|
export { PktDatepicker } from '@/components/datepicker/datepicker.js'
|
|
15
15
|
export { PktHeader, PktHeaderService, PktHeaderUserMenu } from '@/components/header'
|
|
16
|
+
export { PktHeaderMenu } from '@/components/header-menu'
|
|
16
17
|
export { PktHelptext } from '@/components/helptext'
|
|
17
18
|
export { PktHeading } from '@/components/heading'
|
|
18
19
|
export { PktIcon } from '@/components/icon'
|
|
@@ -59,6 +60,8 @@ export type {
|
|
|
59
60
|
IPktHeader,
|
|
60
61
|
} from '@/components/header'
|
|
61
62
|
|
|
63
|
+
export type { IPktHeaderMenu } from '@/components/header-menu'
|
|
64
|
+
|
|
62
65
|
export type {
|
|
63
66
|
IPktProgressbar,
|
|
64
67
|
TProgressbarRole,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|