@streamscloud/kit 0.22.0 → 0.24.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/styles/_semantic.scss +16 -0
- package/dist/ui/badge/cmp.badge.svelte +30 -19
- package/dist/ui/badge/cmp.badge.svelte.d.ts +5 -3
- package/dist/ui/badge/index.d.ts +1 -1
- package/dist/ui/badge/types.d.ts +2 -1
- package/dist/ui/fit-box/cmp.fit-box.svelte +46 -0
- package/dist/ui/fit-box/cmp.fit-box.svelte.d.ts +42 -0
- package/dist/ui/fit-box/contain-fit.d.ts +8 -0
- package/dist/ui/fit-box/contain-fit.js +47 -0
- package/dist/ui/fit-box/index.d.ts +2 -0
- package/dist/ui/fit-box/index.js +1 -0
- package/dist/ui/fit-box/types.d.ts +2 -0
- package/dist/ui/fit-box/types.js +1 -0
- package/dist/ui/grid-card/fields/cmp.grid-card-progress-field.svelte +5 -60
- package/dist/ui/grid-card/fields/cmp.grid-card-progress-field.svelte.d.ts +5 -17
- package/dist/ui/page-toolbar/cmp.toolbar-icon-button.svelte +9 -8
- package/dist/ui/page-toolbar/cmp.toolbar-icon-button.svelte.d.ts +6 -5
- package/dist/ui/page-toolbar/cmp.toolbar-sort-select.svelte +17 -7
- package/dist/ui/page-toolbar/cmp.toolbar-sort-select.svelte.d.ts +5 -4
- package/dist/ui/page-toolbar/cmp.toolbar-value-stepper.svelte +8 -8
- package/dist/ui/page-toolbar/cmp.toolbar-value-stepper.svelte.d.ts +4 -4
- package/dist/ui/page-toolbar/cmp.toolbar-view-controls.svelte +11 -6
- package/dist/ui/page-toolbar/cmp.toolbar-view-controls.svelte.d.ts +7 -4
- package/dist/ui/page-toolbar/toolbar-view-controls-localization.d.ts +1 -0
- package/dist/ui/page-toolbar/toolbar-view-controls-localization.js +5 -1
- package/dist/ui/progress-bar/cmp.progress-bar.svelte +92 -0
- package/dist/ui/progress-bar/cmp.progress-bar.svelte.d.ts +32 -0
- package/dist/ui/progress-bar/index.d.ts +2 -0
- package/dist/ui/progress-bar/index.js +1 -0
- package/dist/ui/progress-bar/progress-bar-localization.d.ts +3 -0
- package/dist/ui/progress-bar/progress-bar-localization.js +9 -0
- package/dist/ui/progress-bar/types.d.ts +7 -0
- package/dist/ui/progress-bar/types.js +1 -0
- package/dist/ui/table/table-cells/table-badge-cell.svelte +1 -1
- package/dist/ui/table/table-cells/table-image-cell.svelte +19 -2
- package/dist/ui/table/types.d.ts +4 -2
- package/package.json +9 -1
|
@@ -61,6 +61,22 @@
|
|
|
61
61
|
--sc-kit--color--info--hover: light-dark(#{$color-blue-700}, #{$color-blue-300});
|
|
62
62
|
--sc-kit--color--info--soft: light-dark(#{$color-blue-50}, #{$color-blue-900});
|
|
63
63
|
|
|
64
|
+
// ============================================================
|
|
65
|
+
// COLOR — TONES (categorical palette; fg + soft bg, component-neutral)
|
|
66
|
+
// ============================================================
|
|
67
|
+
--sc-kit--tone--gray--fg: light-dark(#{$color-neutral-500}, #{$color-neutral-400});
|
|
68
|
+
--sc-kit--tone--gray--bg: light-dark(#{$color-neutral-50}, #{$color-dark-600});
|
|
69
|
+
--sc-kit--tone--blue--fg: light-dark(#{$color-blue-500}, #{$color-blue-400});
|
|
70
|
+
--sc-kit--tone--blue--bg: light-dark(#{$color-blue-50}, #{$color-blue-900});
|
|
71
|
+
--sc-kit--tone--green--fg: light-dark(#{$color-green-500}, #{$color-green-400});
|
|
72
|
+
--sc-kit--tone--green--bg: light-dark(#{$color-green-50}, #{$color-green-900});
|
|
73
|
+
--sc-kit--tone--red--fg: light-dark(#{$color-red-500}, #{$color-red-400});
|
|
74
|
+
--sc-kit--tone--red--bg: light-dark(#{$color-red-50}, #{$color-red-900});
|
|
75
|
+
--sc-kit--tone--orange--fg: light-dark(#{$color-orange-500}, #{$color-orange-400});
|
|
76
|
+
--sc-kit--tone--orange--bg: light-dark(#{$color-orange-100}, #{$color-orange-900});
|
|
77
|
+
--sc-kit--tone--teal--fg: light-dark(#3d7068, #5f938a);
|
|
78
|
+
--sc-kit--tone--teal--bg: light-dark(#eef4f3, #16302b);
|
|
79
|
+
|
|
64
80
|
// ============================================================
|
|
65
81
|
// COLOR — OVERLAYS
|
|
66
82
|
// ============================================================
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">import { IconSlot } from '../icon';
|
|
2
|
-
const { variant = 'neutral', size = 'md', style = 'soft', fullWidth = false, icon, children } = $props();
|
|
2
|
+
const { variant = 'neutral', size = 'md', style = 'soft', shape = 'pill', fullWidth = false, icon, children } = $props();
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
|
-
<span class="badge badge--{size} badge--{variant}-{style}" class:badge--full={fullWidth}>
|
|
5
|
+
<span class="badge badge--{size} badge--{variant}-{style}" class:badge--square={shape === 'square'} class:badge--full={fullWidth}>
|
|
6
6
|
{#if icon}
|
|
7
7
|
<span class="badge__icon" aria-hidden="true"><IconSlot icon={icon} /></span>
|
|
8
8
|
{/if}
|
|
@@ -11,7 +11,7 @@ const { variant = 'neutral', size = 'md', style = 'soft', fullWidth = false, ico
|
|
|
11
11
|
|
|
12
12
|
<!--
|
|
13
13
|
@component
|
|
14
|
-
|
|
14
|
+
Status indicator, pill-shaped by default (`shape="square"` for a 4px-radius chip). Use `solid` for stronger emphasis, `soft` (default) for a calm tinted fill that pairs with body content. Optional leading `icon` renders in its own slot; the text content keeps its own ellipsis when it overflows.
|
|
15
15
|
|
|
16
16
|
### CSS Custom Properties
|
|
17
17
|
| Property | Description | Default |
|
|
@@ -20,7 +20,7 @@ Pill-shaped status indicator. Use `solid` for stronger emphasis, `soft` (default
|
|
|
20
20
|
| `--sc-kit--badge--background` | Background color | per variant + style |
|
|
21
21
|
| `--sc-kit--badge--height` | Badge height | per size (20 / 24px) |
|
|
22
22
|
| `--sc-kit--badge--padding-inline` | Horizontal padding | `0.875rem 0.75rem` |
|
|
23
|
-
| `--sc-kit--badge--border-radius` | Corner radius | `var(--sc-kit--radius--pill)` |
|
|
23
|
+
| `--sc-kit--badge--border-radius` | Corner radius | `shape="square"` → `var(--sc-kit--radius--sm)`, else `var(--sc-kit--radius--pill)` |
|
|
24
24
|
| `--sc-kit--badge--font-size` | Content font size | per size |
|
|
25
25
|
| `--sc-kit--badge--icon-size` | Leading icon size | per size (14 / 16px) |
|
|
26
26
|
| `--sc-kit--badge--font-weight` | Font weight | `var(--sc-kit--font-weight--medium)` |
|
|
@@ -57,6 +57,9 @@ Pill-shaped status indicator. Use `solid` for stronger emphasis, `soft` (default
|
|
|
57
57
|
text-overflow: ellipsis;
|
|
58
58
|
white-space: nowrap;
|
|
59
59
|
}
|
|
60
|
+
.badge--square {
|
|
61
|
+
--_badge--border-radius: var(--sc-kit--badge--border-radius, var(--sc-kit--radius--sm));
|
|
62
|
+
}
|
|
60
63
|
.badge--full {
|
|
61
64
|
display: flex;
|
|
62
65
|
width: 100%;
|
|
@@ -73,42 +76,50 @@ Pill-shaped status indicator. Use `solid` for stronger emphasis, `soft` (default
|
|
|
73
76
|
--_badge--icon-size: var(--sc-kit--badge--icon-size, 1rem);
|
|
74
77
|
}
|
|
75
78
|
.badge--neutral-soft {
|
|
76
|
-
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--
|
|
77
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
79
|
+
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--tone--gray--fg));
|
|
80
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--gray--bg));
|
|
78
81
|
}
|
|
79
82
|
.badge--accent-soft {
|
|
80
|
-
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--
|
|
81
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
83
|
+
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--tone--blue--fg));
|
|
84
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--blue--bg));
|
|
85
|
+
}
|
|
86
|
+
.badge--auxiliary-soft {
|
|
87
|
+
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--tone--teal--fg));
|
|
88
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--teal--bg));
|
|
82
89
|
}
|
|
83
90
|
.badge--success-soft {
|
|
84
|
-
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--
|
|
85
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
91
|
+
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--tone--green--fg));
|
|
92
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--green--bg));
|
|
86
93
|
}
|
|
87
94
|
.badge--warning-soft {
|
|
88
|
-
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--
|
|
89
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
95
|
+
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--tone--orange--fg));
|
|
96
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--orange--bg));
|
|
90
97
|
}
|
|
91
98
|
.badge--danger-soft {
|
|
92
|
-
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--
|
|
93
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
99
|
+
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--tone--red--fg));
|
|
100
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--red--bg));
|
|
94
101
|
}
|
|
95
102
|
.badge--neutral-solid {
|
|
96
103
|
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--color--bg--panel));
|
|
97
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
104
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--gray--fg));
|
|
98
105
|
}
|
|
99
106
|
.badge--accent-solid {
|
|
100
107
|
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--color--text--on-accent));
|
|
101
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
108
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--blue--fg));
|
|
109
|
+
}
|
|
110
|
+
.badge--auxiliary-solid {
|
|
111
|
+
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--color--text--on-accent));
|
|
112
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--teal--fg));
|
|
102
113
|
}
|
|
103
114
|
.badge--success-solid {
|
|
104
115
|
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--color--text--on-accent));
|
|
105
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
116
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--green--fg));
|
|
106
117
|
}
|
|
107
118
|
.badge--warning-solid {
|
|
108
119
|
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--color--text--primary));
|
|
109
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
120
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--orange--fg));
|
|
110
121
|
}
|
|
111
122
|
.badge--danger-solid {
|
|
112
123
|
--_badge--color: var(--sc-kit--badge--color, var(--sc-kit--color--text--on-accent));
|
|
113
|
-
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--
|
|
124
|
+
--_badge--background: var(--sc-kit--badge--background, var(--sc-kit--tone--red--fg));
|
|
114
125
|
}</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type IconProp } from '../icon';
|
|
2
|
-
import type { BadgeVariant } from './types';
|
|
2
|
+
import type { BadgeShape, BadgeVariant } from './types';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
type Props = {
|
|
5
5
|
/** Color variant @default 'neutral' */
|
|
@@ -8,6 +8,8 @@ type Props = {
|
|
|
8
8
|
size?: 'sm' | 'md';
|
|
9
9
|
/** Filled chip vs soft-tinted fill @default 'soft' */
|
|
10
10
|
style?: 'soft' | 'solid';
|
|
11
|
+
/** Corner style — pill (fully rounded) vs square (4px radius). @default 'pill' */
|
|
12
|
+
shape?: BadgeShape;
|
|
11
13
|
/** Stretch to fill the parent's inline axis, centering the content. @default false */
|
|
12
14
|
fullWidth?: boolean;
|
|
13
15
|
/** Optional leading icon. */
|
|
@@ -15,7 +17,7 @@ type Props = {
|
|
|
15
17
|
children: Snippet;
|
|
16
18
|
};
|
|
17
19
|
/**
|
|
18
|
-
*
|
|
20
|
+
* Status indicator, pill-shaped by default (`shape="square"` for a 4px-radius chip). Use `solid` for stronger emphasis, `soft` (default) for a calm tinted fill that pairs with body content. Optional leading `icon` renders in its own slot; the text content keeps its own ellipsis when it overflows.
|
|
19
21
|
*
|
|
20
22
|
* ### CSS Custom Properties
|
|
21
23
|
* | Property | Description | Default |
|
|
@@ -24,7 +26,7 @@ type Props = {
|
|
|
24
26
|
* | `--sc-kit--badge--background` | Background color | per variant + style |
|
|
25
27
|
* | `--sc-kit--badge--height` | Badge height | per size (20 / 24px) |
|
|
26
28
|
* | `--sc-kit--badge--padding-inline` | Horizontal padding | `0.875rem 0.75rem` |
|
|
27
|
-
* | `--sc-kit--badge--border-radius` | Corner radius | `var(--sc-kit--radius--pill)` |
|
|
29
|
+
* | `--sc-kit--badge--border-radius` | Corner radius | `shape="square"` → `var(--sc-kit--radius--sm)`, else `var(--sc-kit--radius--pill)` |
|
|
28
30
|
* | `--sc-kit--badge--font-size` | Content font size | per size |
|
|
29
31
|
* | `--sc-kit--badge--icon-size` | Leading icon size | per size (14 / 16px) |
|
|
30
32
|
* | `--sc-kit--badge--font-weight` | Font weight | `var(--sc-kit--font-weight--medium)` |
|
package/dist/ui/badge/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as Badge } from './cmp.badge.svelte';
|
|
2
|
-
export type { BadgeVariant } from './types';
|
|
2
|
+
export type { BadgeShape, BadgeVariant } from './types';
|
package/dist/ui/badge/types.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export type BadgeVariant = 'neutral' | 'accent' | 'success' | 'warning' | 'danger';
|
|
1
|
+
export type BadgeVariant = 'neutral' | 'accent' | 'auxiliary' | 'success' | 'warning' | 'danger';
|
|
2
|
+
export type BadgeShape = 'pill' | 'square';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script lang="ts">import { containFit } from './contain-fit';
|
|
2
|
+
const { basis = 'box', allowUpscale = false, alignInline = 'center', alignBlock = 'center', children } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="fit-box">
|
|
6
|
+
<div class="fit-box__scaler" class:fit-box__scaler--natural={basis === 'content'} use:containFit={{ allowUpscale, alignInline, alignBlock }}>
|
|
7
|
+
{@render children()}
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<!--
|
|
12
|
+
@component
|
|
13
|
+
Scales its content as one unit to fit the box like `object-fit: contain` — proportions preserved, bounded by both the box's width and
|
|
14
|
+
height, no crop, no scroll. A generic fit primitive for **cards and media content** (a card, an image/video, a fixed-size widget), not a
|
|
15
|
+
page-layout tool.
|
|
16
|
+
|
|
17
|
+
How it works: the box is `FitBox`'s **parent**, which must have a **definite size** (fixed / `%` / grid-track / aspect-ratio+width — a
|
|
18
|
+
content-driven `height: auto` parent collapses and nothing shows). The content is measured, then transform-scaled by
|
|
19
|
+
`min(boxW / contentW, boxH / contentH)` and positioned; a `ResizeObserver` re-fits on any box or content change.
|
|
20
|
+
|
|
21
|
+
Pick the mode with `basis`:
|
|
22
|
+
- `basis="box"` (default) — fluid, width-driven content (cards, text, single-aspect media). Fills the box width, scales down to fit the
|
|
23
|
+
height. Never enlarges.
|
|
24
|
+
- `basis="content"` — intrinsic content (`<img>` / `<video>`, fixed-size widgets). Keeps the content's natural size and fits both axes;
|
|
25
|
+
set `allowUpscale` to also enlarge it to fill.
|
|
26
|
+
|
|
27
|
+
`alignInline` / `alignBlock` place the content along an axis when fitting leaves free space there. Requires client-side JS (scales via
|
|
28
|
+
`transform`, so `position: fixed` descendants are relative to the box); content with a hard fixed width wider than the box is clipped
|
|
29
|
+
rather than contained.
|
|
30
|
+
-->
|
|
31
|
+
|
|
32
|
+
<style>.fit-box {
|
|
33
|
+
position: relative;
|
|
34
|
+
inline-size: 100%;
|
|
35
|
+
block-size: 100%;
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
}
|
|
38
|
+
.fit-box__scaler {
|
|
39
|
+
position: absolute;
|
|
40
|
+
inline-size: 100%;
|
|
41
|
+
max-inline-size: 100%;
|
|
42
|
+
visibility: hidden;
|
|
43
|
+
}
|
|
44
|
+
.fit-box__scaler--natural {
|
|
45
|
+
inline-size: auto;
|
|
46
|
+
}</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FitAlign, FitBasis } from './types';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
type Props = {
|
|
4
|
+
/**
|
|
5
|
+
* What the content's width is based on before it is scaled to fit:
|
|
6
|
+
* - `'box'` — content is laid out at the **box width** first (use for fluid, width-driven content: a card, a text block, single-aspect
|
|
7
|
+
* media). Height follows from that width; the content is then scaled **down** only if too tall. Never enlarges.
|
|
8
|
+
* - `'content'` — content keeps its **own natural size** (use for intrinsic content: `<img>` / `<video>`, a fixed-size widget). It is
|
|
9
|
+
* scaled to fit **both** axes and may be enlarged when `allowUpscale` is set.
|
|
10
|
+
* @default 'box'
|
|
11
|
+
*/
|
|
12
|
+
basis?: FitBasis;
|
|
13
|
+
/** Allow scaling content up past its natural size to fill the box. Takes effect only with `basis="content"` (`'box'` never enlarges). @default false */
|
|
14
|
+
allowUpscale?: boolean;
|
|
15
|
+
/** Inline-axis (horizontal) placement of the fitted content, visible only when free width remains after fitting. @default 'center' */
|
|
16
|
+
alignInline?: FitAlign;
|
|
17
|
+
/** Block-axis (vertical) placement of the fitted content, visible only when free height remains after fitting. @default 'center' */
|
|
18
|
+
alignBlock?: FitAlign;
|
|
19
|
+
children: Snippet;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Scales its content as one unit to fit the box like `object-fit: contain` — proportions preserved, bounded by both the box's width and
|
|
23
|
+
* height, no crop, no scroll. A generic fit primitive for **cards and media content** (a card, an image/video, a fixed-size widget), not a
|
|
24
|
+
* page-layout tool.
|
|
25
|
+
*
|
|
26
|
+
* How it works: the box is `FitBox`'s **parent**, which must have a **definite size** (fixed / `%` / grid-track / aspect-ratio+width — a
|
|
27
|
+
* content-driven `height: auto` parent collapses and nothing shows). The content is measured, then transform-scaled by
|
|
28
|
+
* `min(boxW / contentW, boxH / contentH)` and positioned; a `ResizeObserver` re-fits on any box or content change.
|
|
29
|
+
*
|
|
30
|
+
* Pick the mode with `basis`:
|
|
31
|
+
* - `basis="box"` (default) — fluid, width-driven content (cards, text, single-aspect media). Fills the box width, scales down to fit the
|
|
32
|
+
* height. Never enlarges.
|
|
33
|
+
* - `basis="content"` — intrinsic content (`<img>` / `<video>`, fixed-size widgets). Keeps the content's natural size and fits both axes;
|
|
34
|
+
* set `allowUpscale` to also enlarge it to fill.
|
|
35
|
+
*
|
|
36
|
+
* `alignInline` / `alignBlock` place the content along an axis when fitting leaves free space there. Requires client-side JS (scales via
|
|
37
|
+
* `transform`, so `position: fixed` descendants are relative to the box); content with a hard fixed width wider than the box is clipped
|
|
38
|
+
* rather than contained.
|
|
39
|
+
*/
|
|
40
|
+
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
41
|
+
type Cmp = ReturnType<typeof Cmp>;
|
|
42
|
+
export default Cmp;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FitAlign } from './types';
|
|
2
|
+
import type { Action } from 'svelte/action';
|
|
3
|
+
export type ContainFitParams = {
|
|
4
|
+
allowUpscale?: boolean;
|
|
5
|
+
alignInline?: FitAlign;
|
|
6
|
+
alignBlock?: FitAlign;
|
|
7
|
+
};
|
|
8
|
+
export declare const containFit: Action<HTMLElement, ContainFitParams | undefined>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const offset = (align, free) => (align === 'start' ? 0 : align === 'end' ? free : free / 2);
|
|
2
|
+
export const containFit = (node, params = {}) => {
|
|
3
|
+
const container = node.parentElement;
|
|
4
|
+
let allowUpscale = params.allowUpscale ?? false;
|
|
5
|
+
let alignInline = params.alignInline ?? 'center';
|
|
6
|
+
let alignBlock = params.alignBlock ?? 'center';
|
|
7
|
+
const apply = () => {
|
|
8
|
+
if (!container) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const boxWidth = container.clientWidth;
|
|
12
|
+
const boxHeight = container.clientHeight;
|
|
13
|
+
const naturalWidth = node.offsetWidth;
|
|
14
|
+
const naturalHeight = node.offsetHeight;
|
|
15
|
+
if (!boxWidth || !boxHeight || !naturalWidth || !naturalHeight) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
let scale = Math.min(boxWidth / naturalWidth, boxHeight / naturalHeight);
|
|
19
|
+
if (!allowUpscale) {
|
|
20
|
+
scale = Math.min(scale, 1);
|
|
21
|
+
}
|
|
22
|
+
const left = offset(alignInline, boxWidth - naturalWidth * scale);
|
|
23
|
+
const top = offset(alignBlock, boxHeight - naturalHeight * scale);
|
|
24
|
+
node.style.transformOrigin = '0 0';
|
|
25
|
+
node.style.transform = `scale(${scale})`;
|
|
26
|
+
node.style.left = `${left}px`;
|
|
27
|
+
node.style.top = `${top}px`;
|
|
28
|
+
node.style.visibility = 'visible';
|
|
29
|
+
};
|
|
30
|
+
const observer = new ResizeObserver(apply);
|
|
31
|
+
if (container) {
|
|
32
|
+
observer.observe(container);
|
|
33
|
+
}
|
|
34
|
+
observer.observe(node);
|
|
35
|
+
apply();
|
|
36
|
+
return {
|
|
37
|
+
update(next = {}) {
|
|
38
|
+
allowUpscale = next.allowUpscale ?? false;
|
|
39
|
+
alignInline = next.alignInline ?? 'center';
|
|
40
|
+
alignBlock = next.alignBlock ?? 'center';
|
|
41
|
+
apply();
|
|
42
|
+
},
|
|
43
|
+
destroy() {
|
|
44
|
+
observer.disconnect();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as FitBox } from './cmp.fit-box.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,69 +1,14 @@
|
|
|
1
|
-
<script lang="ts">import
|
|
1
|
+
<script lang="ts">import { ProgressBar } from '../../progress-bar';
|
|
2
|
+
import GridCardField from './cmp.grid-card-field.svelte';
|
|
2
3
|
const { label, stage, stages } = $props();
|
|
3
|
-
const currentStage = $derived(stages[Math.max(0, Math.min(stage, stages.length - 1))]);
|
|
4
|
-
const currentLabel = $derived(currentStage?.label ?? '');
|
|
5
4
|
</script>
|
|
6
5
|
|
|
7
6
|
<GridCardField label={label}>
|
|
8
|
-
<
|
|
9
|
-
<div
|
|
10
|
-
class="grid-card-progress-field__bar"
|
|
11
|
-
role="progressbar"
|
|
12
|
-
aria-valuemin={1}
|
|
13
|
-
aria-valuemax={stages.length}
|
|
14
|
-
aria-valuenow={stage + 1}
|
|
15
|
-
aria-label={currentLabel}>
|
|
16
|
-
{#each stages as s, i (i)}
|
|
17
|
-
<span class="grid-card-progress-field__segment" class:grid-card-progress-field__segment--filled={i <= stage} style:--_seg-color={s.color}></span>
|
|
18
|
-
{/each}
|
|
19
|
-
</div>
|
|
20
|
-
{#if currentLabel}
|
|
21
|
-
<span class="grid-card-progress-field__label">{currentLabel}</span>
|
|
22
|
-
{/if}
|
|
23
|
-
</div>
|
|
7
|
+
<ProgressBar stage={stage} stages={stages} />
|
|
24
8
|
</GridCardField>
|
|
25
9
|
|
|
26
10
|
<!--
|
|
27
11
|
@component
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
filled with the per-stage color, the rest are muted. Consumer supplies the per-stage color
|
|
31
|
-
(string — any valid CSS color or `var(--sc-kit--color--*)` reference). Useful for content
|
|
32
|
-
lifecycle indicators (Ideas → Draft → Review → Scheduled → Published).
|
|
33
|
-
|
|
34
|
-
### CSS Custom Properties
|
|
35
|
-
| Property | Description | Default |
|
|
36
|
-
|---|---|---|
|
|
37
|
-
| `--sc-kit--grid-card-progress-field--segment--height` | Bar segment height | `4px` |
|
|
38
|
-
| `--sc-kit--grid-card-progress-field--segment--gap` | Gap between segments | `2px` |
|
|
39
|
-
| `--sc-kit--grid-card-progress-field--segment--background--empty` | Empty segment color | `var(--sc-kit--color--bg--active)` |
|
|
12
|
+
Grid-card field pairing a field label with a multi-stage `ProgressBar`. See `ProgressBar`
|
|
13
|
+
(`@streamscloud/kit/ui/progress-bar`) for the bar's own CSS Custom Properties.
|
|
40
14
|
-->
|
|
41
|
-
|
|
42
|
-
<style>.grid-card-progress-field {
|
|
43
|
-
--_gcp--seg-height: var(--sc-kit--grid-card-progress-field--segment--height, 0.25rem);
|
|
44
|
-
--_gcp--seg-gap: var(--sc-kit--grid-card-progress-field--segment--gap, 0.125rem);
|
|
45
|
-
--_gcp--seg-empty: var(--sc-kit--grid-card-progress-field--segment--background--empty, var(--sc-kit--color--bg--active));
|
|
46
|
-
display: flex;
|
|
47
|
-
flex-direction: column;
|
|
48
|
-
gap: 0.125rem;
|
|
49
|
-
min-width: 0;
|
|
50
|
-
}
|
|
51
|
-
.grid-card-progress-field__bar {
|
|
52
|
-
display: flex;
|
|
53
|
-
gap: var(--_gcp--seg-gap);
|
|
54
|
-
width: 100%;
|
|
55
|
-
}
|
|
56
|
-
.grid-card-progress-field__segment {
|
|
57
|
-
flex: 1;
|
|
58
|
-
height: var(--_gcp--seg-height);
|
|
59
|
-
background: var(--_gcp--seg-empty);
|
|
60
|
-
border-radius: 0.0625rem;
|
|
61
|
-
}
|
|
62
|
-
.grid-card-progress-field__segment--filled {
|
|
63
|
-
background: var(--_seg-color);
|
|
64
|
-
}
|
|
65
|
-
.grid-card-progress-field__label {
|
|
66
|
-
font-size: 0.625rem;
|
|
67
|
-
color: var(--sc-kit--color--text--secondary);
|
|
68
|
-
line-height: var(--sc-kit--leading--tight);
|
|
69
|
-
}</style>
|
|
@@ -1,29 +1,17 @@
|
|
|
1
|
+
import { type ProgressBarStage } from '../../progress-bar';
|
|
1
2
|
type Props = {
|
|
2
3
|
label: string;
|
|
3
4
|
/** Current stage index, 0-based. Stages 0..stage (inclusive) are filled. */
|
|
4
5
|
stage: number;
|
|
5
6
|
/**
|
|
6
7
|
* Stage descriptors — at minimum `{ color }` per stage (consumer provides the CSS color).
|
|
7
|
-
* Length determines the total segment count.
|
|
8
|
+
* Length determines the total segment count.
|
|
8
9
|
*/
|
|
9
|
-
stages:
|
|
10
|
-
color: string;
|
|
11
|
-
label?: string;
|
|
12
|
-
}[];
|
|
10
|
+
stages: ProgressBarStage[];
|
|
13
11
|
};
|
|
14
12
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* filled with the per-stage color, the rest are muted. Consumer supplies the per-stage color
|
|
18
|
-
* (string — any valid CSS color or `var(--sc-kit--color--*)` reference). Useful for content
|
|
19
|
-
* lifecycle indicators (Ideas → Draft → Review → Scheduled → Published).
|
|
20
|
-
*
|
|
21
|
-
* ### CSS Custom Properties
|
|
22
|
-
* | Property | Description | Default |
|
|
23
|
-
* |---|---|---|
|
|
24
|
-
* | `--sc-kit--grid-card-progress-field--segment--height` | Bar segment height | `4px` |
|
|
25
|
-
* | `--sc-kit--grid-card-progress-field--segment--gap` | Gap between segments | `2px` |
|
|
26
|
-
* | `--sc-kit--grid-card-progress-field--segment--background--empty` | Empty segment color | `var(--sc-kit--color--bg--active)` |
|
|
13
|
+
* Grid-card field pairing a field label with a multi-stage `ProgressBar`. See `ProgressBar`
|
|
14
|
+
* (`@streamscloud/kit/ui/progress-bar`) for the bar's own CSS Custom Properties.
|
|
27
15
|
*/
|
|
28
16
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
29
17
|
type Cmp = ReturnType<typeof Cmp>;
|
|
@@ -4,7 +4,7 @@ const { icon, 'aria-label': ariaLabel, size = 'sm', active = false, disabled = f
|
|
|
4
4
|
|
|
5
5
|
{#if presentational}
|
|
6
6
|
<span class="toolbar-icon-button toolbar-icon-button--{size}" class:toolbar-icon-button--active={active}>
|
|
7
|
-
<Icon src={icon} size=
|
|
7
|
+
<Icon src={icon} size={size === 'sm' ? 'control-md' : 'md'} />
|
|
8
8
|
</span>
|
|
9
9
|
{:else}
|
|
10
10
|
<button
|
|
@@ -15,28 +15,29 @@ const { icon, 'aria-label': ariaLabel, size = 'sm', active = false, disabled = f
|
|
|
15
15
|
aria-label={ariaLabel}
|
|
16
16
|
aria-pressed={active}
|
|
17
17
|
onclick={() => on?.click?.()}>
|
|
18
|
-
<Icon src={icon} size=
|
|
18
|
+
<Icon src={icon} size={size === 'sm' ? 'control-md' : 'md'} />
|
|
19
19
|
</button>
|
|
20
20
|
{/if}
|
|
21
21
|
|
|
22
22
|
<!--
|
|
23
23
|
@component
|
|
24
|
-
A flat, square toolbar icon button (28px at `sm`, 32px at `md`, with a
|
|
25
|
-
hover only; `active` tints the glyph with the
|
|
26
|
-
a non-interactive `<span>` for use inside a wrapper that owns the click +
|
|
27
|
-
Shared by `ToolbarViewControls` and the `
|
|
24
|
+
A flat, square toolbar icon button (28px at `sm`, 32px at `md`, with a 16px glyph at `sm` / 20px at `md`).
|
|
25
|
+
Background appears on hover only; `active` tints the glyph with the primary text color (no background). Pass
|
|
26
|
+
`presentational` to render a non-interactive `<span>` for use inside a wrapper that owns the click +
|
|
27
|
+
semantics (e.g. a Popover trigger). Shared by `ToolbarViewControls` and the `ToolbarSortSelect` icon trigger
|
|
28
|
+
so they stay visually identical.
|
|
28
29
|
|
|
29
30
|
### CSS Custom Properties
|
|
30
31
|
| Property | Description | Default |
|
|
31
32
|
|---|---|---|
|
|
32
33
|
| `--sc-kit--toolbar-icon-button--color` | Idle glyph color | `var(--sc-kit--color--text--muted)` |
|
|
33
|
-
| `--sc-kit--toolbar-icon-button--color--active` | Active glyph color | `var(--sc-kit--color--
|
|
34
|
+
| `--sc-kit--toolbar-icon-button--color--active` | Active glyph color | `var(--sc-kit--color--text--primary)` |
|
|
34
35
|
| `--sc-kit--toolbar-icon-button--background--hover` | Hover background | `var(--sc-kit--color--bg--hover)` |
|
|
35
36
|
-->
|
|
36
37
|
|
|
37
38
|
<style>.toolbar-icon-button {
|
|
38
39
|
--_tib--color: var(--sc-kit--toolbar-icon-button--color, var(--sc-kit--color--text--muted));
|
|
39
|
-
--_tib--color-active: var(--sc-kit--toolbar-icon-button--color--active, var(--sc-kit--color--
|
|
40
|
+
--_tib--color-active: var(--sc-kit--toolbar-icon-button--color--active, var(--sc-kit--color--text--primary));
|
|
40
41
|
--_tib--background-hover: var(--sc-kit--toolbar-icon-button--background--hover, var(--sc-kit--color--bg--hover));
|
|
41
42
|
--_tib--size: var(--_tib--size-height);
|
|
42
43
|
--sc-kit--icon--color: var(--_tib--color);
|
|
@@ -15,16 +15,17 @@ type Props = {
|
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
* A flat, square toolbar icon button (28px at `sm`, 32px at `md`, with a
|
|
19
|
-
* hover only; `active` tints the glyph with the
|
|
20
|
-
* a non-interactive `<span>` for use inside a wrapper that owns the click +
|
|
21
|
-
* Shared by `ToolbarViewControls` and the `
|
|
18
|
+
* A flat, square toolbar icon button (28px at `sm`, 32px at `md`, with a 16px glyph at `sm` / 20px at `md`).
|
|
19
|
+
* Background appears on hover only; `active` tints the glyph with the primary text color (no background). Pass
|
|
20
|
+
* `presentational` to render a non-interactive `<span>` for use inside a wrapper that owns the click +
|
|
21
|
+
* semantics (e.g. a Popover trigger). Shared by `ToolbarViewControls` and the `ToolbarSortSelect` icon trigger
|
|
22
|
+
* so they stay visually identical.
|
|
22
23
|
*
|
|
23
24
|
* ### CSS Custom Properties
|
|
24
25
|
* | Property | Description | Default |
|
|
25
26
|
* |---|---|---|
|
|
26
27
|
* | `--sc-kit--toolbar-icon-button--color` | Idle glyph color | `var(--sc-kit--color--text--muted)` |
|
|
27
|
-
* | `--sc-kit--toolbar-icon-button--color--active` | Active glyph color | `var(--sc-kit--color--
|
|
28
|
+
* | `--sc-kit--toolbar-icon-button--color--active` | Active glyph color | `var(--sc-kit--color--text--primary)` |
|
|
28
29
|
* | `--sc-kit--toolbar-icon-button--background--hover` | Hover background | `var(--sc-kit--color--bg--hover)` |
|
|
29
30
|
*/
|
|
30
31
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
@@ -3,7 +3,7 @@ import ToolbarIconButton from './cmp.toolbar-icon-button.svelte';
|
|
|
3
3
|
import ToolbarSectionTitle from './cmp.toolbar-section-title.svelte';
|
|
4
4
|
import { ToolbarSortSelectLocalization } from './toolbar-sort-select-localization';
|
|
5
5
|
import IconFilter from '@fluentui/svg-icons/icons/filter_20_regular.svg?raw';
|
|
6
|
-
const { options, value, label, size = 'sm',
|
|
6
|
+
const { options, value, label, size = 'sm', trigger, compare, on } = $props();
|
|
7
7
|
const localization = new ToolbarSortSelectLocalization();
|
|
8
8
|
const orderByLabel = $derived(label ?? localization.orderBy);
|
|
9
9
|
const selection = $derived([value]);
|
|
@@ -14,18 +14,28 @@ const onChange = (next) => {
|
|
|
14
14
|
};
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
{#snippet defaultTrigger()}
|
|
18
|
+
<ToolbarIconButton presentational icon={IconFilter} size={size} />
|
|
19
|
+
{/snippet}
|
|
20
|
+
|
|
21
|
+
<ToolbarFilterSelect
|
|
22
|
+
label={orderByLabel}
|
|
23
|
+
single
|
|
24
|
+
footer={false}
|
|
25
|
+
size={size}
|
|
26
|
+
options={options}
|
|
27
|
+
value={selection}
|
|
28
|
+
compare={compare}
|
|
29
|
+
trigger={trigger ?? defaultTrigger}
|
|
30
|
+
on={{ change: onChange }}>
|
|
18
31
|
{#snippet header()}
|
|
19
32
|
<ToolbarSectionTitle>{orderByLabel}</ToolbarSectionTitle>
|
|
20
33
|
{/snippet}
|
|
21
|
-
{#snippet trigger()}
|
|
22
|
-
<ToolbarIconButton presentational icon={icon ?? IconFilter} size={size} />
|
|
23
|
-
{/snippet}
|
|
24
34
|
</ToolbarFilterSelect>
|
|
25
35
|
|
|
26
36
|
<!--
|
|
27
37
|
@component
|
|
28
38
|
A toolbar sort control: a `ToolbarFilterSelect` in single-select mode behind a flat icon trigger
|
|
29
|
-
(`ToolbarIconButton`), with an `Order by` section title above the
|
|
30
|
-
`ToolbarViewControls` via its `prepend` snippet.
|
|
39
|
+
(`ToolbarIconButton` by default, or a custom `trigger` snippet), with an `Order by` section title above the
|
|
40
|
+
options. Stand-alone, or composed into `ToolbarViewControls` via its `prepend` snippet.
|
|
31
41
|
-->
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SelectItem } from '../select';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
declare function $$render<S>(): {
|
|
3
4
|
props: {
|
|
4
5
|
options: SelectItem<S>[];
|
|
@@ -7,8 +8,8 @@ declare function $$render<S>(): {
|
|
|
7
8
|
label?: string;
|
|
8
9
|
/** Button size preset — sm = 28px, md = 32px. @default 'sm' */
|
|
9
10
|
size?: "sm" | "md";
|
|
10
|
-
/**
|
|
11
|
-
|
|
11
|
+
/** Custom presentational trigger content — replaces the default filter-icon button (the popover owns the click and is named by `label`). */
|
|
12
|
+
trigger?: Snippet;
|
|
12
13
|
/** Equality comparator — required when `S` is an object. */
|
|
13
14
|
compare?: (a: S, b: S) => boolean;
|
|
14
15
|
on?: {
|
|
@@ -36,8 +37,8 @@ interface $$IsomorphicComponent {
|
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
39
|
* A toolbar sort control: a `ToolbarFilterSelect` in single-select mode behind a flat icon trigger
|
|
39
|
-
* (`ToolbarIconButton`), with an `Order by` section title above the
|
|
40
|
-
* `ToolbarViewControls` via its `prepend` snippet.
|
|
40
|
+
* (`ToolbarIconButton` by default, or a custom `trigger` snippet), with an `Order by` section title above the
|
|
41
|
+
* options. Stand-alone, or composed into `ToolbarViewControls` via its `prepend` snippet.
|
|
41
42
|
*/
|
|
42
43
|
declare const Cmp: $$IsomorphicComponent;
|
|
43
44
|
type Cmp<S> = InstanceType<typeof Cmp<S>>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">import { CONTROL_ICON_SIZE, Icon, scaleUp } from '../icon';
|
|
2
2
|
import { ToolbarValueStepperLocalization } from './toolbar-value-stepper-localization';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import IconAdd from '@fluentui/svg-icons/icons/add_16_regular.svg?raw';
|
|
4
|
+
import IconSubtract from '@fluentui/svg-icons/icons/subtract_16_regular.svg?raw';
|
|
5
5
|
const { value, min = 0, max = 100, step = 1, valueSuffix = '', size = 'sm', on } = $props();
|
|
6
6
|
const localization = new ToolbarValueStepperLocalization();
|
|
7
7
|
const iconSize = $derived(scaleUp(CONTROL_ICON_SIZE[size]));
|
|
@@ -17,25 +17,25 @@ const increase = () => {
|
|
|
17
17
|
|
|
18
18
|
<div class="toolbar-value-stepper toolbar-value-stepper--{size}" role="group" aria-label={localization.value}>
|
|
19
19
|
<button type="button" class="toolbar-value-stepper__btn" onclick={decrease} disabled={atMin} aria-label={localization.decreaseValue}>
|
|
20
|
-
<Icon src={
|
|
20
|
+
<Icon src={IconSubtract} size={iconSize} />
|
|
21
21
|
</button>
|
|
22
22
|
<span class="toolbar-value-stepper__value">{value}{valueSuffix}</span>
|
|
23
23
|
<button type="button" class="toolbar-value-stepper__btn" onclick={increase} disabled={atMax} aria-label={localization.increaseValue}>
|
|
24
|
-
<Icon src={
|
|
24
|
+
<Icon src={IconAdd} size={iconSize} />
|
|
25
25
|
</button>
|
|
26
26
|
</div>
|
|
27
27
|
|
|
28
28
|
<!--
|
|
29
29
|
@component
|
|
30
|
-
A compact `[
|
|
31
|
-
and `max` by `step`, with an optional `valueSuffix` (e.g. ` %`).
|
|
30
|
+
A compact `[−] value [+]` stepper for toolbars — increment / decrement a numeric value between `min`
|
|
31
|
+
and `max` by `step`, with an optional `valueSuffix` (e.g. ` %`). The − / + buttons disable at the limits.
|
|
32
32
|
Shares the field-height preset so it lines up with other toolbar controls.
|
|
33
33
|
|
|
34
34
|
### CSS Custom Properties
|
|
35
35
|
| Property | Description | Default |
|
|
36
36
|
|---|---|---|
|
|
37
|
-
| `--sc-kit--toolbar-value-stepper--value--min-inline-size` | Min width of the value readout (keeps
|
|
38
|
-
| `--sc-kit--toolbar-value-stepper--icon--color` |
|
|
37
|
+
| `--sc-kit--toolbar-value-stepper--value--min-inline-size` | Min width of the value readout (keeps the buttons from shifting) | `2.25rem` |
|
|
38
|
+
| `--sc-kit--toolbar-value-stepper--icon--color` | Button icon color | `var(--sc-kit--color--accent)` |
|
|
39
39
|
-->
|
|
40
40
|
|
|
41
41
|
<style>.toolbar-value-stepper {
|
|
@@ -15,15 +15,15 @@ type Props = {
|
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
* A compact `[
|
|
19
|
-
* and `max` by `step`, with an optional `valueSuffix` (e.g. ` %`).
|
|
18
|
+
* A compact `[−] value [+]` stepper for toolbars — increment / decrement a numeric value between `min`
|
|
19
|
+
* and `max` by `step`, with an optional `valueSuffix` (e.g. ` %`). The − / + buttons disable at the limits.
|
|
20
20
|
* Shares the field-height preset so it lines up with other toolbar controls.
|
|
21
21
|
*
|
|
22
22
|
* ### CSS Custom Properties
|
|
23
23
|
* | Property | Description | Default |
|
|
24
24
|
* |---|---|---|
|
|
25
|
-
* | `--sc-kit--toolbar-value-stepper--value--min-inline-size` | Min width of the value readout (keeps
|
|
26
|
-
* | `--sc-kit--toolbar-value-stepper--icon--color` |
|
|
25
|
+
* | `--sc-kit--toolbar-value-stepper--value--min-inline-size` | Min width of the value readout (keeps the buttons from shifting) | `2.25rem` |
|
|
26
|
+
* | `--sc-kit--toolbar-value-stepper--icon--color` | Button icon color | `var(--sc-kit--color--accent)` |
|
|
27
27
|
*/
|
|
28
28
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
29
29
|
type Cmp = ReturnType<typeof Cmp>;
|
|
@@ -3,8 +3,9 @@ import ToolbarIconButton from './cmp.toolbar-icon-button.svelte';
|
|
|
3
3
|
import { ToolbarViewControlsLocalization } from './toolbar-view-controls-localization';
|
|
4
4
|
import IconAppsList from '@fluentui/svg-icons/icons/apps_list_20_regular.svg?raw';
|
|
5
5
|
import IconGrid from '@fluentui/svg-icons/icons/grid_20_regular.svg?raw';
|
|
6
|
+
import IconCard from '@fluentui/svg-icons/icons/square_20_regular.svg?raw';
|
|
6
7
|
import { untrack } from 'svelte';
|
|
7
|
-
const { view, size = 'sm', viewStorageKey, prepend, on } = $props();
|
|
8
|
+
const { view, size = 'sm', showCard = false, viewStorageKey, prepend, on } = $props();
|
|
8
9
|
const localization = new ToolbarViewControlsLocalization();
|
|
9
10
|
let storage = null;
|
|
10
11
|
$effect(() => untrack(() => {
|
|
@@ -13,7 +14,7 @@ $effect(() => untrack(() => {
|
|
|
13
14
|
}
|
|
14
15
|
storage = LocalStorageItem.of(viewStorageKey);
|
|
15
16
|
const saved = storage.get();
|
|
16
|
-
if (saved === 'list' || saved === 'grid') {
|
|
17
|
+
if (saved === 'list' || saved === 'grid' || (saved === 'card' && showCard)) {
|
|
17
18
|
on?.viewChange?.(saved);
|
|
18
19
|
}
|
|
19
20
|
}));
|
|
@@ -29,22 +30,26 @@ const setView = (next) => {
|
|
|
29
30
|
{/if}
|
|
30
31
|
<ToolbarIconButton icon={IconAppsList} size={size} active={view === 'list'} aria-label={localization.listView} on={{ click: () => setView('list') }} />
|
|
31
32
|
<ToolbarIconButton icon={IconGrid} size={size} active={view === 'grid'} aria-label={localization.gridView} on={{ click: () => setView('grid') }} />
|
|
33
|
+
{#if showCard}
|
|
34
|
+
<ToolbarIconButton icon={IconCard} size={size} active={view === 'card'} aria-label={localization.cardView} on={{ click: () => setView('card') }} />
|
|
35
|
+
{/if}
|
|
32
36
|
</div>
|
|
33
37
|
|
|
34
38
|
<!--
|
|
35
39
|
@component
|
|
36
40
|
A toolbar view switcher: a `list` / `grid` toggle built on the shared `ToolbarIconButton` (flat, 28px at
|
|
37
|
-
`sm` / 32px at `md`, 20px glyph)
|
|
38
|
-
leading control — e.g. `ToolbarSortSelect` — through the
|
|
41
|
+
`sm` / 32px at `md`, 20px glyph), with an opt-in `card` button via `showCard`. Pass `viewStorageKey` to
|
|
42
|
+
persist the view across reloads, and compose a leading control — e.g. `ToolbarSortSelect` — through the
|
|
43
|
+
`prepend` snippet.
|
|
39
44
|
|
|
40
45
|
### CSS Custom Properties
|
|
41
46
|
| Property | Description | Default |
|
|
42
47
|
|---|---|---|
|
|
43
|
-
| `--sc-kit--toolbar-view-controls--gap` | Gap between the controls | `var(--sc-kit--space--
|
|
48
|
+
| `--sc-kit--toolbar-view-controls--gap` | Gap between the controls | `var(--sc-kit--space--2)` |
|
|
44
49
|
-->
|
|
45
50
|
|
|
46
51
|
<style>.toolbar-view-controls {
|
|
47
|
-
--_tvc--gap: var(--sc-kit--toolbar-view-controls--gap, var(--sc-kit--space--
|
|
52
|
+
--_tvc--gap: var(--sc-kit--toolbar-view-controls--gap, var(--sc-kit--space--2));
|
|
48
53
|
display: inline-flex;
|
|
49
54
|
align-items: center;
|
|
50
55
|
gap: var(--_tvc--gap);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
type ViewMode = 'list' | 'grid';
|
|
2
|
+
type ViewMode = 'list' | 'grid' | 'card';
|
|
3
3
|
type Props = {
|
|
4
4
|
view: ViewMode;
|
|
5
5
|
/** Button size preset — sm = 28px, md = 32px. @default 'sm' */
|
|
6
6
|
size?: 'sm' | 'md';
|
|
7
|
+
/** Reveal the `card` view button alongside list / grid. @default false */
|
|
8
|
+
showCard?: boolean;
|
|
7
9
|
/** Persist the view mode under this localStorage key. */
|
|
8
10
|
viewStorageKey?: string;
|
|
9
11
|
/** Leading content before the view toggle — e.g. a composed `ToolbarSortSelect`. */
|
|
@@ -14,13 +16,14 @@ type Props = {
|
|
|
14
16
|
};
|
|
15
17
|
/**
|
|
16
18
|
* A toolbar view switcher: a `list` / `grid` toggle built on the shared `ToolbarIconButton` (flat, 28px at
|
|
17
|
-
* `sm` / 32px at `md`, 20px glyph)
|
|
18
|
-
* leading control — e.g. `ToolbarSortSelect` — through the
|
|
19
|
+
* `sm` / 32px at `md`, 20px glyph), with an opt-in `card` button via `showCard`. Pass `viewStorageKey` to
|
|
20
|
+
* persist the view across reloads, and compose a leading control — e.g. `ToolbarSortSelect` — through the
|
|
21
|
+
* `prepend` snippet.
|
|
19
22
|
*
|
|
20
23
|
* ### CSS Custom Properties
|
|
21
24
|
* | Property | Description | Default |
|
|
22
25
|
* |---|---|---|
|
|
23
|
-
* | `--sc-kit--toolbar-view-controls--gap` | Gap between the controls | `var(--sc-kit--space--
|
|
26
|
+
* | `--sc-kit--toolbar-view-controls--gap` | Gap between the controls | `var(--sc-kit--space--2)` |
|
|
24
27
|
*/
|
|
25
28
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
26
29
|
type Cmp = ReturnType<typeof Cmp>;
|
|
@@ -2,7 +2,8 @@ import { AppLocale } from '../../core/locale';
|
|
|
2
2
|
const loc = {
|
|
3
3
|
label: { en: 'View options', no: 'Visningsalternativer' },
|
|
4
4
|
listView: { en: 'List view', no: 'Listevisning' },
|
|
5
|
-
gridView: { en: 'Grid view', no: 'Rutenettvisning' }
|
|
5
|
+
gridView: { en: 'Grid view', no: 'Rutenettvisning' },
|
|
6
|
+
cardView: { en: 'Card view', no: 'Kortvisning' }
|
|
6
7
|
};
|
|
7
8
|
export class ToolbarViewControlsLocalization {
|
|
8
9
|
get label() {
|
|
@@ -14,4 +15,7 @@ export class ToolbarViewControlsLocalization {
|
|
|
14
15
|
get gridView() {
|
|
15
16
|
return loc.gridView[AppLocale.current];
|
|
16
17
|
}
|
|
18
|
+
get cardView() {
|
|
19
|
+
return loc.cardView[AppLocale.current];
|
|
20
|
+
}
|
|
17
21
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script lang="ts">import { Tooltip } from '../tooltip';
|
|
2
|
+
import { ProgressBarLocalization } from './progress-bar-localization';
|
|
3
|
+
import { PROGRESS_BAR_TONES } from './types';
|
|
4
|
+
const { stage, stages, showLabel = true } = $props();
|
|
5
|
+
const localization = new ProgressBarLocalization();
|
|
6
|
+
const currentStage = $derived(stages[Math.max(0, Math.min(stage, stages.length - 1))]);
|
|
7
|
+
const currentLabel = $derived(currentStage?.label ?? '');
|
|
8
|
+
const tooltipLabel = $derived(!showLabel ? currentLabel : '');
|
|
9
|
+
const segmentColor = (s) => {
|
|
10
|
+
const value = s.color ?? 'gray';
|
|
11
|
+
return PROGRESS_BAR_TONES.includes(value) ? `var(--sc-kit--tone--${value}--fg)` : value;
|
|
12
|
+
};
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div class="progress-bar">
|
|
16
|
+
{#if tooltipLabel}
|
|
17
|
+
<Tooltip text={tooltipLabel}>
|
|
18
|
+
{@render track()}
|
|
19
|
+
</Tooltip>
|
|
20
|
+
{:else}
|
|
21
|
+
{@render track()}
|
|
22
|
+
{/if}
|
|
23
|
+
{#if showLabel && currentLabel}
|
|
24
|
+
<span class="progress-bar__label">{currentLabel}</span>
|
|
25
|
+
{/if}
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
{#snippet track()}
|
|
29
|
+
<div
|
|
30
|
+
class="progress-bar__track"
|
|
31
|
+
role="progressbar"
|
|
32
|
+
aria-valuemin={1}
|
|
33
|
+
aria-valuemax={stages.length}
|
|
34
|
+
aria-valuenow={stage + 1}
|
|
35
|
+
aria-valuetext={currentLabel || undefined}
|
|
36
|
+
aria-label={localization.label}>
|
|
37
|
+
{#each stages as s, i (i)}
|
|
38
|
+
<span class="progress-bar__segment" class:progress-bar__segment--filled={i <= stage} style:--_--pb--segment-color={segmentColor(s)}></span>
|
|
39
|
+
{/each}
|
|
40
|
+
</div>
|
|
41
|
+
{/snippet}
|
|
42
|
+
|
|
43
|
+
<!--
|
|
44
|
+
@component
|
|
45
|
+
Multi-stage workflow progress indicator. Pass `stages` as an array of `{ color, label? }`
|
|
46
|
+
descriptors — the bar renders one segment per stage; segments `0..stage` (inclusive) are filled,
|
|
47
|
+
the rest use the empty-segment color. Each stage's `color` is either a named tone from the shared
|
|
48
|
+
palette (same schemes as `Badge`, e.g. `'green'`) or a raw CSS color / `var(--sc-kit--*)`. Useful
|
|
49
|
+
for content-lifecycle indicators (Ideas → Draft → Review → Scheduled → Published) in cards, table
|
|
50
|
+
cells, or post pages. `showLabel={false}` drops the inline current-stage label (e.g. in a compact
|
|
51
|
+
table cell) and surfaces it in a hover tooltip instead.
|
|
52
|
+
|
|
53
|
+
### CSS Custom Properties
|
|
54
|
+
| Property | Description | Default |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `--sc-kit--progress-bar--segment--height` | Bar segment height | `4px` |
|
|
57
|
+
| `--sc-kit--progress-bar--segment--gap` | Gap between segments | `2px` |
|
|
58
|
+
| `--sc-kit--progress-bar--segment--background--empty` | Empty segment color | `var(--sc-kit--color--bg--active)` |
|
|
59
|
+
| `--sc-kit--progress-bar--track--padding-block` | Vertical padding around the bar (enlarges the hover/click target) | `5px` |
|
|
60
|
+
-->
|
|
61
|
+
|
|
62
|
+
<style>.progress-bar {
|
|
63
|
+
--_pb--seg-height: var(--sc-kit--progress-bar--segment--height, 0.25rem);
|
|
64
|
+
--_pb--seg-gap: var(--sc-kit--progress-bar--segment--gap, 0.125rem);
|
|
65
|
+
--_pb--seg-empty: var(--sc-kit--progress-bar--segment--background--empty, var(--sc-kit--color--bg--active));
|
|
66
|
+
--_pb--track-padding-block: var(--sc-kit--progress-bar--track--padding-block, 0.3125rem);
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: 0.125rem;
|
|
70
|
+
min-width: 0;
|
|
71
|
+
}
|
|
72
|
+
.progress-bar__track {
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: var(--_pb--seg-gap);
|
|
76
|
+
width: 100%;
|
|
77
|
+
padding-block: var(--_pb--track-padding-block);
|
|
78
|
+
}
|
|
79
|
+
.progress-bar__segment {
|
|
80
|
+
flex: 1;
|
|
81
|
+
height: var(--_pb--seg-height);
|
|
82
|
+
background: var(--_pb--seg-empty);
|
|
83
|
+
border-radius: 0.0625rem;
|
|
84
|
+
}
|
|
85
|
+
.progress-bar__segment--filled {
|
|
86
|
+
background: var(--_--pb--segment-color);
|
|
87
|
+
}
|
|
88
|
+
.progress-bar__label {
|
|
89
|
+
font-size: 0.625rem;
|
|
90
|
+
color: var(--sc-kit--color--text--secondary);
|
|
91
|
+
line-height: var(--sc-kit--leading--tight);
|
|
92
|
+
}</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ProgressBarStage } from './types';
|
|
2
|
+
type Props = {
|
|
3
|
+
/** Current stage index, 0-based. Stages 0..stage (inclusive) are filled. */
|
|
4
|
+
stage: number;
|
|
5
|
+
/**
|
|
6
|
+
* Stage descriptors — at minimum `{ color }` per stage (consumer provides the CSS color).
|
|
7
|
+
* Length determines the total segment count.
|
|
8
|
+
*/
|
|
9
|
+
stages: ProgressBarStage[];
|
|
10
|
+
/** Show the current stage's label below the bar. When `false`, the label surfaces in a tooltip on hover instead. @default true */
|
|
11
|
+
showLabel?: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Multi-stage workflow progress indicator. Pass `stages` as an array of `{ color, label? }`
|
|
15
|
+
* descriptors — the bar renders one segment per stage; segments `0..stage` (inclusive) are filled,
|
|
16
|
+
* the rest use the empty-segment color. Each stage's `color` is either a named tone from the shared
|
|
17
|
+
* palette (same schemes as `Badge`, e.g. `'green'`) or a raw CSS color / `var(--sc-kit--*)`. Useful
|
|
18
|
+
* for content-lifecycle indicators (Ideas → Draft → Review → Scheduled → Published) in cards, table
|
|
19
|
+
* cells, or post pages. `showLabel={false}` drops the inline current-stage label (e.g. in a compact
|
|
20
|
+
* table cell) and surfaces it in a hover tooltip instead.
|
|
21
|
+
*
|
|
22
|
+
* ### CSS Custom Properties
|
|
23
|
+
* | Property | Description | Default |
|
|
24
|
+
* |---|---|---|
|
|
25
|
+
* | `--sc-kit--progress-bar--segment--height` | Bar segment height | `4px` |
|
|
26
|
+
* | `--sc-kit--progress-bar--segment--gap` | Gap between segments | `2px` |
|
|
27
|
+
* | `--sc-kit--progress-bar--segment--background--empty` | Empty segment color | `var(--sc-kit--color--bg--active)` |
|
|
28
|
+
* | `--sc-kit--progress-bar--track--padding-block` | Vertical padding around the bar (enlarges the hover/click target) | `5px` |
|
|
29
|
+
*/
|
|
30
|
+
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
31
|
+
type Cmp = ReturnType<typeof Cmp>;
|
|
32
|
+
export default Cmp;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ProgressBar } from './cmp.progress-bar.svelte';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const PROGRESS_BAR_TONES: readonly ["gray", "blue", "green", "red", "orange", "teal"];
|
|
2
|
+
export type ProgressBarTone = (typeof PROGRESS_BAR_TONES)[number];
|
|
3
|
+
export type ProgressBarStage = {
|
|
4
|
+
/** A named tone from the shared palette (→ `--sc-kit--tone--<tone>--fg`) or any raw CSS color / `var(--sc-kit--*)`. @default 'gray' */
|
|
5
|
+
color?: ProgressBarTone | (string & {});
|
|
6
|
+
label?: string;
|
|
7
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PROGRESS_BAR_TONES = ['gray', 'blue', 'green', 'red', 'orange', 'teal'];
|
|
@@ -6,7 +6,7 @@ const variant = $derived(column.variantFactory ? column.variantFactory(item) : '
|
|
|
6
6
|
|
|
7
7
|
{#if getValueForColumn(column, item) !== null}
|
|
8
8
|
<div class="table-badge-cell" class:table-badge-cell--centered={centered}>
|
|
9
|
-
<Badge variant={variant} fullWidth={column.fullWidth}>{getValueForColumn(column, item)}</Badge>
|
|
9
|
+
<Badge variant={variant} shape={column.shape ?? 'pill'} fullWidth={column.fullWidth}>{getValueForColumn(column, item)}</Badge>
|
|
10
10
|
</div>
|
|
11
11
|
{/if}
|
|
12
12
|
|
|
@@ -21,6 +21,14 @@ const getValueForColumn = (column, item) => (column.valueFactory ? column.valueF
|
|
|
21
21
|
</div>
|
|
22
22
|
</ProportionalContainer>
|
|
23
23
|
</div>
|
|
24
|
+
{:else if column.format === 'vertical-cover'}
|
|
25
|
+
<div class="table-image-cell__image table-image-cell__image--vertical-cover">
|
|
26
|
+
<Image src={getValueForColumn(column, item)} />
|
|
27
|
+
</div>
|
|
28
|
+
{:else if column.format === 'horizontal-cover'}
|
|
29
|
+
<div class="table-image-cell__image table-image-cell__image--horizontal-cover">
|
|
30
|
+
<Image src={getValueForColumn(column, item)} />
|
|
31
|
+
</div>
|
|
24
32
|
{/if}
|
|
25
33
|
</div>
|
|
26
34
|
|
|
@@ -35,6 +43,7 @@ const getValueForColumn = (column, item) => (column.valueFactory ? column.valueF
|
|
|
35
43
|
justify-content: center;
|
|
36
44
|
}
|
|
37
45
|
.table-image-cell__image {
|
|
46
|
+
--_table-image-cell--cover-height: var(--_table-image-cell--image--size, 2.5em);
|
|
38
47
|
display: flex;
|
|
39
48
|
justify-content: center;
|
|
40
49
|
align-items: center;
|
|
@@ -51,10 +60,18 @@ const getValueForColumn = (column, item) => (column.valueFactory ? column.valueF
|
|
|
51
60
|
}
|
|
52
61
|
.table-image-cell__image--contain {
|
|
53
62
|
--sc-kit--image--object-fit: contain;
|
|
54
|
-
height:
|
|
63
|
+
height: var(--_table-image-cell--cover-height);
|
|
55
64
|
}
|
|
56
65
|
.table-image-cell__image--cover {
|
|
57
|
-
height:
|
|
66
|
+
height: var(--_table-image-cell--cover-height);
|
|
67
|
+
}
|
|
68
|
+
.table-image-cell__image--vertical-cover {
|
|
69
|
+
height: var(--_table-image-cell--cover-height);
|
|
70
|
+
width: calc(var(--_table-image-cell--cover-height) * 9 / 16);
|
|
71
|
+
}
|
|
72
|
+
.table-image-cell__image--horizontal-cover {
|
|
73
|
+
height: var(--_table-image-cell--cover-height);
|
|
74
|
+
width: calc(var(--_table-image-cell--cover-height) * 16 / 9);
|
|
58
75
|
}
|
|
59
76
|
.table-image-cell__cover-image-placer {
|
|
60
77
|
width: 100%;
|
package/dist/ui/table/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Repository } from '../../core/repository';
|
|
2
|
-
import type { BadgeVariant } from '../badge';
|
|
2
|
+
import type { BadgeShape, BadgeVariant } from '../badge';
|
|
3
3
|
import type { ButtonSize, ButtonVariant } from '../button';
|
|
4
4
|
import { DynamicComponentModel } from '../dynamic-component';
|
|
5
5
|
import type { IconProp } from '../icon';
|
|
@@ -37,6 +37,8 @@ export interface ITableBadgeColumn<T> extends ITableColumn<T> {
|
|
|
37
37
|
valueFactory?: ((item: T) => string) | null;
|
|
38
38
|
/** Stretch the badge to fill the column width. @default false */
|
|
39
39
|
fullWidth?: boolean;
|
|
40
|
+
/** Corner style of the rendered badge. @default 'pill' */
|
|
41
|
+
shape?: BadgeShape;
|
|
40
42
|
}
|
|
41
43
|
export interface ITableByColumn<T> extends ITableColumn<T> {
|
|
42
44
|
type: 'by';
|
|
@@ -101,7 +103,7 @@ export interface ITableTextColumn<T> extends ITableColumn<T> {
|
|
|
101
103
|
valueFactory?: ((item: T) => string) | null;
|
|
102
104
|
}
|
|
103
105
|
export type TableTextColumnFormat = 'text' | 'html' | 'money' | 'date' | 'date-time';
|
|
104
|
-
export type TableImageColumnFormat = 'circle' | 'square-contain' | 'square-cover';
|
|
106
|
+
export type TableImageColumnFormat = 'circle' | 'square-contain' | 'square-cover' | 'vertical-cover' | 'horizontal-cover';
|
|
105
107
|
export type TableItemAction<T> = {
|
|
106
108
|
title: string;
|
|
107
109
|
icon?: IconProp;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamscloud/kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"author": "StreamsCloud",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -195,6 +195,10 @@
|
|
|
195
195
|
"types": "./dist/ui/file-uploader/index.d.ts",
|
|
196
196
|
"svelte": "./dist/ui/file-uploader/index.js"
|
|
197
197
|
},
|
|
198
|
+
"./ui/fit-box": {
|
|
199
|
+
"types": "./dist/ui/fit-box/index.d.ts",
|
|
200
|
+
"svelte": "./dist/ui/fit-box/index.js"
|
|
201
|
+
},
|
|
198
202
|
"./ui/form-field": {
|
|
199
203
|
"types": "./dist/ui/form-field/index.d.ts",
|
|
200
204
|
"svelte": "./dist/ui/form-field/index.js"
|
|
@@ -331,6 +335,10 @@
|
|
|
331
335
|
"types": "./dist/ui/progress/index.d.ts",
|
|
332
336
|
"svelte": "./dist/ui/progress/index.js"
|
|
333
337
|
},
|
|
338
|
+
"./ui/progress-bar": {
|
|
339
|
+
"types": "./dist/ui/progress-bar/index.d.ts",
|
|
340
|
+
"svelte": "./dist/ui/progress-bar/index.js"
|
|
341
|
+
},
|
|
334
342
|
"./ui/proportional-container": {
|
|
335
343
|
"types": "./dist/ui/proportional-container/index.d.ts",
|
|
336
344
|
"svelte": "./dist/ui/proportional-container/index.js"
|