@streamscloud/kit 0.82.1 → 0.82.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/files/upload-media-store.svelte.d.ts +3 -3
- package/dist/core/files/upload-media-store.svelte.js +4 -4
- package/dist/ui/button/cmp.button.svelte +8 -6
- package/dist/ui/button/cmp.button.svelte.d.ts +4 -2
- package/dist/ui/numeral-input/cmp.numeral-input.svelte +5 -3
- package/dist/ui/numeral-input/cmp.numeral-input.svelte.d.ts +4 -0
- package/dist/ui/open-file-button/cmp.open-file-button.svelte +2 -1
- package/dist/ui/open-file-button/cmp.open-file-button.svelte.d.ts +3 -1
- package/package.json +1 -1
|
@@ -44,7 +44,7 @@ export type UploadMediaStoreOptions = {
|
|
|
44
44
|
* 1. `add(file)` — push raw `File` into the queue (`status: 'queued'`).
|
|
45
45
|
* 2. `upload()` — fetch presigned URLs from the strategy, then PUT each blob in parallel.
|
|
46
46
|
* Per-file status flows `queued → uploading → success | error`; `progress` updates as
|
|
47
|
-
* bytes are sent.
|
|
47
|
+
* bytes are sent. The next `upload()` retries every file left in `error`.
|
|
48
48
|
* 3. Consumer wires `on.blobId` to capture each blob ID for form state, or reads `files[i].blobId`.
|
|
49
49
|
*
|
|
50
50
|
* @example
|
|
@@ -94,8 +94,8 @@ export declare class UploadMediaStore {
|
|
|
94
94
|
readUrl: string;
|
|
95
95
|
} | null;
|
|
96
96
|
/**
|
|
97
|
-
* Run the upload pipeline for every file currently in `'queued'` status
|
|
98
|
-
* `
|
|
97
|
+
* Run the upload pipeline for every file currently in `'queued'` status, and retry every file in
|
|
98
|
+
* `'error'` status. Files already `uploading` / `success` are skipped. Resolves once **every** run of this store —
|
|
99
99
|
* including ones started by earlier `upload()` calls — has reached a terminal status, so
|
|
100
100
|
* `await store.upload()` always means "nothing of mine is in flight any more". Each run registers
|
|
101
101
|
* itself in `AppUploadActivity` for its duration, so a mounted `UploadProgressToaster` picks it up
|
|
@@ -20,7 +20,7 @@ const revokePreviewUrl = (file) => {
|
|
|
20
20
|
* 1. `add(file)` — push raw `File` into the queue (`status: 'queued'`).
|
|
21
21
|
* 2. `upload()` — fetch presigned URLs from the strategy, then PUT each blob in parallel.
|
|
22
22
|
* Per-file status flows `queued → uploading → success | error`; `progress` updates as
|
|
23
|
-
* bytes are sent.
|
|
23
|
+
* bytes are sent. The next `upload()` retries every file left in `error`.
|
|
24
24
|
* 3. Consumer wires `on.blobId` to capture each blob ID for form state, or reads `files[i].blobId`.
|
|
25
25
|
*
|
|
26
26
|
* @example
|
|
@@ -79,15 +79,15 @@ export class UploadMediaStore {
|
|
|
79
79
|
return file?.status === 'success' ? { blobId: file.blobId, readUrl: file.readUrl } : null;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Run the upload pipeline for every file currently in `'queued'` status
|
|
83
|
-
* `
|
|
82
|
+
* Run the upload pipeline for every file currently in `'queued'` status, and retry every file in
|
|
83
|
+
* `'error'` status. Files already `uploading` / `success` are skipped. Resolves once **every** run of this store —
|
|
84
84
|
* including ones started by earlier `upload()` calls — has reached a terminal status, so
|
|
85
85
|
* `await store.upload()` always means "nothing of mine is in flight any more". Each run registers
|
|
86
86
|
* itself in `AppUploadActivity` for its duration, so a mounted `UploadProgressToaster` picks it up
|
|
87
87
|
* with no extra wiring.
|
|
88
88
|
*/
|
|
89
89
|
async upload() {
|
|
90
|
-
const queued = this._files.filter((f) => f.status === 'queued');
|
|
90
|
+
const queued = this._files.filter((f) => f.status === 'queued' || f.status === 'error');
|
|
91
91
|
if (queued.length === 0) {
|
|
92
92
|
await this.settled();
|
|
93
93
|
return this.outcome();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">import { Spinner } from '../_internal/spinner';
|
|
2
2
|
import { CONTROL_ICON_SIZE, IconSlot } from '../icon';
|
|
3
|
-
const { variant = 'primary', size = 'md', iconScale = 'default', disabled = false, loading = false, fullWidth = false, 'aria-label': ariaLabel, icon, iconPosition = 'leading', children, ...mode } = $props();
|
|
4
|
-
const inactive = $derived(disabled || loading);
|
|
3
|
+
const { variant = 'primary', size = 'md', iconScale = 'default', disabled = false, loading = false, fullWidth = false, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, icon, iconPosition = 'leading', children, ...mode } = $props();
|
|
4
|
+
const inactive = $derived(disabled !== false || loading);
|
|
5
5
|
const isIconOnly = $derived(!children && (!!icon || loading));
|
|
6
6
|
const iconFallbacks = $derived({ size: CONTROL_ICON_SIZE[size], scale: iconScale });
|
|
7
7
|
const btnClass = $derived([
|
|
@@ -63,9 +63,10 @@ const handleClick = (e) => {
|
|
|
63
63
|
download={mode.download}
|
|
64
64
|
role="button"
|
|
65
65
|
aria-label={ariaLabel}
|
|
66
|
+
aria-describedby={ariaDescribedby}
|
|
66
67
|
aria-disabled={inactive ? 'true' : undefined}
|
|
67
68
|
aria-busy={loading ? 'true' : undefined}
|
|
68
|
-
tabindex={
|
|
69
|
+
tabindex={disabled === true ? -1 : undefined}
|
|
69
70
|
onclick={handleClick}>
|
|
70
71
|
{@render content()}
|
|
71
72
|
</a>
|
|
@@ -77,9 +78,10 @@ const handleClick = (e) => {
|
|
|
77
78
|
<button
|
|
78
79
|
class={btnClass}
|
|
79
80
|
type={mode.type}
|
|
80
|
-
disabled={disabled}
|
|
81
|
+
disabled={disabled === true}
|
|
81
82
|
aria-label={ariaLabel}
|
|
82
|
-
aria-
|
|
83
|
+
aria-describedby={ariaDescribedby}
|
|
84
|
+
aria-disabled={inactive ? 'true' : undefined}
|
|
83
85
|
aria-busy={loading ? 'true' : undefined}
|
|
84
86
|
form={mode.form}
|
|
85
87
|
name={mode.name}
|
|
@@ -91,7 +93,7 @@ const handleClick = (e) => {
|
|
|
91
93
|
|
|
92
94
|
<!--
|
|
93
95
|
@component
|
|
94
|
-
A button with configurable variant, size, loading state, and an anchor mode. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
|
|
96
|
+
A button with configurable variant, size, loading state, and an anchor mode. `disabled: 'focusable'` looks and acts disabled but stays focusable, so a wrapping `Tooltip` can tell keyboard and screen-reader users why. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
|
|
95
97
|
|
|
96
98
|
The `icon` slot accepts an SVG source string or a snippet; `iconPosition` ('leading' / 'trailing', default leading) flips which side of the label the icon renders on. Icon-only mode kicks in automatically when `icon` is set without `children` — the button becomes square and requires an `aria-label`.
|
|
97
99
|
|
|
@@ -8,7 +8,8 @@ type BaseProps = {
|
|
|
8
8
|
size?: ButtonSize;
|
|
9
9
|
/** Bump the icon one step up the scale relative to `size`, leaving height/text unchanged. @default 'default' */
|
|
10
10
|
iconScale?: 'default' | 'large';
|
|
11
|
-
disabled
|
|
11
|
+
/** `'focusable'` keeps the button in the tab order as `aria-disabled`, so a wrapping `Tooltip` can explain why it is disabled. */
|
|
12
|
+
disabled?: boolean | 'focusable';
|
|
12
13
|
/** Renders a spinner in place of the icon (when leading) and blocks clicks. */
|
|
13
14
|
loading?: boolean;
|
|
14
15
|
/** Stretch to fill the parent's inline axis. */
|
|
@@ -21,6 +22,7 @@ type BaseProps = {
|
|
|
21
22
|
children?: Snippet;
|
|
22
23
|
/** Required when icon-only (no children). */
|
|
23
24
|
'aria-label'?: string;
|
|
25
|
+
'aria-describedby'?: string;
|
|
24
26
|
};
|
|
25
27
|
type ButtonModeProps = BaseProps & {
|
|
26
28
|
/** Native button mode. */
|
|
@@ -48,7 +50,7 @@ type PresentationalModeProps = BaseProps & {
|
|
|
48
50
|
};
|
|
49
51
|
type Props = ButtonModeProps | AnchorModeProps | PresentationalModeProps;
|
|
50
52
|
/**
|
|
51
|
-
* A button with configurable variant, size, loading state, and an anchor mode. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
|
|
53
|
+
* A button with configurable variant, size, loading state, and an anchor mode. `disabled: 'focusable'` looks and acts disabled but stays focusable, so a wrapping `Tooltip` can tell keyboard and screen-reader users why. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
|
|
52
54
|
*
|
|
53
55
|
* The `icon` slot accepts an SVG source string or a snippet; `iconPosition` ('leading' / 'trailing', default leading) flips which side of the label the icon renders on. Icon-only mode kicks in automatically when `icon` is set without `children` — the button becomes square and requires an `aria-label`.
|
|
54
56
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { IconSlot } from '../icon';
|
|
3
3
|
import { formatNumeral, NumeralThousandGroupStyles, registerCursorTracker } from 'cleave-zen';
|
|
4
4
|
import { untrack } from 'svelte';
|
|
5
|
-
const { size = 'md', disabled = false, readonly = false, inert = false, error = false, borderless = false, placeholder = '', title = '', name = '', id = null, autofocus = false, min = null, max = null, decimals = 2, suffix, hideResolvedValue = false, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, 'aria-required': ariaRequired, icon, iconPosition = 'leading', secondaryIcon, ...mode } = $props();
|
|
5
|
+
const { size = 'md', disabled = false, readonly = false, inert = false, error = false, borderless = false, placeholder = '', title = '', name = '', id = null, autofocus = false, min = null, max = null, minExclusive = false, maxExclusive = false, decimals = 2, suffix, hideResolvedValue = false, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, 'aria-required': ariaRequired, icon, iconPosition = 'leading', secondaryIcon, ...mode } = $props();
|
|
6
6
|
let inputEl = $state.raw(undefined);
|
|
7
7
|
let focused = $state(false);
|
|
8
8
|
let typedValue = $state(undefined);
|
|
@@ -26,11 +26,13 @@ const parseValue = (formatted) => {
|
|
|
26
26
|
const n = parseFloat(formatted.replace(/\s/g, ''));
|
|
27
27
|
return Number.isNaN(n) ? null : n;
|
|
28
28
|
};
|
|
29
|
+
const lowerBound = $derived(min === null ? null : minExclusive ? min + 1 : min);
|
|
30
|
+
const upperBound = $derived(max === null ? null : maxExclusive ? max - 1 : max);
|
|
29
31
|
const resolveValue = (typed) => {
|
|
30
32
|
if (typed !== null) {
|
|
31
|
-
return Math.min(Math.max(typed,
|
|
33
|
+
return Math.min(Math.max(typed, lowerBound ?? -Infinity), upperBound ?? Infinity);
|
|
32
34
|
}
|
|
33
|
-
return mode.clearable ? null : (
|
|
35
|
+
return mode.clearable ? null : (lowerBound ?? mode.value);
|
|
34
36
|
};
|
|
35
37
|
const resolvedTypedValue = $derived(typedValue === undefined ? undefined : resolveValue(typedValue));
|
|
36
38
|
const valueHintVisible = $derived(!hideResolvedValue && resolvedTypedValue !== typedValue);
|
|
@@ -20,6 +20,10 @@ type BaseProps = {
|
|
|
20
20
|
min?: number | null;
|
|
21
21
|
/** Maximum value emitted to the consumer; the typed text is left alone until commit. */
|
|
22
22
|
max?: number | null;
|
|
23
|
+
/** Integer bound, matching `numberValidationSchema`: an exclusive `min` resolves to `min + 1`. @default false */
|
|
24
|
+
minExclusive?: boolean;
|
|
25
|
+
/** Integer bound, matching `numberValidationSchema`: an exclusive `max` resolves to `max - 1`. @default false */
|
|
26
|
+
maxExclusive?: boolean;
|
|
23
27
|
/** Number of decimal places shown. @default 2 */
|
|
24
28
|
decimals?: number;
|
|
25
29
|
/** Muted unit rendered after the value, outside the editable text (`px`, `%`, `°`). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">import { deriveAccept, FileValidator, openFile } from '../../core/files';
|
|
2
2
|
import { Button } from '../button';
|
|
3
|
-
const { size = 'md', variant = 'secondary', disabled = false, multiple = false, capture, validationRules, icon, iconPosition = 'leading', children, 'aria-label': ariaLabel, on } = $props();
|
|
3
|
+
const { size = 'md', variant = 'secondary', disabled = false, multiple = false, capture, validationRules, icon, iconPosition = 'leading', children, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, on } = $props();
|
|
4
4
|
const accept = $derived(deriveAccept(validationRules ?? []));
|
|
5
5
|
const handleClick = async () => {
|
|
6
6
|
const files = await openFile({ multiple, accept, capture });
|
|
@@ -22,6 +22,7 @@ const handleClick = async () => {
|
|
|
22
22
|
icon={icon}
|
|
23
23
|
iconPosition={iconPosition}
|
|
24
24
|
aria-label={ariaLabel}
|
|
25
|
+
aria-describedby={ariaDescribedby}
|
|
25
26
|
on={{ click: handleClick }}>
|
|
26
27
|
{#if children}{@render children()}{/if}
|
|
27
28
|
</Button>
|
|
@@ -7,7 +7,8 @@ type Props = {
|
|
|
7
7
|
size?: ButtonSize;
|
|
8
8
|
/** @default 'secondary' */
|
|
9
9
|
variant?: ButtonVariant;
|
|
10
|
-
disabled
|
|
10
|
+
/** `'focusable'` keeps the button in the tab order as `aria-disabled`, so a wrapping `Tooltip` can explain why it is disabled. */
|
|
11
|
+
disabled?: boolean | 'focusable';
|
|
11
12
|
multiple?: boolean;
|
|
12
13
|
/** Optional file-picker capture hint (mobile camera / mic). */
|
|
13
14
|
capture?: 'user' | 'environment';
|
|
@@ -26,6 +27,7 @@ type Props = {
|
|
|
26
27
|
children?: Snippet;
|
|
27
28
|
/** Required when no `children` (icon-only mode). */
|
|
28
29
|
'aria-label'?: string;
|
|
30
|
+
'aria-describedby'?: string;
|
|
29
31
|
on?: {
|
|
30
32
|
/**
|
|
31
33
|
* Fires after the user picks files. Every result is delivered — consumer reads
|