@helsevestikt/hviktor-angular 0.0.3
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/LICENSE +21 -0
- package/README.md +58 -0
- package/fesm2022/helsevestikt-hviktor-angular.mjs +1776 -0
- package/fesm2022/helsevestikt-hviktor-angular.mjs.map +1 -0
- package/package.json +37 -0
- package/styles.css +33 -0
|
@@ -0,0 +1,1776 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Input, Component, booleanAttribute, Directive, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, inject, ElementRef, HostListener, Output, ViewChild, DestroyRef, HostBinding, ViewEncapsulation } from '@angular/core';
|
|
3
|
+
import '@u-elements/u-details';
|
|
4
|
+
import { FormGroupDirective, NgControl, ControlContainer, ReactiveFormsModule } from '@angular/forms';
|
|
5
|
+
import * as i1 from '@angular/common/http';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Alert provides users with information that is especially important for them to see and understand.
|
|
9
|
+
* The component is designed to capture users' attention.
|
|
10
|
+
* The text in the alert should be short and clear.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```html
|
|
14
|
+
* <hvi-alert color="warning">
|
|
15
|
+
* Dette er et advarselsvarsel!
|
|
16
|
+
* </hvi-alert>
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Documentation: https://designsystemet.no/en/components/docs/alert/code/
|
|
20
|
+
*/
|
|
21
|
+
class HviAlert {
|
|
22
|
+
/** Sets the type of alert by changing the color and */
|
|
23
|
+
color;
|
|
24
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviAlert, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
25
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviAlert, isStandalone: true, selector: "hvi-alert", inputs: { color: "color" }, host: { properties: { "attr.data-color": "color" }, classAttribute: "ds-alert" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
26
|
+
}
|
|
27
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviAlert, decorators: [{
|
|
28
|
+
type: Component,
|
|
29
|
+
args: [{
|
|
30
|
+
selector: 'hvi-alert',
|
|
31
|
+
standalone: true,
|
|
32
|
+
template: `<ng-content />`,
|
|
33
|
+
host: {
|
|
34
|
+
class: 'ds-alert',
|
|
35
|
+
'[attr.data-color]': 'color',
|
|
36
|
+
},
|
|
37
|
+
}]
|
|
38
|
+
}], propDecorators: { color: [{
|
|
39
|
+
type: Input
|
|
40
|
+
}] } });
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Avatar displays an image, initials, or icon for a person, entity, or profile.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```html
|
|
47
|
+
* <hvi-avatar
|
|
48
|
+
* ariaLabel="Ola Nordmann"
|
|
49
|
+
* variant="circle"
|
|
50
|
+
* initials="ON"
|
|
51
|
+
* size="md"
|
|
52
|
+
* color="brand1">
|
|
53
|
+
* </hvi-avatar>
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* Documentation: https://designsystemet.no/en/components/docs/avatar/code/
|
|
57
|
+
*/
|
|
58
|
+
class HviAvatar {
|
|
59
|
+
/** The name of the person the avatar represents */
|
|
60
|
+
ariaLabel;
|
|
61
|
+
/** The shape of the avatar */
|
|
62
|
+
variant;
|
|
63
|
+
/** Initials displayed inside the avatar */
|
|
64
|
+
initials;
|
|
65
|
+
/** The size of the avatar */
|
|
66
|
+
size;
|
|
67
|
+
/** The color theme of the avatar */
|
|
68
|
+
color;
|
|
69
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
70
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviAvatar, isStandalone: true, selector: "hvi-avatar", inputs: { ariaLabel: "ariaLabel", variant: "variant", initials: "initials", size: "size", color: "color" }, host: { attributes: { "role": "img" }, properties: { "attr.aria-label": "ariaLabel ?? null", "attr.data-variant": "variant ?? null", "attr.data-initials": "initials ?? null", "attr.data-size": "size ?? null", "attr.data-color": "color ?? null" }, classAttribute: "ds-avatar" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
71
|
+
}
|
|
72
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviAvatar, decorators: [{
|
|
73
|
+
type: Component,
|
|
74
|
+
args: [{
|
|
75
|
+
selector: 'hvi-avatar',
|
|
76
|
+
standalone: true,
|
|
77
|
+
template: '<ng-content />',
|
|
78
|
+
host: {
|
|
79
|
+
class: 'ds-avatar',
|
|
80
|
+
role: 'img',
|
|
81
|
+
'[attr.aria-label]': 'ariaLabel ?? null',
|
|
82
|
+
'[attr.data-variant]': 'variant ?? null',
|
|
83
|
+
'[attr.data-initials]': 'initials ?? null',
|
|
84
|
+
'[attr.data-size]': 'size ?? null',
|
|
85
|
+
'[attr.data-color]': 'color ?? null',
|
|
86
|
+
},
|
|
87
|
+
}]
|
|
88
|
+
}], propDecorators: { ariaLabel: [{
|
|
89
|
+
type: Input
|
|
90
|
+
}], variant: [{
|
|
91
|
+
type: Input
|
|
92
|
+
}], initials: [{
|
|
93
|
+
type: Input
|
|
94
|
+
}], size: [{
|
|
95
|
+
type: Input
|
|
96
|
+
}], color: [{
|
|
97
|
+
type: Input
|
|
98
|
+
}] } });
|
|
99
|
+
|
|
100
|
+
class HviBadgePosition {
|
|
101
|
+
/** Overlap of the badge */
|
|
102
|
+
overlap = 'rectangle';
|
|
103
|
+
/** Placement of the badge */
|
|
104
|
+
placement = 'top-right';
|
|
105
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviBadgePosition, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
106
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviBadgePosition, isStandalone: true, selector: "hvi-badge-position", inputs: { overlap: "overlap", placement: "placement" }, host: { properties: { "attr.data-overlap": "overlap", "attr.data-placement": "placement" }, classAttribute: "ds-badge--position" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
107
|
+
}
|
|
108
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviBadgePosition, decorators: [{
|
|
109
|
+
type: Component,
|
|
110
|
+
args: [{
|
|
111
|
+
selector: 'hvi-badge-position',
|
|
112
|
+
standalone: true,
|
|
113
|
+
template: '<ng-content />',
|
|
114
|
+
host: {
|
|
115
|
+
class: 'ds-badge--position',
|
|
116
|
+
'[attr.data-overlap]': 'overlap',
|
|
117
|
+
'[attr.data-placement]': 'placement',
|
|
118
|
+
},
|
|
119
|
+
}]
|
|
120
|
+
}], propDecorators: { overlap: [{
|
|
121
|
+
type: Input
|
|
122
|
+
}], placement: [{
|
|
123
|
+
type: Input
|
|
124
|
+
}] } });
|
|
125
|
+
|
|
126
|
+
class HviBadge {
|
|
127
|
+
/** The variants of the badge */
|
|
128
|
+
variant;
|
|
129
|
+
/** count text of the badge*/
|
|
130
|
+
count;
|
|
131
|
+
/** The color theme of the badge */
|
|
132
|
+
color;
|
|
133
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
134
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviBadge, isStandalone: true, selector: "hvi-badge", inputs: { variant: "variant", count: "count", color: "color" }, host: { properties: { "attr.data-variant": "variant ?? null", "attr.data-count": "count ?? null", "attr.data-color": "color ?? null" }, classAttribute: "ds-badge" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
135
|
+
}
|
|
136
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviBadge, decorators: [{
|
|
137
|
+
type: Component,
|
|
138
|
+
args: [{
|
|
139
|
+
selector: 'hvi-badge',
|
|
140
|
+
standalone: true,
|
|
141
|
+
template: '<ng-content />',
|
|
142
|
+
host: {
|
|
143
|
+
class: 'ds-badge',
|
|
144
|
+
'[attr.data-variant]': 'variant ?? null',
|
|
145
|
+
'[attr.data-count]': 'count ?? null',
|
|
146
|
+
'[attr.data-color]': 'color ?? null',
|
|
147
|
+
},
|
|
148
|
+
}]
|
|
149
|
+
}], propDecorators: { variant: [{
|
|
150
|
+
type: Input
|
|
151
|
+
}], count: [{
|
|
152
|
+
type: Input
|
|
153
|
+
}], color: [{
|
|
154
|
+
type: Input
|
|
155
|
+
}] } });
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Info
|
|
159
|
+
*
|
|
160
|
+
* Eksempel på bruk:
|
|
161
|
+
* ```html
|
|
162
|
+
* <nav hviBreadcrumbs
|
|
163
|
+
* ariaLabel="Du er her:"
|
|
164
|
+
* [backLink]="{
|
|
165
|
+
* label: 'Nivå 3',
|
|
166
|
+
* href: '#',
|
|
167
|
+
* ariaLabel: 'Tilbake til Nivå 3'
|
|
168
|
+
* }"
|
|
169
|
+
* [items]="[
|
|
170
|
+
* { label: 'Nivå 1', href: '#' },
|
|
171
|
+
* { label: 'Nivå 2', href: '#' },
|
|
172
|
+
* { label: 'Nivå 3', href: '#' },
|
|
173
|
+
* { label: 'Nivå 4', href: '#' }
|
|
174
|
+
* ]"
|
|
175
|
+
* ></nav>
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* Dokumentasjon: https://designsystemet.no/no/components/docs/breadcrumbs/overview
|
|
179
|
+
*/
|
|
180
|
+
class HviBreadcrumbs {
|
|
181
|
+
/** Accessible label for the breadcrumb navigation */
|
|
182
|
+
ariaLabel = 'Du er her:';
|
|
183
|
+
/** Optional back link object */
|
|
184
|
+
backLink;
|
|
185
|
+
/** Array of breadcrumb items */
|
|
186
|
+
items = [];
|
|
187
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviBreadcrumbs, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
188
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: HviBreadcrumbs, isStandalone: true, selector: "nav[hviBreadcrumbs]", inputs: { ariaLabel: "ariaLabel", backLink: "backLink", items: "items" }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "ariaLabel ?? null" }, classAttribute: "ds-breadcrumbs" }, ngImport: i0, template: `
|
|
189
|
+
<!-- Back link (optional) -->
|
|
190
|
+
@if (backLink) {
|
|
191
|
+
<a class="ds-link" [href]="backLink.href" [attr.aria-label]="backLink.ariaLabel ?? null">
|
|
192
|
+
{{ backLink.label }}
|
|
193
|
+
</a>
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
<ol>
|
|
197
|
+
@for (item of items; let last = $last; track item) {
|
|
198
|
+
<li>
|
|
199
|
+
<a
|
|
200
|
+
class="ds-link"
|
|
201
|
+
[href]="item.href"
|
|
202
|
+
[attr.aria-label]="item.ariaLabel ?? null"
|
|
203
|
+
[attr.aria-current]="last ? 'page' : null"
|
|
204
|
+
>
|
|
205
|
+
{{ item.label }}
|
|
206
|
+
</a>
|
|
207
|
+
</li>
|
|
208
|
+
}
|
|
209
|
+
</ol>
|
|
210
|
+
`, isInline: true });
|
|
211
|
+
}
|
|
212
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviBreadcrumbs, decorators: [{
|
|
213
|
+
type: Component,
|
|
214
|
+
args: [{
|
|
215
|
+
selector: 'nav[hviBreadcrumbs]',
|
|
216
|
+
standalone: true,
|
|
217
|
+
template: `
|
|
218
|
+
<!-- Back link (optional) -->
|
|
219
|
+
@if (backLink) {
|
|
220
|
+
<a class="ds-link" [href]="backLink.href" [attr.aria-label]="backLink.ariaLabel ?? null">
|
|
221
|
+
{{ backLink.label }}
|
|
222
|
+
</a>
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
<ol>
|
|
226
|
+
@for (item of items; let last = $last; track item) {
|
|
227
|
+
<li>
|
|
228
|
+
<a
|
|
229
|
+
class="ds-link"
|
|
230
|
+
[href]="item.href"
|
|
231
|
+
[attr.aria-label]="item.ariaLabel ?? null"
|
|
232
|
+
[attr.aria-current]="last ? 'page' : null"
|
|
233
|
+
>
|
|
234
|
+
{{ item.label }}
|
|
235
|
+
</a>
|
|
236
|
+
</li>
|
|
237
|
+
}
|
|
238
|
+
</ol>
|
|
239
|
+
`,
|
|
240
|
+
host: {
|
|
241
|
+
class: 'ds-breadcrumbs',
|
|
242
|
+
role: 'navigation',
|
|
243
|
+
'[attr.aria-label]': 'ariaLabel ?? null',
|
|
244
|
+
},
|
|
245
|
+
}]
|
|
246
|
+
}], propDecorators: { ariaLabel: [{
|
|
247
|
+
type: Input
|
|
248
|
+
}], backLink: [{
|
|
249
|
+
type: Input
|
|
250
|
+
}], items: [{
|
|
251
|
+
type: Input
|
|
252
|
+
}] } });
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @summary
|
|
256
|
+
* Button allows users to perform actions.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```html
|
|
260
|
+
* <button hviButton color="brand1" variant="primary" size="md">
|
|
261
|
+
* Click me
|
|
262
|
+
* </button>
|
|
263
|
+
* ```
|
|
264
|
+
*
|
|
265
|
+
* Documentation: https://designsystemet.no/en/components/docs/button/code/
|
|
266
|
+
*/
|
|
267
|
+
class HviButton {
|
|
268
|
+
/** The size of the button */
|
|
269
|
+
size;
|
|
270
|
+
/** Used to change the appearance of the button. */
|
|
271
|
+
variant;
|
|
272
|
+
/** The type of button */
|
|
273
|
+
type;
|
|
274
|
+
/** The color of the button */
|
|
275
|
+
color;
|
|
276
|
+
/** If you have only an icon in the button, you can set icon="true" to make it square.
|
|
277
|
+
* If you have other content, such as text, the button will automatically have space around the icon.
|
|
278
|
+
*/
|
|
279
|
+
icon = false;
|
|
280
|
+
/** Sets the button in a loading state.
|
|
281
|
+
* Loading indicators such as spinners must be added manually, e.g., with hvi-spinner
|
|
282
|
+
*/
|
|
283
|
+
loading = false;
|
|
284
|
+
/** Makes the button full width */
|
|
285
|
+
fullWidth = false;
|
|
286
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviButton, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
287
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.1.2", type: HviButton, isStandalone: true, selector: "button[hviButton], a[hviButton]", inputs: { size: "size", variant: "variant", type: "type", color: "color", icon: ["icon", "icon", booleanAttribute], loading: ["loading", "loading", booleanAttribute], fullWidth: ["fullWidth", "fullWidth", booleanAttribute] }, host: { properties: { "attr.type": "type", "attr.data-size": "size", "attr.data-variant": "variant", "attr.data-color": "color", "attr.data-fullwidth": "fullWidth ? \"\" : null", "attr.data-icon": "icon ? \"\" : null", "attr.aria-busy": "loading ? \"true\" : null" }, classAttribute: "ds-button" }, ngImport: i0 });
|
|
288
|
+
}
|
|
289
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviButton, decorators: [{
|
|
290
|
+
type: Directive,
|
|
291
|
+
args: [{
|
|
292
|
+
selector: 'button[hviButton], a[hviButton]',
|
|
293
|
+
standalone: true,
|
|
294
|
+
host: {
|
|
295
|
+
class: 'ds-button',
|
|
296
|
+
'[attr.type]': 'type',
|
|
297
|
+
'[attr.data-size]': 'size',
|
|
298
|
+
'[attr.data-variant]': 'variant',
|
|
299
|
+
'[attr.data-color]': 'color',
|
|
300
|
+
'[attr.data-fullwidth]': 'fullWidth ? "" : null',
|
|
301
|
+
'[attr.data-icon]': 'icon ? "" : null',
|
|
302
|
+
'[attr.aria-busy]': 'loading ? "true" : null',
|
|
303
|
+
},
|
|
304
|
+
}]
|
|
305
|
+
}], propDecorators: { size: [{
|
|
306
|
+
type: Input
|
|
307
|
+
}], variant: [{
|
|
308
|
+
type: Input
|
|
309
|
+
}], type: [{
|
|
310
|
+
type: Input
|
|
311
|
+
}], color: [{
|
|
312
|
+
type: Input
|
|
313
|
+
}], icon: [{
|
|
314
|
+
type: Input,
|
|
315
|
+
args: [{ transform: booleanAttribute }]
|
|
316
|
+
}], loading: [{
|
|
317
|
+
type: Input,
|
|
318
|
+
args: [{ transform: booleanAttribute }]
|
|
319
|
+
}], fullWidth: [{
|
|
320
|
+
type: Input,
|
|
321
|
+
args: [{ transform: booleanAttribute }]
|
|
322
|
+
}] } });
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Use multiple <div hviCardBlock> elements if you want to divide the card with separators or add images or video that extend to the edge.
|
|
326
|
+
* Note that when you use <div hviCardBlock>, all content must be placed inside a <div hviCardBlock> and not directly in the Card.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```html
|
|
330
|
+
* <hvi-card variant="tinted" color="brand1" maxWidth="400px">
|
|
331
|
+
* <div hviCardBlock>
|
|
332
|
+
* <h2>This is a card</h2>
|
|
333
|
+
* <p>The content of the card goes here.</p>
|
|
334
|
+
* </div>
|
|
335
|
+
* </hvi-card>
|
|
336
|
+
* ```
|
|
337
|
+
*
|
|
338
|
+
* Documentation: https://designsystemet.no/en/components/docs/card/code#with-sections
|
|
339
|
+
*/
|
|
340
|
+
class HviCardBlock {
|
|
341
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviCardBlock, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
342
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviCardBlock, isStandalone: true, selector: "[hviCardBlock]", host: { classAttribute: "ds-card__block" }, ngImport: i0 });
|
|
343
|
+
}
|
|
344
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviCardBlock, decorators: [{
|
|
345
|
+
type: Directive,
|
|
346
|
+
args: [{
|
|
347
|
+
selector: '[hviCardBlock]',
|
|
348
|
+
standalone: true,
|
|
349
|
+
host: { class: 'ds-card__block' },
|
|
350
|
+
}]
|
|
351
|
+
}] });
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Card highlight information or tasks that are related.
|
|
355
|
+
* The component comes in two variants and can contain text, images, text fields, buttons, and links.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```html
|
|
359
|
+
* <hvi-card variant="tinted" color="brand1" maxWidth="400px">
|
|
360
|
+
* <h2>This is a card</h2>
|
|
361
|
+
* <p>The content of the card goes here.</p>
|
|
362
|
+
* </hvi-card>
|
|
363
|
+
* ```
|
|
364
|
+
*
|
|
365
|
+
* Documentation: https://designsystemet.no/en/components/docs/card/overview
|
|
366
|
+
*/
|
|
367
|
+
class HviCard {
|
|
368
|
+
/** Sets the background of the card */
|
|
369
|
+
variant;
|
|
370
|
+
/** The color theme of the card */
|
|
371
|
+
color;
|
|
372
|
+
/** Maximum width of the card, for example '320px' or '20rem' */
|
|
373
|
+
maxWidth;
|
|
374
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
375
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviCard, isStandalone: true, selector: "hvi-card", inputs: { variant: "variant", color: "color", maxWidth: "maxWidth" }, host: { properties: { "attr.data-variant": "variant", "attr.data-color": "color", "style.max-width": "maxWidth" }, classAttribute: "ds-card" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
376
|
+
}
|
|
377
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviCard, decorators: [{
|
|
378
|
+
type: Component,
|
|
379
|
+
args: [{
|
|
380
|
+
selector: 'hvi-card',
|
|
381
|
+
standalone: true,
|
|
382
|
+
template: '<ng-content />',
|
|
383
|
+
host: {
|
|
384
|
+
class: 'ds-card',
|
|
385
|
+
'[attr.data-variant]': 'variant',
|
|
386
|
+
'[attr.data-color]': 'color',
|
|
387
|
+
'[style.max-width]': 'maxWidth',
|
|
388
|
+
},
|
|
389
|
+
}]
|
|
390
|
+
}], propDecorators: { variant: [{
|
|
391
|
+
type: Input
|
|
392
|
+
}], color: [{
|
|
393
|
+
type: Input
|
|
394
|
+
}], maxWidth: [{
|
|
395
|
+
type: Input
|
|
396
|
+
}] } });
|
|
397
|
+
|
|
398
|
+
class HviChipButton {
|
|
399
|
+
/** Whether the chip is removable*/
|
|
400
|
+
removable = false;
|
|
401
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviChipButton, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
402
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.1.2", type: HviChipButton, isStandalone: true, selector: "button[hviChip]", inputs: { removable: ["removable", "removable", booleanAttribute] }, host: { properties: { "attr.data-removable": "removable ? \"true\" : null" }, classAttribute: "ds-chip" }, ngImport: i0 });
|
|
403
|
+
}
|
|
404
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviChipButton, decorators: [{
|
|
405
|
+
type: Directive,
|
|
406
|
+
args: [{
|
|
407
|
+
selector: 'button[hviChip]',
|
|
408
|
+
standalone: true,
|
|
409
|
+
host: {
|
|
410
|
+
class: 'ds-chip',
|
|
411
|
+
'[attr.data-removable]': 'removable ? "true" : null',
|
|
412
|
+
},
|
|
413
|
+
}]
|
|
414
|
+
}], propDecorators: { removable: [{
|
|
415
|
+
type: Input,
|
|
416
|
+
args: [{ transform: booleanAttribute }]
|
|
417
|
+
}] } });
|
|
418
|
+
|
|
419
|
+
class HviChipLabel {
|
|
420
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviChipLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
421
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviChipLabel, isStandalone: true, selector: "label[hviChip]", host: { classAttribute: "ds-chip" }, ngImport: i0 });
|
|
422
|
+
}
|
|
423
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviChipLabel, decorators: [{
|
|
424
|
+
type: Directive,
|
|
425
|
+
args: [{
|
|
426
|
+
selector: 'label[hviChip]',
|
|
427
|
+
standalone: true,
|
|
428
|
+
host: {
|
|
429
|
+
class: 'ds-chip',
|
|
430
|
+
},
|
|
431
|
+
}]
|
|
432
|
+
}] });
|
|
433
|
+
|
|
434
|
+
class HviDetailsContent {
|
|
435
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDetailsContent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
436
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviDetailsContent, isStandalone: true, selector: "hvi-details-content", ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
437
|
+
}
|
|
438
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDetailsContent, decorators: [{
|
|
439
|
+
type: Component,
|
|
440
|
+
args: [{
|
|
441
|
+
selector: 'hvi-details-content',
|
|
442
|
+
template: `<ng-content />`,
|
|
443
|
+
}]
|
|
444
|
+
}] });
|
|
445
|
+
|
|
446
|
+
class HviDetailsSummary {
|
|
447
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDetailsSummary, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
448
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviDetailsSummary, isStandalone: true, selector: "hvi-details-summary", ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
449
|
+
}
|
|
450
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDetailsSummary, decorators: [{
|
|
451
|
+
type: Component,
|
|
452
|
+
args: [{
|
|
453
|
+
selector: 'hvi-details-summary',
|
|
454
|
+
template: `<ng-content />`,
|
|
455
|
+
}]
|
|
456
|
+
}] });
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Info
|
|
460
|
+
*
|
|
461
|
+
* Eksempel på bruk:
|
|
462
|
+
* ```html
|
|
463
|
+
* <hvi-details>
|
|
464
|
+
* <hvi-details-summary>
|
|
465
|
+
* <p size="md">Detaljer</p>
|
|
466
|
+
* </hvi-details-summary>
|
|
467
|
+
* <hvi-details-content>
|
|
468
|
+
* <p>Her er innholdet i detaljene.</p>
|
|
469
|
+
* </hvi-details-content>
|
|
470
|
+
* </hvi-details>
|
|
471
|
+
* ```
|
|
472
|
+
*
|
|
473
|
+
* Dokumentasjon: https://designsystemet.no/no/components/docs/details/overview
|
|
474
|
+
*/
|
|
475
|
+
class HviDetails {
|
|
476
|
+
/** Variant of the details component */
|
|
477
|
+
variant = 'default';
|
|
478
|
+
/** Control open state of the details component */
|
|
479
|
+
open = false;
|
|
480
|
+
/** Set default open state of the details component */
|
|
481
|
+
defaultOpen = false;
|
|
482
|
+
/** Event handler for toggle event */
|
|
483
|
+
onToggle = () => { };
|
|
484
|
+
handleToggle(event) {
|
|
485
|
+
console.log('Toggled!', event);
|
|
486
|
+
}
|
|
487
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDetails, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
488
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviDetails, isStandalone: true, selector: "hvi-details", inputs: { variant: "variant", open: "open", defaultOpen: "defaultOpen", onToggle: "onToggle" }, ngImport: i0, template: ` <u-details
|
|
489
|
+
#detailsRef
|
|
490
|
+
class="ds-details"
|
|
491
|
+
[attr.data-variant]="variant"
|
|
492
|
+
[attr.defaultOpen]="defaultOpen || undefined"
|
|
493
|
+
[attr.open]="open || undefined"
|
|
494
|
+
(toggle)="onToggle($event)"
|
|
495
|
+
>
|
|
496
|
+
<u-summary>
|
|
497
|
+
<ng-content select="hvi-details-summary" />
|
|
498
|
+
</u-summary>
|
|
499
|
+
<div>
|
|
500
|
+
<ng-content select="hvi-details-content" />
|
|
501
|
+
</div>
|
|
502
|
+
</u-details>`, isInline: true });
|
|
503
|
+
}
|
|
504
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDetails, decorators: [{
|
|
505
|
+
type: Component,
|
|
506
|
+
args: [{
|
|
507
|
+
selector: 'hvi-details',
|
|
508
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
509
|
+
standalone: true,
|
|
510
|
+
template: ` <u-details
|
|
511
|
+
#detailsRef
|
|
512
|
+
class="ds-details"
|
|
513
|
+
[attr.data-variant]="variant"
|
|
514
|
+
[attr.defaultOpen]="defaultOpen || undefined"
|
|
515
|
+
[attr.open]="open || undefined"
|
|
516
|
+
(toggle)="onToggle($event)"
|
|
517
|
+
>
|
|
518
|
+
<u-summary>
|
|
519
|
+
<ng-content select="hvi-details-summary" />
|
|
520
|
+
</u-summary>
|
|
521
|
+
<div>
|
|
522
|
+
<ng-content select="hvi-details-content" />
|
|
523
|
+
</div>
|
|
524
|
+
</u-details>`,
|
|
525
|
+
host: {},
|
|
526
|
+
}]
|
|
527
|
+
}], propDecorators: { variant: [{
|
|
528
|
+
type: Input
|
|
529
|
+
}], open: [{
|
|
530
|
+
type: Input
|
|
531
|
+
}], defaultOpen: [{
|
|
532
|
+
type: Input
|
|
533
|
+
}], onToggle: [{
|
|
534
|
+
type: Input
|
|
535
|
+
}] } });
|
|
536
|
+
|
|
537
|
+
class HviDialogBlock {
|
|
538
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDialogBlock, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
539
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviDialogBlock, isStandalone: true, selector: "[hviDialogBlock]", host: { classAttribute: "ds-dialog__block" }, ngImport: i0 });
|
|
540
|
+
}
|
|
541
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDialogBlock, decorators: [{
|
|
542
|
+
type: Directive,
|
|
543
|
+
args: [{
|
|
544
|
+
selector: '[hviDialogBlock]',
|
|
545
|
+
standalone: true,
|
|
546
|
+
host: {
|
|
547
|
+
class: 'ds-dialog__block',
|
|
548
|
+
},
|
|
549
|
+
}]
|
|
550
|
+
}] });
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @summary
|
|
554
|
+
* Dialog allows you to create both modal and non-modal dialogs based on the HTML dialog element.
|
|
555
|
+
* You have to connect the <dialog> element to a trigger yourself, and handle opening and closing the dialog with JavaScript.
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* HTML:
|
|
559
|
+
* ```html
|
|
560
|
+
* <dialog hviDialog [id]="exampleDialog" [open]="dialogOpen()" (openChange)="toggleDialog($event)">
|
|
561
|
+
* <h3 hviHeading size="md">Example dialog</h3>
|
|
562
|
+
* <p hviParagraph>This is an example of a dialog component.</p>
|
|
563
|
+
* <button hviButton variant="primary" (click)="closeDialog()">Close dialog</button>
|
|
564
|
+
* </dialog>
|
|
565
|
+
* ```
|
|
566
|
+
*
|
|
567
|
+
* TypeScript:
|
|
568
|
+
* ```ts
|
|
569
|
+
* export class DialogExampleComponent {
|
|
570
|
+
* readonly dialogOpen = signal(false);
|
|
571
|
+
* toggleDialog(nextState?: boolean): void {
|
|
572
|
+
* if (typeof nextState === 'boolean') {
|
|
573
|
+
* this.dialogOpen.set(nextState);
|
|
574
|
+
* return;
|
|
575
|
+
* }
|
|
576
|
+
* this.dialogOpen.update((current) => !current);
|
|
577
|
+
* }
|
|
578
|
+
* }
|
|
579
|
+
*
|
|
580
|
+
* Documentation: https://designsystemet.no/en/components/docs/dialog/code/
|
|
581
|
+
*/
|
|
582
|
+
class HviDialog {
|
|
583
|
+
id;
|
|
584
|
+
set open(value) {
|
|
585
|
+
this.setOpen(Boolean(value));
|
|
586
|
+
}
|
|
587
|
+
get open() {
|
|
588
|
+
return this.element.open;
|
|
589
|
+
}
|
|
590
|
+
modal = true;
|
|
591
|
+
openChange = new EventEmitter();
|
|
592
|
+
element = inject((ElementRef)).nativeElement;
|
|
593
|
+
openModal() {
|
|
594
|
+
this.setOpen(true);
|
|
595
|
+
}
|
|
596
|
+
close() {
|
|
597
|
+
this.setOpen(false);
|
|
598
|
+
}
|
|
599
|
+
handleClose() {
|
|
600
|
+
this.openChange.emit(false);
|
|
601
|
+
}
|
|
602
|
+
handleCancel(event) {
|
|
603
|
+
event.preventDefault();
|
|
604
|
+
this.setOpen(false);
|
|
605
|
+
}
|
|
606
|
+
setOpen(shouldOpen) {
|
|
607
|
+
if (shouldOpen) {
|
|
608
|
+
if (this.element.open) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (this.modal) {
|
|
612
|
+
this.element.showModal();
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
this.element.show();
|
|
616
|
+
}
|
|
617
|
+
this.openChange.emit(true);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
if (!this.element.open) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
this.element.close();
|
|
624
|
+
}
|
|
625
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDialog, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
626
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviDialog, isStandalone: true, selector: "dialog[hviDialog]", inputs: { id: "id", open: "open", modal: "modal" }, outputs: { openChange: "openChange" }, host: { attributes: { "id": "{{ id }}" }, listeners: { "close": "handleClose()", "cancel": "handleCancel($event)" }, classAttribute: "ds-dialog" }, ngImport: i0 });
|
|
627
|
+
}
|
|
628
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDialog, decorators: [{
|
|
629
|
+
type: Directive,
|
|
630
|
+
args: [{
|
|
631
|
+
selector: 'dialog[hviDialog]',
|
|
632
|
+
standalone: true,
|
|
633
|
+
host: {
|
|
634
|
+
class: 'ds-dialog',
|
|
635
|
+
id: '{{ id }}',
|
|
636
|
+
},
|
|
637
|
+
}]
|
|
638
|
+
}], propDecorators: { id: [{
|
|
639
|
+
type: Input
|
|
640
|
+
}], open: [{
|
|
641
|
+
type: Input
|
|
642
|
+
}], modal: [{
|
|
643
|
+
type: Input
|
|
644
|
+
}], openChange: [{
|
|
645
|
+
type: Output
|
|
646
|
+
}], handleClose: [{
|
|
647
|
+
type: HostListener,
|
|
648
|
+
args: ['close']
|
|
649
|
+
}], handleCancel: [{
|
|
650
|
+
type: HostListener,
|
|
651
|
+
args: ['cancel', ['$event']]
|
|
652
|
+
}] } });
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Info
|
|
656
|
+
*
|
|
657
|
+
* Eksempel på bruk:
|
|
658
|
+
* ```html
|
|
659
|
+
* <hr hviDivider />
|
|
660
|
+
* ```
|
|
661
|
+
*
|
|
662
|
+
* Dokumentasjon: https://designsystemet.no/no/components/docs/divider/overview
|
|
663
|
+
*/
|
|
664
|
+
class HviDivider {
|
|
665
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDivider, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
666
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviDivider, isStandalone: true, selector: "hr[hviDivider]", host: { attributes: { "aria-hidden": "true" }, classAttribute: "ds-divider" }, ngImport: i0 });
|
|
667
|
+
}
|
|
668
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviDivider, decorators: [{
|
|
669
|
+
type: Directive,
|
|
670
|
+
args: [{
|
|
671
|
+
selector: 'hr[hviDivider]',
|
|
672
|
+
standalone: true,
|
|
673
|
+
host: {
|
|
674
|
+
class: 'ds-divider',
|
|
675
|
+
'aria-hidden': 'true',
|
|
676
|
+
},
|
|
677
|
+
}]
|
|
678
|
+
}] });
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* @summary
|
|
682
|
+
* Adds submit handling for Angular reactive forms:
|
|
683
|
+
* - Tracks submitted state
|
|
684
|
+
* - Marks all controls as touched on submit
|
|
685
|
+
* - Optionally focuses an ErrorSummary when invalid
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```html
|
|
689
|
+
* <[hviForm]></[hviForm]>
|
|
690
|
+
* ```
|
|
691
|
+
*
|
|
692
|
+
* Documentation: https://designsystemet.no/en/components/docs/form/code
|
|
693
|
+
*/
|
|
694
|
+
class HviForm {
|
|
695
|
+
/** Emits when the form has been submitted */
|
|
696
|
+
hviSubmitted = new EventEmitter();
|
|
697
|
+
/** True after first submit attempt */
|
|
698
|
+
submitted = false;
|
|
699
|
+
/** Optional focus target (e.g. HviErrorSummaryComponent) */
|
|
700
|
+
focusOnInvalid;
|
|
701
|
+
// Optional injection: present when the form uses [formGroup] and ReactiveFormsModule is in scope
|
|
702
|
+
formGroupDir = inject(FormGroupDirective, { optional: true });
|
|
703
|
+
onSubmit(event) {
|
|
704
|
+
this.submitted = true;
|
|
705
|
+
this.hviSubmitted.emit();
|
|
706
|
+
const form = this.formGroupDir?.form;
|
|
707
|
+
if (!form)
|
|
708
|
+
return;
|
|
709
|
+
form.markAllAsTouched();
|
|
710
|
+
if (form.invalid) {
|
|
711
|
+
event.preventDefault();
|
|
712
|
+
queueMicrotask(() => this.focusOnInvalid?.focus?.());
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviForm, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
716
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviForm, isStandalone: true, selector: "form[hviForm]", inputs: { focusOnInvalid: "focusOnInvalid" }, outputs: { hviSubmitted: "hviSubmitted" }, host: { listeners: { "submit": "onSubmit($event)" } }, ngImport: i0 });
|
|
717
|
+
}
|
|
718
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviForm, decorators: [{
|
|
719
|
+
type: Directive,
|
|
720
|
+
args: [{
|
|
721
|
+
selector: 'form[hviForm]',
|
|
722
|
+
standalone: true,
|
|
723
|
+
}]
|
|
724
|
+
}], propDecorators: { hviSubmitted: [{
|
|
725
|
+
type: Output
|
|
726
|
+
}], focusOnInvalid: [{
|
|
727
|
+
type: Input
|
|
728
|
+
}], onSubmit: [{
|
|
729
|
+
type: HostListener,
|
|
730
|
+
args: ['submit', ['$event']]
|
|
731
|
+
}] } });
|
|
732
|
+
|
|
733
|
+
const DEFAULT_ERROR_PRIORITY$1 = [
|
|
734
|
+
'required',
|
|
735
|
+
'minlength',
|
|
736
|
+
'maxlength',
|
|
737
|
+
'email',
|
|
738
|
+
'pattern',
|
|
739
|
+
'min',
|
|
740
|
+
'max',
|
|
741
|
+
];
|
|
742
|
+
let errorSummaryIdCounter = 0;
|
|
743
|
+
const nextErrorSummaryHeadingId = () => `hvi-error-summary-heading-${++errorSummaryIdCounter}`;
|
|
744
|
+
/**
|
|
745
|
+
* @summary
|
|
746
|
+
* ErrorSummary lists blocking validation errors so users can quickly find and fix them.
|
|
747
|
+
*
|
|
748
|
+
* @remarks
|
|
749
|
+
* Modes:
|
|
750
|
+
* - Manual: provide `[errors]` as `{ message, href }[]`.
|
|
751
|
+
* - Auto (recommended): provide `[form]` + `[messages]` (and optionally `[idMap]`) to derive errors from invalid
|
|
752
|
+
* controls in a reactive form.
|
|
753
|
+
*
|
|
754
|
+
* Auto mode should be rendered inside `<form hviForm ...>` so it can follow submit visibility (`showWhen`) and
|
|
755
|
+
* be focused automatically via `[focusOnInvalid]`.
|
|
756
|
+
*
|
|
757
|
+
* Each item link must point to a field id (e.g. `href="#firstName"`). Prefer `id === formControlName`.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* Manual mode:
|
|
761
|
+
* ```html
|
|
762
|
+
* <hvi-error-summary
|
|
763
|
+
* [errors]="[
|
|
764
|
+
* { message: 'Fornavn må være minst 2 tegn', href: '#firstName' },
|
|
765
|
+
* { message: 'Telefonnummer kan kun inneholde siffer', href: '#phone' }
|
|
766
|
+
* ]"
|
|
767
|
+
* />
|
|
768
|
+
* ```
|
|
769
|
+
*
|
|
770
|
+
* @example
|
|
771
|
+
* Auto mode (HTML + TS):
|
|
772
|
+
*
|
|
773
|
+
* ```html
|
|
774
|
+
* HTML:
|
|
775
|
+
* <form hviForm [formGroup]="form" [focusOnInvalid]="summary">
|
|
776
|
+
* <hvi-error-summary #summary [form]="form" [messages]="messages" showWhen="submitted" />
|
|
777
|
+
*
|
|
778
|
+
* <hvi-field>
|
|
779
|
+
* <label hviLabel for="firstName" weight="medium">Fornavn</label>
|
|
780
|
+
* <input hviInput id="firstName" formControlName="firstName" hviControlInvalid />
|
|
781
|
+
* <p hviFieldValidation hviValidationMessage="firstName" [messages]="messages.firstName"></p>
|
|
782
|
+
* </hvi-field>
|
|
783
|
+
*
|
|
784
|
+
* <hvi-field>
|
|
785
|
+
* <label hviLabel for="phone" weight="medium">Telefon</label>
|
|
786
|
+
* <input hviInput id="phone" type="tel" formControlName="phone" hviControlInvalid />
|
|
787
|
+
* <p hviFieldValidation hviValidationMessage="phone" [messages]="messages.phone"></p>
|
|
788
|
+
* </hvi-field>
|
|
789
|
+
*
|
|
790
|
+
* <button hviButton type="submit" variant="primary">Send inn</button>
|
|
791
|
+
* </form>
|
|
792
|
+
* ```
|
|
793
|
+
*
|
|
794
|
+
* ```ts
|
|
795
|
+
* TS:
|
|
796
|
+
* form = new FormGroup({
|
|
797
|
+
* firstName: new FormControl('', [Validators.required, Validators.minLength(2)]),
|
|
798
|
+
* phone: new FormControl('', [Validators.required, Validators.pattern(/^\d+$/)]),
|
|
799
|
+
* });
|
|
800
|
+
*
|
|
801
|
+
* messages = {
|
|
802
|
+
* firstName: { required: 'Fornavn er påkrevd', minlength: 'Fornavn må være minst 2 tegn' },
|
|
803
|
+
* phone: { required: 'Telefon er påkrevd', pattern: 'Telefonnummer kan kun inneholde siffer' },
|
|
804
|
+
* } as const;
|
|
805
|
+
* ```
|
|
806
|
+
*
|
|
807
|
+
* Documentation: https://designsystemet.no/en/components/docs/error-summary/code
|
|
808
|
+
*/
|
|
809
|
+
class HviErrorSummary {
|
|
810
|
+
/** Heading text shown above the list */
|
|
811
|
+
heading = 'For å gå videre må du rette opp følgende feil:';
|
|
812
|
+
/** Heading level for the heading element (1-6). Defaults to 2 per DS */
|
|
813
|
+
headingLevel = 2;
|
|
814
|
+
/**
|
|
815
|
+
* Manual mode: items displayed in the summary.
|
|
816
|
+
* If non-empty, manual mode takes precedence over auto mode.
|
|
817
|
+
*/
|
|
818
|
+
errors = [];
|
|
819
|
+
/** Auto mode: reactive form to derive errors from */
|
|
820
|
+
form;
|
|
821
|
+
/**
|
|
822
|
+
* Auto mode: messages per controlName.
|
|
823
|
+
* Example:
|
|
824
|
+
* {
|
|
825
|
+
* firstName: { required: 'Fornavn er påkrevd', minlength: 'Fornavn må være minst 2 tegn' },
|
|
826
|
+
* phone: { required: 'Telefon er påkrevd', pattern: 'Telefonnummer kan kun inneholde siffer' }
|
|
827
|
+
* }
|
|
828
|
+
*/
|
|
829
|
+
messages;
|
|
830
|
+
/**
|
|
831
|
+
* Auto mode: map controlName -> element id.
|
|
832
|
+
* Default is `id === controlName`.
|
|
833
|
+
*/
|
|
834
|
+
idMap;
|
|
835
|
+
/** Auto mode: error key priority (first match wins) */
|
|
836
|
+
errorPriority = DEFAULT_ERROR_PRIORITY$1;
|
|
837
|
+
/** Used for aria-labelledby on the container */
|
|
838
|
+
headingId = nextErrorSummaryHeadingId();
|
|
839
|
+
/** When to show errors from the form controls */
|
|
840
|
+
showWhen = 'submitted';
|
|
841
|
+
container;
|
|
842
|
+
focus() {
|
|
843
|
+
this.container?.nativeElement.focus();
|
|
844
|
+
}
|
|
845
|
+
get computedErrors() {
|
|
846
|
+
// 1) Manual mode wins
|
|
847
|
+
if (this.errors?.length)
|
|
848
|
+
return this.errors;
|
|
849
|
+
// 2) Auto mode
|
|
850
|
+
const form = this.form;
|
|
851
|
+
const messages = this.messages;
|
|
852
|
+
if (!form || !messages)
|
|
853
|
+
return [];
|
|
854
|
+
const items = [];
|
|
855
|
+
for (const controlName of Object.keys(form.controls)) {
|
|
856
|
+
const ctrl = form.controls[controlName];
|
|
857
|
+
if (!ctrl?.invalid)
|
|
858
|
+
continue;
|
|
859
|
+
const errs = ctrl.errors ?? {};
|
|
860
|
+
const msgMap = messages[controlName] ?? {};
|
|
861
|
+
const message = this.pickMessage(errs, msgMap);
|
|
862
|
+
if (!message)
|
|
863
|
+
continue;
|
|
864
|
+
const id = this.idMap?.[controlName] ?? controlName;
|
|
865
|
+
items.push({ message, href: `#${id}` });
|
|
866
|
+
}
|
|
867
|
+
return items;
|
|
868
|
+
}
|
|
869
|
+
pickMessage(errors, messages) {
|
|
870
|
+
for (const key of this.errorPriority) {
|
|
871
|
+
if (errors[key] && messages[key])
|
|
872
|
+
return messages[key];
|
|
873
|
+
}
|
|
874
|
+
for (const key of Object.keys(errors)) {
|
|
875
|
+
if (messages[key])
|
|
876
|
+
return messages[key];
|
|
877
|
+
}
|
|
878
|
+
return Object.keys(errors).length ? 'Ugyldig verdi' : '';
|
|
879
|
+
}
|
|
880
|
+
hviForm = inject(HviForm, { optional: true });
|
|
881
|
+
get shouldShow() {
|
|
882
|
+
if (this.computedErrors.length === 0)
|
|
883
|
+
return false;
|
|
884
|
+
switch (this.showWhen) {
|
|
885
|
+
case 'always':
|
|
886
|
+
return true;
|
|
887
|
+
case 'touched':
|
|
888
|
+
// show when any invalid control is touched OR the form has been submitted
|
|
889
|
+
return (this.hviForm?.submitted ?? false) || this.anyInvalidTouched();
|
|
890
|
+
case 'submitted':
|
|
891
|
+
default:
|
|
892
|
+
return this.hviForm?.submitted ?? false;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
anyInvalidTouched() {
|
|
896
|
+
const form = this.form;
|
|
897
|
+
if (!form)
|
|
898
|
+
return false;
|
|
899
|
+
for (const name of Object.keys(form.controls)) {
|
|
900
|
+
const c = form.controls[name];
|
|
901
|
+
if (c?.invalid && c.touched)
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviErrorSummary, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
907
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: HviErrorSummary, isStandalone: true, selector: "hvi-error-summary", inputs: { heading: "heading", headingLevel: "headingLevel", errors: "errors", form: "form", messages: "messages", idMap: "idMap", errorPriority: "errorPriority", headingId: "headingId", showWhen: "showWhen" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, static: true }], ngImport: i0, template: `
|
|
908
|
+
<div
|
|
909
|
+
#container
|
|
910
|
+
class="ds-error-summary"
|
|
911
|
+
tabindex="-1"
|
|
912
|
+
[attr.aria-labelledby]="headingId"
|
|
913
|
+
[hidden]="!shouldShow"
|
|
914
|
+
>
|
|
915
|
+
@switch (headingLevel) {
|
|
916
|
+
@case (1) {
|
|
917
|
+
<h1 class="ds-heading" [id]="headingId">{{ heading }}</h1>
|
|
918
|
+
}
|
|
919
|
+
@case (2) {
|
|
920
|
+
<h2 class="ds-heading" [id]="headingId">{{ heading }}</h2>
|
|
921
|
+
}
|
|
922
|
+
@case (3) {
|
|
923
|
+
<h3 class="ds-heading" [id]="headingId">{{ heading }}</h3>
|
|
924
|
+
}
|
|
925
|
+
@case (4) {
|
|
926
|
+
<h4 class="ds-heading" [id]="headingId">{{ heading }}</h4>
|
|
927
|
+
}
|
|
928
|
+
@case (5) {
|
|
929
|
+
<h5 class="ds-heading" [id]="headingId">{{ heading }}</h5>
|
|
930
|
+
}
|
|
931
|
+
@default {
|
|
932
|
+
<h6 class="ds-heading" [id]="headingId">{{ heading }}</h6>
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
<ul class="ds-list">
|
|
937
|
+
@for (err of computedErrors; track err.href) {
|
|
938
|
+
<li>
|
|
939
|
+
<a class="ds-link" data-color="neutral" [href]="err.href">{{ err.message }}</a>
|
|
940
|
+
</li>
|
|
941
|
+
}
|
|
942
|
+
</ul>
|
|
943
|
+
</div>
|
|
944
|
+
`, isInline: true });
|
|
945
|
+
}
|
|
946
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviErrorSummary, decorators: [{
|
|
947
|
+
type: Component,
|
|
948
|
+
args: [{
|
|
949
|
+
selector: 'hvi-error-summary',
|
|
950
|
+
standalone: true,
|
|
951
|
+
template: `
|
|
952
|
+
<div
|
|
953
|
+
#container
|
|
954
|
+
class="ds-error-summary"
|
|
955
|
+
tabindex="-1"
|
|
956
|
+
[attr.aria-labelledby]="headingId"
|
|
957
|
+
[hidden]="!shouldShow"
|
|
958
|
+
>
|
|
959
|
+
@switch (headingLevel) {
|
|
960
|
+
@case (1) {
|
|
961
|
+
<h1 class="ds-heading" [id]="headingId">{{ heading }}</h1>
|
|
962
|
+
}
|
|
963
|
+
@case (2) {
|
|
964
|
+
<h2 class="ds-heading" [id]="headingId">{{ heading }}</h2>
|
|
965
|
+
}
|
|
966
|
+
@case (3) {
|
|
967
|
+
<h3 class="ds-heading" [id]="headingId">{{ heading }}</h3>
|
|
968
|
+
}
|
|
969
|
+
@case (4) {
|
|
970
|
+
<h4 class="ds-heading" [id]="headingId">{{ heading }}</h4>
|
|
971
|
+
}
|
|
972
|
+
@case (5) {
|
|
973
|
+
<h5 class="ds-heading" [id]="headingId">{{ heading }}</h5>
|
|
974
|
+
}
|
|
975
|
+
@default {
|
|
976
|
+
<h6 class="ds-heading" [id]="headingId">{{ heading }}</h6>
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
<ul class="ds-list">
|
|
981
|
+
@for (err of computedErrors; track err.href) {
|
|
982
|
+
<li>
|
|
983
|
+
<a class="ds-link" data-color="neutral" [href]="err.href">{{ err.message }}</a>
|
|
984
|
+
</li>
|
|
985
|
+
}
|
|
986
|
+
</ul>
|
|
987
|
+
</div>
|
|
988
|
+
`,
|
|
989
|
+
}]
|
|
990
|
+
}], propDecorators: { heading: [{
|
|
991
|
+
type: Input
|
|
992
|
+
}], headingLevel: [{
|
|
993
|
+
type: Input
|
|
994
|
+
}], errors: [{
|
|
995
|
+
type: Input
|
|
996
|
+
}], form: [{
|
|
997
|
+
type: Input
|
|
998
|
+
}], messages: [{
|
|
999
|
+
type: Input
|
|
1000
|
+
}], idMap: [{
|
|
1001
|
+
type: Input
|
|
1002
|
+
}], errorPriority: [{
|
|
1003
|
+
type: Input
|
|
1004
|
+
}], headingId: [{
|
|
1005
|
+
type: Input
|
|
1006
|
+
}], showWhen: [{
|
|
1007
|
+
type: Input
|
|
1008
|
+
}], container: [{
|
|
1009
|
+
type: ViewChild,
|
|
1010
|
+
args: ['container', { static: true }]
|
|
1011
|
+
}] } });
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Decorative affix container displayed alongside a text input.
|
|
1015
|
+
*
|
|
1016
|
+
* @remarks
|
|
1017
|
+
* Used together with the `hvi-field-affixes` component to wrap leading and trailing adornments.
|
|
1018
|
+
*
|
|
1019
|
+
* @example
|
|
1020
|
+
* ```html
|
|
1021
|
+
* <hvi-field-affixes>
|
|
1022
|
+
* <hvi-field-affix>NOK</hvi-field-affix>
|
|
1023
|
+
* <input hviInput type="text" placeholder="Amount" />
|
|
1024
|
+
* <hvi-field-affix>per month</hvi-field-affix>
|
|
1025
|
+
* </hvi-field-affixes>
|
|
1026
|
+
* ```
|
|
1027
|
+
*
|
|
1028
|
+
* Documentation: https://designsystemet.no/en/components/docs/field/code#prefixsuffix
|
|
1029
|
+
*/
|
|
1030
|
+
class HviFieldAffix {
|
|
1031
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldAffix, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1032
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviFieldAffix, isStandalone: true, selector: "hvi-field-affix", host: { properties: { "aria-hidden": "true" }, classAttribute: "ds-field-affix" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
1033
|
+
}
|
|
1034
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldAffix, decorators: [{
|
|
1035
|
+
type: Component,
|
|
1036
|
+
args: [{
|
|
1037
|
+
selector: 'hvi-field-affix',
|
|
1038
|
+
template: `<ng-content />`,
|
|
1039
|
+
host: {
|
|
1040
|
+
class: 'ds-field-affix',
|
|
1041
|
+
'[aria-hidden]': 'true',
|
|
1042
|
+
},
|
|
1043
|
+
}]
|
|
1044
|
+
}] });
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Container for decorative affixes displayed alongside a text input.
|
|
1048
|
+
*
|
|
1049
|
+
* @remarks
|
|
1050
|
+
* Wraps leading and trailing adornments provided by `hvi-field-affix` components.
|
|
1051
|
+
*
|
|
1052
|
+
* @example
|
|
1053
|
+
* ```html
|
|
1054
|
+
* <hvi-field-affixes>
|
|
1055
|
+
* <hvi-field-affix>NOK</hvi-field-affix>
|
|
1056
|
+
* <input hviInput type="text" placeholder="Amount" />
|
|
1057
|
+
* <hvi-field-affix>per month</hvi-field-affix>
|
|
1058
|
+
* </hvi-field-affixes>
|
|
1059
|
+
* ```
|
|
1060
|
+
*
|
|
1061
|
+
* Documentation: https://designsystemet.no/en/components/docs/field/code#prefixsuffix
|
|
1062
|
+
*/
|
|
1063
|
+
class HviFieldAffixes {
|
|
1064
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldAffixes, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1065
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviFieldAffixes, isStandalone: true, selector: "hvi-field-affixes", host: { classAttribute: "ds-field-affixes" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
1066
|
+
}
|
|
1067
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldAffixes, decorators: [{
|
|
1068
|
+
type: Component,
|
|
1069
|
+
args: [{
|
|
1070
|
+
selector: 'hvi-field-affixes',
|
|
1071
|
+
template: `<ng-content />`,
|
|
1072
|
+
host: {
|
|
1073
|
+
class: 'ds-field-affixes',
|
|
1074
|
+
},
|
|
1075
|
+
}]
|
|
1076
|
+
}] });
|
|
1077
|
+
|
|
1078
|
+
class HviFieldDescription {
|
|
1079
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1080
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviFieldDescription, isStandalone: true, selector: "[hviFieldDescription]", host: { attributes: { "data-field": "description" } }, ngImport: i0 });
|
|
1081
|
+
}
|
|
1082
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldDescription, decorators: [{
|
|
1083
|
+
type: Directive,
|
|
1084
|
+
args: [{
|
|
1085
|
+
selector: '[hviFieldDescription]',
|
|
1086
|
+
standalone: true,
|
|
1087
|
+
host: {
|
|
1088
|
+
'data-field': 'description',
|
|
1089
|
+
},
|
|
1090
|
+
}]
|
|
1091
|
+
}] });
|
|
1092
|
+
|
|
1093
|
+
class HviFieldOptional {
|
|
1094
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldOptional, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1095
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviFieldOptional, isStandalone: true, selector: "[hviFieldOptional]", host: { attributes: { "data-field": "optional" } }, ngImport: i0 });
|
|
1096
|
+
}
|
|
1097
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldOptional, decorators: [{
|
|
1098
|
+
type: Directive,
|
|
1099
|
+
args: [{
|
|
1100
|
+
selector: '[hviFieldOptional]',
|
|
1101
|
+
standalone: true,
|
|
1102
|
+
host: {
|
|
1103
|
+
'data-field': 'optional',
|
|
1104
|
+
},
|
|
1105
|
+
}]
|
|
1106
|
+
}] });
|
|
1107
|
+
|
|
1108
|
+
class HviFieldValidation {
|
|
1109
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldValidation, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1110
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviFieldValidation, isStandalone: true, selector: "[hviFieldValidation]", host: { attributes: { "data-field": "validation" }, classAttribute: "ds-validation-message" }, ngImport: i0 });
|
|
1111
|
+
}
|
|
1112
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldValidation, decorators: [{
|
|
1113
|
+
type: Directive,
|
|
1114
|
+
args: [{
|
|
1115
|
+
selector: '[hviFieldValidation]',
|
|
1116
|
+
standalone: true,
|
|
1117
|
+
host: {
|
|
1118
|
+
class: 'ds-validation-message',
|
|
1119
|
+
'data-field': 'validation',
|
|
1120
|
+
},
|
|
1121
|
+
}]
|
|
1122
|
+
}] });
|
|
1123
|
+
|
|
1124
|
+
// Copied from https://github.com/digdir/designsystemet/blob/main/packages/react/src/components/field/field-observer.ts
|
|
1125
|
+
function fieldObserver(fieldElement) {
|
|
1126
|
+
if (!fieldElement)
|
|
1127
|
+
return;
|
|
1128
|
+
const elements = new Map();
|
|
1129
|
+
const typeCounter = new Map(); // Track count for each data-field type
|
|
1130
|
+
const uuid = `:${Date.now().toString(36)}${Math.random().toString(36).slice(2, 5)}`;
|
|
1131
|
+
let input = null;
|
|
1132
|
+
let describedby = '';
|
|
1133
|
+
const process = (mutations) => {
|
|
1134
|
+
const changed = [];
|
|
1135
|
+
const removed = [];
|
|
1136
|
+
// Merge MutationRecords
|
|
1137
|
+
for (const mutation of mutations) {
|
|
1138
|
+
if (mutation.attributeName)
|
|
1139
|
+
changed.push(mutation.target ?? fieldElement);
|
|
1140
|
+
changed.push(...(mutation.addedNodes || []));
|
|
1141
|
+
removed.push(...(mutation.removedNodes || []));
|
|
1142
|
+
}
|
|
1143
|
+
// Register elements
|
|
1144
|
+
for (const el of changed) {
|
|
1145
|
+
if (!isElement(el))
|
|
1146
|
+
continue;
|
|
1147
|
+
if (isLabel(el))
|
|
1148
|
+
elements.set(el, el.htmlFor);
|
|
1149
|
+
else if (el.hasAttribute('data-field'))
|
|
1150
|
+
elements.set(el, el.id);
|
|
1151
|
+
else if (isInputLike(el) && !el.hidden) {
|
|
1152
|
+
input = el;
|
|
1153
|
+
describedby = el.getAttribute('aria-describedby') || '';
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
// Reset removed elements
|
|
1157
|
+
for (const el of removed) {
|
|
1158
|
+
if (!isElement(el))
|
|
1159
|
+
continue;
|
|
1160
|
+
if (input === el)
|
|
1161
|
+
input = null;
|
|
1162
|
+
if (elements.has(el)) {
|
|
1163
|
+
setAttr(el, isLabel(el) ? 'for' : 'id', elements.get(el));
|
|
1164
|
+
elements.delete(el);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
// Connect elements
|
|
1168
|
+
const describedbyIds = describedby ? describedby.split(' ') : []; // Keep original aria-describedby
|
|
1169
|
+
const inputId = input?.id || uuid;
|
|
1170
|
+
// Reset type counters since we reprocess all elements
|
|
1171
|
+
typeCounter.clear();
|
|
1172
|
+
for (const [el, value] of elements) {
|
|
1173
|
+
const descriptionType = el.getAttribute('data-field');
|
|
1174
|
+
let id;
|
|
1175
|
+
if (descriptionType) {
|
|
1176
|
+
// Increment type counter for this type
|
|
1177
|
+
const count = (typeCounter.get(descriptionType) || 0) + 1;
|
|
1178
|
+
typeCounter.set(descriptionType, count);
|
|
1179
|
+
id = `${inputId}:${descriptionType}:${count}`;
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
id = inputId;
|
|
1183
|
+
}
|
|
1184
|
+
if (!value)
|
|
1185
|
+
setAttr(el, isLabel(el) ? 'for' : 'id', id); // Ensure we have a value
|
|
1186
|
+
if (!describedbyIds.includes(el.id)) {
|
|
1187
|
+
if (descriptionType === 'validation')
|
|
1188
|
+
describedbyIds.unshift(el.id); // Validations to the front
|
|
1189
|
+
else if (descriptionType)
|
|
1190
|
+
describedbyIds.push(el.id); // Other descriptions to the back
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
// Prune aria-describedby so it does not reference removed elements
|
|
1194
|
+
const prunedDescribedbyIds = describedbyIds.filter((id) => {
|
|
1195
|
+
if (!id)
|
|
1196
|
+
return false;
|
|
1197
|
+
if (id === inputId)
|
|
1198
|
+
return true;
|
|
1199
|
+
// Keep if it still exists anywhere in the document.
|
|
1200
|
+
// This preserves "original aria-describedby" entries that live outside the field subtree.
|
|
1201
|
+
return !!fieldElement.ownerDocument?.getElementById(id);
|
|
1202
|
+
});
|
|
1203
|
+
setAttr(input, 'id', inputId);
|
|
1204
|
+
setAttr(input, 'aria-describedby', prunedDescribedbyIds.join(' ').trim());
|
|
1205
|
+
};
|
|
1206
|
+
const observer = createOptimizedMutationObserver(process);
|
|
1207
|
+
observer.observe(fieldElement, {
|
|
1208
|
+
attributeFilter: ['id', 'for', 'aria-describedby'],
|
|
1209
|
+
attributes: true,
|
|
1210
|
+
childList: true,
|
|
1211
|
+
subtree: true,
|
|
1212
|
+
});
|
|
1213
|
+
process([{ addedNodes: fieldElement.querySelectorAll('*') }]); // Initial setup
|
|
1214
|
+
observer.takeRecords(); // Clear initial setup queue
|
|
1215
|
+
return () => observer.disconnect();
|
|
1216
|
+
}
|
|
1217
|
+
// Utilities
|
|
1218
|
+
const isElement = (node) => node instanceof Element;
|
|
1219
|
+
const isLabel = (node) => node instanceof HTMLLabelElement;
|
|
1220
|
+
const isInputLike = (node) => node instanceof HTMLElement && 'validity' in node && !(node instanceof HTMLButtonElement); // Matches input, textarea, select and form accosiated custom elements
|
|
1221
|
+
const setAttr = (el, name, value) => value ? el?.setAttribute(name, value) : el?.removeAttribute(name);
|
|
1222
|
+
// Speed up MutationObserver by debouncing, clearing internal queue after changes and only running when page is visible
|
|
1223
|
+
function createOptimizedMutationObserver(callback) {
|
|
1224
|
+
const queue = [];
|
|
1225
|
+
const observer = new MutationObserver((mutations) => {
|
|
1226
|
+
if (!queue.length)
|
|
1227
|
+
requestAnimationFrame(process);
|
|
1228
|
+
queue.push(...mutations);
|
|
1229
|
+
});
|
|
1230
|
+
const process = () => {
|
|
1231
|
+
callback(queue, observer);
|
|
1232
|
+
queue.length = 0; // Reset queue
|
|
1233
|
+
observer.takeRecords(); // Clear queue due to DOM changes in callback
|
|
1234
|
+
};
|
|
1235
|
+
return observer;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Field is a helper component to automatically associate a field with hviLabel, hviFieldDescription and hviFieldValidation.
|
|
1240
|
+
*
|
|
1241
|
+
* @example
|
|
1242
|
+
* ```html
|
|
1243
|
+
* <hvi-field>
|
|
1244
|
+
* <label hviLabel>Name</label>
|
|
1245
|
+
* <span hviFieldDescription>Fill in your full name.</span>
|
|
1246
|
+
* <input type="text" />
|
|
1247
|
+
* <span hviFieldValidation>This field is required.</span>
|
|
1248
|
+
* </hvi-field>
|
|
1249
|
+
* ```
|
|
1250
|
+
*
|
|
1251
|
+
* Documentation: https://designsystemet.no/en/components/docs/field/overview
|
|
1252
|
+
*/
|
|
1253
|
+
class HviField {
|
|
1254
|
+
/** Position of toggle inputs (radio, checkbox, switch) in field */
|
|
1255
|
+
position;
|
|
1256
|
+
el = inject((ElementRef));
|
|
1257
|
+
destroyRef = inject(DestroyRef);
|
|
1258
|
+
ngAfterViewInit() {
|
|
1259
|
+
const stop = fieldObserver(this.el.nativeElement);
|
|
1260
|
+
this.destroyRef.onDestroy(() => stop?.());
|
|
1261
|
+
}
|
|
1262
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1263
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviField, isStandalone: true, selector: "hvi-field", inputs: { position: "position" }, host: { properties: { "attr.data-position": "position ?? null" }, classAttribute: "ds-field" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
1264
|
+
}
|
|
1265
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviField, decorators: [{
|
|
1266
|
+
type: Component,
|
|
1267
|
+
args: [{
|
|
1268
|
+
selector: 'hvi-field',
|
|
1269
|
+
standalone: true,
|
|
1270
|
+
template: '<ng-content />',
|
|
1271
|
+
host: {
|
|
1272
|
+
class: 'ds-field',
|
|
1273
|
+
'[attr.data-position]': 'position ?? null',
|
|
1274
|
+
},
|
|
1275
|
+
}]
|
|
1276
|
+
}], propDecorators: { position: [{
|
|
1277
|
+
type: Input
|
|
1278
|
+
}] } });
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
* Fieldset is used to group and label fields that naturally belong together, such as date fields or address fields.
|
|
1282
|
+
* The component helps organize information, make forms more manageable, and improve accessibility for screen readers.
|
|
1283
|
+
*
|
|
1284
|
+
* @example
|
|
1285
|
+
* ```html
|
|
1286
|
+
* <fieldset hviFieldset>
|
|
1287
|
+
* <legend hviLabel>Which framework do you like best?</legend>
|
|
1288
|
+
* <p hviParagraph>Your choice will help us improve the service.</p>
|
|
1289
|
+
* <hvi-field>
|
|
1290
|
+
* <input type="radio" id="angular" name="framework" value="angular" />
|
|
1291
|
+
* <label hviLabel for="angular">Angular</label>
|
|
1292
|
+
* </hvi-field>
|
|
1293
|
+
* <hvi-field>
|
|
1294
|
+
* <input type="radio" id="react" name="framework" value="react" />
|
|
1295
|
+
* <label hviLabel for="react">React</label>
|
|
1296
|
+
* </hvi-field>
|
|
1297
|
+
* <hvi-field>
|
|
1298
|
+
* <input type="radio" id="vue" name="framework" value="vue" />
|
|
1299
|
+
* <label hviLabel for="vue">Vue</label>
|
|
1300
|
+
* </hvi-field>
|
|
1301
|
+
* </fieldset>
|
|
1302
|
+
* ```
|
|
1303
|
+
*
|
|
1304
|
+
* Documentation: https://designsystemet.no/en/components/docs/fieldset/overview
|
|
1305
|
+
*/
|
|
1306
|
+
class HviFieldset {
|
|
1307
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldset, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1308
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviFieldset, isStandalone: true, selector: "fieldset[hviFieldset]", host: { classAttribute: "ds-fieldset" }, ngImport: i0 });
|
|
1309
|
+
}
|
|
1310
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviFieldset, decorators: [{
|
|
1311
|
+
type: Directive,
|
|
1312
|
+
args: [{
|
|
1313
|
+
selector: 'fieldset[hviFieldset]',
|
|
1314
|
+
standalone: true,
|
|
1315
|
+
host: {
|
|
1316
|
+
class: 'ds-fieldset',
|
|
1317
|
+
},
|
|
1318
|
+
}]
|
|
1319
|
+
}] });
|
|
1320
|
+
|
|
1321
|
+
class HviInput {
|
|
1322
|
+
type;
|
|
1323
|
+
size;
|
|
1324
|
+
/** Set role, e.g. `switch` when `checkbox` or `radio` */
|
|
1325
|
+
role;
|
|
1326
|
+
_disabled = false;
|
|
1327
|
+
_readOnly = false;
|
|
1328
|
+
set disabled(value) {
|
|
1329
|
+
this._disabled = value;
|
|
1330
|
+
}
|
|
1331
|
+
get disabled() {
|
|
1332
|
+
return this._disabled;
|
|
1333
|
+
}
|
|
1334
|
+
set readOnly(value) {
|
|
1335
|
+
this._readOnly = value;
|
|
1336
|
+
}
|
|
1337
|
+
get readOnly() {
|
|
1338
|
+
return this._readOnly;
|
|
1339
|
+
}
|
|
1340
|
+
set readonly(value) {
|
|
1341
|
+
this._readOnly = value;
|
|
1342
|
+
}
|
|
1343
|
+
get isToggle() {
|
|
1344
|
+
return this.type === 'checkbox' || this.type === 'radio';
|
|
1345
|
+
}
|
|
1346
|
+
onClick(event) {
|
|
1347
|
+
if (this._readOnly && this.isToggle) {
|
|
1348
|
+
event.preventDefault();
|
|
1349
|
+
event.stopImmediatePropagation();
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
onChange(event) {
|
|
1353
|
+
if (this._readOnly && this.isToggle) {
|
|
1354
|
+
event.preventDefault();
|
|
1355
|
+
event.stopImmediatePropagation();
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
onKeydown(event) {
|
|
1359
|
+
if (!this._readOnly || !this.isToggle)
|
|
1360
|
+
return;
|
|
1361
|
+
if (event.key === ' ' || event.key === 'Spacebar' || event.key === 'Enter') {
|
|
1362
|
+
event.preventDefault();
|
|
1363
|
+
event.stopImmediatePropagation();
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1367
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.1.2", type: HviInput, isStandalone: true, selector: "input[hviInput]", inputs: { type: "type", size: "size", role: "role", disabled: ["disabled", "disabled", booleanAttribute], readOnly: ["readOnly", "readOnly", booleanAttribute], readonly: ["readonly", "readonly", booleanAttribute] }, host: { listeners: { "click": "onClick($event)", "change": "onChange($event)", "keydown": "onKeydown($event)" }, properties: { "attr.type": "type ?? null", "attr.size": "size ?? null", "attr.disabled": "_disabled ? \"\" : null", "attr.readonly": "_readOnly ? \"\" : null", "attr.role": "role ?? null" }, classAttribute: "ds-input" }, ngImport: i0 });
|
|
1368
|
+
}
|
|
1369
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviInput, decorators: [{
|
|
1370
|
+
type: Directive,
|
|
1371
|
+
args: [{
|
|
1372
|
+
selector: 'input[hviInput]',
|
|
1373
|
+
standalone: true,
|
|
1374
|
+
host: {
|
|
1375
|
+
class: 'ds-input',
|
|
1376
|
+
'[attr.type]': 'type ?? null',
|
|
1377
|
+
'[attr.size]': 'size ?? null',
|
|
1378
|
+
'[attr.disabled]': '_disabled ? "" : null',
|
|
1379
|
+
'[attr.readonly]': '_readOnly ? "" : null',
|
|
1380
|
+
'[attr.role]': 'role ?? null',
|
|
1381
|
+
},
|
|
1382
|
+
}]
|
|
1383
|
+
}], propDecorators: { type: [{
|
|
1384
|
+
type: Input
|
|
1385
|
+
}], size: [{
|
|
1386
|
+
type: Input
|
|
1387
|
+
}], role: [{
|
|
1388
|
+
type: Input
|
|
1389
|
+
}], disabled: [{
|
|
1390
|
+
type: Input,
|
|
1391
|
+
args: [{ transform: booleanAttribute }]
|
|
1392
|
+
}], readOnly: [{
|
|
1393
|
+
type: Input,
|
|
1394
|
+
args: [{ transform: booleanAttribute }]
|
|
1395
|
+
}], readonly: [{
|
|
1396
|
+
type: Input,
|
|
1397
|
+
args: [{ transform: booleanAttribute }]
|
|
1398
|
+
}], onClick: [{
|
|
1399
|
+
type: HostListener,
|
|
1400
|
+
args: ['click', ['$event']]
|
|
1401
|
+
}], onChange: [{
|
|
1402
|
+
type: HostListener,
|
|
1403
|
+
args: ['change', ['$event']]
|
|
1404
|
+
}], onKeydown: [{
|
|
1405
|
+
type: HostListener,
|
|
1406
|
+
args: ['keydown', ['$event']]
|
|
1407
|
+
}] } });
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* @summary
|
|
1411
|
+
* Directive to set `aria-invalid` on form controls based on their validation state.
|
|
1412
|
+
*
|
|
1413
|
+
* Usage:
|
|
1414
|
+
* ```html
|
|
1415
|
+
* <input hviControlInvalid formControlName="email" />
|
|
1416
|
+
* ```
|
|
1417
|
+
*/
|
|
1418
|
+
class HviControlInvalid {
|
|
1419
|
+
ngControl = inject(NgControl, { self: true, optional: true });
|
|
1420
|
+
hviForm = inject(HviForm, { optional: true });
|
|
1421
|
+
get ariaInvalid() {
|
|
1422
|
+
const control = this.ngControl?.control;
|
|
1423
|
+
if (!control)
|
|
1424
|
+
return null;
|
|
1425
|
+
const submitted = this.hviForm?.submitted ?? false;
|
|
1426
|
+
const show = control.invalid && (control.touched || submitted);
|
|
1427
|
+
return show ? 'true' : null;
|
|
1428
|
+
}
|
|
1429
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviControlInvalid, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1430
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviControlInvalid, isStandalone: true, selector: "[hviControlInvalid]", host: { properties: { "attr.aria-invalid": "this.ariaInvalid" } }, ngImport: i0 });
|
|
1431
|
+
}
|
|
1432
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviControlInvalid, decorators: [{
|
|
1433
|
+
type: Directive,
|
|
1434
|
+
args: [{
|
|
1435
|
+
selector: '[hviControlInvalid]',
|
|
1436
|
+
standalone: true,
|
|
1437
|
+
}]
|
|
1438
|
+
}], propDecorators: { ariaInvalid: [{
|
|
1439
|
+
type: HostBinding,
|
|
1440
|
+
args: ['attr.aria-invalid']
|
|
1441
|
+
}] } });
|
|
1442
|
+
|
|
1443
|
+
const DEFAULT_ERROR_PRIORITY = [
|
|
1444
|
+
'required',
|
|
1445
|
+
'minlength',
|
|
1446
|
+
'maxlength',
|
|
1447
|
+
'email',
|
|
1448
|
+
'pattern',
|
|
1449
|
+
'min',
|
|
1450
|
+
'max',
|
|
1451
|
+
];
|
|
1452
|
+
class HviValidationMessage {
|
|
1453
|
+
/**
|
|
1454
|
+
* Control name in the nearest parent FormGroup (matches formControlName).
|
|
1455
|
+
* Example: hviValidationMessage="firstName"
|
|
1456
|
+
*/
|
|
1457
|
+
controlName;
|
|
1458
|
+
/**
|
|
1459
|
+
* Map errorKey -> message, e.g. { required: 'Påkrevd', minlength: 'Minst 2 tegn' }
|
|
1460
|
+
*/
|
|
1461
|
+
messages = {};
|
|
1462
|
+
/**
|
|
1463
|
+
* Optional error priority order. Defaults to a sensible order.
|
|
1464
|
+
*/
|
|
1465
|
+
errorPriority = DEFAULT_ERROR_PRIORITY;
|
|
1466
|
+
container = inject(ControlContainer, { optional: true });
|
|
1467
|
+
hviForm = inject(HviForm, { optional: true });
|
|
1468
|
+
get control() {
|
|
1469
|
+
const group = this.container?.control;
|
|
1470
|
+
if (!group || !this.controlName)
|
|
1471
|
+
return null;
|
|
1472
|
+
return group.get(this.controlName);
|
|
1473
|
+
}
|
|
1474
|
+
get message() {
|
|
1475
|
+
const ctrl = this.control;
|
|
1476
|
+
if (!ctrl)
|
|
1477
|
+
return '';
|
|
1478
|
+
const submitted = this.hviForm?.submitted ?? false;
|
|
1479
|
+
const show = ctrl.invalid && (ctrl.touched || submitted);
|
|
1480
|
+
if (!show)
|
|
1481
|
+
return '';
|
|
1482
|
+
const errors = ctrl.errors ?? {};
|
|
1483
|
+
// 1) Prioritized keys first
|
|
1484
|
+
for (const key of this.errorPriority) {
|
|
1485
|
+
if (errors[key] && this.messages[key])
|
|
1486
|
+
return this.messages[key];
|
|
1487
|
+
}
|
|
1488
|
+
// 2) Any remaining mapped key
|
|
1489
|
+
for (const key of Object.keys(errors)) {
|
|
1490
|
+
if (this.messages[key])
|
|
1491
|
+
return this.messages[key];
|
|
1492
|
+
}
|
|
1493
|
+
return 'Ugyldig verdi';
|
|
1494
|
+
}
|
|
1495
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviValidationMessage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1496
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviValidationMessage, isStandalone: true, selector: "[hviValidationMessage]", inputs: { controlName: ["hviValidationMessage", "controlName"], messages: "messages", errorPriority: "errorPriority" }, host: { properties: { "textContent": "message", "hidden": "!message" } }, ngImport: i0 });
|
|
1497
|
+
}
|
|
1498
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviValidationMessage, decorators: [{
|
|
1499
|
+
type: Directive,
|
|
1500
|
+
args: [{
|
|
1501
|
+
selector: '[hviValidationMessage]',
|
|
1502
|
+
standalone: true,
|
|
1503
|
+
host: {
|
|
1504
|
+
// Sett tekstinnhold direkte på elementet (typisk <p hviFieldValidation ...>)
|
|
1505
|
+
'[textContent]': 'message',
|
|
1506
|
+
// Skjul elementet når det ikke er noen feilmelding
|
|
1507
|
+
'[hidden]': '!message',
|
|
1508
|
+
},
|
|
1509
|
+
}]
|
|
1510
|
+
}], propDecorators: { controlName: [{
|
|
1511
|
+
type: Input,
|
|
1512
|
+
args: ['hviValidationMessage']
|
|
1513
|
+
}], messages: [{
|
|
1514
|
+
type: Input
|
|
1515
|
+
}], errorPriority: [{
|
|
1516
|
+
type: Input
|
|
1517
|
+
}] } });
|
|
1518
|
+
|
|
1519
|
+
// 1) Bare field-byggesteiner
|
|
1520
|
+
const HviFieldKit = [
|
|
1521
|
+
HviField,
|
|
1522
|
+
HviFieldValidation,
|
|
1523
|
+
HviFieldDescription,
|
|
1524
|
+
HviFieldOptional,
|
|
1525
|
+
HviFieldAffix,
|
|
1526
|
+
HviFieldAffixes,
|
|
1527
|
+
];
|
|
1528
|
+
// 2) Reactive forms + invalid/validation glue + submit behavior
|
|
1529
|
+
const HviValidationKit = [
|
|
1530
|
+
ReactiveFormsModule,
|
|
1531
|
+
HviForm,
|
|
1532
|
+
HviControlInvalid,
|
|
1533
|
+
HviValidationMessage,
|
|
1534
|
+
];
|
|
1535
|
+
// 3) “Alt du trenger for DS forms”
|
|
1536
|
+
const HviForms = [
|
|
1537
|
+
...HviValidationKit,
|
|
1538
|
+
...HviFieldKit,
|
|
1539
|
+
HviInput,
|
|
1540
|
+
HviFieldset,
|
|
1541
|
+
HviErrorSummary,
|
|
1542
|
+
];
|
|
1543
|
+
|
|
1544
|
+
/**
|
|
1545
|
+
* Heading is used to structure content and create hierarchy on a page.
|
|
1546
|
+
*
|
|
1547
|
+
* @example
|
|
1548
|
+
* ```html
|
|
1549
|
+
* <h1 hviHeading size="xl">This is a heading</h1>
|
|
1550
|
+
* ```
|
|
1551
|
+
*
|
|
1552
|
+
* Documentation: https://designsystemet.no/en/components/docs/heading/overview
|
|
1553
|
+
*/
|
|
1554
|
+
class HviHeading {
|
|
1555
|
+
/** The size of the heading */
|
|
1556
|
+
size;
|
|
1557
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviHeading, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1558
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviHeading, isStandalone: true, selector: "h1[hviHeading], h2[hviHeading], h3[hviHeading], h4[hviHeading], h5[hviHeading], h6[hviHeading]", inputs: { size: "size" }, host: { properties: { "attr.data-size": "size" }, classAttribute: "ds-heading" }, ngImport: i0 });
|
|
1559
|
+
}
|
|
1560
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviHeading, decorators: [{
|
|
1561
|
+
type: Directive,
|
|
1562
|
+
args: [{
|
|
1563
|
+
selector: 'h1[hviHeading], h2[hviHeading], h3[hviHeading], h4[hviHeading], h5[hviHeading], h6[hviHeading]',
|
|
1564
|
+
standalone: true,
|
|
1565
|
+
host: {
|
|
1566
|
+
class: 'ds-heading',
|
|
1567
|
+
'[attr.data-size]': 'size',
|
|
1568
|
+
},
|
|
1569
|
+
}]
|
|
1570
|
+
}], propDecorators: { size: [{
|
|
1571
|
+
type: Input
|
|
1572
|
+
}] } });
|
|
1573
|
+
|
|
1574
|
+
class HviIcon {
|
|
1575
|
+
http;
|
|
1576
|
+
elementRef;
|
|
1577
|
+
cdr;
|
|
1578
|
+
icon;
|
|
1579
|
+
color;
|
|
1580
|
+
size = 'md';
|
|
1581
|
+
ariaHidden = false;
|
|
1582
|
+
constructor(http, elementRef, cdr) {
|
|
1583
|
+
this.http = http;
|
|
1584
|
+
this.elementRef = elementRef;
|
|
1585
|
+
this.cdr = cdr;
|
|
1586
|
+
}
|
|
1587
|
+
ngOnChanges() {
|
|
1588
|
+
this.loadIcon();
|
|
1589
|
+
}
|
|
1590
|
+
loadIcon() {
|
|
1591
|
+
this.http
|
|
1592
|
+
.get(`/assets/icons/${this.icon}.svg`, {
|
|
1593
|
+
responseType: 'text',
|
|
1594
|
+
})
|
|
1595
|
+
.subscribe({
|
|
1596
|
+
next: (svg) => {
|
|
1597
|
+
this.elementRef.nativeElement.innerHTML = svg;
|
|
1598
|
+
this.cdr.markForCheck();
|
|
1599
|
+
},
|
|
1600
|
+
error: (err) => {
|
|
1601
|
+
console.error('Failed to load icon:', this.icon, err);
|
|
1602
|
+
},
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviIcon, deps: [{ token: i1.HttpClient }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1606
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "21.1.2", type: HviIcon, isStandalone: true, selector: "hvi-icon", inputs: { icon: "icon", color: "color", size: "size", ariaHidden: ["ariaHidden", "ariaHidden", booleanAttribute] }, host: { properties: { "attr.data-size": "size", "attr.data-color": "color", "attr.aria-hidden": "ariaHidden" }, classAttribute: "hvi-icon" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, styles: ["hvi-icon{display:inline-block}svg{display:inline-block}hvi-icon[data-size=sm] svg{width:16px;height:16px}hvi-icon[data-size=md] svg{width:24px;height:24px}hvi-icon[data-size=lg] svg{width:32px;height:32px}hvi-icon[data-size=xl] svg{width:40px;height:40px}hvi-icon svg path{fill:currentColor}hvi-icon[data-color=danger] svg path{fill:var(--ds-color-danger-base-default)}\n"], encapsulation: i0.ViewEncapsulation.None });
|
|
1607
|
+
}
|
|
1608
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviIcon, decorators: [{
|
|
1609
|
+
type: Component,
|
|
1610
|
+
args: [{ selector: 'hvi-icon', standalone: true, template: '', encapsulation: ViewEncapsulation.None, host: {
|
|
1611
|
+
class: 'hvi-icon',
|
|
1612
|
+
'[attr.data-size]': 'size',
|
|
1613
|
+
'[attr.data-color]': 'color',
|
|
1614
|
+
'[attr.aria-hidden]': 'ariaHidden',
|
|
1615
|
+
}, styles: ["hvi-icon{display:inline-block}svg{display:inline-block}hvi-icon[data-size=sm] svg{width:16px;height:16px}hvi-icon[data-size=md] svg{width:24px;height:24px}hvi-icon[data-size=lg] svg{width:32px;height:32px}hvi-icon[data-size=xl] svg{width:40px;height:40px}hvi-icon svg path{fill:currentColor}hvi-icon[data-color=danger] svg path{fill:var(--ds-color-danger-base-default)}\n"] }]
|
|
1616
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { icon: [{
|
|
1617
|
+
type: Input,
|
|
1618
|
+
args: [{ required: true }]
|
|
1619
|
+
}], color: [{
|
|
1620
|
+
type: Input
|
|
1621
|
+
}], size: [{
|
|
1622
|
+
type: Input
|
|
1623
|
+
}], ariaHidden: [{
|
|
1624
|
+
type: Input,
|
|
1625
|
+
args: [{ transform: booleanAttribute }]
|
|
1626
|
+
}] } });
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* Label functions as a clear and accessible text label that tells the user what an associated form element is about.
|
|
1630
|
+
*
|
|
1631
|
+
* @example
|
|
1632
|
+
* ```html
|
|
1633
|
+
* <label hviLabel weight="semibold">Name</label>
|
|
1634
|
+
* ```
|
|
1635
|
+
*
|
|
1636
|
+
* Documentation: https://designsystemet.no/en/components/docs/label/overview
|
|
1637
|
+
*/
|
|
1638
|
+
class HviLabel {
|
|
1639
|
+
/** The font weight of the label */
|
|
1640
|
+
weight;
|
|
1641
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1642
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviLabel, isStandalone: true, selector: "label[hviLabel], legend[hviLabel]", inputs: { weight: "weight" }, host: { properties: { "attr.data-weight": "weight ?? null" }, classAttribute: "ds-label" }, ngImport: i0 });
|
|
1643
|
+
}
|
|
1644
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviLabel, decorators: [{
|
|
1645
|
+
type: Directive,
|
|
1646
|
+
args: [{
|
|
1647
|
+
selector: 'label[hviLabel], legend[hviLabel]',
|
|
1648
|
+
standalone: true,
|
|
1649
|
+
host: {
|
|
1650
|
+
class: 'ds-label',
|
|
1651
|
+
'[attr.data-weight]': 'weight ?? null',
|
|
1652
|
+
},
|
|
1653
|
+
}]
|
|
1654
|
+
}], propDecorators: { weight: [{
|
|
1655
|
+
type: Input
|
|
1656
|
+
}] } });
|
|
1657
|
+
|
|
1658
|
+
/**
|
|
1659
|
+
* Info
|
|
1660
|
+
*
|
|
1661
|
+
* Eksempel på bruk:
|
|
1662
|
+
* ```html
|
|
1663
|
+
* <a hviLink />
|
|
1664
|
+
* ```
|
|
1665
|
+
*
|
|
1666
|
+
* Dokumentasjon: https://designsystemet.no/no/components/docs/input/overview
|
|
1667
|
+
*/
|
|
1668
|
+
class HviLink {
|
|
1669
|
+
/** Used to change the appearance of the link. */
|
|
1670
|
+
color = 'default';
|
|
1671
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1672
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviLink, isStandalone: true, selector: "a[hviLink]", inputs: { color: "color" }, host: { properties: { "attr.data-color": "color" }, classAttribute: "ds-link" }, ngImport: i0 });
|
|
1673
|
+
}
|
|
1674
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviLink, decorators: [{
|
|
1675
|
+
type: Directive,
|
|
1676
|
+
args: [{
|
|
1677
|
+
selector: 'a[hviLink]',
|
|
1678
|
+
standalone: true,
|
|
1679
|
+
host: {
|
|
1680
|
+
class: 'ds-link',
|
|
1681
|
+
'[attr.data-color]': 'color',
|
|
1682
|
+
},
|
|
1683
|
+
}]
|
|
1684
|
+
}], propDecorators: { color: [{
|
|
1685
|
+
type: Input
|
|
1686
|
+
}] } });
|
|
1687
|
+
|
|
1688
|
+
/**
|
|
1689
|
+
* Paragraph is used for continuous text and is typically applied in articles, components, help text, and similar content.
|
|
1690
|
+
*
|
|
1691
|
+
* @example
|
|
1692
|
+
* ```html
|
|
1693
|
+
* <p hviParagraph variant="long" size="md">
|
|
1694
|
+
* This is a paragraph with body text that can be adjusted in size and variant.
|
|
1695
|
+
* </p>
|
|
1696
|
+
* ```
|
|
1697
|
+
*
|
|
1698
|
+
* Documentation: https://designsystemet.no/en/components/docs/paragraph/overview
|
|
1699
|
+
*/
|
|
1700
|
+
class HviParagraph {
|
|
1701
|
+
/** Adjusts the style for the length of the paragraph */
|
|
1702
|
+
variant;
|
|
1703
|
+
/** Paragraph is available in several text sizes to suit different needs */
|
|
1704
|
+
size;
|
|
1705
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviParagraph, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1706
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviParagraph, isStandalone: true, selector: "p[hviParagraph]", inputs: { variant: "variant", size: "size" }, host: { properties: { "attr.data-variant": "variant ?? null", "attr.data-size": "size ?? null" }, classAttribute: "ds-paragraph" }, ngImport: i0 });
|
|
1707
|
+
}
|
|
1708
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviParagraph, decorators: [{
|
|
1709
|
+
type: Directive,
|
|
1710
|
+
args: [{
|
|
1711
|
+
selector: 'p[hviParagraph]',
|
|
1712
|
+
standalone: true,
|
|
1713
|
+
host: {
|
|
1714
|
+
class: 'ds-paragraph',
|
|
1715
|
+
'[attr.data-variant]': 'variant ?? null',
|
|
1716
|
+
'[attr.data-size]': 'size ?? null',
|
|
1717
|
+
},
|
|
1718
|
+
}]
|
|
1719
|
+
}], propDecorators: { variant: [{
|
|
1720
|
+
type: Input
|
|
1721
|
+
}], size: [{
|
|
1722
|
+
type: Input
|
|
1723
|
+
}] } });
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* @summary
|
|
1727
|
+
* Tag is a label that can be used to categorize items or communicate progress, status, or process. Tags can provide users with a quicker overview of content.
|
|
1728
|
+
*
|
|
1729
|
+
* @example
|
|
1730
|
+
* ```html
|
|
1731
|
+
* <hvi-tag variant="outline" size="sm" color="info">Small info tag</hvi-tag>
|
|
1732
|
+
* ```
|
|
1733
|
+
*
|
|
1734
|
+
* Documentation: https://designsystemet.no/en/components/docs/tag/code
|
|
1735
|
+
*/
|
|
1736
|
+
class HviTag {
|
|
1737
|
+
/** The variants of the tag */
|
|
1738
|
+
variant;
|
|
1739
|
+
/** The sizes of the tag */
|
|
1740
|
+
size;
|
|
1741
|
+
/** The color theme of the tag */
|
|
1742
|
+
color;
|
|
1743
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviTag, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1744
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: HviTag, isStandalone: true, selector: "hvi-tag", inputs: { variant: "variant", size: "size", color: "color" }, host: { properties: { "attr.data-variant": "variant ?? null", "attr.data-size": "size ?? null", "attr.data-color": "color ?? null" }, classAttribute: "ds-tag" }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
1745
|
+
}
|
|
1746
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviTag, decorators: [{
|
|
1747
|
+
type: Component,
|
|
1748
|
+
args: [{
|
|
1749
|
+
selector: 'hvi-tag',
|
|
1750
|
+
standalone: true,
|
|
1751
|
+
template: '<ng-content />',
|
|
1752
|
+
host: {
|
|
1753
|
+
class: 'ds-tag',
|
|
1754
|
+
'[attr.data-variant]': 'variant ?? null',
|
|
1755
|
+
'[attr.data-size]': 'size ?? null',
|
|
1756
|
+
'[attr.data-color]': 'color ?? null',
|
|
1757
|
+
},
|
|
1758
|
+
}]
|
|
1759
|
+
}], propDecorators: { variant: [{
|
|
1760
|
+
type: Input
|
|
1761
|
+
}], size: [{
|
|
1762
|
+
type: Input
|
|
1763
|
+
}], color: [{
|
|
1764
|
+
type: Input
|
|
1765
|
+
}] } });
|
|
1766
|
+
|
|
1767
|
+
/*
|
|
1768
|
+
* Public API Surface of hviktor
|
|
1769
|
+
*/
|
|
1770
|
+
|
|
1771
|
+
/**
|
|
1772
|
+
* Generated bundle index. Do not edit.
|
|
1773
|
+
*/
|
|
1774
|
+
|
|
1775
|
+
export { HviAlert, HviAvatar, HviBadge, HviBadgePosition, HviBreadcrumbs, HviButton, HviCard, HviCardBlock, HviChipButton, HviChipLabel, HviControlInvalid, HviDetails, HviDetailsContent, HviDetailsSummary, HviDialog, HviDialogBlock, HviDivider, HviErrorSummary, HviField, HviFieldAffix, HviFieldAffixes, HviFieldDescription, HviFieldKit, HviFieldOptional, HviFieldValidation, HviFieldset, HviForm, HviForms, HviHeading, HviIcon, HviInput, HviLabel, HviLink, HviParagraph, HviTag, HviValidationKit, HviValidationMessage, fieldObserver, isElement, isInputLike, isLabel };
|
|
1776
|
+
//# sourceMappingURL=helsevestikt-hviktor-angular.mjs.map
|