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

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