@streamscloud/kit 0.9.0 → 0.9.1
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 +11 -11
- package/dist/ui/ai-panel/ai-panel-localization.d.ts +3 -0
- package/dist/ui/ai-panel/ai-panel-localization.js +12 -0
- package/dist/ui/ai-panel/cmp.ai-panel.svelte +90 -0
- package/dist/ui/ai-panel/cmp.ai-panel.svelte.d.ts +58 -0
- package/dist/ui/ai-panel/index.d.ts +1 -0
- package/dist/ui/ai-panel/index.js +1 -0
- package/dist/ui/color-picker/cmp.color-picker.svelte +60 -1
- package/dist/ui/color-picker/cmp.color-picker.svelte.d.ts +6 -0
- package/dist/ui/page-layout/cmp.page-panel-sidebars.svelte +18 -0
- package/dist/ui/page-layout/cmp.page-panel-sidebars.svelte.d.ts +8 -1
- package/dist/ui/page-layout/cmp.page-panel.svelte +2 -0
- package/dist/ui/page-layout/cmp.panel-collapse-button.svelte +3 -2
- package/dist/ui/tabs/cmp.tabs.svelte +15 -102
- package/dist/ui/tabs/cmp.tabs.svelte.d.ts +13 -10
- package/dist/ui/tabs/index.d.ts +1 -1
- package/dist/ui/tabs/tab.svelte +169 -0
- package/dist/ui/tabs/tab.svelte.d.ts +34 -0
- package/dist/ui/tabs/tabs-localization.d.ts +3 -0
- package/dist/ui/tabs/tabs-localization.js +12 -0
- package/dist/ui/tabs/types.d.ts +8 -0
- package/dist/ui/tabs/types.js +1 -0
- package/package.json +5 -2
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
// ============================================================
|
|
8
8
|
// COLOR — SURFACES
|
|
9
9
|
// ============================================================
|
|
10
|
-
--sc-kit--color--bg--app: light-dark(#
|
|
10
|
+
--sc-kit--color--bg--app: light-dark(#{$color-neutral-50}, #{$color-dark-900});
|
|
11
11
|
--sc-kit--color--bg--panel: light-dark(#{$color-white}, #{$color-dark-800});
|
|
12
|
-
--sc-kit--color--bg--element: light-dark(#{$color-neutral-
|
|
12
|
+
--sc-kit--color--bg--element: light-dark(#{$color-neutral-100}, #{$color-dark-700});
|
|
13
13
|
--sc-kit--color--bg--field: light-dark(#{$color-white}, #{$color-dark-700});
|
|
14
|
-
--sc-kit--color--bg--field-alt: light-dark(#
|
|
14
|
+
--sc-kit--color--bg--field-alt: light-dark(#{$color-neutral-50}, #{$color-dark-700});
|
|
15
15
|
--sc-kit--color--bg--hover: light-dark(#f3f5f8, #{$color-dark-700});
|
|
16
16
|
--sc-kit--color--bg--active: light-dark(#eef2f8, #{$color-dark-600});
|
|
17
17
|
--sc-kit--color--bg--menu-active: light-dark(#{$color-blue-50}, #{$color-dark-500});
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
// ============================================================
|
|
21
21
|
// COLOR — TEXT
|
|
22
22
|
// ============================================================
|
|
23
|
-
--sc-kit--color--text--primary: light-dark(#
|
|
23
|
+
--sc-kit--color--text--primary: light-dark(#{$color-dark-800}, #{$color-white});
|
|
24
24
|
--sc-kit--color--text--secondary: light-dark(#5c6370, #{$color-neutral-300});
|
|
25
|
-
--sc-kit--color--text--muted: light-dark(#
|
|
25
|
+
--sc-kit--color--text--muted: light-dark(#{$color-neutral-400}, #{$color-neutral-400});
|
|
26
26
|
--sc-kit--color--text--placeholder: light-dark(#b4b9c2, #{$color-neutral-500});
|
|
27
27
|
--sc-kit--color--text--label: light-dark(#{$color-neutral-500}, #{$color-neutral-300});
|
|
28
28
|
--sc-kit--color--text--on-accent: #{$color-white};
|
|
@@ -31,17 +31,17 @@
|
|
|
31
31
|
// COLOR — BORDERS
|
|
32
32
|
// ============================================================
|
|
33
33
|
--sc-kit--color--border: light-dark(#{$color-neutral-200}, #{$color-dark-600});
|
|
34
|
-
--sc-kit--color--border--strong: light-dark(#
|
|
35
|
-
--sc-kit--color--border--field: light-dark(#
|
|
34
|
+
--sc-kit--color--border--strong: light-dark(#{$color-neutral-400}, #{$color-neutral-600});
|
|
35
|
+
--sc-kit--color--border--field: light-dark(#{$color-neutral-200}, #{$color-dark-600});
|
|
36
36
|
--sc-kit--color--border--focus: light-dark(#{$color-blue-500}, #{$color-blue-400});
|
|
37
37
|
|
|
38
38
|
// ============================================================
|
|
39
39
|
// COLOR — BRAND / ACCENT — anchored at #144AB0
|
|
40
40
|
// ============================================================
|
|
41
41
|
--sc-kit--color--accent: light-dark(#{$color-blue-500}, #{$color-blue-400});
|
|
42
|
-
--sc-kit--color--accent--hover: light-dark(#
|
|
43
|
-
--sc-kit--color--accent--soft: light-dark(#
|
|
44
|
-
--sc-kit--color--accent--softer: light-dark(#
|
|
42
|
+
--sc-kit--color--accent--hover: light-dark(#{$color-blue-700}, #{$color-blue-500});
|
|
43
|
+
--sc-kit--color--accent--soft: light-dark(#{$color-blue-100}, #{$color-blue-900});
|
|
44
|
+
--sc-kit--color--accent--softer: light-dark(#{$color-blue-50}, #{$color-blue-900});
|
|
45
45
|
|
|
46
46
|
// ============================================================
|
|
47
47
|
// COLOR — STATUS
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
// COLOR — OVERLAYS
|
|
66
66
|
// ============================================================
|
|
67
67
|
--sc-kit--color--bg--scrim: light-dark(rgb(0 0 0 / 45%), rgb(0 0 0 / 65%));
|
|
68
|
-
--sc-kit--color--bg--tooltip: light-dark(#
|
|
68
|
+
--sc-kit--color--bg--tooltip: light-dark(#{$color-dark-800}, #{$color-dark-700});
|
|
69
69
|
--sc-kit--color--text--tooltip: #{$color-white};
|
|
70
70
|
|
|
71
71
|
// Z-scale — relative stacking order for the library's overlay components.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts" generics="T">import { Button } from '../button';
|
|
2
|
+
import { SegmentedControl } from '../segmented-control';
|
|
3
|
+
import { Textarea } from '../textarea';
|
|
4
|
+
import { AiPanelLocalization } from './ai-panel-localization';
|
|
5
|
+
import IconAdd from '@fluentui/svg-icons/icons/add_20_regular.svg?raw';
|
|
6
|
+
import IconSparkle from '@fluentui/svg-icons/icons/sparkle_20_regular.svg?raw';
|
|
7
|
+
const { segments, activeSegment, body, prompt = '', placeholder = '', generateLabel = '', on } = $props();
|
|
8
|
+
const localization = new AiPanelLocalization();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div class="ai-panel">
|
|
12
|
+
<div class="ai-panel__tabs">
|
|
13
|
+
<SegmentedControl value={activeSegment} segments={segments} size="sm" fullWidth on={{ change: (value) => on?.tabChange?.(value) }} />
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="ai-panel__body">
|
|
17
|
+
{@render body()}
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="ai-panel__composer">
|
|
21
|
+
<Textarea value={prompt} placeholder={placeholder} borderless rows={3} on={{ input: (value) => on?.promptInput?.(value) }} />
|
|
22
|
+
<span class="ai-panel__attach">
|
|
23
|
+
<Button type="button" variant="ghost" size="sm" icon={IconAdd} aria-label={localization.attach} on={{ click: () => on?.attach?.() }} />
|
|
24
|
+
</span>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<Button type="button" variant="success" fullWidth icon={IconSparkle} on={{ click: () => on?.generate?.() }}>{generateLabel}</Button>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!--
|
|
31
|
+
@component
|
|
32
|
+
AiPanel — right-edge AI assistant panel: a segment strip, a consumer-filled `body`, and a built-in
|
|
33
|
+
composer (prompt textarea + attach button + Generate bar). Compose-only; collapse is the surrounding
|
|
34
|
+
layout's concern. User-facing copy (placeholder, Generate label) is consumer-supplied.
|
|
35
|
+
|
|
36
|
+
### CSS Custom Properties
|
|
37
|
+
| Property | Description | Default |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `--sc-kit--ai-panel--width` | Panel width | `300px` |
|
|
40
|
+
| `--sc-kit--ai-panel--gap` | Gap between sections | `16px` |
|
|
41
|
+
| `--sc-kit--ai-panel--padding` | Panel padding | `16px` |
|
|
42
|
+
| `--sc-kit--ai-panel--background` | Panel background | `--sc-kit--color--bg--panel` |
|
|
43
|
+
| `--sc-kit--ai-panel--border-color` | Left divider color | `--sc-kit--color--border` |
|
|
44
|
+
| `--sc-kit--ai-panel--surface` | Body / composer / tab-tray fill | `--sc-kit--color--bg--app` |
|
|
45
|
+
| `--sc-kit--ai-panel--surface-radius` | Body / composer corner radius | `--sc-kit--radius--md` |
|
|
46
|
+
-->
|
|
47
|
+
|
|
48
|
+
<style>.ai-panel {
|
|
49
|
+
--_ai-panel--width: var(--sc-kit--ai-panel--width, 18.75rem);
|
|
50
|
+
--_ai-panel--gap: var(--sc-kit--ai-panel--gap, 1rem);
|
|
51
|
+
--_ai-panel--padding: var(--sc-kit--ai-panel--padding, 1rem);
|
|
52
|
+
--_ai-panel--background: var(--sc-kit--ai-panel--background, var(--sc-kit--color--bg--panel));
|
|
53
|
+
--_ai-panel--border-color: var(--sc-kit--ai-panel--border-color, var(--sc-kit--color--border));
|
|
54
|
+
--_ai-panel--surface: var(--sc-kit--ai-panel--surface, var(--sc-kit--color--bg--app));
|
|
55
|
+
--_ai-panel--surface-radius: var(--sc-kit--ai-panel--surface-radius, var(--sc-kit--radius--md));
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
gap: var(--_ai-panel--gap);
|
|
59
|
+
width: var(--_ai-panel--width);
|
|
60
|
+
padding: var(--_ai-panel--padding);
|
|
61
|
+
background: var(--_ai-panel--background);
|
|
62
|
+
border-left: 1px solid var(--_ai-panel--border-color);
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
}
|
|
65
|
+
.ai-panel__tabs {
|
|
66
|
+
--sc-kit--segmented-control--tray--background: var(--_ai-panel--surface);
|
|
67
|
+
margin-bottom: -0.5rem;
|
|
68
|
+
}
|
|
69
|
+
.ai-panel__body {
|
|
70
|
+
flex: 1;
|
|
71
|
+
min-height: 15rem;
|
|
72
|
+
background: var(--_ai-panel--surface);
|
|
73
|
+
border-radius: var(--_ai-panel--surface-radius);
|
|
74
|
+
overflow-y: auto;
|
|
75
|
+
}
|
|
76
|
+
.ai-panel__composer {
|
|
77
|
+
position: relative;
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
gap: 0.5rem;
|
|
81
|
+
min-height: 6rem;
|
|
82
|
+
padding: 0.75rem;
|
|
83
|
+
background: var(--_ai-panel--surface);
|
|
84
|
+
border-radius: var(--_ai-panel--surface-radius);
|
|
85
|
+
}
|
|
86
|
+
.ai-panel__attach {
|
|
87
|
+
position: absolute;
|
|
88
|
+
bottom: 0.5rem;
|
|
89
|
+
left: 0.5rem;
|
|
90
|
+
}</style>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Segment } from '../segmented-control';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
declare function $$render<T>(): {
|
|
4
|
+
props: {
|
|
5
|
+
segments: Segment<NoInfer<T>>[];
|
|
6
|
+
activeSegment: T;
|
|
7
|
+
/** Content for the active segment — the consumer renders per-segment UI here. */
|
|
8
|
+
body: Snippet;
|
|
9
|
+
/** Composer textarea value. */
|
|
10
|
+
prompt?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
/** Generate button label — consumer-supplied (kit ships no user-facing copy). */
|
|
13
|
+
generateLabel?: string;
|
|
14
|
+
on?: {
|
|
15
|
+
tabChange?: (value: T) => void;
|
|
16
|
+
promptInput?: (value: string) => void;
|
|
17
|
+
attach?: () => void;
|
|
18
|
+
generate?: () => void;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
exports: {};
|
|
22
|
+
bindings: "";
|
|
23
|
+
slots: {};
|
|
24
|
+
events: {};
|
|
25
|
+
};
|
|
26
|
+
declare class __sveltets_Render<T> {
|
|
27
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
28
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
29
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
30
|
+
bindings(): "";
|
|
31
|
+
exports(): {};
|
|
32
|
+
}
|
|
33
|
+
interface $$IsomorphicComponent {
|
|
34
|
+
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
35
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
36
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
37
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
38
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* AiPanel — right-edge AI assistant panel: a segment strip, a consumer-filled `body`, and a built-in
|
|
42
|
+
* composer (prompt textarea + attach button + Generate bar). Compose-only; collapse is the surrounding
|
|
43
|
+
* layout's concern. User-facing copy (placeholder, Generate label) is consumer-supplied.
|
|
44
|
+
*
|
|
45
|
+
* ### CSS Custom Properties
|
|
46
|
+
* | Property | Description | Default |
|
|
47
|
+
* |---|---|---|
|
|
48
|
+
* | `--sc-kit--ai-panel--width` | Panel width | `300px` |
|
|
49
|
+
* | `--sc-kit--ai-panel--gap` | Gap between sections | `16px` |
|
|
50
|
+
* | `--sc-kit--ai-panel--padding` | Panel padding | `16px` |
|
|
51
|
+
* | `--sc-kit--ai-panel--background` | Panel background | `--sc-kit--color--bg--panel` |
|
|
52
|
+
* | `--sc-kit--ai-panel--border-color` | Left divider color | `--sc-kit--color--border` |
|
|
53
|
+
* | `--sc-kit--ai-panel--surface` | Body / composer / tab-tray fill | `--sc-kit--color--bg--app` |
|
|
54
|
+
* | `--sc-kit--ai-panel--surface-radius` | Body / composer corner radius | `--sc-kit--radius--md` |
|
|
55
|
+
*/
|
|
56
|
+
declare const Cmp: $$IsomorphicComponent;
|
|
57
|
+
type Cmp<T> = InstanceType<typeof Cmp<T>>;
|
|
58
|
+
export default Cmp;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AiPanel } from './cmp.ai-panel.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AiPanel } from './cmp.ai-panel.svelte';
|
|
@@ -8,7 +8,7 @@ import IconColorF from '@fluentui/svg-icons/icons/color_20_regular.svg?raw';
|
|
|
8
8
|
import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
|
|
9
9
|
import { colord } from 'colord';
|
|
10
10
|
import AwesomeColorPicker from 'svelte-awesome-color-picker';
|
|
11
|
-
let { value, size = 'md', defaultColor, clearable = false, disabled = false, placeholder, on, children, previewIcon } = $props();
|
|
11
|
+
let { value, variant = 'fill', label, size = 'md', defaultColor, clearable = false, disabled = false, placeholder, on, children, previewIcon } = $props();
|
|
12
12
|
const loc = new ColorPickerLocalization();
|
|
13
13
|
const displayColor = $derived(value || defaultColor || '');
|
|
14
14
|
const textColor = $derived.by(() => {
|
|
@@ -33,6 +33,7 @@ const previewBackgroundColor = $derived.by(() => {
|
|
|
33
33
|
}
|
|
34
34
|
return colord(displayColor).toHex().substring(0, 7);
|
|
35
35
|
});
|
|
36
|
+
const alphaPercent = $derived(displayColor ? `${Math.round(colord(displayColor).alpha() * 100)}%` : '');
|
|
36
37
|
const triggerPlaceholder = $derived(placeholder ?? loc.notSet);
|
|
37
38
|
const resetValue = (e) => {
|
|
38
39
|
e.preventDefault();
|
|
@@ -52,6 +53,14 @@ const onColorPickerInput = ({ hex }) => {
|
|
|
52
53
|
<div class="color-picker__trigger">
|
|
53
54
|
{#if children}
|
|
54
55
|
{@render children()}
|
|
56
|
+
{:else if variant === 'field'}
|
|
57
|
+
<div class="color-picker__field color-picker__field--{size}">
|
|
58
|
+
<span class="color-picker__field-swatch" style:background={previewBackgroundColor} aria-hidden="true"></span>
|
|
59
|
+
<span class="color-picker__field-label">{label}</span>
|
|
60
|
+
{#if displayColor}
|
|
61
|
+
<span class="color-picker__field-value">{alphaPercent}</span>
|
|
62
|
+
{/if}
|
|
63
|
+
</div>
|
|
55
64
|
{:else}
|
|
56
65
|
<Input
|
|
57
66
|
value={value || ''}
|
|
@@ -118,6 +127,8 @@ between black and white for contrast.
|
|
|
118
127
|
| Prop | Type | Default |
|
|
119
128
|
|---|---|---|
|
|
120
129
|
| `value` | `string \| null \| undefined` | required |
|
|
130
|
+
| `variant` | `'fill' \| 'field'` | `'fill'` |
|
|
131
|
+
| `label` | `string` (field variant row label) | — |
|
|
121
132
|
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` |
|
|
122
133
|
| `clearable` | `boolean` | `false` |
|
|
123
134
|
| `disabled` | `boolean` | `false` |
|
|
@@ -147,6 +158,54 @@ between black and white for contrast.
|
|
|
147
158
|
transition: none;
|
|
148
159
|
--sc-kit--input--cursor--inert: pointer;
|
|
149
160
|
}
|
|
161
|
+
.color-picker__field {
|
|
162
|
+
--_color-picker--field-height: var(--sc-kit--field--height--md);
|
|
163
|
+
--_color-picker--field-padding-inline: var(--sc-kit--field--padding-inline--md);
|
|
164
|
+
display: flex;
|
|
165
|
+
width: 100%;
|
|
166
|
+
align-items: center;
|
|
167
|
+
gap: var(--sc-kit--space--2);
|
|
168
|
+
height: var(--_color-picker--field-height);
|
|
169
|
+
padding-inline: var(--_color-picker--field-padding-inline);
|
|
170
|
+
background: var(--sc-kit--color--bg--field);
|
|
171
|
+
border: 1px solid var(--sc-kit--color--border--field);
|
|
172
|
+
border-radius: var(--sc-kit--radius--md);
|
|
173
|
+
cursor: pointer;
|
|
174
|
+
transition: border-color var(--sc-kit--duration--base) var(--sc-kit--ease--default), background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
175
|
+
}
|
|
176
|
+
.color-picker__field--sm {
|
|
177
|
+
--_color-picker--field-height: var(--sc-kit--field--height--sm);
|
|
178
|
+
--_color-picker--field-padding-inline: var(--sc-kit--field--padding-inline--sm);
|
|
179
|
+
}
|
|
180
|
+
.color-picker__field--lg {
|
|
181
|
+
--_color-picker--field-height: var(--sc-kit--field--height--lg);
|
|
182
|
+
--_color-picker--field-padding-inline: var(--sc-kit--field--padding-inline--lg);
|
|
183
|
+
}
|
|
184
|
+
.color-picker__field:hover {
|
|
185
|
+
border-color: var(--sc-kit--color--border--strong);
|
|
186
|
+
}
|
|
187
|
+
.color-picker__field-swatch {
|
|
188
|
+
width: 1.25rem;
|
|
189
|
+
height: 1.25rem;
|
|
190
|
+
flex-shrink: 0;
|
|
191
|
+
border: 1px solid var(--sc-kit--color--border);
|
|
192
|
+
border-radius: var(--sc-kit--radius--sm);
|
|
193
|
+
}
|
|
194
|
+
.color-picker__field-label {
|
|
195
|
+
flex: 1;
|
|
196
|
+
min-width: 0;
|
|
197
|
+
color: var(--sc-kit--color--text--primary);
|
|
198
|
+
font-size: var(--sc-kit--font-size--sm);
|
|
199
|
+
text-overflow: ellipsis;
|
|
200
|
+
max-width: 100%;
|
|
201
|
+
white-space: nowrap;
|
|
202
|
+
overflow: hidden;
|
|
203
|
+
}
|
|
204
|
+
.color-picker__field-value {
|
|
205
|
+
flex-shrink: 0;
|
|
206
|
+
color: var(--sc-kit--color--text--secondary);
|
|
207
|
+
font-size: var(--sc-kit--font-size--sm);
|
|
208
|
+
}
|
|
150
209
|
.color-picker__chevron, .color-picker__clear {
|
|
151
210
|
display: inline-flex;
|
|
152
211
|
align-items: center;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
type Props = {
|
|
3
3
|
value: string | null | undefined;
|
|
4
|
+
/** Trigger presentation: `'fill'` floods the field with the color; `'field'` is a compact row (swatch + label + opacity). @default 'fill' */
|
|
5
|
+
variant?: 'fill' | 'field';
|
|
6
|
+
/** Row label for the `'field'` variant (e.g. "Background Color"). */
|
|
7
|
+
label?: string;
|
|
4
8
|
/** Size preset (shared with Input / DatePicker / Select). @default 'md' */
|
|
5
9
|
size?: 'sm' | 'md' | 'lg';
|
|
6
10
|
/** Default color shown in the picker UI when value is empty (does not set the value itself). */
|
|
@@ -35,6 +39,8 @@ type Props = {
|
|
|
35
39
|
* | Prop | Type | Default |
|
|
36
40
|
* |---|---|---|
|
|
37
41
|
* | `value` | `string \| null \| undefined` | required |
|
|
42
|
+
* | `variant` | `'fill' \| 'field'` | `'fill'` |
|
|
43
|
+
* | `label` | `string` (field variant row label) | — |
|
|
38
44
|
* | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` |
|
|
39
45
|
* | `clearable` | `boolean` | `false` |
|
|
40
46
|
* | `disabled` | `boolean` | `false` |
|
|
@@ -31,6 +31,21 @@ $effect(() => {
|
|
|
31
31
|
const collapseButtons = $derived([leftPanel?.id, rightPanel?.id].filter((id) => id !== null && id !== undefined));
|
|
32
32
|
const leftCollapsed = $derived(leftPanel?.entry.collapsed ?? false);
|
|
33
33
|
const rightCollapsed = $derived(rightPanel?.entry.collapsed ?? false);
|
|
34
|
+
export function toggleLeft() {
|
|
35
|
+
leftPanel?.entry.toggleCollapsed();
|
|
36
|
+
}
|
|
37
|
+
export function toggleRight() {
|
|
38
|
+
rightPanel?.entry.toggleCollapsed();
|
|
39
|
+
}
|
|
40
|
+
/** Collapses both sidebars, or expands both when they are already all collapsed. */
|
|
41
|
+
export function toggleBoth() {
|
|
42
|
+
const entries = [leftPanel?.entry, rightPanel?.entry].filter((entry) => !!entry);
|
|
43
|
+
if (entries.length === 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const allCollapsed = entries.every((entry) => entry.collapsed);
|
|
47
|
+
entries.forEach((entry) => entry.setCollapsed(!allCollapsed));
|
|
48
|
+
}
|
|
34
49
|
$effect(() => {
|
|
35
50
|
if (leftPanel) {
|
|
36
51
|
leftPanel.entry.isValid = sidebarLeftIsValid;
|
|
@@ -86,6 +101,9 @@ PagePanelSidebars — a `PagePanel` whose body has collapsible left / right side
|
|
|
86
101
|
fictional left/right panels in the shared `PanelState` so the standard collapse buttons in the
|
|
87
102
|
header can target them. Sidebars slide horizontally on collapse / expand.
|
|
88
103
|
|
|
104
|
+
Exposes imperative `toggleLeft()` / `toggleRight()` / `toggleBoth()` via `bind:this` for external
|
|
105
|
+
collapse controls (e.g. a top-bar button), so callers don't reach into `PanelState` by id.
|
|
106
|
+
|
|
89
107
|
### CSS Custom Properties
|
|
90
108
|
| Property | Description | Default |
|
|
91
109
|
|---|---|---|
|
|
@@ -20,11 +20,18 @@ type Props = {
|
|
|
20
20
|
* fictional left/right panels in the shared `PanelState` so the standard collapse buttons in the
|
|
21
21
|
* header can target them. Sidebars slide horizontally on collapse / expand.
|
|
22
22
|
*
|
|
23
|
+
* Exposes imperative `toggleLeft()` / `toggleRight()` / `toggleBoth()` via `bind:this` for external
|
|
24
|
+
* collapse controls (e.g. a top-bar button), so callers don't reach into `PanelState` by id.
|
|
25
|
+
*
|
|
23
26
|
* ### CSS Custom Properties
|
|
24
27
|
* | Property | Description | Default |
|
|
25
28
|
* |---|---|---|
|
|
26
29
|
* | `--sc-kit--page-panel-sidebars--border-color` | Sidebar divider color | `--sc-kit--color--border` |
|
|
27
30
|
*/
|
|
28
|
-
declare const Cmp: import("svelte").Component<Props, {
|
|
31
|
+
declare const Cmp: import("svelte").Component<Props, {
|
|
32
|
+
toggleLeft: () => void;
|
|
33
|
+
toggleRight: () => void;
|
|
34
|
+
toggleBoth: () => void;
|
|
35
|
+
}, "">;
|
|
29
36
|
type Cmp = ReturnType<typeof Cmp>;
|
|
30
37
|
export default Cmp;
|
|
@@ -280,6 +280,7 @@ to min / max; Enter / Space toggle collapsed.
|
|
|
280
280
|
}
|
|
281
281
|
.page-panel__header-left {
|
|
282
282
|
display: flex;
|
|
283
|
+
align-self: stretch;
|
|
283
284
|
align-items: center;
|
|
284
285
|
gap: 1rem;
|
|
285
286
|
min-width: 0;
|
|
@@ -298,6 +299,7 @@ to min / max; Enter / Space toggle collapsed.
|
|
|
298
299
|
}
|
|
299
300
|
.page-panel__header-right {
|
|
300
301
|
display: flex;
|
|
302
|
+
align-self: stretch;
|
|
301
303
|
align-items: center;
|
|
302
304
|
justify-content: flex-end;
|
|
303
305
|
margin-left: auto;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">import { Icon } from '../icon';
|
|
2
2
|
import { PageLayoutLocalization } from './page-layout-localization';
|
|
3
|
-
import
|
|
3
|
+
import IconPanelLeft from '@fluentui/svg-icons/icons/panel_left_20_regular.svg?raw';
|
|
4
4
|
const { panelState, panelId } = $props();
|
|
5
5
|
const localization = new PageLayoutLocalization();
|
|
6
6
|
const entry = $derived(panelState.get(panelId));
|
|
@@ -19,7 +19,7 @@ const handleClick = () => {
|
|
|
19
19
|
onclick={handleClick}
|
|
20
20
|
aria-label={localization.togglePanel}
|
|
21
21
|
aria-pressed={entry.collapsed}>
|
|
22
|
-
<Icon src={
|
|
22
|
+
<Icon src={IconPanelLeft} />
|
|
23
23
|
</button>
|
|
24
24
|
{/if}
|
|
25
25
|
|
|
@@ -38,6 +38,7 @@ state. Renders nothing if the target panel isn't registered. Typically rendered
|
|
|
38
38
|
| `--sc-kit--panel-collapse-button--color--invalid` | Color when the target panel is invalid | `--sc-kit--color--danger` |
|
|
39
39
|
-->
|
|
40
40
|
<style>.panel-collapse-button {
|
|
41
|
+
--sc-kit--icon--size: 1rem;
|
|
41
42
|
--_panel-collapse-button--color: var(--sc-kit--panel-collapse-button--color, var(--sc-kit--color--text--muted));
|
|
42
43
|
--_panel-collapse-button--color-hover: var(--sc-kit--panel-collapse-button--color--hover, var(--sc-kit--color--text--primary));
|
|
43
44
|
--_panel-collapse-button--color-invalid: var(--sc-kit--panel-collapse-button--color--invalid, var(--sc-kit--color--danger));
|
|
@@ -1,40 +1,24 @@
|
|
|
1
|
-
<script lang="ts"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
<script lang="ts" generics="T">const { value, tabs, variant = 'default', size = 'md', compare, on } = $props();
|
|
1
|
+
<script lang="ts" generics="T">import TabItem from './tab.svelte';
|
|
2
|
+
const { value, tabs, variant = 'default', size = 'md', compare, on } = $props();
|
|
5
3
|
const isActive = (tab) => (compare ? compare(value, tab.value) : value === tab.value);
|
|
6
|
-
const handleClick = (tab) => {
|
|
7
|
-
if (tab.disabled || isActive(tab)) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
on?.change?.(tab.value);
|
|
11
|
-
};
|
|
12
4
|
</script>
|
|
13
5
|
|
|
14
|
-
<div class="tabs tabs--{variant}
|
|
6
|
+
<div class="tabs tabs--{variant}" role="tablist">
|
|
15
7
|
{#each tabs as tab, i (i)}
|
|
16
|
-
<
|
|
17
|
-
type="button"
|
|
18
|
-
class="tabs__tab"
|
|
19
|
-
class:tabs__tab--active={isActive(tab)}
|
|
20
|
-
class:tabs__tab--disabled={tab.disabled}
|
|
21
|
-
role="tab"
|
|
22
|
-
aria-selected={isActive(tab)}
|
|
23
|
-
disabled={tab.disabled}
|
|
24
|
-
onclick={() => handleClick(tab)}>
|
|
25
|
-
<span class="tabs__label">{tab.label}</span>
|
|
26
|
-
</button>
|
|
8
|
+
<TabItem tab={tab} variant={variant} size={size} active={isActive(tab)} on={{ select: on?.change, close: on?.close }} />
|
|
27
9
|
{/each}
|
|
28
10
|
</div>
|
|
29
11
|
|
|
30
12
|
<!--
|
|
31
13
|
@component
|
|
32
14
|
Tabs — header strip for switching between content panels. Panel rendering is consumer-controlled
|
|
33
|
-
(switch on the active `value`).
|
|
15
|
+
(switch on the active `value`). Three visual variants:
|
|
34
16
|
|
|
35
17
|
- **`default`**: underline — a 2px accent bar travels under the active tab; the strip has a bottom border.
|
|
36
18
|
- **`pills`**: filled — the active tab lifts as a card on an inset tray.
|
|
19
|
+
- **`document`**: browser-style tabs — flush, rounded-top; the active tab background matches the canvas so it reads as continuous with the content below.
|
|
37
20
|
|
|
21
|
+
Providing `on.close` renders a close affordance on each non-disabled tab (works in any variant).
|
|
38
22
|
Each tab carries an optional `disabled` flag (non-interactive). For navigation, not single-choice
|
|
39
23
|
form input — use `SegmentedControl` for the latter (it's a `radiogroup`; this is a `tablist`).
|
|
40
24
|
|
|
@@ -43,112 +27,41 @@ form input — use `SegmentedControl` for the latter (it's a `radiogroup`; this
|
|
|
43
27
|
|---|---|---|
|
|
44
28
|
| `--sc-kit--tabs--height` | Tab height | per size (28 / 36 px) |
|
|
45
29
|
| `--sc-kit--tabs--font-size` | Tab label font size | per size |
|
|
30
|
+
| `--sc-kit--tabs--font-weight` | Tab label font weight | per size (sm: regular / md: medium) |
|
|
46
31
|
| `--sc-kit--tabs--padding-inline` | Per-tab horizontal padding | per size (10 / 14 px) |
|
|
47
32
|
| `--sc-kit--tabs--radius` | Tab radius (pills) / tray radius | `--sc-kit--radius--sm` / `--sc-kit--radius--md` |
|
|
48
33
|
| `--sc-kit--tabs--color` | Default tab text color | `--sc-kit--color--text--secondary` |
|
|
49
34
|
| `--sc-kit--tabs--color--hover` | Hover text color | `--sc-kit--color--text--primary` |
|
|
50
|
-
| `--sc-kit--tabs--color--active` | Active tab text color | default: `--sc-kit--color--accent`, pills: `--sc-kit--color--text--primary` |
|
|
35
|
+
| `--sc-kit--tabs--color--active` | Active tab text color | default: `--sc-kit--color--accent`, pills/document: `--sc-kit--color--text--primary` |
|
|
51
36
|
| `--sc-kit--tabs--color--disabled` | Disabled tab text color | `--sc-kit--color--text--muted` |
|
|
52
37
|
| `--sc-kit--tabs--accent` | Underline bar color (default variant) | `--sc-kit--color--accent` |
|
|
53
38
|
| `--sc-kit--tabs--border-color` | Bottom border color (default variant) | `--sc-kit--color--border` |
|
|
54
39
|
| `--sc-kit--tabs--pills--background` | Inset tray background (pills variant) | `--sc-kit--color--bg--active` |
|
|
55
40
|
| `--sc-kit--tabs--pills--background--active` | Active tab background (pills variant) | `--sc-kit--color--bg--panel` |
|
|
56
41
|
| `--sc-kit--tabs--pills--shadow--active` | Active tab lift shadow (pills variant) | `--sc-kit--shadow--sm` |
|
|
42
|
+
| `--sc-kit--tabs--document--background` | Inactive tab background (document variant) | `neutral-200` / `dark-500` |
|
|
43
|
+
| `--sc-kit--tabs--document--background--active` | Active tab background (document variant) — matches the canvas | `--sc-kit--color--bg--app` |
|
|
44
|
+
| `--sc-kit--tabs--document--background--hover` | Inactive tab hover background (document variant) | `--sc-kit--color--bg--hover` |
|
|
57
45
|
-->
|
|
58
46
|
|
|
59
47
|
<style>.tabs {
|
|
60
|
-
--_tabs--height: var(--sc-kit--tabs--height, 2.25rem);
|
|
61
|
-
--_tabs--font-size: var(--sc-kit--tabs--font-size, var(--sc-kit--font-size--md));
|
|
62
|
-
--_tabs--padding-inline: var(--sc-kit--tabs--padding-inline, 0.875rem);
|
|
63
|
-
--_tabs--radius: var(--sc-kit--tabs--radius, var(--sc-kit--radius--sm));
|
|
64
|
-
--_tabs--color: var(--sc-kit--tabs--color, var(--sc-kit--color--text--secondary));
|
|
65
|
-
--_tabs--color-hover: var(--sc-kit--tabs--color--hover, var(--sc-kit--color--text--primary));
|
|
66
|
-
--_tabs--color-active: var(--sc-kit--tabs--color--active, var(--_tabs--color-active-default));
|
|
67
|
-
--_tabs--color-disabled: var(--sc-kit--tabs--color--disabled, var(--sc-kit--color--text--muted));
|
|
68
|
-
--_tabs--accent: var(--sc-kit--tabs--accent, var(--sc-kit--color--accent));
|
|
69
48
|
--_tabs--border-color: var(--sc-kit--tabs--border-color, var(--sc-kit--color--border));
|
|
70
49
|
--_tabs--pills-background: var(--sc-kit--tabs--pills--background, var(--sc-kit--color--bg--active));
|
|
71
|
-
--_tabs--pills-background-active: var(--sc-kit--tabs--pills--background--active, var(--sc-kit--color--bg--panel));
|
|
72
|
-
--_tabs--pills-shadow-active: var(--sc-kit--tabs--pills--shadow--active, var(--sc-kit--shadow--sm));
|
|
73
50
|
display: inline-flex;
|
|
74
51
|
align-items: center;
|
|
75
52
|
gap: 0.25rem;
|
|
76
53
|
font-family: inherit;
|
|
77
54
|
}
|
|
78
|
-
.tabs--sm {
|
|
79
|
-
--sc-kit--tabs--height: 1.75rem;
|
|
80
|
-
--sc-kit--tabs--font-size: var(--sc-kit--font-size--sm);
|
|
81
|
-
--sc-kit--tabs--padding-inline: 0.625rem;
|
|
82
|
-
}
|
|
83
|
-
.tabs--md {
|
|
84
|
-
--sc-kit--tabs--height: 2.25rem;
|
|
85
|
-
--sc-kit--tabs--font-size: var(--sc-kit--font-size--md);
|
|
86
|
-
--sc-kit--tabs--padding-inline: 0.875rem;
|
|
87
|
-
}
|
|
88
55
|
.tabs--default {
|
|
89
|
-
--_tabs--color-active-default: var(--_tabs--accent);
|
|
90
56
|
border-bottom: 1px solid var(--_tabs--border-color);
|
|
91
57
|
}
|
|
92
|
-
.tabs--default .tabs__tab {
|
|
93
|
-
border-radius: 0;
|
|
94
|
-
}
|
|
95
|
-
.tabs--default .tabs__tab--active {
|
|
96
|
-
color: var(--_tabs--color-active);
|
|
97
|
-
}
|
|
98
|
-
.tabs--default .tabs__tab--active::after {
|
|
99
|
-
content: "";
|
|
100
|
-
position: absolute;
|
|
101
|
-
left: var(--_tabs--padding-inline);
|
|
102
|
-
right: var(--_tabs--padding-inline);
|
|
103
|
-
bottom: -1px;
|
|
104
|
-
height: 2px;
|
|
105
|
-
background: var(--_tabs--accent);
|
|
106
|
-
border-radius: 2px;
|
|
107
|
-
}
|
|
108
58
|
.tabs--pills {
|
|
109
|
-
--_tabs--color-active-default: var(--sc-kit--color--text--primary);
|
|
110
59
|
gap: 0;
|
|
111
60
|
padding: 0.25rem;
|
|
112
61
|
background: var(--_tabs--pills-background);
|
|
113
62
|
border-radius: var(--sc-kit--radius--md);
|
|
114
63
|
}
|
|
115
|
-
.tabs--
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
box-shadow: var(--_tabs--pills-shadow-active);
|
|
119
|
-
}
|
|
120
|
-
.tabs__tab {
|
|
121
|
-
position: relative;
|
|
122
|
-
display: inline-flex;
|
|
123
|
-
align-items: center;
|
|
124
|
-
justify-content: center;
|
|
125
|
-
height: var(--_tabs--height);
|
|
126
|
-
padding: 0 var(--_tabs--padding-inline);
|
|
127
|
-
background: transparent;
|
|
128
|
-
border: none;
|
|
129
|
-
border-radius: var(--_tabs--radius);
|
|
130
|
-
color: var(--_tabs--color);
|
|
131
|
-
font-family: inherit;
|
|
132
|
-
font-size: var(--_tabs--font-size);
|
|
133
|
-
font-weight: var(--sc-kit--font-weight--medium);
|
|
134
|
-
line-height: var(--sc-kit--leading--tight);
|
|
135
|
-
cursor: pointer;
|
|
136
|
-
white-space: nowrap;
|
|
137
|
-
min-width: 0;
|
|
138
|
-
transition: color var(--sc-kit--duration--base) var(--sc-kit--ease--default), background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
139
|
-
}
|
|
140
|
-
.tabs__tab:where(:hover):not(.tabs__tab--disabled):not(.tabs__tab--active) {
|
|
141
|
-
color: var(--_tabs--color-hover);
|
|
142
|
-
}
|
|
143
|
-
.tabs__tab--disabled {
|
|
144
|
-
color: var(--_tabs--color-disabled);
|
|
145
|
-
cursor: default;
|
|
146
|
-
}
|
|
147
|
-
.tabs__tab:focus-visible {
|
|
148
|
-
outline: 2px solid var(--sc-kit--color--border--focus);
|
|
149
|
-
outline-offset: 2px;
|
|
150
|
-
}
|
|
151
|
-
.tabs__label {
|
|
152
|
-
display: inline-block;
|
|
153
|
-
max-width: 100%;
|
|
64
|
+
.tabs--document {
|
|
65
|
+
gap: 0;
|
|
66
|
+
align-items: flex-end;
|
|
154
67
|
}</style>
|
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
value: TValue;
|
|
3
|
-
label: string;
|
|
4
|
-
/** Non-interactive — click does nothing. */
|
|
5
|
-
disabled?: boolean;
|
|
6
|
-
};
|
|
1
|
+
import type { Tab, TabsSize, TabsVariant } from './types';
|
|
7
2
|
declare function $$render<T>(): {
|
|
8
3
|
props: {
|
|
9
4
|
value: T;
|
|
10
5
|
/** `NoInfer<T>` ensures `T` narrows from `value` only; inline tab literals type-check against the value's T without widening — no `as const` needed at callsites. */
|
|
11
6
|
tabs: Tab<NoInfer<T>>[];
|
|
12
7
|
/** @default 'default' */
|
|
13
|
-
variant?:
|
|
8
|
+
variant?: TabsVariant;
|
|
14
9
|
/** @default 'md' */
|
|
15
|
-
size?:
|
|
10
|
+
size?: TabsSize;
|
|
16
11
|
/** Equality for object T values. @default reference equality (`===`) */
|
|
17
12
|
compare?: (a: T, b: T) => boolean;
|
|
18
13
|
on?: {
|
|
19
14
|
change?: (value: T) => void;
|
|
15
|
+
/** Providing this renders a close affordance on each non-disabled tab (works in any variant). */
|
|
16
|
+
close?: (value: T) => void;
|
|
20
17
|
};
|
|
21
18
|
};
|
|
22
19
|
exports: {};
|
|
@@ -40,11 +37,13 @@ interface $$IsomorphicComponent {
|
|
|
40
37
|
}
|
|
41
38
|
/**
|
|
42
39
|
* Tabs — header strip for switching between content panels. Panel rendering is consumer-controlled
|
|
43
|
-
* (switch on the active `value`).
|
|
40
|
+
* (switch on the active `value`). Three visual variants:
|
|
44
41
|
*
|
|
45
42
|
* - **`default`**: underline — a 2px accent bar travels under the active tab; the strip has a bottom border.
|
|
46
43
|
* - **`pills`**: filled — the active tab lifts as a card on an inset tray.
|
|
44
|
+
* - **`document`**: browser-style tabs — flush, rounded-top; the active tab background matches the canvas so it reads as continuous with the content below.
|
|
47
45
|
*
|
|
46
|
+
* Providing `on.close` renders a close affordance on each non-disabled tab (works in any variant).
|
|
48
47
|
* Each tab carries an optional `disabled` flag (non-interactive). For navigation, not single-choice
|
|
49
48
|
* form input — use `SegmentedControl` for the latter (it's a `radiogroup`; this is a `tablist`).
|
|
50
49
|
*
|
|
@@ -53,17 +52,21 @@ interface $$IsomorphicComponent {
|
|
|
53
52
|
* |---|---|---|
|
|
54
53
|
* | `--sc-kit--tabs--height` | Tab height | per size (28 / 36 px) |
|
|
55
54
|
* | `--sc-kit--tabs--font-size` | Tab label font size | per size |
|
|
55
|
+
* | `--sc-kit--tabs--font-weight` | Tab label font weight | per size (sm: regular / md: medium) |
|
|
56
56
|
* | `--sc-kit--tabs--padding-inline` | Per-tab horizontal padding | per size (10 / 14 px) |
|
|
57
57
|
* | `--sc-kit--tabs--radius` | Tab radius (pills) / tray radius | `--sc-kit--radius--sm` / `--sc-kit--radius--md` |
|
|
58
58
|
* | `--sc-kit--tabs--color` | Default tab text color | `--sc-kit--color--text--secondary` |
|
|
59
59
|
* | `--sc-kit--tabs--color--hover` | Hover text color | `--sc-kit--color--text--primary` |
|
|
60
|
-
* | `--sc-kit--tabs--color--active` | Active tab text color | default: `--sc-kit--color--accent`, pills: `--sc-kit--color--text--primary` |
|
|
60
|
+
* | `--sc-kit--tabs--color--active` | Active tab text color | default: `--sc-kit--color--accent`, pills/document: `--sc-kit--color--text--primary` |
|
|
61
61
|
* | `--sc-kit--tabs--color--disabled` | Disabled tab text color | `--sc-kit--color--text--muted` |
|
|
62
62
|
* | `--sc-kit--tabs--accent` | Underline bar color (default variant) | `--sc-kit--color--accent` |
|
|
63
63
|
* | `--sc-kit--tabs--border-color` | Bottom border color (default variant) | `--sc-kit--color--border` |
|
|
64
64
|
* | `--sc-kit--tabs--pills--background` | Inset tray background (pills variant) | `--sc-kit--color--bg--active` |
|
|
65
65
|
* | `--sc-kit--tabs--pills--background--active` | Active tab background (pills variant) | `--sc-kit--color--bg--panel` |
|
|
66
66
|
* | `--sc-kit--tabs--pills--shadow--active` | Active tab lift shadow (pills variant) | `--sc-kit--shadow--sm` |
|
|
67
|
+
* | `--sc-kit--tabs--document--background` | Inactive tab background (document variant) | `neutral-200` / `dark-500` |
|
|
68
|
+
* | `--sc-kit--tabs--document--background--active` | Active tab background (document variant) — matches the canvas | `--sc-kit--color--bg--app` |
|
|
69
|
+
* | `--sc-kit--tabs--document--background--hover` | Inactive tab hover background (document variant) | `--sc-kit--color--bg--hover` |
|
|
67
70
|
*/
|
|
68
71
|
declare const Cmp: $$IsomorphicComponent;
|
|
69
72
|
type Cmp<T> = InstanceType<typeof Cmp<T>>;
|
package/dist/ui/tabs/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as Tabs } from './cmp.tabs.svelte';
|
|
2
|
-
export type { Tab } from './
|
|
2
|
+
export type { Tab } from './types';
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<script lang="ts" generics="T">import { IconSlot } from '../icon';
|
|
2
|
+
import { TabsLocalization } from './tabs-localization';
|
|
3
|
+
import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
|
|
4
|
+
const { tab, variant, size, active, on } = $props();
|
|
5
|
+
const localization = new TabsLocalization();
|
|
6
|
+
const closable = $derived(!!on?.close && !tab.disabled);
|
|
7
|
+
const handleClick = () => {
|
|
8
|
+
if (tab.disabled || active) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
on?.select?.(tab.value);
|
|
12
|
+
};
|
|
13
|
+
const handleClose = (event) => {
|
|
14
|
+
event.stopPropagation();
|
|
15
|
+
on?.close?.(tab.value);
|
|
16
|
+
};
|
|
17
|
+
const handleCloseKeydown = (event) => {
|
|
18
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
event.stopPropagation();
|
|
21
|
+
on?.close?.(tab.value);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
class="tab tab--{variant} tab--{size}"
|
|
29
|
+
class:tab--disabled={tab.disabled}
|
|
30
|
+
class:tab--hoverable={!active && !tab.disabled}
|
|
31
|
+
class:tab--default-active={variant === 'default' && active}
|
|
32
|
+
class:tab--pills-active={variant === 'pills' && active}
|
|
33
|
+
class:tab--document-active={variant === 'document' && active}
|
|
34
|
+
class:tab--document-hoverable={variant === 'document' && !active && !tab.disabled}
|
|
35
|
+
role="tab"
|
|
36
|
+
aria-selected={active}
|
|
37
|
+
disabled={tab.disabled}
|
|
38
|
+
onclick={handleClick}>
|
|
39
|
+
<span class="tab__label">{tab.label}</span>
|
|
40
|
+
{#if closable}
|
|
41
|
+
<span class="tab__close" role="button" tabindex="0" aria-label={localization.close} onclick={handleClose} onkeydown={handleCloseKeydown}>
|
|
42
|
+
<IconSlot icon={IconDismiss} size="sm" />
|
|
43
|
+
</span>
|
|
44
|
+
{/if}
|
|
45
|
+
</button>
|
|
46
|
+
|
|
47
|
+
<style>.tab {
|
|
48
|
+
--_tab--height: var(--sc-kit--tabs--height, 2.25rem);
|
|
49
|
+
--_tab--font-size: var(--sc-kit--tabs--font-size, var(--sc-kit--font-size--md));
|
|
50
|
+
--_tab--font-weight: var(--sc-kit--tabs--font-weight, var(--sc-kit--font-weight--medium));
|
|
51
|
+
--_tab--padding-inline: var(--sc-kit--tabs--padding-inline, 0.875rem);
|
|
52
|
+
--_tab--radius: var(--sc-kit--tabs--radius, var(--sc-kit--radius--sm));
|
|
53
|
+
--_tab--color: var(--sc-kit--tabs--color, var(--sc-kit--color--text--secondary));
|
|
54
|
+
--_tab--color-hover: var(--sc-kit--tabs--color--hover, var(--sc-kit--color--text--primary));
|
|
55
|
+
--_tab--color-active: var(--sc-kit--tabs--color--active, var(--_tab--color-active-default));
|
|
56
|
+
--_tab--color-disabled: var(--sc-kit--tabs--color--disabled, var(--sc-kit--color--text--muted));
|
|
57
|
+
--_tab--accent: var(--sc-kit--tabs--accent, var(--sc-kit--color--accent));
|
|
58
|
+
--_tab--pills-background-active: var(--sc-kit--tabs--pills--background--active, var(--sc-kit--color--bg--panel));
|
|
59
|
+
--_tab--pills-shadow-active: var(--sc-kit--tabs--pills--shadow--active, var(--sc-kit--shadow--sm));
|
|
60
|
+
--_tab--document-background: var(--sc-kit--tabs--document--background, light-dark(#e5e7eb, #272727));
|
|
61
|
+
--_tab--document-background-active: var(--sc-kit--tabs--document--background--active, var(--sc-kit--color--bg--app));
|
|
62
|
+
--_tab--document-background-hover: var(--sc-kit--tabs--document--background--hover, var(--sc-kit--color--bg--hover));
|
|
63
|
+
position: relative;
|
|
64
|
+
display: inline-flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
height: var(--_tab--height);
|
|
68
|
+
padding: 0 var(--_tab--padding-inline);
|
|
69
|
+
background: transparent;
|
|
70
|
+
border: none;
|
|
71
|
+
border-radius: var(--_tab--radius);
|
|
72
|
+
color: var(--_tab--color);
|
|
73
|
+
font-family: inherit;
|
|
74
|
+
font-size: var(--_tab--font-size);
|
|
75
|
+
font-weight: var(--_tab--font-weight);
|
|
76
|
+
line-height: var(--sc-kit--leading--tight);
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
min-width: 0;
|
|
80
|
+
transition: color var(--sc-kit--duration--base) var(--sc-kit--ease--default), background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
81
|
+
}
|
|
82
|
+
.tab--sm {
|
|
83
|
+
--sc-kit--tabs--height: 1.75rem;
|
|
84
|
+
--sc-kit--tabs--font-size: var(--sc-kit--font-size--sm);
|
|
85
|
+
--sc-kit--tabs--font-weight: var(--sc-kit--font-weight--regular);
|
|
86
|
+
--sc-kit--tabs--padding-inline: 0.625rem;
|
|
87
|
+
}
|
|
88
|
+
.tab--md {
|
|
89
|
+
--sc-kit--tabs--height: 2.25rem;
|
|
90
|
+
--sc-kit--tabs--font-size: var(--sc-kit--font-size--md);
|
|
91
|
+
--sc-kit--tabs--font-weight: var(--sc-kit--font-weight--medium);
|
|
92
|
+
--sc-kit--tabs--padding-inline: 0.875rem;
|
|
93
|
+
}
|
|
94
|
+
.tab--hoverable:hover {
|
|
95
|
+
color: var(--_tab--color-hover);
|
|
96
|
+
}
|
|
97
|
+
.tab--disabled {
|
|
98
|
+
color: var(--_tab--color-disabled);
|
|
99
|
+
cursor: default;
|
|
100
|
+
}
|
|
101
|
+
.tab--default {
|
|
102
|
+
--_tab--color-active-default: var(--_tab--accent);
|
|
103
|
+
border-radius: 0;
|
|
104
|
+
}
|
|
105
|
+
.tab--default-active {
|
|
106
|
+
color: var(--_tab--color-active);
|
|
107
|
+
}
|
|
108
|
+
.tab--default-active::after {
|
|
109
|
+
content: "";
|
|
110
|
+
position: absolute;
|
|
111
|
+
left: var(--_tab--padding-inline);
|
|
112
|
+
right: var(--_tab--padding-inline);
|
|
113
|
+
bottom: -1px;
|
|
114
|
+
height: 2px;
|
|
115
|
+
background: var(--_tab--accent);
|
|
116
|
+
border-radius: 2px;
|
|
117
|
+
}
|
|
118
|
+
.tab--pills {
|
|
119
|
+
--_tab--color-active-default: var(--sc-kit--color--text--primary);
|
|
120
|
+
}
|
|
121
|
+
.tab--pills-active {
|
|
122
|
+
color: var(--_tab--color-active);
|
|
123
|
+
background: var(--_tab--pills-background-active);
|
|
124
|
+
box-shadow: var(--_tab--pills-shadow-active);
|
|
125
|
+
}
|
|
126
|
+
.tab--document {
|
|
127
|
+
--_tab--color-active-default: var(--sc-kit--color--text--primary);
|
|
128
|
+
--_tab--height: 2rem;
|
|
129
|
+
--_tab--padding-inline: 0.75rem;
|
|
130
|
+
justify-content: flex-start;
|
|
131
|
+
gap: 0.5rem;
|
|
132
|
+
min-width: 7rem;
|
|
133
|
+
background: var(--_tab--document-background);
|
|
134
|
+
border-radius: 0.25rem 0.5rem 0 0;
|
|
135
|
+
}
|
|
136
|
+
.tab--document-hoverable:hover {
|
|
137
|
+
background: var(--_tab--document-background-hover);
|
|
138
|
+
}
|
|
139
|
+
.tab--document-active {
|
|
140
|
+
color: var(--_tab--color-active);
|
|
141
|
+
background: var(--_tab--document-background-active);
|
|
142
|
+
}
|
|
143
|
+
.tab:focus-visible {
|
|
144
|
+
outline: 2px solid var(--sc-kit--color--border--focus);
|
|
145
|
+
outline-offset: 2px;
|
|
146
|
+
}
|
|
147
|
+
.tab__label {
|
|
148
|
+
display: inline-block;
|
|
149
|
+
max-width: 100%;
|
|
150
|
+
}
|
|
151
|
+
.tab__close {
|
|
152
|
+
display: inline-flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
flex-shrink: 0;
|
|
156
|
+
margin-left: auto;
|
|
157
|
+
padding: 0.125rem;
|
|
158
|
+
color: var(--sc-kit--color--text--secondary);
|
|
159
|
+
border-radius: var(--sc-kit--radius--sm);
|
|
160
|
+
cursor: pointer;
|
|
161
|
+
transition: color var(--sc-kit--duration--base) var(--sc-kit--ease--default), background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
162
|
+
}
|
|
163
|
+
.tab__close:hover {
|
|
164
|
+
color: var(--sc-kit--color--text--primary);
|
|
165
|
+
}
|
|
166
|
+
.tab__close:focus-visible {
|
|
167
|
+
outline: 2px solid var(--sc-kit--color--border--focus);
|
|
168
|
+
outline-offset: 2px;
|
|
169
|
+
}</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Tab, TabsSize, TabsVariant } from './types';
|
|
2
|
+
declare function $$render<T>(): {
|
|
3
|
+
props: {
|
|
4
|
+
tab: Tab<T>;
|
|
5
|
+
variant: TabsVariant;
|
|
6
|
+
size: TabsSize;
|
|
7
|
+
active: boolean;
|
|
8
|
+
on?: {
|
|
9
|
+
select?: (value: T) => void;
|
|
10
|
+
close?: (value: T) => void;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
exports: {};
|
|
14
|
+
bindings: "";
|
|
15
|
+
slots: {};
|
|
16
|
+
events: {};
|
|
17
|
+
};
|
|
18
|
+
declare class __sveltets_Render<T> {
|
|
19
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
20
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
21
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
22
|
+
bindings(): "";
|
|
23
|
+
exports(): {};
|
|
24
|
+
}
|
|
25
|
+
interface $$IsomorphicComponent {
|
|
26
|
+
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
27
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
28
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
29
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
30
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
31
|
+
}
|
|
32
|
+
declare const Tab: $$IsomorphicComponent;
|
|
33
|
+
type Tab<T> = InstanceType<typeof Tab<T>>;
|
|
34
|
+
export default Tab;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamscloud/kit",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"author": "StreamsCloud",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -91,6 +91,10 @@
|
|
|
91
91
|
"types": "./dist/core/validation/index.d.ts",
|
|
92
92
|
"svelte": "./dist/core/validation/index.js"
|
|
93
93
|
},
|
|
94
|
+
"./ui/ai-panel": {
|
|
95
|
+
"types": "./dist/ui/ai-panel/index.d.ts",
|
|
96
|
+
"svelte": "./dist/ui/ai-panel/index.js"
|
|
97
|
+
},
|
|
94
98
|
"./ui/avatar": {
|
|
95
99
|
"types": "./dist/ui/avatar/index.d.ts",
|
|
96
100
|
"svelte": "./dist/ui/avatar/index.js"
|
|
@@ -465,7 +469,6 @@
|
|
|
465
469
|
"yup": "^1.7.1"
|
|
466
470
|
},
|
|
467
471
|
"devDependencies": {
|
|
468
|
-
"@eslint/compat": "^2.1.0",
|
|
469
472
|
"@eslint/js": "^10.0.1",
|
|
470
473
|
"@floating-ui/dom": "^1.7.6",
|
|
471
474
|
"@fluentui/svg-icons": "^1.1.326",
|