@ks-digital/designsystem-angular 0.0.1-alpha.24 → 0.0.1-alpha.25

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.
Files changed (109) hide show
  1. package/.storybook/customTheme.ts +15 -0
  2. package/.storybook/default-args.ts +18 -0
  3. package/.storybook/main.ts +27 -0
  4. package/.storybook/manager.ts +10 -0
  5. package/.storybook/preview-head.html +16 -0
  6. package/.storybook/preview.ts +70 -0
  7. package/.storybook/themes.ts +9 -0
  8. package/.storybook/tsconfig.json +16 -0
  9. package/.storybook/vite.config.mts +5 -0
  10. package/README.md +3 -3
  11. package/eslint.config.mjs +28 -0
  12. package/ng-package.json +9 -0
  13. package/package.json +18 -27
  14. package/project.json +81 -0
  15. package/src/components/alert/alert.mdx +46 -0
  16. package/src/components/alert/alert.spec.ts +33 -0
  17. package/src/components/alert/alert.stories.ts +138 -0
  18. package/src/components/alert/alert.ts +46 -0
  19. package/src/components/alert/index.ts +1 -0
  20. package/src/components/button/button.mdx +40 -0
  21. package/src/components/button/button.spec.ts +86 -0
  22. package/src/components/button/button.stories.ts +123 -0
  23. package/src/components/button/button.ts +60 -0
  24. package/src/components/button/index.ts +1 -0
  25. package/src/components/card/card-block.ts +10 -0
  26. package/src/components/card/card.mdx +100 -0
  27. package/src/components/card/card.spec.ts +70 -0
  28. package/src/components/card/card.stories.ts +101 -0
  29. package/src/components/card/card.ts +44 -0
  30. package/src/components/card/index.ts +2 -0
  31. package/src/components/checkbox/README.md +13 -0
  32. package/src/components/checkbox/checkbox.mdx +50 -0
  33. package/src/components/checkbox/checkbox.spec.ts +21 -0
  34. package/src/components/checkbox/checkbox.stories.ts +182 -0
  35. package/src/components/checkbox/index.ts +0 -0
  36. package/src/components/colors.ts +36 -0
  37. package/src/components/common-inputs.ts +30 -0
  38. package/src/components/details/controlled-details.ts +63 -0
  39. package/src/components/details/details-content.ts +7 -0
  40. package/src/components/details/details-summary.ts +7 -0
  41. package/src/components/details/details.mdx +89 -0
  42. package/src/components/details/details.spec.ts +56 -0
  43. package/src/components/details/details.stories.ts +129 -0
  44. package/src/components/details/details.ts +69 -0
  45. package/src/components/details/index.ts +3 -0
  46. package/src/components/field/field-counter.ts +56 -0
  47. package/src/components/field/field-description.ts +10 -0
  48. package/src/components/field/field-error.ts +13 -0
  49. package/src/components/field/field-observer.ts +121 -0
  50. package/src/components/field/field-state.ts +21 -0
  51. package/src/components/field/field.mdx +40 -0
  52. package/src/components/field/field.spec.ts +131 -0
  53. package/src/components/field/field.stories.ts +98 -0
  54. package/src/components/field/field.ts +70 -0
  55. package/src/components/field/index.ts +3 -0
  56. package/src/components/fieldset/fieldset-description.ts +8 -0
  57. package/src/components/fieldset/fieldset-legend.ts +11 -0
  58. package/src/components/fieldset/fieldset.spec.ts +80 -0
  59. package/src/components/fieldset/fieldset.ts +11 -0
  60. package/src/components/fieldset/index.ts +3 -0
  61. package/src/components/input/index.ts +1 -0
  62. package/src/components/input/input.mdx +11 -0
  63. package/src/components/input/input.spec.ts +25 -0
  64. package/src/components/input/input.stories.ts +72 -0
  65. package/src/components/input/input.ts +67 -0
  66. package/src/components/label/index.ts +1 -0
  67. package/src/components/label/label.ts +17 -0
  68. package/src/components/paragraph/index.ts +1 -0
  69. package/src/components/paragraph/paragraph.ts +10 -0
  70. package/src/components/popover/controlled-popover.ts +62 -0
  71. package/src/components/popover/index.ts +1 -0
  72. package/src/components/popover/popover.mdx +81 -0
  73. package/src/components/popover/popover.spec.ts +143 -0
  74. package/src/components/popover/popover.stories.ts +63 -0
  75. package/src/components/popover/popover.ts +186 -0
  76. package/src/components/radio/radio.mdx +117 -0
  77. package/src/components/radio/radio.stories.ts +226 -0
  78. package/src/components/search/index.ts +4 -0
  79. package/src/components/search/search-button.ts +35 -0
  80. package/src/components/search/search-clear.ts +57 -0
  81. package/src/components/search/search-input.ts +18 -0
  82. package/src/components/search/search.mdx +56 -0
  83. package/src/components/search/search.spec.ts +48 -0
  84. package/src/components/search/search.stories.ts +205 -0
  85. package/src/components/search/search.ts +50 -0
  86. package/src/components/spinner/index.ts +1 -0
  87. package/src/components/spinner/spinner.mdx +24 -0
  88. package/src/components/spinner/spinner.spec.ts +13 -0
  89. package/src/components/spinner/spinner.stories.ts +54 -0
  90. package/src/components/spinner/spinner.ts +62 -0
  91. package/src/components/switch/switch.mdx +82 -0
  92. package/src/components/switch/switch.stories.ts +94 -0
  93. package/src/components/textarea/textarea.mdx +14 -0
  94. package/src/components/textarea/textarea.stories.ts +52 -0
  95. package/src/components/validation-message/index.ts +1 -0
  96. package/src/components/validation-message/validation-message.ts +11 -0
  97. package/src/index.ts +14 -0
  98. package/src/test-setup.ts +12 -0
  99. package/src/utils/log-if-devmode.ts +13 -0
  100. package/src/utils/random-id.ts +3 -0
  101. package/tsconfig.json +34 -0
  102. package/tsconfig.lib.json +28 -0
  103. package/tsconfig.lib.prod.json +9 -0
  104. package/tsconfig.spec.json +30 -0
  105. package/vite.config.mts +35 -0
  106. package/dist/README.md +0 -55
  107. package/dist/fesm2022/ks-digital-designsystem-angular.mjs +0 -1094
  108. package/dist/fesm2022/ks-digital-designsystem-angular.mjs.map +0 -1
  109. package/dist/index.d.ts +0 -333
@@ -1,1094 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { input, Directive, Component, booleanAttribute, inject, ElementRef, output, viewChild, CUSTOM_ELEMENTS_SCHEMA, signal, computed, Injectable, numberAttribute, effect, contentChild, contentChildren, afterNextRender, isDevMode } from '@angular/core';
3
- import '@u-elements/u-details';
4
- import { autoUpdate, computePosition, offset, flip, shift } from '@floating-ui/dom';
5
-
6
- /* eslint-disable @angular-eslint/no-input-rename */
7
- /**
8
- * We use input aliasing to bridge the gap between Angular's camelCase property naming convention and our HTML data attributes.
9
- * This approach allows us to use valid HTML data attributes as documented by Designsystemet while maintaining
10
- * proper TypeScript intellisense support.
11
- *
12
- * Todo: Some components are using only a subset of colors, e.g., SeverityColors for Alert. We should reconsider this directive
13
- */
14
- class CommonInputs {
15
- /**
16
- * Changes size for descendant Designsystemet components. Select from predefined sizes.
17
- * @attribute data-size
18
- */
19
- dataSize = input(undefined, { alias: 'data-size' });
20
- /**
21
- * Changes color for descendant Designsystemet components.
22
- * Select from predefined colors and colors defined using theme.designsystemet.no.
23
- * @attribute data-color
24
- */
25
- dataColor = input(undefined, { alias: 'data-color' });
26
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: CommonInputs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
27
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.4", type: CommonInputs, isStandalone: true, inputs: { dataSize: { classPropertyName: "dataSize", publicName: "data-size", isSignal: true, isRequired: false, transformFunction: null }, dataColor: { classPropertyName: "dataColor", publicName: "data-color", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
28
- }
29
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: CommonInputs, decorators: [{
30
- type: Directive
31
- }] });
32
-
33
- /**
34
- * Alerts are used to inform users about important information, warnings, errors, or success.
35
- */
36
- class Alert {
37
- /**
38
- * The color variant of the alert.
39
- */
40
- /* eslint-disable-next-line @angular-eslint/no-input-rename */
41
- dataColor = input(undefined, { alias: 'data-color' });
42
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Alert, deps: [], target: i0.ɵɵFactoryTarget.Component });
43
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.4", type: Alert, isStandalone: true, selector: "ksd-alert", inputs: { dataColor: { classPropertyName: "dataColor", publicName: "data-color", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "ds-alert" }, hostDirectives: [{ directive: CommonInputs, inputs: ["data-size", "data-size"] }], ngImport: i0, template: ` <ng-content />`, isInline: true, styles: [":host{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128,24A104,104,0,1,0,232,128A104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z'/%3E%3C/svg%3E\")}:host[data-color=warning]{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M120,136V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0ZM232,91.55v72.9a15.86,15.86,0,0,1-4.69,11.31l-51.55,51.55A15.86,15.86,0,0,1,164.45,232H91.55a15.86,15.86,0,0,1-11.31-4.69L28.69,175.76A15.86,15.86,0,0,1,24,164.45V91.55a15.86,15.86,0,0,1,4.69-11.31L80.24,28.69A15.86,15.86,0,0,1,91.55,24h72.9a15.86,15.86,0,0,1,11.31,4.69l51.55,51.55A15.86,15.86,0,0,1,232,91.55Zm-16,0L164.45,40H91.55L40,91.55v72.9L91.55,216h72.9L216,164.45ZM128,160a12,12,0,1,0,12,12A12,12,0,0,0,128,160Z'/%3E%3C/svg%3E\")}:host[data-color=success]{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M173.66,98.34a8,8,0,0,1,0,11.32l-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35A8,8,0,0,1,173.66,98.34ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'%3E%3C/path%3E%3C/svg%3E\")}:host[data-color=danger]{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'%3E%3C/path%3E%3C/svg%3E\")}\n"] });
44
- }
45
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Alert, decorators: [{
46
- type: Component,
47
- args: [{ selector: 'ksd-alert', template: ` <ng-content />`, host: {
48
- class: 'ds-alert',
49
- }, hostDirectives: [
50
- {
51
- directive: CommonInputs,
52
- inputs: ['data-size'],
53
- },
54
- ], styles: [":host{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128,24A104,104,0,1,0,232,128A104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z'/%3E%3C/svg%3E\")}:host[data-color=warning]{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M120,136V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0ZM232,91.55v72.9a15.86,15.86,0,0,1-4.69,11.31l-51.55,51.55A15.86,15.86,0,0,1,164.45,232H91.55a15.86,15.86,0,0,1-11.31-4.69L28.69,175.76A15.86,15.86,0,0,1,24,164.45V91.55a15.86,15.86,0,0,1,4.69-11.31L80.24,28.69A15.86,15.86,0,0,1,91.55,24h72.9a15.86,15.86,0,0,1,11.31,4.69l51.55,51.55A15.86,15.86,0,0,1,232,91.55Zm-16,0L164.45,40H91.55L40,91.55v72.9L91.55,216h72.9L216,164.45ZM128,160a12,12,0,1,0,12,12A12,12,0,0,0,128,160Z'/%3E%3C/svg%3E\")}:host[data-color=success]{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M173.66,98.34a8,8,0,0,1,0,11.32l-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35A8,8,0,0,1,173.66,98.34ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'%3E%3C/path%3E%3C/svg%3E\")}:host[data-color=danger]{--dsc-alert-icon-url: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M165.66,101.66,139.31,128l26.35,26.34a8,8,0,0,1-11.32,11.32L128,139.31l-26.34,26.35a8,8,0,0,1-11.32-11.32L116.69,128,90.34,101.66a8,8,0,0,1,11.32-11.32L128,116.69l26.34-26.35a8,8,0,0,1,11.32,11.32ZM232,128A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z'%3E%3C/path%3E%3C/svg%3E\")}\n"] }]
55
- }] });
56
-
57
- /* eslint-disable @angular-eslint/no-input-rename */
58
- class Spinner {
59
- /**
60
- * Aria-label for the spinner
61
- */
62
- ariaLabel = input(undefined, { alias: 'aria-label' });
63
- /**
64
- * Aria-label for the spinner
65
- */
66
- dataSize = input(undefined, { alias: 'data-size' });
67
- /**
68
- * Aria-label for the spinner
69
- */
70
- dataColor = input(undefined, { alias: 'data-color' });
71
- /**
72
- * Aria-hidden for the spinner
73
- */
74
- ariaHidden = input(undefined, {
75
- transform: booleanAttribute,
76
- alias: 'aria-hidden',
77
- });
78
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
79
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.4", type: Spinner, isStandalone: true, selector: "ksd-spinner", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, dataSize: { classPropertyName: "dataSize", publicName: "data-size", isSignal: true, isRequired: false, transformFunction: null }, dataColor: { classPropertyName: "dataColor", publicName: "data-color", isSignal: true, isRequired: false, transformFunction: null }, ariaHidden: { classPropertyName: "ariaHidden", publicName: "aria-hidden", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
80
- <svg
81
- class="ds-spinner"
82
- role="img"
83
- viewBox="0 0 50 50"
84
- [attr.data-size]="dataSize()"
85
- [attr.data-color]="dataColor()"
86
- >
87
- <circle
88
- class="ds-spinner__background"
89
- cx="25"
90
- cy="25"
91
- r="20"
92
- fill="none"
93
- stroke-width="5"
94
- />
95
- <circle
96
- class="ds-spinner__circle"
97
- cx="25"
98
- cy="25"
99
- r="20"
100
- fill="none"
101
- stroke-width="5"
102
- />
103
- </svg>
104
- `, isInline: true, styles: [":host{display:contents}\n"] });
105
- }
106
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Spinner, decorators: [{
107
- type: Component,
108
- args: [{ selector: 'ksd-spinner', template: `
109
- <svg
110
- class="ds-spinner"
111
- role="img"
112
- viewBox="0 0 50 50"
113
- [attr.data-size]="dataSize()"
114
- [attr.data-color]="dataColor()"
115
- >
116
- <circle
117
- class="ds-spinner__background"
118
- cx="25"
119
- cy="25"
120
- r="20"
121
- fill="none"
122
- stroke-width="5"
123
- />
124
- <circle
125
- class="ds-spinner__circle"
126
- cx="25"
127
- cy="25"
128
- r="20"
129
- fill="none"
130
- stroke-width="5"
131
- />
132
- </svg>
133
- `, styles: [":host{display:contents}\n"] }]
134
- }] });
135
-
136
- class Button {
137
- /**
138
- * Specify which variant to use
139
- * @default 'primary'
140
- */
141
- variant = input('primary');
142
- /**
143
- * Toggle loading state.
144
- * Pass an element if you want to display a custom loader.
145
- *
146
- * @default false
147
- */
148
- loading = input(false, { transform: booleanAttribute });
149
- /**
150
- * Disables element
151
- */
152
- disabled = input(false, { transform: booleanAttribute });
153
- /**
154
- * If this is a button with only an icon
155
- */
156
- icon = input(false, { transform: booleanAttribute });
157
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
158
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: Button, isStandalone: true, selector: "button[ksd-button], a[ksd-button]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button" }, properties: { "attr.data-variant": "variant()", "attr.data-icon": "icon() || null", "attr.disabled": "disabled() ? true : null", "attr.aria-busy": "loading() ? true : null" }, classAttribute: "ds-button" }, hostDirectives: [{ directive: CommonInputs, inputs: ["data-size", "data-size", "data-color", "data-color"] }], ngImport: i0, template: `
159
- @if (loading()) {
160
- <ksd-spinner aria-hidden="true" />
161
- }
162
- <ng-content />
163
- `, isInline: true, styles: [":host ::ng-deep>*{display:inline-flex}\n"], dependencies: [{ kind: "component", type: Spinner, selector: "ksd-spinner", inputs: ["aria-label", "data-size", "data-color", "aria-hidden"] }] });
164
- }
165
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Button, decorators: [{
166
- type: Component,
167
- args: [{ selector: 'button[ksd-button], a[ksd-button]', hostDirectives: [
168
- {
169
- directive: CommonInputs,
170
- inputs: ['data-size', 'data-color'],
171
- },
172
- ], imports: [Spinner], host: {
173
- class: 'ds-button',
174
- type: 'button',
175
- '[attr.data-variant]': 'variant()',
176
- '[attr.data-icon]': 'icon() || null',
177
- '[attr.disabled]': 'disabled() ? true : null',
178
- '[attr.aria-busy]': 'loading() ? true : null',
179
- }, template: `
180
- @if (loading()) {
181
- <ksd-spinner aria-hidden="true" />
182
- }
183
- <ng-content />
184
- `, styles: [":host ::ng-deep>*{display:inline-flex}\n"] }]
185
- }] });
186
-
187
- class Card {
188
- /**
189
- * Change the background color of the card
190
- * @default 'default'
191
- */
192
- variant = input('default');
193
- elementRef = inject(ElementRef);
194
- projectedLink() {
195
- const el = this.elementRef.nativeElement;
196
- return el?.querySelector('h1 a, h2 a, h3 a, h4 a, h5 a, h6 a');
197
- }
198
- handleClick = (event) => {
199
- const link = this.projectedLink();
200
- if (!link)
201
- return;
202
- if (event.metaKey || event.ctrlKey) {
203
- window.open(link.href, '_blank', 'noopener,noreferrer');
204
- }
205
- else {
206
- link.click();
207
- }
208
- };
209
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Card, deps: [], target: i0.ɵɵFactoryTarget.Component });
210
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.4", type: Card, isStandalone: true, selector: "[ksd-card]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "handleClick($event)" }, properties: { "attr.data-variant": "variant()" }, classAttribute: "ds-card" }, hostDirectives: [{ directive: CommonInputs, inputs: ["data-size", "data-size", "data-color", "data-color"] }], ngImport: i0, template: ` <ng-content /> `, isInline: true });
211
- }
212
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Card, decorators: [{
213
- type: Component,
214
- args: [{
215
- selector: '[ksd-card]',
216
- template: ` <ng-content /> `,
217
- hostDirectives: [
218
- {
219
- directive: CommonInputs,
220
- inputs: ['data-size', 'data-color'],
221
- },
222
- ],
223
- host: {
224
- class: 'ds-card',
225
- '[attr.data-variant]': 'variant()',
226
- '(click)': 'handleClick($event)',
227
- },
228
- }]
229
- }] });
230
-
231
- class CardBlock {
232
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: CardBlock, deps: [], target: i0.ɵɵFactoryTarget.Component });
233
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: CardBlock, isStandalone: true, selector: "[ksd-card-block]", host: { classAttribute: "ds-card__block" }, ngImport: i0, template: `<ng-content />`, isInline: true });
234
- }
235
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: CardBlock, decorators: [{
236
- type: Component,
237
- args: [{
238
- selector: '[ksd-card-block]',
239
- host: {
240
- class: 'ds-card__block',
241
- },
242
- template: `<ng-content />`,
243
- }]
244
- }] });
245
-
246
- class Details {
247
- dataSize = input(undefined, {
248
- // eslint-disable-next-line @angular-eslint/no-input-rename
249
- alias: 'data-size',
250
- });
251
- dataColor = input(undefined, {
252
- // eslint-disable-next-line @angular-eslint/no-input-rename
253
- alias: 'data-color',
254
- });
255
- variant = input('default');
256
- defaultOpen = input(false);
257
- open = input(undefined);
258
- toggled = output();
259
- detailsRef = viewChild('detailsRef');
260
- onToggle(event) {
261
- const details = this.detailsRef()?.nativeElement;
262
- if (details && details.open !== this.open()) {
263
- this.toggled.emit(event);
264
- }
265
- }
266
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Details, deps: [], target: i0.ɵɵFactoryTarget.Component });
267
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.4", type: Details, isStandalone: true, selector: "ksd-details", inputs: { dataSize: { classPropertyName: "dataSize", publicName: "data-size", isSignal: true, isRequired: false, transformFunction: null }, dataColor: { classPropertyName: "dataColor", publicName: "data-color", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggled: "toggled" }, viewQueries: [{ propertyName: "detailsRef", first: true, predicate: ["detailsRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
268
- <u-details
269
- #detailsRef
270
- class="ds-details"
271
- [attr.data-variant]="variant()"
272
- [attr.open]="(open() ?? defaultOpen()) || undefined"
273
- [attr.data-color]="dataColor()"
274
- [attr.data-size]="dataSize()"
275
- (toggle)="onToggle($event)"
276
- >
277
- <u-summary>
278
- <ng-content select="ksd-details-summary" />
279
- </u-summary>
280
- <div>
281
- <ng-content select="ksd-details-content" />
282
- </div>
283
- </u-details>
284
- `, isInline: true, styles: [".ds-card>:host(:last-of-type)>.ds-details{border-bottom:0}.ds-card>:host(:first-of-type)>.ds-details{border-top:0}:host(:not(:first-of-type))>.ds-details{border-top:0;margin-top:0}\n"] });
285
- }
286
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Details, decorators: [{
287
- type: Component,
288
- args: [{ selector: 'ksd-details', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
289
- <u-details
290
- #detailsRef
291
- class="ds-details"
292
- [attr.data-variant]="variant()"
293
- [attr.open]="(open() ?? defaultOpen()) || undefined"
294
- [attr.data-color]="dataColor()"
295
- [attr.data-size]="dataSize()"
296
- (toggle)="onToggle($event)"
297
- >
298
- <u-summary>
299
- <ng-content select="ksd-details-summary" />
300
- </u-summary>
301
- <div>
302
- <ng-content select="ksd-details-content" />
303
- </div>
304
- </u-details>
305
- `, styles: [".ds-card>:host(:last-of-type)>.ds-details{border-bottom:0}.ds-card>:host(:first-of-type)>.ds-details{border-top:0}:host(:not(:first-of-type))>.ds-details{border-top:0;margin-top:0}\n"] }]
306
- }] });
307
-
308
- class DetailsContent {
309
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DetailsContent, deps: [], target: i0.ɵɵFactoryTarget.Component });
310
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: DetailsContent, isStandalone: true, selector: "ksd-details-content", ngImport: i0, template: `<ng-content />`, isInline: true });
311
- }
312
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DetailsContent, decorators: [{
313
- type: Component,
314
- args: [{
315
- selector: 'ksd-details-content',
316
- template: `<ng-content />`,
317
- }]
318
- }] });
319
-
320
- class DetailsSummary {
321
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DetailsSummary, deps: [], target: i0.ɵɵFactoryTarget.Component });
322
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: DetailsSummary, isStandalone: true, selector: "ksd-details-summary", ngImport: i0, template: `<ng-content />`, isInline: true });
323
- }
324
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: DetailsSummary, decorators: [{
325
- type: Component,
326
- args: [{
327
- selector: 'ksd-details-summary',
328
- template: `<ng-content />`,
329
- }]
330
- }] });
331
-
332
- class FieldState {
333
- /**
334
- * Whether the field counter has exceeded its limit
335
- */
336
- hasExceededCounter = signal(false);
337
- /**
338
- * Whether the field has errors projected from the outside
339
- */
340
- hasProjectedErrors = signal(false);
341
- /**
342
- * Whether the field has any errors associated with it
343
- */
344
- hasError = computed(() => this.hasExceededCounter() || this.hasProjectedErrors());
345
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
346
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldState });
347
- }
348
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldState, decorators: [{
349
- type: Injectable
350
- }] });
351
-
352
- class Input {
353
- /**
354
- * The value of the input
355
- */
356
- value = signal('');
357
- /**
358
- * Whether the input is readonly
359
- */
360
- readonly = input(false, { transform: booleanAttribute });
361
- /**
362
- * Disables element
363
- */
364
- disabled = input(false, { transform: booleanAttribute });
365
- /**
366
- * Whether the element is invalid.
367
- */
368
- ariaInvalid = input(false, {
369
- transform: booleanAttribute,
370
- alias: 'aria-invalid',
371
- });
372
- /**
373
- * Displays a character counter. pass a number to set a limit.
374
- */
375
- counter = input(0, { transform: numberAttribute });
376
- fieldState = inject(FieldState, { optional: true });
377
- onClick(event) {
378
- if (this.readonly()) {
379
- event.preventDefault();
380
- }
381
- }
382
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Input, deps: [], target: i0.ɵɵFactoryTarget.Directive });
383
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.4", type: Input, isStandalone: true, selector: "input[ksd-input], textarea[ksd-input], select[ksd-input]", inputs: { readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaInvalid: { classPropertyName: "ariaInvalid", publicName: "aria-invalid", isSignal: true, isRequired: false, transformFunction: null }, counter: { classPropertyName: "counter", publicName: "counter", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick($event)", "input": "value.set($event.target.value)" }, properties: { "attr.readonly": "readonly() ? true : null", "attr.disabled": "disabled() ? true : null", "attr.aria-invalid": "ariaInvalid() ? true : (fieldState?.hasError() ? true: null)" }, classAttribute: "ds-input" }, hostDirectives: [{ directive: CommonInputs, inputs: ["data-size", "data-size", "data-color", "data-color"] }], ngImport: i0 });
384
- }
385
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Input, decorators: [{
386
- type: Directive,
387
- args: [{
388
- // eslint-disable-next-line @angular-eslint/directive-selector
389
- selector: 'input[ksd-input], textarea[ksd-input], select[ksd-input]',
390
- hostDirectives: [
391
- {
392
- directive: CommonInputs,
393
- inputs: ['data-size', 'data-color'],
394
- },
395
- ],
396
- host: {
397
- class: 'ds-input',
398
- '[attr.readonly]': 'readonly() ? true : null',
399
- '[attr.disabled]': 'disabled() ? true : null',
400
- '[attr.aria-invalid]': 'ariaInvalid() ? true : (fieldState?.hasError() ? true: null)',
401
- '(click)': 'onClick($event)',
402
- '(input)': 'value.set($event.target.value)',
403
- },
404
- }]
405
- }] });
406
-
407
- class Label {
408
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Label, deps: [], target: i0.ɵɵFactoryTarget.Component });
409
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: Label, isStandalone: true, selector: "ksd-label", hostDirectives: [{ directive: CommonInputs, inputs: ["data-size", "data-size", "data-color", "data-color"] }], ngImport: i0, template: `
410
- <!-- eslint-disable @angular-eslint/template/label-has-associated-control -- Fieldobserver handles binding the label to the input -->
411
- <label class="ds-label"><ng-content /></label>
412
- `, isInline: true });
413
- }
414
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Label, decorators: [{
415
- type: Component,
416
- args: [{
417
- selector: 'ksd-label',
418
- hostDirectives: [
419
- {
420
- directive: CommonInputs,
421
- inputs: ['data-size', 'data-color'],
422
- },
423
- ],
424
- template: `
425
- <!-- eslint-disable @angular-eslint/template/label-has-associated-control -- Fieldobserver handles binding the label to the input -->
426
- <label class="ds-label"><ng-content /></label>
427
- `,
428
- }]
429
- }] });
430
-
431
- class ValidationMessage {
432
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValidationMessage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
433
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.4", type: ValidationMessage, isStandalone: true, selector: "[ksd-validation-message]", host: { attributes: { "data-field": "validation" }, classAttribute: "ds-validation-message" }, ngImport: i0 });
434
- }
435
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: ValidationMessage, decorators: [{
436
- type: Directive,
437
- args: [{
438
- // eslint-disable-next-line @angular-eslint/directive-selector
439
- selector: '[ksd-validation-message]',
440
- host: {
441
- class: 'ds-validation-message',
442
- 'data-field': 'validation',
443
- },
444
- }]
445
- }] });
446
-
447
- class FieldCounter {
448
- /**
449
- * The maximum allowed characters.
450
- *
451
- **/
452
- limit = input.required();
453
- /**
454
- * How many characters have been typed.
455
- *
456
- **/
457
- count = input.required();
458
- remainder = computed(() => this.limit() - this.count());
459
- excessCount = computed(() => Math.abs(this.remainder()));
460
- hasExceededLimit = computed(() => this.count() > this.limit());
461
- fieldState = inject(FieldState);
462
- constructor() {
463
- effect(() => {
464
- this.fieldState.hasExceededCounter.set(this.hasExceededLimit());
465
- });
466
- }
467
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldCounter, deps: [], target: i0.ɵɵFactoryTarget.Component });
468
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: FieldCounter, isStandalone: true, selector: "ksd-field-counter", inputs: { limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: true, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
469
- <div data-field="description" class="ds-sr-only" aria-live="polite">
470
- @if (hasExceededLimit()) {
471
- {{ excessCount() }} tegn for mye
472
- }
473
- </div>
474
- @if (hasExceededLimit()) {
475
- <p ksd-validation-message>{{ excessCount() }} tegn for mye</p>
476
- } @else {
477
- <p data-field="validation">{{ remainder() }} tegn igjen</p>
478
- }
479
- `, isInline: true, styles: [":host>*{margin-top:var(--dsc-field-content-spacing)}\n"], dependencies: [{ kind: "directive", type: ValidationMessage, selector: "[ksd-validation-message]" }] });
480
- }
481
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldCounter, decorators: [{
482
- type: Component,
483
- args: [{ selector: 'ksd-field-counter', imports: [ValidationMessage], template: `
484
- <div data-field="description" class="ds-sr-only" aria-live="polite">
485
- @if (hasExceededLimit()) {
486
- {{ excessCount() }} tegn for mye
487
- }
488
- </div>
489
- @if (hasExceededLimit()) {
490
- <p ksd-validation-message>{{ excessCount() }} tegn for mye</p>
491
- } @else {
492
- <p data-field="validation">{{ remainder() }} tegn igjen</p>
493
- }
494
- `, styles: [":host>*{margin-top:var(--dsc-field-content-spacing)}\n"] }]
495
- }], ctorParameters: () => [] });
496
-
497
- /**
498
- * Lifted from Designsystemet core repo.
499
- * Takes care of binding ids, labels and aria-describedby attributes
500
- *
501
- * @param fieldElement - The field element to observe
502
- * @returns A function to disconnect the observer
503
- * */
504
- function fieldObserver(fieldElement) {
505
- if (!fieldElement)
506
- return;
507
- const elements = new Map();
508
- const typeCounter = new Map(); // Track count for each data-field type
509
- const uuid = `:${Date.now().toString(36)}${Math.random().toString(36).slice(2, 5)}`;
510
- let input = null;
511
- let describedby = '';
512
- const process = (mutations) => {
513
- const changed = [];
514
- const removed = [];
515
- // Merge MutationRecords
516
- for (const mutation of mutations) {
517
- if (mutation.attributeName)
518
- changed.push(mutation.target ?? fieldElement);
519
- // @ts-expect-error - addedNodes is not typed
520
- changed.push(...(mutation.addedNodes || []));
521
- removed.push(...(mutation.removedNodes || []));
522
- }
523
- // Register elements
524
- for (const el of changed) {
525
- if (!isElement(el))
526
- continue;
527
- if (isLabel(el))
528
- elements.set(el, el.htmlFor);
529
- else if (el.hasAttribute('data-field'))
530
- elements.set(el, el.id);
531
- else if (isInputLike(el)) {
532
- input = el;
533
- describedby = el.getAttribute('aria-describedby') || '';
534
- }
535
- }
536
- // Reset removed elements
537
- for (const el of removed) {
538
- if (!isElement(el))
539
- continue;
540
- if (input === el)
541
- input = null;
542
- if (elements.has(el)) {
543
- setAttr(el, isLabel(el) ? 'for' : 'id', elements.get(el));
544
- elements.delete(el);
545
- }
546
- }
547
- // Connect elements
548
- const describedbyIds = [describedby]; // Keep original aria-describedby
549
- const inputId = input?.id || uuid;
550
- // Reset type counters since we reprocess all elements
551
- typeCounter.clear();
552
- for (const [el, value] of elements) {
553
- const descriptionType = el.getAttribute('data-field');
554
- let id;
555
- if (descriptionType) {
556
- // Increment type counter for this type
557
- const count = (typeCounter.get(descriptionType) || 0) + 1;
558
- typeCounter.set(descriptionType, count);
559
- id = `${inputId}:${descriptionType}:${count}`;
560
- }
561
- else {
562
- id = inputId;
563
- }
564
- if (!value)
565
- setAttr(el, isLabel(el) ? 'for' : 'id', id); // Ensure we have a value
566
- if (descriptionType === 'validation')
567
- describedbyIds.unshift(el.id); // Validations to the front
568
- else if (descriptionType)
569
- describedbyIds.push(el.id); // Other descriptions to the back
570
- }
571
- setAttr(input, 'id', inputId);
572
- setAttr(input, 'aria-describedby', describedbyIds.join(' ').trim());
573
- };
574
- const observer = createOptimizedMutationObserver(process);
575
- observer.observe(fieldElement, {
576
- attributeFilter: ['id', 'for', 'aria-describedby'],
577
- attributes: true,
578
- childList: true,
579
- subtree: true,
580
- });
581
- process([{ addedNodes: fieldElement.querySelectorAll('*') }]); // Initial setup
582
- observer.takeRecords(); // Clear initial setup queue
583
- return () => observer.disconnect();
584
- }
585
- // Utilities
586
- const isElement = (node) => node instanceof Element;
587
- const isLabel = (node) => node instanceof HTMLLabelElement;
588
- const isInputLike = (node) => node instanceof HTMLElement &&
589
- 'validity' in node &&
590
- !(node instanceof HTMLButtonElement); // Matches input, textarea, select and form accosiated custom elements
591
- const setAttr = (el, name, value) => value ? el?.setAttribute(name, value) : el?.removeAttribute(name);
592
- // Speed up MutationObserver by debouncing, clearing internal queue after changes and only running when page is visible
593
- function createOptimizedMutationObserver(callback) {
594
- const queue = [];
595
- const observer = new MutationObserver((mutations) => {
596
- if (!queue.length)
597
- requestAnimationFrame(process);
598
- queue.push(...mutations);
599
- });
600
- const process = () => {
601
- callback(queue, observer);
602
- queue.length = 0; // Reset queue
603
- observer.takeRecords(); // Clear queue due to DOM changes in callback
604
- };
605
- return observer;
606
- }
607
-
608
- /**
609
- * Use the Field component to connect inputs and labels
610
- */
611
- class Field {
612
- /**
613
- * Position of toggle inputs (radio, checkbox, switch) in field
614
- * @default start
615
- */
616
- position = input('start');
617
- fieldState = inject(FieldState);
618
- input = contentChild(Input);
619
- label = contentChild(Label);
620
- projectedErrors = contentChildren(ValidationMessage);
621
- el = inject(ElementRef);
622
- count = computed(() => this.input()?.value().length);
623
- limit = computed(() => this.input()?.counter());
624
- hasCounter = computed(() => this.limit());
625
- constructor() {
626
- afterNextRender(() => {
627
- fieldObserver(this.el.nativeElement);
628
- });
629
- effect(() => {
630
- this.fieldState.hasProjectedErrors.set(this.projectedErrors().length > 0);
631
- });
632
- }
633
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Field, deps: [], target: i0.ɵɵFactoryTarget.Component });
634
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: Field, isStandalone: true, selector: "ksd-field", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.dataPosition": "position()" }, classAttribute: "ds-field" }, providers: [FieldState], queries: [{ propertyName: "input", first: true, predicate: Input, descendants: true, isSignal: true }, { propertyName: "label", first: true, predicate: Label, descendants: true, isSignal: true }, { propertyName: "projectedErrors", predicate: ValidationMessage, isSignal: true }], hostDirectives: [{ directive: CommonInputs, inputs: ["data-size", "data-size", "data-color", "data-color"] }], ngImport: i0, template: `
635
- <ng-content />
636
- @if (hasCounter()) {
637
- <ksd-field-counter [limit]="limit() ?? 0" [count]="count() ?? 0" />
638
- }
639
- `, isInline: true, dependencies: [{ kind: "component", type: FieldCounter, selector: "ksd-field-counter", inputs: ["limit", "count"] }] });
640
- }
641
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Field, decorators: [{
642
- type: Component,
643
- args: [{
644
- selector: 'ksd-field',
645
- hostDirectives: [
646
- {
647
- directive: CommonInputs,
648
- inputs: ['data-size', 'data-color'],
649
- },
650
- ],
651
- host: {
652
- class: 'ds-field',
653
- '[attr.dataPosition]': 'position()',
654
- },
655
- template: `
656
- <ng-content />
657
- @if (hasCounter()) {
658
- <ksd-field-counter [limit]="limit() ?? 0" [count]="count() ?? 0" />
659
- }
660
- `,
661
- imports: [FieldCounter],
662
- providers: [FieldState],
663
- }]
664
- }], ctorParameters: () => [] });
665
-
666
- class FieldDescription {
667
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldDescription, deps: [], target: i0.ɵɵFactoryTarget.Component });
668
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: FieldDescription, isStandalone: true, selector: "[ksd-field-description]", host: { attributes: { "data-field": "description" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
669
- }
670
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldDescription, decorators: [{
671
- type: Component,
672
- args: [{
673
- selector: '[ksd-field-description]',
674
- host: {
675
- 'data-field': 'description',
676
- },
677
- template: `<ng-content />`,
678
- }]
679
- }] });
680
-
681
- class FieldError {
682
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldError, deps: [], target: i0.ɵɵFactoryTarget.Component });
683
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: FieldError, isStandalone: true, selector: "[ksd-error]", hostDirectives: [{ directive: ValidationMessage }], ngImport: i0, template: `<ng-content />`, isInline: true });
684
- }
685
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldError, decorators: [{
686
- type: Component,
687
- args: [{
688
- selector: '[ksd-error]',
689
- template: `<ng-content />`,
690
- hostDirectives: [
691
- {
692
- directive: ValidationMessage,
693
- },
694
- ],
695
- }]
696
- }] });
697
-
698
- class Fieldset {
699
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Fieldset, deps: [], target: i0.ɵɵFactoryTarget.Component });
700
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: Fieldset, isStandalone: true, selector: "fieldset[ksd-fieldset]", host: { attributes: { "role": "fieldset" }, classAttribute: "ds-fieldset" }, ngImport: i0, template: ` <ng-content /> `, isInline: true });
701
- }
702
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Fieldset, decorators: [{
703
- type: Component,
704
- args: [{
705
- selector: 'fieldset[ksd-fieldset]',
706
- host: {
707
- role: 'fieldset',
708
- class: 'ds-fieldset',
709
- },
710
- template: ` <ng-content /> `,
711
- }]
712
- }] });
713
-
714
- class FieldsetDescription {
715
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldsetDescription, deps: [], target: i0.ɵɵFactoryTarget.Component });
716
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: FieldsetDescription, isStandalone: true, selector: "p[ksd-fieldset-description]", ngImport: i0, template: `<ng-content />`, isInline: true });
717
- }
718
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldsetDescription, decorators: [{
719
- type: Component,
720
- args: [{
721
- selector: 'p[ksd-fieldset-description]',
722
- template: `<ng-content />`,
723
- host: {},
724
- }]
725
- }] });
726
-
727
- class FieldsetLegend {
728
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldsetLegend, deps: [], target: i0.ɵɵFactoryTarget.Component });
729
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: FieldsetLegend, isStandalone: true, selector: "legend[ksd-fieldset-legend]", host: { attributes: { "role": "legend" }, classAttribute: "ds-label" }, ngImport: i0, template: ` <ng-content /> `, isInline: true });
730
- }
731
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: FieldsetLegend, decorators: [{
732
- type: Component,
733
- args: [{
734
- selector: 'legend[ksd-fieldset-legend]',
735
- host: {
736
- role: 'legend',
737
- class: 'ds-label',
738
- },
739
- template: ` <ng-content /> `,
740
- }]
741
- }] });
742
-
743
- class Paragraph {
744
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Paragraph, deps: [], target: i0.ɵɵFactoryTarget.Component });
745
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.0.4", type: Paragraph, isStandalone: true, selector: "p[ksd-paragraph]", host: { classAttribute: "ds-paragraph" }, ngImport: i0, template: `<ng-content />`, isInline: true });
746
- }
747
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Paragraph, decorators: [{
748
- type: Component,
749
- args: [{
750
- selector: 'p[ksd-paragraph]',
751
- template: `<ng-content />`,
752
- host: {
753
- class: 'ds-paragraph',
754
- },
755
- }]
756
- }] });
757
-
758
- /* eslint-disable @angular-eslint/no-input-rename */
759
- class Popover {
760
- // use popoverId instead of id since id will be put on the angular element and not the popover-div
761
- popoverId = input.required();
762
- placement = input('top');
763
- autoPlacement = input(true, { transform: booleanAttribute });
764
- // for controlled component
765
- open = input(undefined, { transform: booleanAttribute });
766
- /*
767
- the naming here is different from Designsystemet
768
- since we need to use outputs for onOpen and onClose
769
- */
770
- triggeredClose = output();
771
- triggeredOpen = output();
772
- internalOpen = signal(false);
773
- controlledOpen = computed(() => this.open() ?? this.internalOpen());
774
- variant = input('default');
775
- dataSize = input('md', { alias: 'data-size' });
776
- dataColor = input('neutral', {
777
- alias: 'data-color',
778
- });
779
- popoverRef = viewChild('myPopover');
780
- // enable controlled component
781
- controlledComponent = effect((onCleanup) => {
782
- const popover = this.popoverRef()?.nativeElement;
783
- const handleClick = (event) => {
784
- const el = event.target;
785
- const isTrigger = el?.closest?.(`[popovertarget="${this.popoverId()}"]`);
786
- const isOutside = !isTrigger && !popover?.contains(el);
787
- if (isTrigger) {
788
- event.preventDefault(); // Prevent native Popover API
789
- this.internalOpen.update((open) => !open);
790
- this.triggeredOpen.emit();
791
- }
792
- if (isOutside) {
793
- this.internalOpen.set(false);
794
- this.triggeredClose.emit();
795
- }
796
- };
797
- const handleKeydown = (event) => {
798
- if (event.key !== 'Escape' || !this.controlledOpen())
799
- return;
800
- event.preventDefault(); // Prevent closing fullscreen in Safari
801
- this.internalOpen.set(false);
802
- this.triggeredClose.emit();
803
- };
804
- popover?.togglePopover?.(this.controlledOpen());
805
- document.addEventListener('click', handleClick, true); // Use capture to execute before React event API
806
- document.addEventListener('keydown', handleKeydown);
807
- onCleanup(() => {
808
- document.removeEventListener('click', handleClick, true);
809
- document.removeEventListener('keydown', handleKeydown);
810
- });
811
- }, {});
812
- positionPopover = effect(() => {
813
- const popover = this.popoverRef()?.nativeElement;
814
- const trigger = document.querySelector(`[popovertarget="${this.popoverId()}"]`);
815
- const placement = this.placement();
816
- if (popover && trigger && this.controlledOpen()) {
817
- autoUpdate(trigger, popover, () => {
818
- computePosition(trigger, popover, {
819
- placement,
820
- strategy: 'fixed',
821
- middleware: [
822
- offset((data) => {
823
- // get pseudo element arrow size
824
- const styles = getComputedStyle(data.elements.floating, '::before');
825
- return parseFloat(styles.height);
826
- }),
827
- ...(this.autoPlacement()
828
- ? [flip({ fallbackAxisSideDirection: 'start' }), shift()]
829
- : []),
830
- this.arrowPseudoElement,
831
- ],
832
- }).then(({ x, y }) => {
833
- popover.style.translate = `${x}px ${y}px`;
834
- });
835
- });
836
- }
837
- }, {});
838
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
839
- arrowPseudoElement = {
840
- name: 'ArrowPseudoElement',
841
- fn(data) {
842
- const { elements, rects, placement } = data;
843
- let arrowX = `${Math.round(rects.reference.width / 2 + rects.reference.x - data.x)}px`;
844
- let arrowY = `${Math.round(rects.reference.height / 2 + rects.reference.y - data.y)}px`;
845
- if (rects.reference.width > rects.floating.width) {
846
- arrowX = `${Math.round(rects.floating.width / 2)}px`;
847
- }
848
- if (rects.reference.height > rects.floating.height) {
849
- arrowY = `${Math.round(rects.floating.height / 2)}px`;
850
- }
851
- switch (placement.split('-')[0]) {
852
- case 'top':
853
- arrowY = '100%';
854
- break;
855
- case 'right':
856
- arrowX = '0';
857
- break;
858
- case 'bottom':
859
- arrowY = '0';
860
- break;
861
- case 'left':
862
- arrowX = '100%';
863
- break;
864
- }
865
- elements.floating.setAttribute('data-placement', placement.split('-')[0]); // We only need top/left/right/bottom
866
- elements.floating.style.setProperty('--ds-popover-arrow-x', arrowX);
867
- elements.floating.style.setProperty('--ds-popover-arrow-y', arrowY);
868
- return data;
869
- },
870
- };
871
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Popover, deps: [], target: i0.ɵɵFactoryTarget.Component });
872
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: Popover, isStandalone: true, selector: "ksd-popover", inputs: { popoverId: { classPropertyName: "popoverId", publicName: "popoverId", isSignal: true, isRequired: true, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, autoPlacement: { classPropertyName: "autoPlacement", publicName: "autoPlacement", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, dataSize: { classPropertyName: "dataSize", publicName: "data-size", isSignal: true, isRequired: false, transformFunction: null }, dataColor: { classPropertyName: "dataColor", publicName: "data-color", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { triggeredClose: "triggeredClose", triggeredOpen: "triggeredOpen" }, viewQueries: [{ propertyName: "popoverRef", first: true, predicate: ["myPopover"], descendants: true, isSignal: true }], ngImport: i0, template: `
873
- <div
874
- #myPopover
875
- popover="manual"
876
- class="ds-popover"
877
- data-testid="popover"
878
- [id]="popoverId()"
879
- [attr.data-size]="dataSize()"
880
- [attr.data-color]="dataColor()"
881
- [attr.data-variant]="variant()"
882
- >
883
- @if (controlledOpen()) {
884
- <ng-content />
885
- }
886
- </div>
887
- `, isInline: true });
888
- }
889
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Popover, decorators: [{
890
- type: Component,
891
- args: [{
892
- selector: 'ksd-popover',
893
- template: `
894
- <div
895
- #myPopover
896
- popover="manual"
897
- class="ds-popover"
898
- data-testid="popover"
899
- [id]="popoverId()"
900
- [attr.data-size]="dataSize()"
901
- [attr.data-color]="dataColor()"
902
- [attr.data-variant]="variant()"
903
- >
904
- @if (controlledOpen()) {
905
- <ng-content />
906
- }
907
- </div>
908
- `,
909
- imports: [],
910
- }]
911
- }] });
912
-
913
- const logIfDevMode = ({ component, message, }) => {
914
- if (isDevMode()) {
915
- console.log(`[${component}] ${message}`);
916
- }
917
- };
918
-
919
- /**
920
- * Search input
921
- *
922
- * Used within Search to provide a search input.
923
- */
924
- class SearchInput {
925
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: SearchInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
926
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.4", type: SearchInput, isStandalone: true, selector: "input[ksd-search-input]", host: { attributes: { "type": "search", "placeholder": "" }, classAttribute: "ds-input" }, ngImport: i0 });
927
- }
928
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: SearchInput, decorators: [{
929
- type: Directive,
930
- args: [{
931
- // eslint-disable-next-line @angular-eslint/directive-selector
932
- selector: 'input[ksd-search-input]',
933
- standalone: true,
934
- host: {
935
- class: 'ds-input',
936
- type: 'search',
937
- placeholder: '', // Need empty placeholder to enable show/hide for clear button
938
- },
939
- }]
940
- }] });
941
-
942
- /**
943
- * Search Component
944
- *
945
- * Use to contain the search input and buttons.
946
- * Only `SearchInput` is required, while `SearchClear` and `SearchButton` are optional.
947
- *
948
- * @example
949
- * <div ksd-search>
950
- * <input ksd-search-input />
951
- * <button ksd-search-clear></button>
952
- * <button ksd-search-button></button>
953
- * </div>
954
- */
955
- class Search {
956
- input = contentChild(SearchInput);
957
- constructor() {
958
- afterNextRender(() => {
959
- if (!this.input()) {
960
- logIfDevMode({
961
- component: 'Search',
962
- message: 'Missing required elements: ksd-search-input must be provided as child. Check imports and markup.',
963
- });
964
- }
965
- });
966
- }
967
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Search, deps: [], target: i0.ɵɵFactoryTarget.Component });
968
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.4", type: Search, isStandalone: true, selector: "ksd-search", host: { classAttribute: "ds-search" }, queries: [{ propertyName: "input", first: true, predicate: SearchInput, descendants: true, isSignal: true }], hostDirectives: [{ directive: CommonInputs, inputs: ["data-size", "data-size", "data-color", "data-color"] }], ngImport: i0, template: `
969
- <ng-content select="[ksd-search-input]" />
970
- <ng-content select="[ksd-search-clear]" />
971
- <ng-content select="[ksd-search-button]" />
972
- `, isInline: true });
973
- }
974
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: Search, decorators: [{
975
- type: Component,
976
- args: [{
977
- selector: 'ksd-search',
978
- template: `
979
- <ng-content select="[ksd-search-input]" />
980
- <ng-content select="[ksd-search-clear]" />
981
- <ng-content select="[ksd-search-button]" />
982
- `,
983
- host: {
984
- class: 'ds-search',
985
- },
986
- hostDirectives: [
987
- {
988
- directive: CommonInputs,
989
- inputs: ['data-size', 'data-color'],
990
- },
991
- ],
992
- }]
993
- }], ctorParameters: () => [] });
994
-
995
- /**
996
- * Search button
997
- *
998
- * Used within Search to provide a submit button.
999
- *
1000
- * @param {('primary' | 'secondary')} [variant] - Specify which button variant to use
1001
- * @param {string} [aria-label] - Aria label for the button
1002
- *
1003
- */
1004
- class SearchButton {
1005
- /**
1006
- * Specify which button variant to use
1007
- *
1008
- * @default 'primary'
1009
- */
1010
- variant = input('primary');
1011
- /**
1012
- * Aria label for the button
1013
- */
1014
- ariaLabel = input('', { alias: 'aria-label' });
1015
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: SearchButton, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1016
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.4", type: SearchButton, isStandalone: true, selector: "button[ksd-search-button]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "submit" }, properties: { "attr.aria-label": "this.ariaLabel()", "attr.data-variant": "this.variant()" }, classAttribute: "ds-button" }, ngImport: i0 });
1017
- }
1018
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: SearchButton, decorators: [{
1019
- type: Directive,
1020
- args: [{
1021
- // eslint-disable-next-line @angular-eslint/directive-selector
1022
- selector: 'button[ksd-search-button]',
1023
- standalone: true,
1024
- host: {
1025
- class: 'ds-button',
1026
- type: 'submit',
1027
- '[attr.aria-label]': 'this.ariaLabel()',
1028
- '[attr.data-variant]': 'this.variant()',
1029
- },
1030
- }]
1031
- }] });
1032
-
1033
- /**
1034
- * Search clear button
1035
- *
1036
- * Used within Search to provide a clear button.
1037
- *
1038
- * @param {string} [aria-label] - Aria label for the button.
1039
- *
1040
- * @event clearInput - Emitted when the clear button is clicked.
1041
- * Use this to notify controlled forms that the input should be cleared.
1042
- *
1043
- */
1044
- class SearchClear {
1045
- /**
1046
- * Aria label for the button
1047
- * @default 'Tøm'
1048
- */
1049
- ariaLabel = input('Tøm', { alias: 'aria-label' });
1050
- /**
1051
- * Output to notify controlled forms that input should be cleared
1052
- */
1053
- clearInput = output();
1054
- handleClear(e) {
1055
- const target = e.target;
1056
- let inputElement = null;
1057
- if (target instanceof HTMLElement) {
1058
- inputElement = target.closest('.ds-search')?.querySelector('input');
1059
- }
1060
- if (!inputElement)
1061
- throw new Error('Input is missing');
1062
- if (!(inputElement instanceof HTMLInputElement)) {
1063
- throw new Error('Input is not an input element');
1064
- }
1065
- e.preventDefault();
1066
- inputElement.value = '';
1067
- this.clearInput.emit();
1068
- inputElement.focus();
1069
- }
1070
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: SearchClear, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1071
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.4", type: SearchClear, isStandalone: true, selector: "button[ksd-search-clear]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clearInput: "clearInput" }, host: { attributes: { "type": "reset" }, listeners: { "click": "handleClear($event)" }, properties: { "attr.data-variant": "'tertiary'", "attr.aria-label": "this.ariaLabel()" }, classAttribute: "ds-button" }, ngImport: i0 });
1072
- }
1073
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: SearchClear, decorators: [{
1074
- type: Directive,
1075
- args: [{
1076
- // eslint-disable-next-line @angular-eslint/directive-selector
1077
- selector: 'button[ksd-search-clear]',
1078
- standalone: true,
1079
- host: {
1080
- class: 'ds-button',
1081
- type: 'reset',
1082
- '[attr.data-variant]': "'tertiary'",
1083
- '[attr.aria-label]': 'this.ariaLabel()',
1084
- '(click)': 'handleClear($event)',
1085
- },
1086
- }]
1087
- }] });
1088
-
1089
- /**
1090
- * Generated bundle index. Do not edit.
1091
- */
1092
-
1093
- export { Alert, Button, Card, CardBlock, CommonInputs, Details, DetailsContent, DetailsSummary, Field, FieldDescription, FieldError, Fieldset, FieldsetDescription, FieldsetLegend, Input, Label, Paragraph, Popover, Search, SearchButton, SearchClear, SearchInput, Spinner, ValidationMessage };
1094
- //# sourceMappingURL=ks-digital-designsystem-angular.mjs.map