@politiet/sheriff-web-components 2.13.1 → 2.14.1

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 (159) hide show
  1. package/custom-elements.json +3942 -1450
  2. package/dist/controllers/index.d.ts +3 -0
  3. package/dist/controllers/index.d.ts.map +1 -0
  4. package/dist/controllers/index.js +3 -0
  5. package/dist/controllers/index.js.map +1 -0
  6. package/dist/controllers/sds-drag-drop-controller.d.ts +25 -0
  7. package/dist/controllers/sds-drag-drop-controller.d.ts.map +1 -0
  8. package/dist/controllers/sds-drag-drop-controller.js +52 -0
  9. package/dist/controllers/sds-drag-drop-controller.js.map +1 -0
  10. package/dist/controllers/sds-file-preview-controller.d.ts +38 -0
  11. package/dist/controllers/sds-file-preview-controller.d.ts.map +1 -0
  12. package/dist/controllers/sds-file-preview-controller.js +90 -0
  13. package/dist/controllers/sds-file-preview-controller.js.map +1 -0
  14. package/dist/frameworks/preact.d.ts +15 -1
  15. package/dist/frameworks/preact.d.ts.map +1 -1
  16. package/dist/frameworks/react.d.ts +33 -1
  17. package/dist/frameworks/react.d.ts.map +1 -1
  18. package/dist/frameworks/solidjs.d.ts +4 -0
  19. package/dist/frameworks/solidjs.d.ts.map +1 -1
  20. package/dist/frameworks/svelte.d.ts +4 -0
  21. package/dist/frameworks/svelte.d.ts.map +1 -1
  22. package/dist/frameworks/vue.d.ts +24 -1
  23. package/dist/frameworks/vue.d.ts.map +1 -1
  24. package/dist/index.d.ts +8 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +4 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/react-wrappers/SDSDialogWrapper.d.ts +1 -0
  29. package/dist/react-wrappers/SDSDialogWrapper.d.ts.map +1 -1
  30. package/dist/react-wrappers/SDSDialogWrapper.js +1 -0
  31. package/dist/react-wrappers/SDSDialogWrapper.js.map +1 -1
  32. package/dist/react-wrappers/SDSFileUploadFileListWrapper.d.ts +3 -0
  33. package/dist/react-wrappers/SDSFileUploadFileListWrapper.d.ts.map +1 -0
  34. package/dist/react-wrappers/SDSFileUploadFileListWrapper.js +10 -0
  35. package/dist/react-wrappers/SDSFileUploadFileListWrapper.js.map +1 -0
  36. package/dist/react-wrappers/SDSFileUploadItemWrapper.d.ts +6 -0
  37. package/dist/react-wrappers/SDSFileUploadItemWrapper.d.ts.map +1 -0
  38. package/dist/react-wrappers/SDSFileUploadItemWrapper.js +12 -0
  39. package/dist/react-wrappers/SDSFileUploadItemWrapper.js.map +1 -0
  40. package/dist/react-wrappers/SDSFileUploadWrapper.d.ts +12 -0
  41. package/dist/react-wrappers/SDSFileUploadWrapper.d.ts.map +1 -0
  42. package/dist/react-wrappers/SDSFileUploadWrapper.js +18 -0
  43. package/dist/react-wrappers/SDSFileUploadWrapper.js.map +1 -0
  44. package/dist/react-wrappers/SDSSearchWrapper.d.ts +15 -0
  45. package/dist/react-wrappers/SDSSearchWrapper.d.ts.map +1 -0
  46. package/dist/react-wrappers/SDSSearchWrapper.js +19 -0
  47. package/dist/react-wrappers/SDSSearchWrapper.js.map +1 -0
  48. package/dist/react-wrappers/SDSStepperStepWrapper.d.ts +4 -1
  49. package/dist/react-wrappers/SDSStepperStepWrapper.d.ts.map +1 -1
  50. package/dist/react-wrappers/SDSStepperStepWrapper.js +3 -1
  51. package/dist/react-wrappers/SDSStepperStepWrapper.js.map +1 -1
  52. package/dist/react-wrappers/index.d.ts +4 -0
  53. package/dist/react-wrappers/index.d.ts.map +1 -1
  54. package/dist/react-wrappers/index.js +4 -0
  55. package/dist/react-wrappers/index.js.map +1 -1
  56. package/dist/sds-alert/sds-alert.styles.js +1 -1
  57. package/dist/sds-alert/sds-alert.styles.js.map +1 -1
  58. package/dist/sds-button/sds-button.component.d.ts +0 -1
  59. package/dist/sds-button/sds-button.component.d.ts.map +1 -1
  60. package/dist/sds-button/sds-button.component.js +10 -16
  61. package/dist/sds-button/sds-button.component.js.map +1 -1
  62. package/dist/sds-checkbox/sds-checkbox.styles.d.ts.map +1 -1
  63. package/dist/sds-checkbox/sds-checkbox.styles.js +6 -0
  64. package/dist/sds-checkbox/sds-checkbox.styles.js.map +1 -1
  65. package/dist/sds-dialog/sds-dialog.component.d.ts.map +1 -1
  66. package/dist/sds-dialog/sds-dialog.component.js +11 -1
  67. package/dist/sds-dialog/sds-dialog.component.js.map +1 -1
  68. package/dist/sds-file-upload/sds-file-upload.d.ts +102 -0
  69. package/dist/sds-file-upload/sds-file-upload.d.ts.map +1 -0
  70. package/dist/sds-file-upload/sds-file-upload.js +384 -0
  71. package/dist/sds-file-upload/sds-file-upload.js.map +1 -0
  72. package/dist/sds-file-upload/sds-file-upload.styles.d.ts +2 -0
  73. package/dist/sds-file-upload/sds-file-upload.styles.d.ts.map +1 -0
  74. package/dist/sds-file-upload/sds-file-upload.styles.js +234 -0
  75. package/dist/sds-file-upload/sds-file-upload.styles.js.map +1 -0
  76. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.d.ts +14 -0
  77. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.d.ts.map +1 -0
  78. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.js +26 -0
  79. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.js.map +1 -0
  80. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.d.ts +2 -0
  81. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.d.ts.map +1 -0
  82. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.js +19 -0
  83. package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.js.map +1 -0
  84. package/dist/sds-file-upload-item/assets/u-progress.js +2 -0
  85. package/dist/sds-file-upload-item/sds-file-upload-item.d.ts +62 -0
  86. package/dist/sds-file-upload-item/sds-file-upload-item.d.ts.map +1 -0
  87. package/dist/sds-file-upload-item/sds-file-upload-item.js +205 -0
  88. package/dist/sds-file-upload-item/sds-file-upload-item.js.map +1 -0
  89. package/dist/sds-file-upload-item/sds-file-upload-item.styles.d.ts +2 -0
  90. package/dist/sds-file-upload-item/sds-file-upload-item.styles.d.ts.map +1 -0
  91. package/dist/sds-file-upload-item/sds-file-upload-item.styles.js +227 -0
  92. package/dist/sds-file-upload-item/sds-file-upload-item.styles.js.map +1 -0
  93. package/dist/sds-icon/sds-icon.system.js +7 -7
  94. package/dist/sds-icon/sds-icon.system.js.map +1 -1
  95. package/dist/sds-internal-header/sds-internal-header.component.js +1 -1
  96. package/dist/sds-internal-header/sds-internal-header.component.js.map +1 -1
  97. package/dist/sds-internal-header/sds-internal-header.styles.js +1 -1
  98. package/dist/sds-internal-header/sds-internal-header.styles.js.map +1 -1
  99. package/dist/sds-notification/sds-notification.styles.js +1 -1
  100. package/dist/sds-notification/sds-notification.styles.js.map +1 -1
  101. package/dist/sds-popover/assets/popover-styles.d.ts.map +1 -1
  102. package/dist/sds-popover/assets/popover-styles.js +1 -1
  103. package/dist/sds-popover/assets/popover-styles.js.map +1 -1
  104. package/dist/sds-radio/sds-radio.styles.d.ts.map +1 -1
  105. package/dist/sds-radio/sds-radio.styles.js +6 -1
  106. package/dist/sds-radio/sds-radio.styles.js.map +1 -1
  107. package/dist/sds-search/sds-search.component.d.ts +128 -0
  108. package/dist/sds-search/sds-search.component.d.ts.map +1 -0
  109. package/dist/sds-search/sds-search.component.js +345 -0
  110. package/dist/sds-search/sds-search.component.js.map +1 -0
  111. package/dist/sds-search/sds-search.d.ts +13 -0
  112. package/dist/sds-search/sds-search.d.ts.map +1 -0
  113. package/dist/sds-search/sds-search.js +18 -0
  114. package/dist/sds-search/sds-search.js.map +1 -0
  115. package/dist/sds-search/sds-search.styles.d.ts +2 -0
  116. package/dist/sds-search/sds-search.styles.d.ts.map +1 -0
  117. package/dist/sds-search/sds-search.styles.js +74 -0
  118. package/dist/sds-search/sds-search.styles.js.map +1 -0
  119. package/dist/sds-stepper-step/sds-stepper-step.d.ts +3 -0
  120. package/dist/sds-stepper-step/sds-stepper-step.d.ts.map +1 -1
  121. package/dist/sds-stepper-step/sds-stepper-step.js +4 -3
  122. package/dist/sds-stepper-step/sds-stepper-step.js.map +1 -1
  123. package/dist/sds-stepper-step/sds-stepper-step.styles.js +3 -3
  124. package/dist/sds-stepper-step/sds-stepper-step.styles.js.map +1 -1
  125. package/dist/sds-textfield/sds-textfield.component.d.ts +4 -1
  126. package/dist/sds-textfield/sds-textfield.component.d.ts.map +1 -1
  127. package/dist/sds-textfield/sds-textfield.component.js +20 -5
  128. package/dist/sds-textfield/sds-textfield.component.js.map +1 -1
  129. package/dist/sds-textfield/sds-textfield.styles.d.ts.map +1 -1
  130. package/dist/sds-textfield/sds-textfield.styles.js +11 -0
  131. package/dist/sds-textfield/sds-textfield.styles.js.map +1 -1
  132. package/dist/sds-tooltip/assets/tooltip-styles.d.ts.map +1 -1
  133. package/dist/sds-tooltip/assets/tooltip-styles.js +1 -1
  134. package/dist/sds-tooltip/assets/tooltip-styles.js.map +1 -1
  135. package/dist/sds-tooltip/sds-tooltip.d.ts.map +1 -1
  136. package/dist/sds-tooltip/sds-tooltip.js +1 -1
  137. package/dist/sds-tooltip/sds-tooltip.js.map +1 -1
  138. package/dist/shared/assets/positioning.js +1 -1
  139. package/dist/sheriff-web-components.js +1103 -371
  140. package/dist/skill/SKILL.md +61 -50
  141. package/dist/skill/references/components.md +91 -3
  142. package/dist/skill/references/react-wrappers.md +28 -19
  143. package/dist/utils/file-validation.d.ts +31 -0
  144. package/dist/utils/file-validation.d.ts.map +1 -0
  145. package/dist/utils/file-validation.js +57 -0
  146. package/dist/utils/file-validation.js.map +1 -0
  147. package/dist/utils/format-bytes.d.ts +6 -0
  148. package/dist/utils/format-bytes.d.ts.map +1 -0
  149. package/dist/utils/format-bytes.js +13 -0
  150. package/dist/utils/format-bytes.js.map +1 -0
  151. package/dist/utils/video-thumbnail.d.ts +8 -0
  152. package/dist/utils/video-thumbnail.d.ts.map +1 -0
  153. package/dist/utils/video-thumbnail.js +77 -0
  154. package/dist/utils/video-thumbnail.js.map +1 -0
  155. package/dist/version.d.ts +1 -1
  156. package/dist/version.js +1 -1
  157. package/dist/version.js.map +1 -1
  158. package/package.json +27 -7
  159. package/web-types.json +3437 -1120
@@ -2,10 +2,11 @@
2
2
  name: sheriff-web-components
3
3
  description: >
4
4
  Assists developers using @politiet/sheriff-web-components made of Lit-based custom
5
- elements with the sds-* prefix. Covers all 29 components (alert, button, card,
5
+ elements with the sds-* prefix. Covers all 33 components (alert, button, card,
6
6
  checkbox, checkbox-group, chip, details, details-group, dialog,
7
- environment-banner, heading, icon, internal-header, loader, notification,
8
- popover, process, process-step, radio, radio-group, select, stepper,
7
+ environment-banner, file-upload, file-upload-item, file-upload-file-list,
8
+ heading, icon, internal-header, loader, notification,
9
+ popover, process, process-step, radio, radio-group, search, select, stepper,
9
10
  stepper-step, switch, tag, text, textarea, textfield, tooltip), React
10
11
  wrappers, framework type definitions, form-associated elements, slots,
11
12
  events, and data-palette theming. Use when the user references sds-*
@@ -19,11 +20,11 @@ description: >
19
20
 
20
21
  When looking up component details not covered here, read the source directly:
21
22
 
22
- - Most components: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.ts`
23
- - Exception components (alert, button, dialog, environment-banner, heading, icon, internal-header, select, text, textfield): `packages/sheriff/web-components/src/sds-{name}/sds-{name}.component.ts`
24
- - Styles and CSS custom properties: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.styles.ts`
25
- - Usage examples: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.stories.ts`
26
- - Documentation: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.mdx`, `variables.mdx` (CSS variables), `guidelines.mdx` (design guidance)
23
+ - Most components: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.ts`
24
+ - Exception components (alert, button, dialog, environment-banner, heading, icon, internal-header, select, text, textfield): `packages/sheriff/web-components/src/sds-{name}/sds-{name}.component.ts`
25
+ - Styles and CSS custom properties: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.styles.ts`
26
+ - Usage examples: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.stories.ts`
27
+ - Documentation: `packages/sheriff/web-components/src/sds-{name}/sds-{name}.mdx`, `variables.mdx` (CSS variables), `guidelines.mdx` (design guidance)
27
28
 
28
29
  Inside an installed package the same files live under `node_modules/@politiet/sheriff-web-components/dist/sds-{name}/`.
29
30
 
@@ -78,37 +79,41 @@ Every component has its own subpath export matching its tag name (`./sds-alert`
78
79
 
79
80
  All components use the `sds-` prefix. Sizes are `"lg"` | `"md"` | `"sm"`, default `"md"` — **except `sds-button`, which defaults to `"lg"`**. `sds-heading` and `sds-text` use the wider `"xl" | "lg" | "md" | "sm" | "xs"` scale.
80
81
 
81
- | Component | Description | Key Props |
82
- | ------------------------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------ |
83
- | `sds-alert` | Contextual alert banner | `variant`, `size`, `removable-label`, `data-palette` |
84
- | `sds-button` | Button or link-as-button | `variant`, `size`, `type`, `disabled`, `loading`, `icon`, `inverted`, `label`, `href`, `command` |
85
- | `sds-card` | Content container; link card when `link` set | `link` |
86
- | `sds-checkbox` | Single checkbox | `label`, `name`, `value`, `checked`, `error`, `help-text` |
87
- | `sds-checkbox-group` | Grouped checkboxes with legend | `legend`, `error-message`, `help-text` |
88
- | `sds-chip` | Selectable or removable chip/pill | `variant`, `checked`, `name`, `value`, `size` |
89
- | `sds-details` | Expandable accordion | `open`, `standalone`, `name` (exclusive group) |
90
- | `sds-details-group` | Wraps `sds-details` into one accordion group | `outlined`, `name` (makes the group exclusive) |
91
- | `sds-dialog` | Modal/dialog wrapping native `<dialog>` | `open`, `modal`, `closedby`, `close-button-label` |
92
- | `sds-environment-banner` | Environment/build strip for internal apps | `env`, `build` |
93
- | `sds-heading` | Semantic heading | `level`, `size` |
94
- | `sds-icon` | Dynamic SVG icon | `name`, `size`, `label`, `color` |
95
- | `sds-internal-header` | App header for internal (non-public) apps | `app-name`, `home-href` |
96
- | `sds-loader` | Loading spinner | `size`, `contrast` |
97
- | `sds-notification` | **Deprecated** — use `sds-alert` | `variant`, `removable-label` |
98
- | `sds-popover` | Anchored popover built on the Popover API | `placement`, `autoplacement`, `overscroll` |
99
- | `sds-process` | Timeline / process list | `heading-level` |
100
- | `sds-process-step` | Individual step in a process | `heading`, `timestamp` |
101
- | `sds-radio` | Single radio button | `label`, `name`, `value`, `checked`, `hide-label` |
102
- | `sds-radio-group` | Grouped radios with legend | `legend`, `direction`, `error-message` |
103
- | `sds-select` | Dropdown select | `label`, `name`, `default-value`, `error-message` |
104
- | `sds-stepper` | Step indicator for multi-step flows | `orientation` |
105
- | `sds-stepper-step` | Individual step in a stepper | `complete`, `interactive`, `active` |
106
- | `sds-switch` | Toggle switch | `label`, `name`, `checked`, `reverse`, `hide-label` |
107
- | `sds-tag` | Non-interactive label/badge | `size`, `data-palette` |
108
- | `sds-text` | Text container with typography | `size`, `strong`, `subtle`, `tall` |
109
- | `sds-textarea` | Multi-line text input | `label`, `name`, `counter`, `autosize`, `error-message` |
110
- | `sds-textfield` | Single-line text input | `label`, `name`, `type`, `error-message` |
111
- | `sds-tooltip` | Hover/focus tooltip anchored to a trigger | `id` (required), `placement` |
82
+ | Component | Description | Key Props |
83
+ | --------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------ |
84
+ | `sds-alert` | Contextual alert banner | `variant`, `size`, `removable-label`, `data-palette` |
85
+ | `sds-button` | Button or link-as-button | `variant`, `size`, `type`, `disabled`, `loading`, `icon`, `inverted`, `label`, `href`, `command` |
86
+ | `sds-card` | Content container; link card when `link` set | `link` |
87
+ | `sds-checkbox` | Single checkbox | `label`, `name`, `value`, `checked`, `error`, `help-text` |
88
+ | `sds-checkbox-group` | Grouped checkboxes with legend | `legend`, `error-message`, `help-text` |
89
+ | `sds-chip` | Selectable or removable chip/pill | `variant`, `checked`, `name`, `value`, `size` |
90
+ | `sds-details` | Expandable accordion | `open`, `standalone`, `name` (exclusive group) |
91
+ | `sds-details-group` | Wraps `sds-details` into one accordion group | `outlined`, `name` (makes the group exclusive) |
92
+ | `sds-dialog` | Modal/dialog wrapping native `<dialog>` | `open`, `modal`, `closedby`, `close-button-label` |
93
+ | `sds-environment-banner` | Environment/build strip for internal apps | `env`, `build` |
94
+ | `sds-file-upload` | File picker with drag and drop (beta) | `name`, `accept`, `multiple`, `max-size-in-bytes`, `error-message`, `hide-file-list` |
95
+ | `sds-file-upload-item` | One file row with remove button and progress | `file`, `size-in-bytes`, `uploaded-percent`, `error-message`, `for` |
96
+ | `sds-file-upload-file-list` | List container for `sds-file-upload-item` | — |
97
+ | `sds-heading` | Semantic heading | `level`, `size` |
98
+ | `sds-icon` | Dynamic SVG icon | `name`, `size`, `label`, `color` |
99
+ | `sds-internal-header` | App header for internal (non-public) apps | `app-name`, `home-href` |
100
+ | `sds-loader` | Loading spinner | `size`, `contrast` |
101
+ | `sds-notification` | **Deprecated** — use `sds-alert` | `variant`, `removable-label` |
102
+ | `sds-popover` | Anchored popover built on the Popover API | `placement`, `autoplacement`, `overscroll` |
103
+ | `sds-process` | Timeline / process list | `heading-level` |
104
+ | `sds-process-step` | Individual step in a process | `heading`, `timestamp` |
105
+ | `sds-radio` | Single radio button | `label`, `name`, `value`, `checked`, `hide-label` |
106
+ | `sds-radio-group` | Grouped radios with legend | `legend`, `direction`, `error-message` |
107
+ | `sds-search` | Search field with search button | `label`, `hide-label`, `name`, `button-variant` |
108
+ | `sds-select` | Dropdown select | `label`, `name`, `default-value`, `error-message` |
109
+ | `sds-stepper` | Step indicator for multi-step flows | `orientation` |
110
+ | `sds-stepper-step` | Individual step in a stepper | `complete`, `interactive`, `active` |
111
+ | `sds-switch` | Toggle switch | `label`, `name`, `checked`, `reverse`, `hide-label` |
112
+ | `sds-tag` | Non-interactive label/badge | `size`, `data-palette` |
113
+ | `sds-text` | Text container with typography | `size`, `strong`, `subtle`, `tall` |
114
+ | `sds-textarea` | Multi-line text input | `label`, `name`, `counter`, `autosize`, `error-message` |
115
+ | `sds-textfield` | Single-line text input | `label`, `hide-label`, `name`, `type`, `error-message` |
116
+ | `sds-tooltip` | Hover/focus tooltip anchored to a trigger | `id` (required), `placement` |
112
117
 
113
118
  For full component API (all props, slots, events, code examples), see `references/components.md`.
114
119
 
@@ -116,15 +121,15 @@ For full component API (all props, slots, events, code examples), see `reference
116
121
 
117
122
  `data-palette` works on **every** component and is inherited from any ancestor container, with three exceptions:
118
123
 
119
- - `sds-alert`: `data-palette` is a typed component property, so it must be set **on the alert itself** (it does not inherit from a container). Accepted values are only `info` | `success` | `attention` | `danger`; it also selects the alert's status icon.
120
- - `sds-tooltip` and `sds-environment-banner`: fixed colors, palette has no effect. The environment banner colors itself from `env`.
121
- - `sds-loader`: the palette variant is designed for a loader sitting **on** a palette-colored surface. Add `contrast` to flip it back for a plain background.
124
+ - `sds-alert`: `data-palette` is a typed component property, so it must be set **on the alert itself** (it does not inherit from a container). Accepted values are only `info` | `success` | `attention` | `danger`; it also selects the alert's status icon.
125
+ - `sds-tooltip` and `sds-environment-banner`: fixed colors, palette has no effect. The environment banner colors itself from `env`.
126
+ - `sds-loader`: the palette variant is designed for a loader sitting **on** a palette-colored surface. Add `contrast` to flip it back for a plain background.
122
127
 
123
128
  `sds-tag` additionally accepts `data-palette="danger-action"` (deep red), which is not a general framework palette.
124
129
 
125
130
  ## Form-Associated Components
126
131
 
127
- `sds-button`, `sds-checkbox`, `sds-chip`, `sds-radio`, `sds-select`, `sds-switch`, `sds-textarea`, and `sds-textfield` are form-associated: they participate in `<form>` submission, validation, and reset without extra wiring. Set both `name` and `value` for the value to be submitted. `sds-switch` submits `"on"` / `"off"`.
132
+ `sds-button`, `sds-checkbox`, `sds-chip`, `sds-file-upload`, `sds-radio`, `sds-search`, `sds-select`, `sds-switch`, `sds-textarea`, and `sds-textfield` are form-associated: they participate in `<form>` submission, validation, and reset without extra wiring. Set both `name` and `value` for the value to be submitted. `sds-switch` submits `"on"` / `"off"`. `sds-file-upload` only needs `name` and submits the selected files, and it has no `readonly`.
128
133
 
129
134
  Shared props from the form-control mixin: `name`, `disabled`, `readonly`. In a `sds-checkbox-group` / `sds-radio-group`, setting `disabled` or `readonly` on the group propagates to children unless the child sets it explicitly.
130
135
 
@@ -140,6 +145,8 @@ Shared props from the form-control mixin: `name`, `disabled`, `readonly`. In a `
140
145
 
141
146
  The attribute is the default slot content, so use one or the other — not both.
142
147
 
148
+ `sds-file-upload` has **only** the `label` slot, no `label` attribute.
149
+
143
150
  ## React Wrappers
144
151
 
145
152
  React versions before v19 do not natively handle web component events. Use the provided wrappers from `@politiet/sheriff-web-components/react-wrappers`:
@@ -159,7 +166,10 @@ function MyForm() {
159
166
  }
160
167
  ```
161
168
 
162
- React 19+ supports custom elements natively — can use web components directly without wrappers.
169
+ React 19+ supports custom elements natively and can use web components directly without wrappers.
170
+ Event props on raw elements must match the event name exactly: `onclose`, `oncancel`, `ontoggle`, `onclick` (lowercase).
171
+ React treats camelCase `onClose`/`onCancel`/`onToggle` as its own synthetic events and never fires them on custom elements.
172
+ hey are only typed for backwards compatibility and marked deprecated.
163
173
 
164
174
  For the full list of available wrappers and event mappings, see `references/react-wrappers.md`.
165
175
 
@@ -250,9 +260,10 @@ Put the accessible name on the button via `label` (it becomes `aria-label` on th
250
260
 
251
261
  ## Error Handling
252
262
 
253
- - **Component does not render or render weird**: Verify the CSS framework is imported. This is needed for CSS custom properties.
254
- - **Events do not fire in React (pre-v19)**: Confirm React wrappers are used instead of web components.
255
- - **Event listener never fires**: Check the exact event name in `references/components.md`. `sds-details`/`sds-popover`/`sds-tooltip` dispatch `toggle` (not `sds-toggle`) and `sds-dialog` dispatches `close` (not `sds-close`, which is deprecated).
256
- - **Icons do not load**: Check the icon name format (`{name}-{source}-{variant}`) and network access to jsDelivr CDN. If in secure/isolated network, use Artifactory. If needed, see `@politiet/sheriff-icons` for icon skill.
257
- - **Form values not submitted**: Ensure the component has both `name` and `value` attributes set.
258
- - **`data-palette` seems ignored**: On `sds-alert` it must be set on the element itself, and only `info`/`success`/`attention`/`danger` are valid. `sds-tooltip` and `sds-environment-banner` do not support palettes.
263
+ - **Component does not render or render weird**: Verify the CSS framework is imported. This is needed for CSS custom properties.
264
+ - **Events do not fire in React (pre-v19)**: Confirm React wrappers are used instead of web components.
265
+ - **`onClose`/`onToggle` never fires in React 19**: On raw `sds-*` elements use lowercase `onclose`/`ontoggle`/`oncancel`, or use the wrapper, where camelCase is correct. The same applies to `click` on `sds-stepper-step`: use `onclick` on the raw element, `onClick` on `SDSStepperStepWrapper`.
266
+ - **Event listener never fires**: Check the exact event name in `references/components.md`. `sds-details`/`sds-popover`/`sds-tooltip` dispatch `toggle` (not `sds-toggle`) and `sds-dialog` dispatches `close` (not `sds-close`, which is deprecated).
267
+ - **Icons do not load**: Check the icon name format (`{name}-{source}-{variant}`) and network access to jsDelivr CDN. If in secure/isolated network, use Artifactory. If needed, see `@politiet/sheriff-icons` for icon skill.
268
+ - **Form values not submitted**: Ensure the component has both `name` and `value` attributes set.
269
+ - **`data-palette` seems ignored**: On `sds-alert` it must be set on the element itself, and only `info`/`success`/`attention`/`danger` are valid. `sds-tooltip` and `sds-environment-banner` do not support palettes.
@@ -278,7 +278,7 @@ Modal/dialog wrapping native `<dialog>`.
278
278
 
279
279
  **Props:** `open`, `modal`, `closedby` (`"none"` | `"closerequest"` | `"any"`, default `"closerequest"`), `close-button-label` (default `"Lukk dialogvindu"`, empty string hides the built-in close button)
280
280
  **Slots:** `header`, default (body), `footer`
281
- **Events:** `close`. `sds-close` still fires but is **deprecated** — listening for it logs a console warning and it is removed in the next major.
281
+ **Events:** `close`, `cancel` (fires before the dialog closes via ESC, outside click or `requestClose()`; call `preventDefault()` to keep it open). `sds-close` still fires but is **deprecated** — listening for it logs a console warning and it is removed in the next major.
282
282
  **Methods:** `show()`, `showModal()` (also sets `modal`), `close()`, `requestClose()`
283
283
  **Notes:**
284
284
 
@@ -286,6 +286,7 @@ Modal/dialog wrapping native `<dialog>`.
286
286
  - `command="show-modal"` requires `commandfor="{dialog id}"` and wires `aria-haspopup`/`aria-expanded`/`aria-controls` on the trigger.
287
287
  - `closedby="any"` includes a Safari polyfill for outside clicks.
288
288
  - An element in the default slot carrying `autofocus` receives focus once the open animation ends.
289
+ - `close` bubbles, so a listener on an outer dialog also receives it from a nested dialog. Check `event.target === event.currentTarget` when nesting. `cancel` does not bubble, matching the native event.
289
290
 
290
291
  ---
291
292
 
@@ -303,6 +304,68 @@ Thin strip identifying the running environment and build, for internal apps. Sou
303
304
 
304
305
  ---
305
306
 
307
+ ## sds-file-upload
308
+
309
+ File picker with drag and drop and a built-in list of selected files. Form-associated. **Beta** — larger API changes may still happen.
310
+
311
+ ```html
312
+ <sds-file-upload name="attachments" multiple accept=".pdf,image/*" max-size-in-bytes="10485760" file-limitation-text="PDF eller bilde, maks 10 MB">
313
+ <span slot="label">Vedlegg</span>
314
+ </sds-file-upload>
315
+
316
+ <sds-file-upload name="cv" variant="compact" size="sm" required>
317
+ <span slot="label">CV</span>
318
+ </sds-file-upload>
319
+ ```
320
+
321
+ **Props:** `name`, `label`, `accept` (same syntax as native: extensions, `image/*`, exact MIME types), `multiple`, `required`, `disabled`, `max-size-in-bytes`, `help-text`, `error-message`, `file-limitation-text`, `hide-file-list`, `variant` (`"default"` | `"compact"`), `size`
322
+ **Slots:** `label` (alternative to the `label` attribute, wins if both are set). Without a label, the input is named "Velg fil" / "Velg filer".
323
+ **Events:**
324
+
325
+ - `input`, `change` — the accepted selection changed (picked, dropped, removed or cleared). `input` fires right before `change`.
326
+ - `cancel` — the user closed the file picker without choosing anything. Not cancelable.
327
+ - `focus`, `blur`
328
+ - `sds-reject` — detail `{ rejected: { file, reason, errorCode }[] }`, `errorCode` is `"type-mismatch"` | `"too-large"`. `reason` is a Norwegian message.
329
+ - `sds-preview-ready` — detail `{ file, url }`, fired when an image or video thumbnail is ready.
330
+
331
+ **Properties and methods:** `files` (`File[]`, read-only), `rejectedFiles` (`{ file, reason, errorCode }[]`, read-only), `value` (`C:\fakepath\…` of the first file; only `""` may be assigned, which clears, other values throw), `removeFile(file)`, `clear()`, `dismissRejected(file)`, `getPreviewUrl(file)`, `focus()`, `blur()`, `checkValidity()`, `reportValidity()`, `setCustomValidity(message)`
332
+ **Notes:**
333
+
334
+ - Behaves like `<input type="file">`: a single-file selection always replaces the previous one, even if the new file is rejected. The same goes for `multiple`: every new selection replaces the previous files. Duplicates within a selection (same name, size and last modified) are dropped.
335
+ - Submits every accepted file under `name` as `FormData`, or an empty `File` when nothing is selected, like native. There is no `value` attribute to set, and no `readonly`.
336
+ - Constraint errors (`required`) use the native validation bubble. `setCustomValidity(message)` sets `error-message`.
337
+ - Don't call `URL.revokeObjectURL` on URLs from `getPreviewUrl()` or `sds-preview-ready` — the component cleans them up.
338
+ - Rejected files show in the built-in list with "Feil ved opplasting" and the `reason` as the error message.
339
+
340
+ ### sds-file-upload-item
341
+
342
+ One file row: icon or thumbnail, name, size, a remove button and optional upload progress. Used by the built-in list, and for building your own list.
343
+
344
+ **Props:** `file` (file name), `size-in-bytes`, `error-message`, `preview` (image URL), `uploaded-percent` (shows a progress bar while set), `uploaded-size-in-bytes`, `disabled`, `for` (`id` of the owning `sds-file-upload`)
345
+ **Slots:** `actions-end` (extra buttons, placed before the remove button)
346
+ **Events:** `sds-remove` (cancelable) — fired when the remove button is pressed.
347
+ **Note:** Without `for`, listen for `sds-remove` and call `removeFile(file)` (or `dismissRejected(file)` for an item with `error-message`) on the upload yourself. With `for`, the item does that and removes itself from the DOM; `preventDefault()` on `sds-remove` cancels both. `disabled` doesn't reach buttons in `actions-end`, so disable those yourself, and include the file name in their labels (`label="Last ned rapport.pdf"`).
348
+
349
+ ### sds-file-upload-file-list
350
+
351
+ List container (`role="list"`) for `sds-file-upload-item` children. No props or events.
352
+
353
+ **Slots:** default (`sds-file-upload-item` children)
354
+
355
+ Custom file list — set `hide-file-list` on the upload and render the items yourself, for example to show upload progress:
356
+
357
+ ```html
358
+ <sds-file-upload id="docs" name="docs" multiple hide-file-list>
359
+ <span slot="label">Dokumenter</span>
360
+ </sds-file-upload>
361
+
362
+ <sds-file-upload-file-list>
363
+ <sds-file-upload-item for="docs" file="rapport.pdf" size-in-bytes="482133" uploaded-percent="40"></sds-file-upload-item>
364
+ </sds-file-upload-file-list>
365
+ ```
366
+
367
+ ---
368
+
306
369
  ## sds-heading
307
370
 
308
371
  Semantic heading. Source: `sds-heading.component.ts`.
@@ -512,6 +575,28 @@ Groups radio buttons with shared legend and validation.
512
575
 
513
576
  ---
514
577
 
578
+ ## sds-search
579
+
580
+ Search field: an `sds-textfield` (`type="search"`) and an icon button, shoulder to shoulder.
581
+
582
+ ```html
583
+ <search aria-label="Registersøk">
584
+ <form action="/sok">
585
+ <sds-search label="Søk i registeret" hide-label name="q"></sds-search>
586
+ </form>
587
+ </search>
588
+
589
+ <sds-search label="Søk" button-variant="secondary"></sds-search>
590
+ ```
591
+
592
+ **Props:** `label`, `hide-label`, `name`, `value`, `placeholder`, `disabled`, `readonly`, `error-message`, `help-text`, `size`, `button-label` (default `"Søk"`), `button-variant`, `required`, `minlength`, `maxlength`, `pattern`, `autocomplete` (forwarded to the inner input)
593
+ **Slots:** `label` (rich label content, takes precedence over the `label` prop), `button` (replaces the default icon button; the slotted control owns its own click, `disabled` and form submission)
594
+ **Events:** `search` (detail `{ value }`, cancelable), `input`, `change`, `select`, `keydown` (cancelling Enter prevents `search`), `focus`, `blur`, `invalid`. `focus`/`blur` fire when focus enters or leaves the component as a whole, not when it moves between the field and the button.
595
+ **Methods:** `focus()`, `blur()`, `select()`, `setSelectionRange()`, `selectionStart`/`selectionEnd`, `checkValidity()`, `reportValidity()`, `setCustomValidity()`, plus `validity`, `validationMessage`, `willValidate`.
596
+ **Note:** Clicking the button or pressing Enter validates the field (unless the form has `novalidate`), emits `search` if it is valid, then submits the surrounding form unless `search` was cancelled with `preventDefault()`. Always set `label`; use `hide-label` to hide it visually. The placeholder is not an accessible name. In React/Preact JSX without the wrapper, the listener must be lowercase: `onsearch`.
597
+
598
+ ---
599
+
515
600
  ## sds-select
516
601
 
517
602
  Dropdown select using native `<option>`/`<optgroup>` elements.
@@ -574,7 +659,10 @@ Step indicator for multi-step flows. Contains `sds-stepper-step` children. Gets
574
659
 
575
660
  **Props:** `complete`, `interactive` (clickable), `active` (current step)
576
661
  **Slots:** default (label text)
662
+ **Events:** `click` (native `MouseEvent`)
577
663
  **Note:** Step numbers are generated automatically from the child order.
664
+ **Note:** `click` fires on every step, interactive or not, and also when an `interactive` step is activated with Enter or Space. Inside an `sds-stepper` the stepper's `change` fires first, so the step is already active by the time `click` arrives. The event bubbles, so a listener on `sds-stepper` sees it with `target` set to the clicked `sds-stepper-step`.
665
+ **React/Preact:** On a raw `<sds-stepper-step>` write the listener lowercase, `onclick`. CamelCase `onClick` is React's own synthetic event and is typed as deprecated. With `SDSStepperStepWrapper` the prop is `onClick`.
578
666
 
579
667
  ---
580
668
 
@@ -665,7 +753,7 @@ Single-line text input.
665
753
  <sds-textfield label="ID" name="id" value="12345" readonly></sds-textfield>
666
754
  ```
667
755
 
668
- **Props:** `label`, `name`, `value`, `type` (HTML input types), `placeholder`, `disabled`, `readonly`, `required`, `autocomplete`, `pattern`, `minlength`, `maxlength`, `min`, `max`, `help-text`, `error-message`, `size`
756
+ **Props:** `label`, `hide-label`, `name`, `value`, `type` (HTML input types), `placeholder`, `disabled`, `readonly`, `required`, `autocomplete`, `pattern`, `minlength`, `maxlength`, `min`, `max`, `help-text`, `error-message`, `size`
669
757
  **Slots:** `after` (e.g., icon), `label` (alternative to the `label` attribute)
670
758
  **Events:** `input`, `change`, `select`, `focus`, `blur`, `keydown`
671
759
  **Note:** When both `error-message` and `help-text` are set, `aria-describedby` lists the error first, then the help text. `readonly` renders a lock icon next to the label.
@@ -689,5 +777,5 @@ Hover/focus tooltip. The tooltip is a sibling of its trigger, and the trigger po
689
777
 
690
778
  **Props:** `id` (**required** — the value the trigger's `aria-describedby` references), `placement` (`"top"` | `"bottom"` | `"left"` | `"right"`, each also with `-start` / `-end`, or `"none"`; default `"top"`)
691
779
  **Slots:** default (tooltip text — keep it to plain text)
692
- **Events:** `toggle` (detail: the trigger element that opened it)
780
+ **Events:** `toggle` (detail: the trigger element that opened it). The native popover `ToggleEvent` also fires on the host.
693
781
  **Notes:** Sets `role="tooltip"` and `popover="manual"` on itself. Multiple triggers may reference the same tooltip. Give the trigger and tooltip a `position: relative` wrapper. `data-palette` has no effect — tooltip colours are fixed.
@@ -7,7 +7,7 @@ React 19+ supports custom elements natively and may use web components directly
7
7
 
8
8
  ## Available Wrappers
9
9
 
10
- All 29 components have a wrapper:
10
+ All 33 components have a wrapper:
11
11
 
12
12
  ```tsx
13
13
  import {
@@ -21,6 +21,9 @@ import {
21
21
  SDSDetailsGroupWrapper,
22
22
  SDSDialogWrapper,
23
23
  SDSEnvironmentBannerWrapper,
24
+ SDSFileUploadWrapper,
25
+ SDSFileUploadItemWrapper,
26
+ SDSFileUploadFileListWrapper,
24
27
  SDSHeadingWrapper,
25
28
  SDSIconWrapper,
26
29
  SDSInternalHeaderWrapper,
@@ -31,6 +34,7 @@ import {
31
34
  SDSProcessStepWrapper,
32
35
  SDSRadioWrapper,
33
36
  SDSRadioGroupWrapper,
37
+ SDSSearchWrapper,
34
38
  SDSSelectWrapper,
35
39
  SDSStepperWrapper,
36
40
  SDSStepperStepWrapper,
@@ -67,25 +71,30 @@ function MyForm() {
67
71
 
68
72
  Handler names follow React idioms and **drop the `sds-` prefix** — the DOM event `sds-remove` becomes `onRemove`, not `onSdsRemove`.
69
73
 
70
- | Wrapper | React prop | DOM event |
71
- | ------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- |
72
- | `SDSAlertWrapper`, `SDSNotificationWrapper` | `onRemove` | `sds-remove` |
73
- | `SDSButtonWrapper` | `onClick`, `onSubmit`, `onReset` | `click`, `submit`, `reset` |
74
- | `SDSCheckboxWrapper`, `SDSRadioWrapper` | `onChange`, `onFocus`, `onBlur` | `change`, `focus`, `blur` |
75
- | `SDSCheckboxGroupWrapper`, `SDSRadioGroupWrapper` | `onUpdate`, `onChange` | `sds-update`, `change` |
76
- | `SDSChipWrapper` | `onChange`, `onRemove` | `change`, `sds-remove` |
77
- | `SDSDetailsWrapper`, `SDSTooltipWrapper` | `onToggle` | `toggle` |
78
- | `SDSPopoverWrapper` | `onToggle` | `toggle` (native `ToggleEvent`) |
79
- | `SDSDialogWrapper` | `onClose` | `close` |
80
- | `SDSSelectWrapper`, `SDSSwitchWrapper` | `onChange`, `onFocus`, `onBlur` | `change`, `focus`, `blur` |
81
- | `SDSStepperWrapper` | `onChange` | `change` (detail `{ index }`) |
82
- | `SDSTextFieldWrapper` | `onInput`, `onChange`, `onSelect`, `onFocus`, `onBlur`, `onKeyDown` | `input`, **`input`**, `select`, `focus`, `blur`, `keydown` |
83
- | `SDSTextAreaWrapper` | `onInput`, `onChange`, `onSelect`, `onFocus`, `onBlur` | `input`, **`input`**, `select`, `focus`, `blur` |
74
+ | Wrapper | React prop | DOM event |
75
+ | ------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
76
+ | `SDSAlertWrapper`, `SDSNotificationWrapper` | `onRemove` | `sds-remove` |
77
+ | `SDSButtonWrapper` | `onClick`, `onSubmit`, `onReset` | `click`, `submit`, `reset` |
78
+ | `SDSCheckboxWrapper`, `SDSRadioWrapper` | `onChange`, `onFocus`, `onBlur` | `change`, `focus`, `blur` |
79
+ | `SDSCheckboxGroupWrapper`, `SDSRadioGroupWrapper` | `onUpdate`, `onChange` | `sds-update`, `change` |
80
+ | `SDSChipWrapper` | `onChange`, `onRemove` | `change`, `sds-remove` |
81
+ | `SDSDetailsWrapper`, `SDSTooltipWrapper` | `onToggle` | `toggle` |
82
+ | `SDSFileUploadWrapper` | `onInput`, `onChange`, `onCancel`, `onFocus`, `onBlur`, `onReject`, `onPreviewReady` | `input`, `change`, `cancel`, `focus`, `blur`, `sds-reject`, `sds-preview-ready` |
83
+ | `SDSFileUploadItemWrapper` | `onRemove` | `sds-remove` |
84
+ | `SDSPopoverWrapper` | `onToggle` | `toggle` (native `ToggleEvent`) |
85
+ | `SDSDialogWrapper` | `onClose`, `onCancel` | `close`, `cancel` |
86
+ | `SDSSearchWrapper` | `onSearch`, `onInput`, `onChange`, `onSelect`, `onFocus`, `onBlur`, `onKeyDown`, `onInvalid` | `search` (detail `{ value }`), `input`, **`input`**, `select`, `focus`, `blur`, `keydown`, `invalid` |
87
+ | `SDSSelectWrapper`, `SDSSwitchWrapper` | `onChange`, `onFocus`, `onBlur` | `change`, `focus`, `blur` |
88
+ | `SDSStepperWrapper` | `onChange` | `change` (detail `{ index }`) |
89
+ | `SDSStepperStepWrapper` | `onClick` | `click` (native `MouseEvent`) |
90
+ | `SDSTextFieldWrapper` | `onInput`, `onChange`, `onSelect`, `onFocus`, `onBlur`, `onKeyDown` | `input`, **`input`**, `select`, `focus`, `blur`, `keydown` |
91
+ | `SDSTextAreaWrapper` | `onInput`, `onChange`, `onSelect`, `onFocus`, `onBlur` | `input`, **`input`**, `select`, `focus`, `blur` |
84
92
 
85
- Wrappers with no event mappings (presentational only): `SDSCardWrapper`, `SDSDetailsGroupWrapper`, `SDSEnvironmentBannerWrapper`, `SDSHeadingWrapper`, `SDSIconWrapper`, `SDSInternalHeaderWrapper`, `SDSLoaderWrapper`, `SDSProcessWrapper`, `SDSProcessStepWrapper`, `SDSStepperStepWrapper`, `SDSTagWrapper`, `SDSTextWrapper`.
93
+ Wrappers with no event mappings (presentational only): `SDSCardWrapper`, `SDSDetailsGroupWrapper`, `SDSEnvironmentBannerWrapper`, `SDSFileUploadFileListWrapper`, `SDSHeadingWrapper`, `SDSIconWrapper`, `SDSInternalHeaderWrapper`, `SDSLoaderWrapper`, `SDSProcessWrapper`, `SDSProcessStepWrapper`, `SDSTagWrapper`, `SDSTextWrapper`.
86
94
 
87
95
  ## Gotchas
88
96
 
89
- - On `SDSTextFieldWrapper` and `SDSTextAreaWrapper`, **`onChange` is wired to the `input` event**, so it fires on every keystroke like React's own `onChange` — not on blur like the native DOM `change` event. Use `onBlur` if you need commit-on-blur semantics.
90
- - `onKeyDown` (capital D) is the correct casing on `SDSTextFieldWrapper`; there is no `onKeydown`.
91
- - Wrappers forward all other props straight through as attributes, so kebab-case component attributes (`help-text`, `error-message`, `removable-label`, `app-name`) keep their kebab-case names in JSX.
97
+ - On `SDSTextFieldWrapper`, `SDSTextAreaWrapper` and `SDSSearchWrapper`, **`onChange` is wired to the `input` event**, so it fires on every keystroke like React's own `onChange` — not on blur like the native DOM `change` event. Use `onBlur` if you need commit-on-blur semantics.
98
+ - `onKeyDown` (capital D) is the correct casing on `SDSTextFieldWrapper` and `SDSSearchWrapper`; there is no `onKeydown`.
99
+ - `SDSStepperStepWrapper` maps `onClick` to the step's native `click`. On a raw `<sds-stepper-step>` write it lowercase as `onclick`; camelCase `onClick` there is React's own synthetic event and is typed as deprecated.
100
+ - Wrappers forward all other props straight through as attributes, so kebab-case component attributes (`help-text`, `error-message`, `removable-label`, `app-name`) keep their kebab-case names in JSX.
@@ -0,0 +1,31 @@
1
+ export interface SDSRejectedFile {
2
+ file: File;
3
+ reason: string;
4
+ errorCode: 'type-mismatch' | 'too-large';
5
+ }
6
+ export interface PartitionFilesOptions {
7
+ accept?: string;
8
+ maxSizeInBytes?: number;
9
+ multiple?: boolean;
10
+ }
11
+ /**
12
+ * Mirrors the native file input's `accept` matching: a token is either an extension (`.pdf`),
13
+ * a MIME wildcard (`image/*`) or an exact MIME type. Matches if any token matches.
14
+ */
15
+ export declare function matchesAccept(file: File, accept?: string): boolean;
16
+ /**
17
+ * Splits an incoming selection into files that pass validation and files that don't, with a reason.
18
+ * Like the native file input, every selection replaces the current one. Duplicates within the
19
+ * selection (same name, size and last modified) are silently dropped rather than rejected. In
20
+ * single-file mode only the first incoming file is considered.
21
+ */
22
+ export declare function partitionFiles(incoming: File[], options: PartitionFilesOptions): {
23
+ accepted: File[];
24
+ rejected: SDSRejectedFile[];
25
+ };
26
+ /**
27
+ * Identity check for files that survives the copies browsers hand out across separate
28
+ * `change`/`drop` events, where reference equality no longer holds.
29
+ */
30
+ export declare function isSameFile(a: File, b: File): boolean;
31
+ //# sourceMappingURL=file-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-validation.d.ts","sourceRoot":"","sources":["../../src/utils/file-validation.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,eAAe,GAAG,WAAW,CAAC;CACzC;AAED,MAAM,WAAW,qBAAqB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAgBlE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAAC,QAAQ,EAAE,eAAe,EAAE,CAAA;CAAE,CAoBlI;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,OAAO,CAEpD"}
@@ -0,0 +1,57 @@
1
+ import { formatBytes } from './format-bytes';
2
+ /**
3
+ * Mirrors the native file input's `accept` matching: a token is either an extension (`.pdf`),
4
+ * a MIME wildcard (`image/*`) or an exact MIME type. Matches if any token matches.
5
+ */
6
+ export function matchesAccept(file, accept) {
7
+ if (!accept)
8
+ return true;
9
+ const tokens = accept
10
+ .split(',')
11
+ .map((token) => token.trim().toLowerCase())
12
+ .filter(Boolean);
13
+ if (tokens.length === 0)
14
+ return true;
15
+ const fileName = file.name.toLowerCase();
16
+ const mimeType = file.type.toLowerCase();
17
+ return tokens.some((token) => {
18
+ if (token.startsWith('.'))
19
+ return fileName.endsWith(token);
20
+ if (token.endsWith('/*'))
21
+ return mimeType.startsWith(token.slice(0, -1));
22
+ return mimeType === token;
23
+ });
24
+ }
25
+ /**
26
+ * Splits an incoming selection into files that pass validation and files that don't, with a reason.
27
+ * Like the native file input, every selection replaces the current one. Duplicates within the
28
+ * selection (same name, size and last modified) are silently dropped rather than rejected. In
29
+ * single-file mode only the first incoming file is considered.
30
+ */
31
+ export function partitionFiles(incoming, options) {
32
+ const { accept, maxSizeInBytes, multiple = false } = options;
33
+ const accepted = [];
34
+ const rejected = [];
35
+ const candidates = multiple ? incoming : incoming.slice(0, 1);
36
+ for (const file of candidates) {
37
+ if (!matchesAccept(file, accept)) {
38
+ rejected.push({ file, reason: 'Filtypen støttes ikke.', errorCode: 'type-mismatch' });
39
+ continue;
40
+ }
41
+ if (maxSizeInBytes && file.size > maxSizeInBytes) {
42
+ rejected.push({ file, reason: `Filen er for stor. Maks ${formatBytes(maxSizeInBytes)}.`, errorCode: 'too-large' });
43
+ continue;
44
+ }
45
+ if (!accepted.some((candidate) => isSameFile(candidate, file)))
46
+ accepted.push(file);
47
+ }
48
+ return { accepted, rejected };
49
+ }
50
+ /**
51
+ * Identity check for files that survives the copies browsers hand out across separate
52
+ * `change`/`drop` events, where reference equality no longer holds.
53
+ */
54
+ export function isSameFile(a, b) {
55
+ return a === b || (a.name === b.name && a.size === b.size && a.lastModified === b.lastModified);
56
+ }
57
+ //# sourceMappingURL=file-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-validation.js","sourceRoot":"","sources":["../../src/utils/file-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAc7C;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAU,EAAE,MAAe;IACxD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM;SACnB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SAC1C,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAEzC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5B,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC3D,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACzE,OAAO,QAAQ,KAAK,KAAK,CAAC;IAC3B,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,OAA8B;IAC9E,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAE7D,MAAM,QAAQ,GAAW,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9D,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,wBAAwB,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;YACtF,SAAS;QACV,CAAC;QACD,IAAI,cAAc,IAAI,IAAI,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;YAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,2BAA2B,WAAW,CAAC,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;YACnH,SAAS;QACV,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrF,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,CAAO,EAAE,CAAO;IAC1C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;AACjG,CAAC","sourcesContent":["import { formatBytes } from './format-bytes';\n\nexport interface SDSRejectedFile {\n\tfile: File;\n\treason: string;\n\terrorCode: 'type-mismatch' | 'too-large';\n}\n\nexport interface PartitionFilesOptions {\n\taccept?: string;\n\tmaxSizeInBytes?: number;\n\tmultiple?: boolean;\n}\n\n/**\n * Mirrors the native file input's `accept` matching: a token is either an extension (`.pdf`),\n * a MIME wildcard (`image/*`) or an exact MIME type. Matches if any token matches.\n */\nexport function matchesAccept(file: File, accept?: string): boolean {\n\tif (!accept) return true;\n\tconst tokens = accept\n\t\t.split(',')\n\t\t.map((token) => token.trim().toLowerCase())\n\t\t.filter(Boolean);\n\tif (tokens.length === 0) return true;\n\n\tconst fileName = file.name.toLowerCase();\n\tconst mimeType = file.type.toLowerCase();\n\n\treturn tokens.some((token) => {\n\t\tif (token.startsWith('.')) return fileName.endsWith(token);\n\t\tif (token.endsWith('/*')) return mimeType.startsWith(token.slice(0, -1));\n\t\treturn mimeType === token;\n\t});\n}\n\n/**\n * Splits an incoming selection into files that pass validation and files that don't, with a reason.\n * Like the native file input, every selection replaces the current one. Duplicates within the\n * selection (same name, size and last modified) are silently dropped rather than rejected. In\n * single-file mode only the first incoming file is considered.\n */\nexport function partitionFiles(incoming: File[], options: PartitionFilesOptions): { accepted: File[]; rejected: SDSRejectedFile[] } {\n\tconst { accept, maxSizeInBytes, multiple = false } = options;\n\n\tconst accepted: File[] = [];\n\tconst rejected: SDSRejectedFile[] = [];\n\tconst candidates = multiple ? incoming : incoming.slice(0, 1);\n\n\tfor (const file of candidates) {\n\t\tif (!matchesAccept(file, accept)) {\n\t\t\trejected.push({ file, reason: 'Filtypen støttes ikke.', errorCode: 'type-mismatch' });\n\t\t\tcontinue;\n\t\t}\n\t\tif (maxSizeInBytes && file.size > maxSizeInBytes) {\n\t\t\trejected.push({ file, reason: `Filen er for stor. Maks ${formatBytes(maxSizeInBytes)}.`, errorCode: 'too-large' });\n\t\t\tcontinue;\n\t\t}\n\t\tif (!accepted.some((candidate) => isSameFile(candidate, file))) accepted.push(file);\n\t}\n\n\treturn { accepted, rejected };\n}\n\n/**\n * Identity check for files that survives the copies browsers hand out across separate\n * `change`/`drop` events, where reference equality no longer holds.\n */\nexport function isSameFile(a: File, b: File): boolean {\n\treturn a === b || (a.name === b.name && a.size === b.size && a.lastModified === b.lastModified);\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formats a byte count as a human-readable string using Norwegian formatting
3
+ * (comma decimal separator), e.g. `7400` → `7,2 kB`.
4
+ */
5
+ export declare function formatBytes(bytes: number): string;
6
+ //# sourceMappingURL=format-bytes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-bytes.d.ts","sourceRoot":"","sources":["../../src/utils/format-bytes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMjD"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Formats a byte count as a human-readable string using Norwegian formatting
3
+ * (comma decimal separator), e.g. `7400` → `7,2 kB`.
4
+ */
5
+ export function formatBytes(bytes) {
6
+ if (bytes === 0)
7
+ return '0 B';
8
+ const units = ['B', 'kB', 'MB', 'GB'];
9
+ const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
10
+ const value = bytes / 1024 ** exponent;
11
+ return `${value.toFixed(exponent === 0 ? 0 : 1).replace('.', ',')} ${units[exponent]}`;
12
+ }
13
+ //# sourceMappingURL=format-bytes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-bytes.js","sourceRoot":"","sources":["../../src/utils/format-bytes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACxC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1F,MAAM,KAAK,GAAG,KAAK,GAAG,IAAI,IAAI,QAAQ,CAAC;IACvC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;AACxF,CAAC","sourcesContent":["/**\n * Formats a byte count as a human-readable string using Norwegian formatting\n * (comma decimal separator), e.g. `7400` → `7,2 kB`.\n */\nexport function formatBytes(bytes: number): string {\n\tif (bytes === 0) return '0 B';\n\tconst units = ['B', 'kB', 'MB', 'GB'];\n\tconst exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n\tconst value = bytes / 1024 ** exponent;\n\treturn `${value.toFixed(exponent === 0 ? 0 : 1).replace('.', ',')} ${units[exponent]}`;\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Generates a square-agnostic still-frame thumbnail for a video file and returns an object URL
3
+ * for the encoded image blob (WebP where supported, otherwise JPEG). The caller owns the URL and
4
+ * must revoke it with `URL.revokeObjectURL` when done. Rejects if the video can't be decoded in
5
+ * time, so callers can fall back to a file icon.
6
+ */
7
+ export declare function generateVideoThumbnail(file: File): Promise<string>;
8
+ //# sourceMappingURL=video-thumbnail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-thumbnail.d.ts","sourceRoot":"","sources":["../../src/utils/video-thumbnail.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CA0ElE"}