@streamscloud/kit 0.64.0 → 0.65.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/dist/core/local-storage/index.d.ts +1 -1
- package/dist/core/local-storage/local-storage-item.d.ts +14 -2
- package/dist/core/local-storage/local-storage-item.js +35 -10
- package/dist/core/utils/price-helper.d.ts +2 -0
- package/dist/core/utils/price-helper.js +12 -1
- package/dist/ui/image/cmp.image.svelte +23 -5
- package/dist/ui/image/cmp.image.svelte.d.ts +1 -1
- package/dist/ui/page-toolbar/cmp.toolbar-segmented-control.svelte +2 -2
- package/dist/ui/page-toolbar/cmp.toolbar-view-controls.svelte +4 -2
- package/dist/ui/table/table-columns-manager/cmp.table-columns-manager.svelte +4 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
export { LocalStorageItem } from './local-storage-item';
|
|
1
|
+
export { LocalStorageItem, type LocalStorageItemOptions } from './local-storage-item';
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
export type LocalStorageItemOptions<T> = {
|
|
2
|
+
/** Rejects a stored value that parses but no longer fits (a stale number, a removed enum member) — `get()` then returns the fallback. */
|
|
3
|
+
validate?: (value: unknown) => value is T;
|
|
4
|
+
};
|
|
1
5
|
export declare class LocalStorageItem<T, TFallback = null> {
|
|
2
6
|
readonly key: string;
|
|
3
7
|
private readonly _fallback;
|
|
8
|
+
private readonly _validate;
|
|
4
9
|
private constructor();
|
|
5
|
-
static of: <T_1>(key: string) => LocalStorageItem<T_1>;
|
|
6
|
-
static withDefault: <T_1>(key: string, fallback: T_1) => LocalStorageItem<T_1, T_1>;
|
|
10
|
+
static of: <T_1>(key: string, options?: LocalStorageItemOptions<T_1>) => LocalStorageItem<T_1>;
|
|
11
|
+
static withDefault: <T_1>(key: string, fallback: T_1, options?: LocalStorageItemOptions<T_1>) => LocalStorageItem<T_1, T_1>;
|
|
12
|
+
/**
|
|
13
|
+
* The stored value — or the fallback when nothing is stored, storage is unavailable, the value doesn't parse, or
|
|
14
|
+
* `validate` rejects it (or throws). Without `validate` a parsed value comes back unchecked — a stored `null`, `0` or
|
|
15
|
+
* `false` included.
|
|
16
|
+
*/
|
|
7
17
|
get(): T | TFallback;
|
|
18
|
+
/** Removes the stored value; does nothing when storage is unavailable. */
|
|
8
19
|
remove(): void;
|
|
20
|
+
/** Stores the value; when storage is unavailable or full it just isn't remembered. */
|
|
9
21
|
set(value: T): void;
|
|
10
22
|
}
|
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
export class LocalStorageItem {
|
|
2
2
|
key;
|
|
3
3
|
_fallback;
|
|
4
|
-
|
|
4
|
+
_validate;
|
|
5
|
+
constructor(key, fallback, options) {
|
|
5
6
|
this.key = key;
|
|
6
7
|
this._fallback = fallback;
|
|
8
|
+
this._validate = options?.validate ?? null;
|
|
7
9
|
}
|
|
8
|
-
static of = (key) => {
|
|
9
|
-
return new LocalStorageItem(key, null);
|
|
10
|
+
static of = (key, options) => {
|
|
11
|
+
return new LocalStorageItem(key, null, options);
|
|
10
12
|
};
|
|
11
|
-
static withDefault = (key, fallback) => {
|
|
12
|
-
return new LocalStorageItem(key, fallback);
|
|
13
|
+
static withDefault = (key, fallback, options) => {
|
|
14
|
+
return new LocalStorageItem(key, fallback, options);
|
|
13
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* The stored value — or the fallback when nothing is stored, storage is unavailable, the value doesn't parse, or
|
|
18
|
+
* `validate` rejects it (or throws). Without `validate` a parsed value comes back unchecked — a stored `null`, `0` or
|
|
19
|
+
* `false` included.
|
|
20
|
+
*/
|
|
14
21
|
get() {
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
try {
|
|
23
|
+
const stringValue = localStorage.getItem(this.key);
|
|
24
|
+
if (!stringValue) {
|
|
25
|
+
return this._fallback;
|
|
26
|
+
}
|
|
27
|
+
const value = JSON.parse(stringValue);
|
|
28
|
+
return !this._validate || this._validate(value) ? value : this._fallback;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
17
31
|
return this._fallback;
|
|
18
32
|
}
|
|
19
|
-
return JSON.parse(stringValue);
|
|
20
33
|
}
|
|
34
|
+
/** Removes the stored value; does nothing when storage is unavailable. */
|
|
21
35
|
remove() {
|
|
22
|
-
|
|
36
|
+
try {
|
|
37
|
+
localStorage.removeItem(this.key);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Storage blocked (private mode, sandboxed iframe) — there is nothing to remove.
|
|
41
|
+
}
|
|
23
42
|
}
|
|
43
|
+
/** Stores the value; when storage is unavailable or full it just isn't remembered. */
|
|
24
44
|
set(value) {
|
|
25
|
-
|
|
45
|
+
try {
|
|
46
|
+
localStorage.setItem(this.key, JSON.stringify(value));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Storage blocked or over quota — losing a remembered setting beats breaking the caller.
|
|
50
|
+
}
|
|
26
51
|
}
|
|
27
52
|
}
|
|
@@ -4,6 +4,8 @@ export type PriceRepresentationOptions = {
|
|
|
4
4
|
currencyMode?: CurrencyMode;
|
|
5
5
|
missingFractionMode?: 'hide' | 'currency-placeholder' | 'zeros' | 'zeros-or-placeholder';
|
|
6
6
|
locale?: string;
|
|
7
|
+
/** Render a zero amount as a localized "Free" (in the app's `AppLocale`) instead of a number. @default false */
|
|
8
|
+
zeroAsFree?: boolean;
|
|
7
9
|
};
|
|
8
10
|
export declare const toPriceRepresentation: (data: {
|
|
9
11
|
amountMinor: number;
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import { AppLocale } from '../locale/app-locale.svelte';
|
|
2
|
+
import {} from '../locale/types';
|
|
1
3
|
import { Utils } from './utils';
|
|
4
|
+
const loc = {
|
|
5
|
+
free: {
|
|
6
|
+
en: 'Free',
|
|
7
|
+
no: 'Gratis'
|
|
8
|
+
}
|
|
9
|
+
};
|
|
2
10
|
// `amountMinor` is an integer in the currency's smallest unit (e.g. cents) — the canonical money
|
|
3
11
|
// representation. It is converted to a major amount here via the currency's minor-unit exponent.
|
|
4
12
|
export const toPriceRepresentation = (data) => {
|
|
5
13
|
const { amountMinor, currency, options } = data;
|
|
6
|
-
const { currencyMode = 'symbol', missingFractionMode = 'zeros-or-placeholder', locale = 'nb-NO' } = options ?? {};
|
|
14
|
+
const { currencyMode = 'symbol', missingFractionMode = 'zeros-or-placeholder', locale = 'nb-NO', zeroAsFree = false } = options ?? {};
|
|
15
|
+
if (zeroAsFree && amountMinor === 0) {
|
|
16
|
+
return loc.free[AppLocale.current];
|
|
17
|
+
}
|
|
7
18
|
const cfg = currencyDefaults[currency];
|
|
8
19
|
const amount = amountMinor / 10 ** cfg.minorUnit;
|
|
9
20
|
// Helper to attach currency per mode
|
|
@@ -4,6 +4,10 @@ import IconImageOff from '@fluentui/svg-icons/icons/image_off_20_regular.svg?raw
|
|
|
4
4
|
let { src, alt = '', showStubOnError = false, on, stub } = $props();
|
|
5
5
|
let hasError = $state(false);
|
|
6
6
|
const imageState = $derived(calculateImageState(src, hasError, showStubOnError));
|
|
7
|
+
// A 0.01x density makes the intrinsic size 100× the natural one, so `contain` sizing always scales the picture
|
|
8
|
+
// down to fit — small pictures upscale too — and the box hugs the picture, rounding its own corners.
|
|
9
|
+
// `src` must stay unset: it would join srcset as the 1x candidate and win.
|
|
10
|
+
const srcset = $derived(src ? `${src.replace(/\s/g, (char) => encodeURIComponent(char))} 0.01x` : undefined);
|
|
7
11
|
const onLoad = () => {
|
|
8
12
|
hasError = false;
|
|
9
13
|
on?.load?.(true);
|
|
@@ -16,7 +20,7 @@ const onLoadError = () => {
|
|
|
16
20
|
|
|
17
21
|
<div class="image">
|
|
18
22
|
{#if !!src}
|
|
19
|
-
<img class="image__img" class:image__img--hidden={imageState !== 'showImage'}
|
|
23
|
+
<img class="image__img" class:image__img--hidden={imageState !== 'showImage'} srcset={srcset} alt={alt || ''} onload={onLoad} onerror={onLoadError} />
|
|
20
24
|
{/if}
|
|
21
25
|
|
|
22
26
|
{#if imageState === 'showError'}
|
|
@@ -29,7 +33,7 @@ const onLoadError = () => {
|
|
|
29
33
|
{#if stub}
|
|
30
34
|
{@render stub()}
|
|
31
35
|
{:else}
|
|
32
|
-
<div class="
|
|
36
|
+
<div class="image__stub"></div>
|
|
33
37
|
{/if}
|
|
34
38
|
{/if}
|
|
35
39
|
</div>
|
|
@@ -43,7 +47,7 @@ Displays an image with loading states: shows the image on success, an error icon
|
|
|
43
47
|
|---|---|---|
|
|
44
48
|
| `--sc-kit--image--background` | Container background | `transparent` |
|
|
45
49
|
| `--sc-kit--image--border-radius` | Corner rounding | `0` |
|
|
46
|
-
| `--sc-kit--image--object-fit` | Image object-fit mode | `cover` |
|
|
50
|
+
| `--sc-kit--image--object-fit` | Image object-fit mode; `contain` hugs the picture (so `--border-radius` rounds it) and upscales small ones where `@container style()` is supported, elsewhere it letterboxes | `cover` |
|
|
47
51
|
| `--sc-kit--image--stub--background` | Stub placeholder background | `var(--sc-kit--color--bg--images)` |
|
|
48
52
|
-->
|
|
49
53
|
|
|
@@ -60,15 +64,29 @@ Displays an image with loading states: shows the image on success, an error icon
|
|
|
60
64
|
background: var(--_image--background);
|
|
61
65
|
}
|
|
62
66
|
.image__img {
|
|
67
|
+
position: absolute;
|
|
68
|
+
inset: 0;
|
|
69
|
+
z-index: 2;
|
|
63
70
|
width: 100%;
|
|
64
71
|
height: 100%;
|
|
65
|
-
|
|
72
|
+
margin: auto;
|
|
73
|
+
border-radius: var(--_image--border-radius);
|
|
66
74
|
object-fit: var(--_image--object-fit);
|
|
67
75
|
}
|
|
76
|
+
@container style(--_image--object-fit: contain) {
|
|
77
|
+
.image__img {
|
|
78
|
+
width: auto;
|
|
79
|
+
height: auto;
|
|
80
|
+
max-width: 100%;
|
|
81
|
+
max-height: 100%;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
68
84
|
.image__img--hidden {
|
|
69
85
|
display: none;
|
|
70
86
|
}
|
|
71
|
-
.
|
|
87
|
+
.image__stub {
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: 100%;
|
|
72
90
|
background: var(--_image--stub--background);
|
|
73
91
|
}
|
|
74
92
|
.image__load-error {
|
|
@@ -20,7 +20,7 @@ type Props = {
|
|
|
20
20
|
* |---|---|---|
|
|
21
21
|
* | `--sc-kit--image--background` | Container background | `transparent` |
|
|
22
22
|
* | `--sc-kit--image--border-radius` | Corner rounding | `0` |
|
|
23
|
-
* | `--sc-kit--image--object-fit` | Image object-fit mode | `cover` |
|
|
23
|
+
* | `--sc-kit--image--object-fit` | Image object-fit mode; `contain` hugs the picture (so `--border-radius` rounds it) and upscales small ones where `@container style()` is supported, elsewhere it letterboxes | `cover` |
|
|
24
24
|
* | `--sc-kit--image--stub--background` | Stub placeholder background | `var(--sc-kit--color--bg--images)` |
|
|
25
25
|
*/
|
|
26
26
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
@@ -8,9 +8,9 @@ $effect(() => untrack(() => {
|
|
|
8
8
|
if (!localStorageKey) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
storage = LocalStorageItem.of(localStorageKey);
|
|
11
|
+
storage = LocalStorageItem.of(localStorageKey, { validate: (saved) => segments.some((s) => s.value === saved) });
|
|
12
12
|
const saved = storage.get();
|
|
13
|
-
if (saved !== null
|
|
13
|
+
if (saved !== null) {
|
|
14
14
|
on?.change?.(saved);
|
|
15
15
|
}
|
|
16
16
|
}));
|
|
@@ -12,9 +12,11 @@ $effect(() => untrack(() => {
|
|
|
12
12
|
if (!viewStorageKey) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
|
-
storage = LocalStorageItem.of(viewStorageKey
|
|
15
|
+
storage = LocalStorageItem.of(viewStorageKey, {
|
|
16
|
+
validate: (value) => value === 'list' || value === 'grid' || (value === 'card' && showCard)
|
|
17
|
+
});
|
|
16
18
|
const saved = storage.get();
|
|
17
|
-
if (saved
|
|
19
|
+
if (saved) {
|
|
18
20
|
on?.viewChange?.(saved);
|
|
19
21
|
}
|
|
20
22
|
}));
|
|
@@ -5,7 +5,9 @@ import IconColumnTwo from '@fluentui/svg-icons/icons/text_column_two_20_regular.
|
|
|
5
5
|
import { untrack } from 'svelte';
|
|
6
6
|
let { model, localStorageKey, on } = $props();
|
|
7
7
|
const localization = new TableColumnsManagerLocalization();
|
|
8
|
-
const
|
|
8
|
+
const isColumnsConfig = (value) => Array.isArray(value) &&
|
|
9
|
+
value.every((entry) => typeof entry === 'object' && entry !== null && 'id' in entry && typeof entry.id === 'string' && 'hidden' in entry && typeof entry.hidden === 'boolean');
|
|
10
|
+
const storage = $derived(localStorageKey ? LocalStorageItem.of(localStorageKey, { validate: isColumnsConfig }) : null);
|
|
9
11
|
const hideableColumns = $derived(model.columns.filter((c) => c.hideable));
|
|
10
12
|
const items = $derived(hideableColumns.map((col) => ({
|
|
11
13
|
id: String(col.id),
|
|
@@ -26,7 +28,7 @@ $effect(() => {
|
|
|
26
28
|
enabled: !('hidden' in col.def && col.def.hidden === true)
|
|
27
29
|
}));
|
|
28
30
|
const saved = storage?.get();
|
|
29
|
-
if (!saved
|
|
31
|
+
if (!saved) {
|
|
30
32
|
return;
|
|
31
33
|
}
|
|
32
34
|
untrack(() => {
|