@politiet/sheriff-web-components 2.13.1 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +3949 -1463
- package/dist/controllers/index.d.ts +3 -0
- package/dist/controllers/index.d.ts.map +1 -0
- package/dist/controllers/index.js +3 -0
- package/dist/controllers/index.js.map +1 -0
- package/dist/controllers/sds-drag-drop-controller.d.ts +25 -0
- package/dist/controllers/sds-drag-drop-controller.d.ts.map +1 -0
- package/dist/controllers/sds-drag-drop-controller.js +52 -0
- package/dist/controllers/sds-drag-drop-controller.js.map +1 -0
- package/dist/controllers/sds-file-preview-controller.d.ts +38 -0
- package/dist/controllers/sds-file-preview-controller.d.ts.map +1 -0
- package/dist/controllers/sds-file-preview-controller.js +90 -0
- package/dist/controllers/sds-file-preview-controller.js.map +1 -0
- package/dist/frameworks/preact.d.ts +14 -1
- package/dist/frameworks/preact.d.ts.map +1 -1
- package/dist/frameworks/react.d.ts +30 -1
- package/dist/frameworks/react.d.ts.map +1 -1
- package/dist/frameworks/solidjs.d.ts +4 -0
- package/dist/frameworks/solidjs.d.ts.map +1 -1
- package/dist/frameworks/svelte.d.ts +4 -0
- package/dist/frameworks/svelte.d.ts.map +1 -1
- package/dist/frameworks/vue.d.ts +24 -1
- package/dist/frameworks/vue.d.ts.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/react-wrappers/SDSDialogWrapper.d.ts +1 -0
- package/dist/react-wrappers/SDSDialogWrapper.d.ts.map +1 -1
- package/dist/react-wrappers/SDSDialogWrapper.js +1 -0
- package/dist/react-wrappers/SDSDialogWrapper.js.map +1 -1
- package/dist/react-wrappers/SDSFileUploadFileListWrapper.d.ts +3 -0
- package/dist/react-wrappers/SDSFileUploadFileListWrapper.d.ts.map +1 -0
- package/dist/react-wrappers/SDSFileUploadFileListWrapper.js +10 -0
- package/dist/react-wrappers/SDSFileUploadFileListWrapper.js.map +1 -0
- package/dist/react-wrappers/SDSFileUploadItemWrapper.d.ts +6 -0
- package/dist/react-wrappers/SDSFileUploadItemWrapper.d.ts.map +1 -0
- package/dist/react-wrappers/SDSFileUploadItemWrapper.js +12 -0
- package/dist/react-wrappers/SDSFileUploadItemWrapper.js.map +1 -0
- package/dist/react-wrappers/SDSFileUploadWrapper.d.ts +12 -0
- package/dist/react-wrappers/SDSFileUploadWrapper.d.ts.map +1 -0
- package/dist/react-wrappers/SDSFileUploadWrapper.js +18 -0
- package/dist/react-wrappers/SDSFileUploadWrapper.js.map +1 -0
- package/dist/react-wrappers/SDSSearchWrapper.d.ts +15 -0
- package/dist/react-wrappers/SDSSearchWrapper.d.ts.map +1 -0
- package/dist/react-wrappers/SDSSearchWrapper.js +19 -0
- package/dist/react-wrappers/SDSSearchWrapper.js.map +1 -0
- package/dist/react-wrappers/index.d.ts +4 -0
- package/dist/react-wrappers/index.d.ts.map +1 -1
- package/dist/react-wrappers/index.js +4 -0
- package/dist/react-wrappers/index.js.map +1 -1
- package/dist/sds-button/sds-button.component.d.ts +0 -1
- package/dist/sds-button/sds-button.component.d.ts.map +1 -1
- package/dist/sds-button/sds-button.component.js +10 -16
- package/dist/sds-button/sds-button.component.js.map +1 -1
- package/dist/sds-dialog/sds-dialog.component.d.ts.map +1 -1
- package/dist/sds-dialog/sds-dialog.component.js +11 -1
- package/dist/sds-dialog/sds-dialog.component.js.map +1 -1
- package/dist/sds-file-upload/sds-file-upload.d.ts +102 -0
- package/dist/sds-file-upload/sds-file-upload.d.ts.map +1 -0
- package/dist/sds-file-upload/sds-file-upload.js +384 -0
- package/dist/sds-file-upload/sds-file-upload.js.map +1 -0
- package/dist/sds-file-upload/sds-file-upload.styles.d.ts +2 -0
- package/dist/sds-file-upload/sds-file-upload.styles.d.ts.map +1 -0
- package/dist/sds-file-upload/sds-file-upload.styles.js +234 -0
- package/dist/sds-file-upload/sds-file-upload.styles.js.map +1 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.d.ts +14 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.d.ts.map +1 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.js +26 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.js.map +1 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.d.ts +2 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.d.ts.map +1 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.js +19 -0
- package/dist/sds-file-upload-file-list/sds-file-upload-file-list.styles.js.map +1 -0
- package/dist/sds-file-upload-item/assets/u-progress.js +2 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.d.ts +62 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.d.ts.map +1 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.js +205 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.js.map +1 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.styles.d.ts +2 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.styles.d.ts.map +1 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.styles.js +227 -0
- package/dist/sds-file-upload-item/sds-file-upload-item.styles.js.map +1 -0
- package/dist/sds-search/sds-search.component.d.ts +128 -0
- package/dist/sds-search/sds-search.component.d.ts.map +1 -0
- package/dist/sds-search/sds-search.component.js +345 -0
- package/dist/sds-search/sds-search.component.js.map +1 -0
- package/dist/sds-search/sds-search.d.ts +13 -0
- package/dist/sds-search/sds-search.d.ts.map +1 -0
- package/dist/sds-search/sds-search.js +18 -0
- package/dist/sds-search/sds-search.js.map +1 -0
- package/dist/sds-search/sds-search.styles.d.ts +2 -0
- package/dist/sds-search/sds-search.styles.d.ts.map +1 -0
- package/dist/sds-search/sds-search.styles.js +74 -0
- package/dist/sds-search/sds-search.styles.js.map +1 -0
- package/dist/sds-textfield/sds-textfield.component.d.ts +4 -1
- package/dist/sds-textfield/sds-textfield.component.d.ts.map +1 -1
- package/dist/sds-textfield/sds-textfield.component.js +20 -5
- package/dist/sds-textfield/sds-textfield.component.js.map +1 -1
- package/dist/sds-textfield/sds-textfield.styles.d.ts.map +1 -1
- package/dist/sds-textfield/sds-textfield.styles.js +11 -0
- package/dist/sds-textfield/sds-textfield.styles.js.map +1 -1
- package/dist/sheriff-web-components.js +1015 -294
- package/dist/skill/SKILL.md +44 -36
- package/dist/skill/references/components.md +88 -3
- package/dist/skill/references/react-wrappers.md +25 -18
- package/dist/utils/file-validation.d.ts +31 -0
- package/dist/utils/file-validation.d.ts.map +1 -0
- package/dist/utils/file-validation.js +57 -0
- package/dist/utils/file-validation.js.map +1 -0
- package/dist/utils/format-bytes.d.ts +6 -0
- package/dist/utils/format-bytes.d.ts.map +1 -0
- package/dist/utils/format-bytes.js +13 -0
- package/dist/utils/format-bytes.js.map +1 -0
- package/dist/utils/video-thumbnail.d.ts +8 -0
- package/dist/utils/video-thumbnail.d.ts.map +1 -0
- package/dist/utils/video-thumbnail.js +77 -0
- package/dist/utils/video-thumbnail.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +24 -4
- package/web-types.json +3431 -1120
package/dist/skill/SKILL.md
CHANGED
|
@@ -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
|
|
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,
|
|
8
|
-
|
|
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-*
|
|
@@ -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
|
|
82
|
-
|
|
|
83
|
-
| `sds-alert`
|
|
84
|
-
| `sds-button`
|
|
85
|
-
| `sds-card`
|
|
86
|
-
| `sds-checkbox`
|
|
87
|
-
| `sds-checkbox-group`
|
|
88
|
-
| `sds-chip`
|
|
89
|
-
| `sds-details`
|
|
90
|
-
| `sds-details-group`
|
|
91
|
-
| `sds-dialog`
|
|
92
|
-
| `sds-environment-banner`
|
|
93
|
-
| `sds-
|
|
94
|
-
| `sds-
|
|
95
|
-
| `sds-
|
|
96
|
-
| `sds-
|
|
97
|
-
| `sds-
|
|
98
|
-
| `sds-
|
|
99
|
-
| `sds-
|
|
100
|
-
| `sds-
|
|
101
|
-
| `sds-
|
|
102
|
-
| `sds-
|
|
103
|
-
| `sds-
|
|
104
|
-
| `sds-
|
|
105
|
-
| `sds-
|
|
106
|
-
| `sds-
|
|
107
|
-
| `sds-
|
|
108
|
-
| `sds-
|
|
109
|
-
| `sds-
|
|
110
|
-
| `sds-
|
|
111
|
-
| `sds-
|
|
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
|
|
|
@@ -124,7 +129,7 @@ For full component API (all props, slots, events, code examples), see `reference
|
|
|
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,7 @@ 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 — can use web components directly without wrappers. Event props on raw elements must match the event name exactly: `onclose`, `oncancel`, `ontoggle` (lowercase). React treats camelCase `onClose`/`onCancel`/`onToggle` as its own synthetic events and never fires them on custom elements. They are only typed for backwards compatibility and marked deprecated.
|
|
163
170
|
|
|
164
171
|
For the full list of available wrappers and event mappings, see `references/react-wrappers.md`.
|
|
165
172
|
|
|
@@ -252,6 +259,7 @@ Put the accessible name on the button via `label` (it becomes `aria-label` on th
|
|
|
252
259
|
|
|
253
260
|
- **Component does not render or render weird**: Verify the CSS framework is imported. This is needed for CSS custom properties.
|
|
254
261
|
- **Events do not fire in React (pre-v19)**: Confirm React wrappers are used instead of web components.
|
|
262
|
+
- **`onClose`/`onToggle` never fires in React 19**: On raw `sds-*` elements use lowercase `onclose`/`ontoggle`/`oncancel`, or use the wrapper, where camelCase is correct.
|
|
255
263
|
- **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
264
|
- **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
265
|
- **Form values not submitted**: Ensure the component has both `name` and `value` attributes set.
|
|
@@ -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
|
|
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.
|
|
@@ -665,7 +750,7 @@ Single-line text input.
|
|
|
665
750
|
<sds-textfield label="ID" name="id" value="12345" readonly></sds-textfield>
|
|
666
751
|
```
|
|
667
752
|
|
|
668
|
-
**Props:** `label`, `name`, `value`, `type` (HTML input types), `placeholder`, `disabled`, `readonly`, `required`, `autocomplete`, `pattern`, `minlength`, `maxlength`, `min`, `max`, `help-text`, `error-message`, `size`
|
|
753
|
+
**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
754
|
**Slots:** `after` (e.g., icon), `label` (alternative to the `label` attribute)
|
|
670
755
|
**Events:** `input`, `change`, `select`, `focus`, `blur`, `keydown`
|
|
671
756
|
**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 +774,5 @@ Hover/focus tooltip. The tooltip is a sibling of its trigger, and the trigger po
|
|
|
689
774
|
|
|
690
775
|
**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
776
|
**Slots:** default (tooltip text — keep it to plain text)
|
|
692
|
-
**Events:** `toggle` (detail: the trigger element that opened it)
|
|
777
|
+
**Events:** `toggle` (detail: the trigger element that opened it). The native popover `ToggleEvent` also fires on the host.
|
|
693
778
|
**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
|
|
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,28 @@ 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
|
|
71
|
-
| ------------------------------------------------- |
|
|
72
|
-
| `SDSAlertWrapper`, `SDSNotificationWrapper` | `onRemove`
|
|
73
|
-
| `SDSButtonWrapper` | `onClick`, `onSubmit`, `onReset`
|
|
74
|
-
| `SDSCheckboxWrapper`, `SDSRadioWrapper` | `onChange`, `onFocus`, `onBlur`
|
|
75
|
-
| `SDSCheckboxGroupWrapper`, `SDSRadioGroupWrapper` | `onUpdate`, `onChange`
|
|
76
|
-
| `SDSChipWrapper` | `onChange`, `onRemove`
|
|
77
|
-
| `SDSDetailsWrapper`, `SDSTooltipWrapper` | `onToggle`
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
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
|
+
| `SDSTextFieldWrapper` | `onInput`, `onChange`, `onSelect`, `onFocus`, `onBlur`, `onKeyDown` | `input`, **`input`**, `select`, `focus`, `blur`, `keydown` |
|
|
90
|
+
| `SDSTextAreaWrapper` | `onInput`, `onChange`, `onSelect`, `onFocus`, `onBlur` | `input`, **`input`**, `select`, `focus`, `blur` |
|
|
84
91
|
|
|
85
|
-
Wrappers with no event mappings (presentational only): `SDSCardWrapper`, `SDSDetailsGroupWrapper`, `SDSEnvironmentBannerWrapper`, `SDSHeadingWrapper`, `SDSIconWrapper`, `SDSInternalHeaderWrapper`, `SDSLoaderWrapper`, `SDSProcessWrapper`, `SDSProcessStepWrapper`, `SDSStepperStepWrapper`, `SDSTagWrapper`, `SDSTextWrapper`.
|
|
92
|
+
Wrappers with no event mappings (presentational only): `SDSCardWrapper`, `SDSDetailsGroupWrapper`, `SDSEnvironmentBannerWrapper`, `SDSFileUploadFileListWrapper`, `SDSHeadingWrapper`, `SDSIconWrapper`, `SDSInternalHeaderWrapper`, `SDSLoaderWrapper`, `SDSProcessWrapper`, `SDSProcessStepWrapper`, `SDSStepperStepWrapper`, `SDSTagWrapper`, `SDSTextWrapper`.
|
|
86
93
|
|
|
87
94
|
## Gotchas
|
|
88
95
|
|
|
89
|
-
- On `SDSTextFieldWrapper` and `
|
|
90
|
-
- `onKeyDown` (capital D) is the correct casing on `SDSTextFieldWrapper`; there is no `onKeydown`.
|
|
96
|
+
- 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.
|
|
97
|
+
- `onKeyDown` (capital D) is the correct casing on `SDSTextFieldWrapper` and `SDSSearchWrapper`; there is no `onKeydown`.
|
|
91
98
|
- 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 @@
|
|
|
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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const THUMBNAIL_TIMEOUT_MS = 5000;
|
|
2
|
+
const THUMBNAIL_QUALITY = 0.75;
|
|
3
|
+
let webpSupported;
|
|
4
|
+
function supportsWebpEncoding() {
|
|
5
|
+
if (webpSupported === undefined) {
|
|
6
|
+
const canvas = document.createElement('canvas');
|
|
7
|
+
canvas.width = canvas.height = 1;
|
|
8
|
+
webpSupported = canvas.toDataURL('image/webp').startsWith('data:image/webp');
|
|
9
|
+
}
|
|
10
|
+
return webpSupported;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Generates a square-agnostic still-frame thumbnail for a video file and returns an object URL
|
|
14
|
+
* for the encoded image blob (WebP where supported, otherwise JPEG). The caller owns the URL and
|
|
15
|
+
* must revoke it with `URL.revokeObjectURL` when done. Rejects if the video can't be decoded in
|
|
16
|
+
* time, so callers can fall back to a file icon.
|
|
17
|
+
*/
|
|
18
|
+
export function generateVideoThumbnail(file) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const source = URL.createObjectURL(file);
|
|
21
|
+
const video = document.createElement('video');
|
|
22
|
+
let settled = false;
|
|
23
|
+
const cleanup = () => {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
video.removeAttribute('src');
|
|
26
|
+
video.load();
|
|
27
|
+
URL.revokeObjectURL(source);
|
|
28
|
+
};
|
|
29
|
+
const fail = (error) => {
|
|
30
|
+
if (settled)
|
|
31
|
+
return;
|
|
32
|
+
settled = true;
|
|
33
|
+
cleanup();
|
|
34
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
35
|
+
};
|
|
36
|
+
const succeed = (url) => {
|
|
37
|
+
if (settled) {
|
|
38
|
+
URL.revokeObjectURL(url);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
settled = true;
|
|
42
|
+
cleanup();
|
|
43
|
+
resolve(url);
|
|
44
|
+
};
|
|
45
|
+
const timer = setTimeout(() => fail(new Error('Video thumbnail generation timed out')), THUMBNAIL_TIMEOUT_MS);
|
|
46
|
+
video.muted = true;
|
|
47
|
+
video.playsInline = true;
|
|
48
|
+
video.preload = 'auto';
|
|
49
|
+
video.addEventListener('error', () => fail(new Error('Video could not be decoded')));
|
|
50
|
+
video.addEventListener('loadeddata', () => {
|
|
51
|
+
// Seek slightly past the start to avoid capturing a black leading frame.
|
|
52
|
+
video.currentTime = Math.min(0.1, (video.duration || 0) / 2);
|
|
53
|
+
}, { once: true });
|
|
54
|
+
video.addEventListener('seeked', () => {
|
|
55
|
+
try {
|
|
56
|
+
const width = video.videoWidth;
|
|
57
|
+
const height = video.videoHeight;
|
|
58
|
+
if (!width || !height)
|
|
59
|
+
throw new Error('Video has no dimensions');
|
|
60
|
+
const canvas = document.createElement('canvas');
|
|
61
|
+
canvas.width = width;
|
|
62
|
+
canvas.height = height;
|
|
63
|
+
const context = canvas.getContext('2d');
|
|
64
|
+
if (!context)
|
|
65
|
+
throw new Error('Canvas 2D context unavailable');
|
|
66
|
+
context.drawImage(video, 0, 0, width, height);
|
|
67
|
+
const type = supportsWebpEncoding() ? 'image/webp' : 'image/jpeg';
|
|
68
|
+
canvas.toBlob((blob) => (blob ? succeed(URL.createObjectURL(blob)) : fail(new Error('Canvas toBlob returned null'))), type, THUMBNAIL_QUALITY);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
fail(error);
|
|
72
|
+
}
|
|
73
|
+
}, { once: true });
|
|
74
|
+
video.src = source;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=video-thumbnail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-thumbnail.js","sourceRoot":"","sources":["../../src/utils/video-thumbnail.ts"],"names":[],"mappings":"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","sourcesContent":["const THUMBNAIL_TIMEOUT_MS = 5000;\nconst THUMBNAIL_QUALITY = 0.75;\n\nlet webpSupported: boolean | undefined;\n\nfunction supportsWebpEncoding(): boolean {\n\tif (webpSupported === undefined) {\n\t\tconst canvas = document.createElement('canvas');\n\t\tcanvas.width = canvas.height = 1;\n\t\twebpSupported = canvas.toDataURL('image/webp').startsWith('data:image/webp');\n\t}\n\treturn webpSupported;\n}\n\n/**\n * Generates a square-agnostic still-frame thumbnail for a video file and returns an object URL\n * for the encoded image blob (WebP where supported, otherwise JPEG). The caller owns the URL and\n * must revoke it with `URL.revokeObjectURL` when done. Rejects if the video can't be decoded in\n * time, so callers can fall back to a file icon.\n */\nexport function generateVideoThumbnail(file: File): Promise<string> {\n\treturn new Promise<string>((resolve, reject) => {\n\t\tconst source = URL.createObjectURL(file);\n\t\tconst video = document.createElement('video');\n\t\tlet settled = false;\n\n\t\tconst cleanup = () => {\n\t\t\tclearTimeout(timer);\n\t\t\tvideo.removeAttribute('src');\n\t\t\tvideo.load();\n\t\t\tURL.revokeObjectURL(source);\n\t\t};\n\t\tconst fail = (error: unknown) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t};\n\t\tconst succeed = (url: string) => {\n\t\t\tif (settled) {\n\t\t\t\tURL.revokeObjectURL(url);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(url);\n\t\t};\n\n\t\tconst timer = setTimeout(() => fail(new Error('Video thumbnail generation timed out')), THUMBNAIL_TIMEOUT_MS);\n\n\t\tvideo.muted = true;\n\t\tvideo.playsInline = true;\n\t\tvideo.preload = 'auto';\n\t\tvideo.addEventListener('error', () => fail(new Error('Video could not be decoded')));\n\n\t\tvideo.addEventListener(\n\t\t\t'loadeddata',\n\t\t\t() => {\n\t\t\t\t// Seek slightly past the start to avoid capturing a black leading frame.\n\t\t\t\tvideo.currentTime = Math.min(0.1, (video.duration || 0) / 2);\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\n\t\tvideo.addEventListener(\n\t\t\t'seeked',\n\t\t\t() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst width = video.videoWidth;\n\t\t\t\t\tconst height = video.videoHeight;\n\t\t\t\t\tif (!width || !height) throw new Error('Video has no dimensions');\n\n\t\t\t\t\tconst canvas = document.createElement('canvas');\n\t\t\t\t\tcanvas.width = width;\n\t\t\t\t\tcanvas.height = height;\n\t\t\t\t\tconst context = canvas.getContext('2d');\n\t\t\t\t\tif (!context) throw new Error('Canvas 2D context unavailable');\n\t\t\t\t\tcontext.drawImage(video, 0, 0, width, height);\n\n\t\t\t\t\tconst type = supportsWebpEncoding() ? 'image/webp' : 'image/jpeg';\n\t\t\t\t\tcanvas.toBlob(\n\t\t\t\t\t\t(blob) => (blob ? succeed(URL.createObjectURL(blob)) : fail(new Error('Canvas toBlob returned null'))),\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tTHUMBNAIL_QUALITY,\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tfail(error);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\n\t\tvideo.src = source;\n\t});\n}\n"]}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SDS_VERSION = "2.
|
|
1
|
+
export declare const SDS_VERSION = "2.14.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED