@streamscloud/kit 0.9.1 → 0.9.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/README.md +28 -0
- package/dist/core/utils/array-helper.d.ts +5 -0
- package/dist/core/utils/array-helper.js +14 -0
- package/dist/ui/announcement-banner/announcement-banner-localization.d.ts +3 -0
- package/dist/ui/announcement-banner/announcement-banner-localization.js +12 -0
- package/dist/ui/announcement-banner/cmp.announcement-banner.svelte +153 -0
- package/dist/ui/announcement-banner/cmp.announcement-banner.svelte.d.ts +39 -0
- package/dist/ui/announcement-banner/index.d.ts +2 -0
- package/dist/ui/announcement-banner/index.js +1 -0
- package/dist/ui/announcement-banner/types.d.ts +1 -0
- package/dist/ui/announcement-banner/types.js +1 -0
- package/dist/ui/avatar/cmp.avatar.svelte +1 -1
- package/dist/ui/button/cmp.button.svelte +3 -3
- package/dist/ui/button/cmp.button.svelte.d.ts +1 -1
- package/dist/ui/checkbox/cmp.checkbox.svelte +10 -6
- package/dist/ui/chip-group/cmp.chip-group.svelte +2 -1
- package/dist/ui/date-picker/cmp.date-picker.svelte +1 -1
- package/dist/ui/input/cmp.input.svelte +1 -1
- package/dist/ui/numeral-input/cmp.numeral-input.svelte +1 -1
- package/dist/ui/option-pill/cmp.option-pill.svelte +2 -1
- package/dist/ui/pin-input/cmp.pin-input.svelte +2 -1
- package/dist/ui/popover/cmp.popover-item.svelte +10 -1
- package/dist/ui/popover/cmp.popover-item.svelte.d.ts +4 -0
- package/dist/ui/radio/cmp.radio-card.svelte +7 -2
- package/dist/ui/radio/cmp.radio.svelte +8 -2
- package/dist/ui/toggle/cmp.toggle.svelte +2 -2
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @streamscloud/kit
|
|
2
|
+
|
|
3
|
+
Shared UI components + utilities for StreamsCloud frontend apps. Svelte 5 (runes), TypeScript strict, SCSS/BEM.
|
|
4
|
+
|
|
5
|
+
## Develop
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install
|
|
9
|
+
npm run dev # dev playground, http://localhost:3011
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
After cloning, run `./setup-claude.sh` (Windows: `.\setup-claude.ps1`) to wire shared Claude hooks and skills from the sibling `development-memory` checkout —
|
|
13
|
+
without it agent sessions run with no hooks and no shared skills. Re-run after changing `.claude/sc-skills`.
|
|
14
|
+
|
|
15
|
+
## Checks
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm run check # svelte-check + tsc for node files
|
|
19
|
+
npm run lint # prettier --check + eslint
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Release
|
|
23
|
+
|
|
24
|
+
Always via the npm scripts — `npm run publish:prod` / `npm run publish:dev`, never raw `npm publish`. The full workflow is in `.claude/CLAUDE.md`.
|
|
25
|
+
|
|
26
|
+
## Context
|
|
27
|
+
|
|
28
|
+
Project facts, decisions (ADRs), and reference live in [`project-dev-memory/`](project-dev-memory/README.md). Agent rules live in `.claude/`.
|
|
@@ -2,6 +2,11 @@ export declare class ArrayHelper {
|
|
|
2
2
|
static distinct<T>(items: T[]): T[];
|
|
3
3
|
static distinctBy<T>(items: T[], keyFn: (elem: T) => unknown): T[];
|
|
4
4
|
static intersect<T>(arrays: T[][]): T[];
|
|
5
|
+
/**
|
|
6
|
+
* Splits `items` into sequential chunks of at most `size` elements.
|
|
7
|
+
* If `size < 1`, returns a single chunk containing all items.
|
|
8
|
+
*/
|
|
9
|
+
static chunk<T>(items: T[], size: number): T[][];
|
|
5
10
|
static assertHasValue<T>(value: T | null | undefined): value is T;
|
|
6
11
|
/**
|
|
7
12
|
* Find position ("from" and "to" indexes) of the element that was moved inside array (only one element should be moved)
|
|
@@ -11,6 +11,20 @@ export class ArrayHelper {
|
|
|
11
11
|
}
|
|
12
12
|
return arrays.reduce((result, current) => result.filter((item) => current.includes(item)));
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Splits `items` into sequential chunks of at most `size` elements.
|
|
16
|
+
* If `size < 1`, returns a single chunk containing all items.
|
|
17
|
+
*/
|
|
18
|
+
static chunk(items, size) {
|
|
19
|
+
if (size < 1) {
|
|
20
|
+
return [[...items]];
|
|
21
|
+
}
|
|
22
|
+
const result = [];
|
|
23
|
+
for (let i = 0; i < items.length; i += size) {
|
|
24
|
+
result.push(items.slice(i, i + size));
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
14
28
|
static assertHasValue(value) {
|
|
15
29
|
return value !== null && value !== undefined;
|
|
16
30
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<script lang="ts">import { Icon, IconSlot } from '../icon';
|
|
2
|
+
import { AnnouncementBannerLocalization } from './announcement-banner-localization';
|
|
3
|
+
import IconDismiss from '@fluentui/svg-icons/icons/dismiss_12_regular.svg?raw';
|
|
4
|
+
let { variant = 'info', title, dismissible = false, icon, action, children, on } = $props();
|
|
5
|
+
const localization = new AnnouncementBannerLocalization();
|
|
6
|
+
let visible = $state(true);
|
|
7
|
+
const role = $derived(variant === 'maintenance' ? 'alert' : 'status');
|
|
8
|
+
const dismiss = () => {
|
|
9
|
+
visible = false;
|
|
10
|
+
on?.dismiss?.();
|
|
11
|
+
};
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
{#if visible}
|
|
15
|
+
<div class="announcement-banner announcement-banner--{variant}" class:announcement-banner--dismissible={dismissible} role={role}>
|
|
16
|
+
<div class="announcement-banner__content">
|
|
17
|
+
{#if icon}
|
|
18
|
+
<span class="announcement-banner__icon" aria-hidden="true"><IconSlot icon={icon} /></span>
|
|
19
|
+
{/if}
|
|
20
|
+
<div class="announcement-banner__body">
|
|
21
|
+
{#if title}<strong class="announcement-banner__title">{title}</strong>{/if}
|
|
22
|
+
{#if children}<span class="announcement-banner__description">{@render children()}</span>{/if}
|
|
23
|
+
</div>
|
|
24
|
+
{#if action}<div class="announcement-banner__action">{@render action()}</div>{/if}
|
|
25
|
+
</div>
|
|
26
|
+
{#if dismissible}
|
|
27
|
+
<button class="announcement-banner__close" type="button" aria-label={localization.dismiss} onclick={dismiss}>
|
|
28
|
+
<Icon src={IconDismiss} />
|
|
29
|
+
</button>
|
|
30
|
+
{/if}
|
|
31
|
+
</div>
|
|
32
|
+
{/if}
|
|
33
|
+
|
|
34
|
+
<!--
|
|
35
|
+
@component
|
|
36
|
+
Full-width ribbon notification for site-wide announcements placed at the top of a page or layout.
|
|
37
|
+
Supports four visual variants; `variant='maintenance'` uses `role="alert"` (assertive); all others use `role="status"` (polite).
|
|
38
|
+
|
|
39
|
+
### CSS Custom Properties
|
|
40
|
+
| Property | Description | Default |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `--sc-kit--announcement-banner--padding-block` | Vertical padding | `--sc-kit--space--3` |
|
|
43
|
+
| `--sc-kit--announcement-banner--padding-inline` | Horizontal padding | `--sc-kit--space--5` |
|
|
44
|
+
| `--sc-kit--announcement-banner--gap` | Gap between icon / body / action | `--sc-kit--space--3` |
|
|
45
|
+
| `--sc-kit--announcement-banner--background` | Background color | per `variant` |
|
|
46
|
+
| `--sc-kit--announcement-banner--border-color` | Bottom border color | per `variant` |
|
|
47
|
+
| `--sc-kit--announcement-banner--accent-color` | Icon + dismiss glyph color | per `variant` |
|
|
48
|
+
| `--sc-kit--announcement-banner--title--color` | Title text color | `--sc-kit--color--text--primary` |
|
|
49
|
+
| `--sc-kit--announcement-banner--description--color` | Body text color | `--sc-kit--color--text--secondary` |
|
|
50
|
+
-->
|
|
51
|
+
|
|
52
|
+
<style>.announcement-banner {
|
|
53
|
+
--_ab--padding-block: var(--sc-kit--announcement-banner--padding-block, var(--sc-kit--space--3));
|
|
54
|
+
--_ab--padding-inline: var(--sc-kit--announcement-banner--padding-inline, var(--sc-kit--space--5));
|
|
55
|
+
--_ab--gap: var(--sc-kit--announcement-banner--gap, var(--sc-kit--space--3));
|
|
56
|
+
--_ab--title-color: var(--sc-kit--announcement-banner--title--color, var(--sc-kit--color--text--primary));
|
|
57
|
+
--_ab--description-color: var(--sc-kit--announcement-banner--description--color, var(--sc-kit--color--text--secondary));
|
|
58
|
+
--_ab--background-default: var(--sc-kit--color--accent--softer);
|
|
59
|
+
--_ab--border-default: var(--sc-kit--color--accent--soft);
|
|
60
|
+
--_ab--accent-default: var(--sc-kit--color--accent);
|
|
61
|
+
--_ab--background: var(--sc-kit--announcement-banner--background, var(--_ab--background-default));
|
|
62
|
+
--_ab--border-color: var(--sc-kit--announcement-banner--border-color, var(--_ab--border-default));
|
|
63
|
+
--_ab--accent-color: var(--sc-kit--announcement-banner--accent-color, var(--_ab--accent-default));
|
|
64
|
+
display: flex;
|
|
65
|
+
width: 100%;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
padding: var(--_ab--padding-block) var(--_ab--padding-inline);
|
|
69
|
+
background: var(--_ab--background);
|
|
70
|
+
border-bottom: 1px solid var(--_ab--border-color);
|
|
71
|
+
color: var(--_ab--accent-color);
|
|
72
|
+
position: relative;
|
|
73
|
+
}
|
|
74
|
+
.announcement-banner--dismissible {
|
|
75
|
+
padding-inline-end: calc(var(--_ab--padding-inline) + 2rem);
|
|
76
|
+
}
|
|
77
|
+
.announcement-banner--info {
|
|
78
|
+
--_ab--background-default: var(--sc-kit--color--accent--softer);
|
|
79
|
+
--_ab--border-default: var(--sc-kit--color--accent--soft);
|
|
80
|
+
--_ab--accent-default: var(--sc-kit--color--accent);
|
|
81
|
+
}
|
|
82
|
+
.announcement-banner--announcement {
|
|
83
|
+
--_ab--background-default: var(--sc-kit--color--accent--soft);
|
|
84
|
+
--_ab--border-default: var(--sc-kit--color--accent);
|
|
85
|
+
--_ab--accent-default: var(--sc-kit--color--accent);
|
|
86
|
+
}
|
|
87
|
+
.announcement-banner--warning {
|
|
88
|
+
--_ab--background-default: var(--sc-kit--color--warning--soft);
|
|
89
|
+
--_ab--border-default: var(--sc-kit--color--warning);
|
|
90
|
+
--_ab--accent-default: var(--sc-kit--color--warning);
|
|
91
|
+
}
|
|
92
|
+
.announcement-banner--maintenance {
|
|
93
|
+
--_ab--background-default: var(--sc-kit--color--danger--soft);
|
|
94
|
+
--_ab--border-default: var(--sc-kit--color--danger);
|
|
95
|
+
--_ab--accent-default: var(--sc-kit--color--danger);
|
|
96
|
+
}
|
|
97
|
+
.announcement-banner__content {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: var(--_ab--gap);
|
|
101
|
+
}
|
|
102
|
+
.announcement-banner__icon {
|
|
103
|
+
flex-shrink: 0;
|
|
104
|
+
display: inline-flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
line-height: 0;
|
|
107
|
+
}
|
|
108
|
+
.announcement-banner__body {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: baseline;
|
|
111
|
+
flex-wrap: wrap;
|
|
112
|
+
gap: var(--sc-kit--space--2);
|
|
113
|
+
}
|
|
114
|
+
.announcement-banner__title {
|
|
115
|
+
font-size: var(--sc-kit--font-size--sm);
|
|
116
|
+
font-weight: var(--sc-kit--font-weight--semibold);
|
|
117
|
+
line-height: var(--sc-kit--leading--tight);
|
|
118
|
+
color: var(--_ab--title-color);
|
|
119
|
+
}
|
|
120
|
+
.announcement-banner__description {
|
|
121
|
+
font-size: var(--sc-kit--font-size--sm);
|
|
122
|
+
line-height: var(--sc-kit--leading--normal);
|
|
123
|
+
color: var(--_ab--description-color);
|
|
124
|
+
}
|
|
125
|
+
.announcement-banner__action {
|
|
126
|
+
flex-shrink: 0;
|
|
127
|
+
}
|
|
128
|
+
.announcement-banner__close {
|
|
129
|
+
position: absolute;
|
|
130
|
+
inset-inline-end: var(--_ab--padding-inline);
|
|
131
|
+
top: 50%;
|
|
132
|
+
transform: translateY(-50%);
|
|
133
|
+
appearance: none;
|
|
134
|
+
width: 1.25rem;
|
|
135
|
+
height: 1.25rem;
|
|
136
|
+
border: 0;
|
|
137
|
+
border-radius: var(--sc-kit--radius--sm);
|
|
138
|
+
background: transparent;
|
|
139
|
+
color: currentColor;
|
|
140
|
+
display: inline-flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
opacity: 0.7;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
--sc-kit--icon--size: 0.75rem;
|
|
146
|
+
}
|
|
147
|
+
.announcement-banner__close:hover {
|
|
148
|
+
opacity: 1;
|
|
149
|
+
}
|
|
150
|
+
.announcement-banner__close:focus-visible {
|
|
151
|
+
outline: 2px solid var(--sc-kit--color--border--focus);
|
|
152
|
+
outline-offset: 2px;
|
|
153
|
+
}</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type IconProp } from '../icon';
|
|
2
|
+
import type { AnnouncementBannerVariant } from './types';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
type Props = {
|
|
5
|
+
/** @default 'info' */
|
|
6
|
+
variant?: AnnouncementBannerVariant;
|
|
7
|
+
/** Short headline rendered in bold. */
|
|
8
|
+
title?: string;
|
|
9
|
+
/** Show a dismiss button. Hides the banner locally when clicked and fires `on.dismiss`. @default false */
|
|
10
|
+
dismissible?: boolean;
|
|
11
|
+
/** Leading icon — string SVG source, `{ src, color?, size? }` object, or custom snippet. */
|
|
12
|
+
icon?: IconProp;
|
|
13
|
+
/** Optional inline call-to-action area (e.g. "Learn more →", "See status page"). */
|
|
14
|
+
action?: Snippet;
|
|
15
|
+
/** Announcement body text. */
|
|
16
|
+
children?: Snippet;
|
|
17
|
+
on?: {
|
|
18
|
+
dismiss?: () => void;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Full-width ribbon notification for site-wide announcements placed at the top of a page or layout.
|
|
23
|
+
* Supports four visual variants; `variant='maintenance'` uses `role="alert"` (assertive); all others use `role="status"` (polite).
|
|
24
|
+
*
|
|
25
|
+
* ### CSS Custom Properties
|
|
26
|
+
* | Property | Description | Default |
|
|
27
|
+
* |---|---|---|
|
|
28
|
+
* | `--sc-kit--announcement-banner--padding-block` | Vertical padding | `--sc-kit--space--3` |
|
|
29
|
+
* | `--sc-kit--announcement-banner--padding-inline` | Horizontal padding | `--sc-kit--space--5` |
|
|
30
|
+
* | `--sc-kit--announcement-banner--gap` | Gap between icon / body / action | `--sc-kit--space--3` |
|
|
31
|
+
* | `--sc-kit--announcement-banner--background` | Background color | per `variant` |
|
|
32
|
+
* | `--sc-kit--announcement-banner--border-color` | Bottom border color | per `variant` |
|
|
33
|
+
* | `--sc-kit--announcement-banner--accent-color` | Icon + dismiss glyph color | per `variant` |
|
|
34
|
+
* | `--sc-kit--announcement-banner--title--color` | Title text color | `--sc-kit--color--text--primary` |
|
|
35
|
+
* | `--sc-kit--announcement-banner--description--color` | Body text color | `--sc-kit--color--text--secondary` |
|
|
36
|
+
*/
|
|
37
|
+
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
38
|
+
type Cmp = ReturnType<typeof Cmp>;
|
|
39
|
+
export default Cmp;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AnnouncementBanner } from './cmp.announcement-banner.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type AnnouncementBannerVariant = 'info' | 'announcement' | 'warning' | 'maintenance';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -62,7 +62,7 @@ the load / error / stub state machine.
|
|
|
62
62
|
--sc-kit--image--border-radius: 50%;
|
|
63
63
|
--sc-kit--image--background: var(--_avatar--background);
|
|
64
64
|
position: relative;
|
|
65
|
-
display:
|
|
65
|
+
display: flex;
|
|
66
66
|
align-items: center;
|
|
67
67
|
justify-content: center;
|
|
68
68
|
width: var(--_avatar--size);
|
|
@@ -105,7 +105,7 @@ Pass `type="anchor"` to render as `<a>` with `href`. Otherwise `type` is the nat
|
|
|
105
105
|
| `--sc-kit--button--gap` | Gap between icon and label | per size |
|
|
106
106
|
| `--sc-kit--button--border-radius` | Corner rounding | `var(--sc-kit--radius--md)` |
|
|
107
107
|
| `--sc-kit--button--font-weight` | Font weight | `var(--sc-kit--font-weight--medium)` |
|
|
108
|
-
| `--sc-kit--button--width` | Explicit width | `
|
|
108
|
+
| `--sc-kit--button--width` | Explicit width | `fit-content` |
|
|
109
109
|
| `--sc-kit--button--min-width` | Minimum width | `0` |
|
|
110
110
|
| `--sc-kit--button--max-width` | Maximum width | `100%` |
|
|
111
111
|
-->
|
|
@@ -122,13 +122,13 @@ Pass `type="anchor"` to render as `<a>` with `href`. Otherwise `type` is the nat
|
|
|
122
122
|
--_btn--padding-inline: var(--sc-kit--button--padding-inline, var(--sc-kit--space--4));
|
|
123
123
|
--_btn--font-size: var(--sc-kit--button--font-size, var(--sc-kit--font-size--md));
|
|
124
124
|
--_btn--gap: var(--sc-kit--button--gap, var(--sc-kit--space--2));
|
|
125
|
-
--_btn--width: var(--sc-kit--button--width,
|
|
125
|
+
--_btn--width: var(--sc-kit--button--width, fit-content);
|
|
126
126
|
--_btn--min-width: var(--sc-kit--button--min-width, 0);
|
|
127
127
|
--_btn--max-width: var(--sc-kit--button--max-width, 100%);
|
|
128
128
|
--_btn--border-radius: var(--sc-kit--button--border-radius, var(--sc-kit--radius--md));
|
|
129
129
|
--_btn--font-weight: var(--sc-kit--button--font-weight, var(--sc-kit--font-weight--medium));
|
|
130
130
|
box-sizing: border-box;
|
|
131
|
-
display:
|
|
131
|
+
display: flex;
|
|
132
132
|
align-items: center;
|
|
133
133
|
justify-content: center;
|
|
134
134
|
gap: var(--_btn--gap);
|
|
@@ -63,7 +63,7 @@ type Props = ButtonModeProps | AnchorModeProps;
|
|
|
63
63
|
* | `--sc-kit--button--gap` | Gap between icon and label | per size |
|
|
64
64
|
* | `--sc-kit--button--border-radius` | Corner rounding | `var(--sc-kit--radius--md)` |
|
|
65
65
|
* | `--sc-kit--button--font-weight` | Font weight | `var(--sc-kit--font-weight--medium)` |
|
|
66
|
-
* | `--sc-kit--button--width` | Explicit width | `
|
|
66
|
+
* | `--sc-kit--button--width` | Explicit width | `fit-content` |
|
|
67
67
|
* | `--sc-kit--button--min-width` | Minimum width | `0` |
|
|
68
68
|
* | `--sc-kit--button--max-width` | Maximum width | `100%` |
|
|
69
69
|
*/
|
|
@@ -17,11 +17,9 @@ const handleClick = (event) => {
|
|
|
17
17
|
|
|
18
18
|
<label class="check" class:check--on={isOn} class:check--indeterminate={isIndeterminate} class:check--disabled={disabled} title={title}>
|
|
19
19
|
<button type="button" class="check__box" role="checkbox" aria-checked={ariaChecked} disabled={disabled} onclick={handleClick}>
|
|
20
|
-
{
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
<span class="check__mark" aria-hidden="true"><Icon src={IconSubtract} /></span>
|
|
24
|
-
{/if}
|
|
20
|
+
<span class="check__mark" class:check__mark--visible={isOn || isIndeterminate} aria-hidden="true">
|
|
21
|
+
<Icon src={isIndeterminate ? IconSubtract : IconCheckmark} />
|
|
22
|
+
</span>
|
|
25
23
|
</button>
|
|
26
24
|
{#if typeof label === 'string'}
|
|
27
25
|
<span class="check__label">{label}</span>
|
|
@@ -66,9 +64,10 @@ so it can be safely nested inside clickable rows.
|
|
|
66
64
|
--_check--label-font-size: var(--sc-kit--checkbox--label--font-size, var(--sc-kit--font-size--md));
|
|
67
65
|
--_check--label-line-height: var(--sc-kit--checkbox--label--line-height, var(--sc-kit--line-height--md));
|
|
68
66
|
--_check--label-color: var(--sc-kit--checkbox--label--color, var(--sc-kit--color--text--primary));
|
|
69
|
-
display:
|
|
67
|
+
display: flex;
|
|
70
68
|
align-items: center;
|
|
71
69
|
gap: var(--_check--gap);
|
|
70
|
+
width: fit-content;
|
|
72
71
|
max-width: 100%;
|
|
73
72
|
cursor: pointer;
|
|
74
73
|
user-select: none;
|
|
@@ -110,6 +109,11 @@ so it can be safely nested inside clickable rows.
|
|
|
110
109
|
.check__mark {
|
|
111
110
|
--sc-kit--icon--size: 0.75rem;
|
|
112
111
|
display: inline-flex;
|
|
112
|
+
opacity: 0;
|
|
113
|
+
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
114
|
+
}
|
|
115
|
+
.check__mark--visible {
|
|
116
|
+
opacity: 1;
|
|
113
117
|
}
|
|
114
118
|
.check__label {
|
|
115
119
|
min-width: 0;
|
|
@@ -85,9 +85,10 @@ checkmark on selected chips.
|
|
|
85
85
|
--_cg--color-selected: var(--sc-kit--chip-group--color--selected, var(--sc-kit--color--accent));
|
|
86
86
|
--_cg--background-selected-hover: var(--sc-kit--chip-group--background--selected-hover, var(--sc-kit--color--accent--softer));
|
|
87
87
|
--_cg--focus-ring-color: var(--sc-kit--chip-group--focus-ring-color, var(--sc-kit--color--border--focus));
|
|
88
|
-
display:
|
|
88
|
+
display: flex;
|
|
89
89
|
flex-wrap: wrap;
|
|
90
90
|
gap: var(--_cg--gap);
|
|
91
|
+
width: fit-content;
|
|
91
92
|
max-width: 100%;
|
|
92
93
|
}
|
|
93
94
|
.chip-group--sm {
|
|
@@ -232,7 +232,7 @@ requires a non-null value. CSS API mirrors `Input` for visual-language compatibi
|
|
|
232
232
|
}
|
|
233
233
|
.numeral-input {
|
|
234
234
|
box-sizing: border-box;
|
|
235
|
-
display:
|
|
235
|
+
display: flex;
|
|
236
236
|
align-items: center;
|
|
237
237
|
width: var(--_ni--width);
|
|
238
238
|
max-width: 100%;
|
|
@@ -77,9 +77,10 @@ or `type="multi"` with `value: T[]`. Provide `compare` for non-primitive values.
|
|
|
77
77
|
--_op--thumb-size: var(--sc-kit--option-pill--thumb--size, 1.75rem);
|
|
78
78
|
--_op--thumb-background: var(--sc-kit--option-pill--thumb--background, var(--sc-kit--color--bg--active));
|
|
79
79
|
--_op--thumb-color: var(--sc-kit--option-pill--thumb--color, var(--sc-kit--color--text--muted));
|
|
80
|
-
display:
|
|
80
|
+
display: flex;
|
|
81
81
|
flex-wrap: wrap;
|
|
82
82
|
gap: var(--_op--gap);
|
|
83
|
+
width: fit-content;
|
|
83
84
|
}
|
|
84
85
|
.option-pill--disabled {
|
|
85
86
|
opacity: 0.6;
|
|
@@ -116,8 +116,9 @@ as `<input type="password">`). Fires `on.complete` when all cells are filled.
|
|
|
116
116
|
--_pi--border-radius: var(--sc-kit--pin-input--border-radius, var(--sc-kit--radius--lg));
|
|
117
117
|
--_pi--color: var(--sc-kit--pin-input--color, var(--sc-kit--color--text--primary));
|
|
118
118
|
--_pi--focus-shadow: var(--sc-kit--pin-input--focus-shadow, var(--sc-kit--shadow--focus));
|
|
119
|
-
display:
|
|
119
|
+
display: flex;
|
|
120
120
|
gap: var(--_pi--gap);
|
|
121
|
+
width: fit-content;
|
|
121
122
|
}
|
|
122
123
|
.pin-input--disabled {
|
|
123
124
|
opacity: 0.6;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">import { popoverIgnore } from './popover-ignore';
|
|
2
|
-
const { disabled = false, keepOpen = false, divider = false, on, children } = $props();
|
|
2
|
+
const { disabled = false, keepOpen = false, divider = false, inset = false, on, children } = $props();
|
|
3
3
|
const handleClick = () => {
|
|
4
4
|
if (disabled) {
|
|
5
5
|
return;
|
|
@@ -12,6 +12,7 @@ const handleClick = () => {
|
|
|
12
12
|
class="popover-item"
|
|
13
13
|
class:popover-item--disabled={disabled}
|
|
14
14
|
class:popover-item--divider={divider}
|
|
15
|
+
class:popover-item--inset={inset}
|
|
15
16
|
use:popoverIgnore={keepOpen}
|
|
16
17
|
role="menuitem"
|
|
17
18
|
tabindex={disabled ? -1 : 0}
|
|
@@ -42,6 +43,8 @@ for toggle items inside menus).
|
|
|
42
43
|
| `--sc-kit--popover-item--divider-color` | Divider line color | `var(--sc-kit--color--border)` |
|
|
43
44
|
| `--sc-kit--popover-item--divider-spacing` | Vertical gap added by divider | `var(--sc-kit--space--1)` |
|
|
44
45
|
| `--sc-kit--popover-item--text-align` | Text alignment | `left` |
|
|
46
|
+
| `--sc-kit--popover-item--margin-inline` | Inline margin when `inset` | `var(--sc-kit--space--2)` |
|
|
47
|
+
| `--sc-kit--popover-item--border-radius` | Corner radius when `inset` | `var(--sc-kit--radius--sm)` |
|
|
45
48
|
-->
|
|
46
49
|
|
|
47
50
|
<style>.popover-item {
|
|
@@ -56,6 +59,8 @@ for toggle items inside menus).
|
|
|
56
59
|
--_di--divider-color: var(--sc-kit--popover-item--divider-color, var(--sc-kit--color--border));
|
|
57
60
|
--_di--divider-spacing: var(--sc-kit--popover-item--divider-spacing, var(--sc-kit--space--1));
|
|
58
61
|
--_di--text-align: var(--sc-kit--popover-item--text-align, left);
|
|
62
|
+
--_di--margin-inline: var(--sc-kit--popover-item--margin-inline, var(--sc-kit--space--2));
|
|
63
|
+
--_di--border-radius: var(--sc-kit--popover-item--border-radius, var(--sc-kit--radius--sm));
|
|
59
64
|
display: flex;
|
|
60
65
|
align-items: center;
|
|
61
66
|
gap: var(--_di--gap);
|
|
@@ -81,6 +86,10 @@ for toggle items inside menus).
|
|
|
81
86
|
margin-bottom: var(--_di--divider-spacing);
|
|
82
87
|
padding-bottom: calc(var(--_di--padding-block) + 1px);
|
|
83
88
|
}
|
|
89
|
+
.popover-item--inset {
|
|
90
|
+
margin-inline: var(--_di--margin-inline);
|
|
91
|
+
border-radius: var(--_di--border-radius);
|
|
92
|
+
}
|
|
84
93
|
.popover-item__content {
|
|
85
94
|
flex: 1;
|
|
86
95
|
min-width: 0;
|
|
@@ -5,6 +5,8 @@ type Props = {
|
|
|
5
5
|
keepOpen?: boolean;
|
|
6
6
|
/** Render a separator line below this item — useful for grouping menu sections. */
|
|
7
7
|
divider?: boolean;
|
|
8
|
+
/** Inset the item from the popover edges: adds an inline margin and rounds the hover background. */
|
|
9
|
+
inset?: boolean;
|
|
8
10
|
on?: {
|
|
9
11
|
click?: () => void;
|
|
10
12
|
};
|
|
@@ -29,6 +31,8 @@ type Props = {
|
|
|
29
31
|
* | `--sc-kit--popover-item--divider-color` | Divider line color | `var(--sc-kit--color--border)` |
|
|
30
32
|
* | `--sc-kit--popover-item--divider-spacing` | Vertical gap added by divider | `var(--sc-kit--space--1)` |
|
|
31
33
|
* | `--sc-kit--popover-item--text-align` | Text alignment | `left` |
|
|
34
|
+
* | `--sc-kit--popover-item--margin-inline` | Inline margin when `inset` | `var(--sc-kit--space--2)` |
|
|
35
|
+
* | `--sc-kit--popover-item--border-radius` | Corner radius when `inset` | `var(--sc-kit--radius--sm)` |
|
|
32
36
|
*/
|
|
33
37
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
34
38
|
type Cmp = ReturnType<typeof Cmp>;
|
|
@@ -19,7 +19,7 @@ const handleClick = (event) => {
|
|
|
19
19
|
disabled={disabled}
|
|
20
20
|
onclick={handleClick}>
|
|
21
21
|
<span class="radio-card__dot" aria-hidden="true">
|
|
22
|
-
|
|
22
|
+
<span class="radio-card__inner" class:radio-card__inner--visible={selected}></span>
|
|
23
23
|
</span>
|
|
24
24
|
<span class="radio-card__body">
|
|
25
25
|
<span class="radio-card__title">{title}</span>
|
|
@@ -78,7 +78,7 @@ label line (plan picker, account type, theme picker).
|
|
|
78
78
|
--_card--dot-background: var(--sc-kit--radio-card--dot--background, var(--sc-kit--color--bg--field));
|
|
79
79
|
--_card--dot-inner-color: var(--sc-kit--radio-card--dot--inner-color, var(--sc-kit--color--accent));
|
|
80
80
|
box-sizing: border-box;
|
|
81
|
-
display:
|
|
81
|
+
display: flex;
|
|
82
82
|
align-items: flex-start;
|
|
83
83
|
gap: var(--_card--gap);
|
|
84
84
|
width: 100%;
|
|
@@ -127,6 +127,11 @@ label line (plan picker, account type, theme picker).
|
|
|
127
127
|
height: var(--_card--dot-inner-size);
|
|
128
128
|
background: var(--_card--dot-inner-color);
|
|
129
129
|
border-radius: var(--sc-kit--radius--circle);
|
|
130
|
+
opacity: 0;
|
|
131
|
+
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
132
|
+
}
|
|
133
|
+
.radio-card__inner--visible {
|
|
134
|
+
opacity: 1;
|
|
130
135
|
}
|
|
131
136
|
.radio-card__body {
|
|
132
137
|
display: inline-flex;
|
|
@@ -12,7 +12,7 @@ export {};
|
|
|
12
12
|
|
|
13
13
|
<label class="radio" class:radio--on={selected} class:radio--disabled={disabled} title={title}>
|
|
14
14
|
<button type="button" class="radio__dot" role="radio" aria-checked={selected} aria-label={ariaLabel} disabled={disabled} onclick={handleClick}>
|
|
15
|
-
|
|
15
|
+
<span class="radio__inner" class:radio__inner--visible={selected} aria-hidden="true"></span>
|
|
16
16
|
</button>
|
|
17
17
|
{#if typeof label === 'string'}
|
|
18
18
|
<span class="radio__label">{label}</span>
|
|
@@ -55,9 +55,10 @@ its state to this option's value. Pair with `RadioCard` for option-card layouts.
|
|
|
55
55
|
--_radio--label-font-size: var(--sc-kit--radio--label--font-size, var(--sc-kit--font-size--md));
|
|
56
56
|
--_radio--label-line-height: var(--sc-kit--radio--label--line-height, var(--sc-kit--line-height--md));
|
|
57
57
|
--_radio--label-color: var(--sc-kit--radio--label--color, var(--sc-kit--color--text--primary));
|
|
58
|
-
display:
|
|
58
|
+
display: flex;
|
|
59
59
|
align-items: center;
|
|
60
60
|
gap: var(--_radio--gap);
|
|
61
|
+
width: fit-content;
|
|
61
62
|
max-width: 100%;
|
|
62
63
|
cursor: pointer;
|
|
63
64
|
user-select: none;
|
|
@@ -95,6 +96,11 @@ its state to this option's value. Pair with `RadioCard` for option-card layouts.
|
|
|
95
96
|
height: var(--_radio--inner-size);
|
|
96
97
|
background: var(--_radio--inner-color);
|
|
97
98
|
border-radius: var(--sc-kit--radius--circle);
|
|
99
|
+
opacity: 0;
|
|
100
|
+
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
101
|
+
}
|
|
102
|
+
.radio__inner--visible {
|
|
103
|
+
opacity: 1;
|
|
98
104
|
}
|
|
99
105
|
.radio__label {
|
|
100
106
|
min-width: 0;
|
|
@@ -80,14 +80,14 @@ switch left + label right, intrinsic width).
|
|
|
80
80
|
--_row--label-color: var(--sc-kit--toggle-row--label--color, var(--sc-kit--color--text--primary));
|
|
81
81
|
--_row--sub-font-size: var(--sc-kit--toggle-row--sub--font-size, var(--sc-kit--font-size--sm));
|
|
82
82
|
--_row--sub-color: var(--sc-kit--toggle-row--sub--color, var(--sc-kit--color--text--muted));
|
|
83
|
-
display:
|
|
83
|
+
display: flex;
|
|
84
84
|
align-items: center;
|
|
85
85
|
gap: var(--_row--gap);
|
|
86
|
+
width: fit-content;
|
|
86
87
|
max-width: 100%;
|
|
87
88
|
cursor: pointer;
|
|
88
89
|
}
|
|
89
90
|
.toggle-row--spread {
|
|
90
|
-
display: flex;
|
|
91
91
|
justify-content: space-between;
|
|
92
92
|
width: 100%;
|
|
93
93
|
}
|