@streamscloud/kit 0.45.0 → 0.47.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.
Files changed (30) hide show
  1. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte +2 -7
  2. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte.d.ts +1 -7
  3. package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.d.ts +1 -1
  4. package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.js +14 -6
  5. package/dist/ui/cropper/img-cropper/img-cropper-contain-worker.svelte.js +17 -5
  6. package/dist/ui/cropper/img-cropper/img-cropper-cover-worker.svelte.js +24 -5
  7. package/dist/ui/cropper/img-cropper/img-cropper-utils.d.ts +12 -0
  8. package/dist/ui/cropper/img-cropper/img-cropper-utils.js +55 -14
  9. package/dist/ui/cropper/img-cropper/img-cropper-worker.svelte.d.ts +1 -1
  10. package/dist/ui/document-tabs/cmp.document-tabs.svelte +245 -0
  11. package/dist/ui/document-tabs/cmp.document-tabs.svelte.d.ts +80 -0
  12. package/dist/ui/document-tabs/document-tab.svelte +210 -0
  13. package/dist/ui/document-tabs/document-tab.svelte.d.ts +34 -0
  14. package/dist/ui/document-tabs/document-tabs-localization.d.ts +6 -0
  15. package/dist/ui/document-tabs/document-tabs-localization.js +33 -0
  16. package/dist/ui/document-tabs/index.d.ts +3 -0
  17. package/dist/ui/document-tabs/index.js +2 -0
  18. package/dist/ui/document-tabs/types.d.ts +20 -0
  19. package/dist/ui/document-tabs/types.js +1 -0
  20. package/dist/ui/page-layout/cmp.page-panel-sidebars.svelte +29 -3
  21. package/dist/ui/page-layout/cmp.page-panel-sidebars.svelte.d.ts +8 -0
  22. package/dist/ui/page-layout/cmp.page-panel.svelte +66 -42
  23. package/dist/ui/tabs/cmp.tabs.svelte +6 -14
  24. package/dist/ui/tabs/cmp.tabs.svelte.d.ts +5 -11
  25. package/dist/ui/tabs/tab.svelte +14 -75
  26. package/dist/ui/tabs/tab.svelte.d.ts +0 -1
  27. package/dist/ui/tabs/types.d.ts +1 -1
  28. package/package.json +5 -1
  29. package/dist/ui/tabs/tabs-localization.d.ts +0 -3
  30. package/dist/ui/tabs/tabs-localization.js +0 -12
@@ -0,0 +1,80 @@
1
+ import type { DocumentTab, DocumentTabsOverflow } from './types';
2
+ declare function $$render<T>(): {
3
+ props: {
4
+ value: T;
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. */
6
+ tabs: DocumentTab<NoInfer<T>>[];
7
+ /** `'scroll'` makes the strip its own scroll container with edge fades and chevrons. Leave it `'visible'` when the container already scrolls the strip. @default 'visible' */
8
+ overflow?: DocumentTabsOverflow;
9
+ /** Equality for object T values. @default reference equality (`===`) */
10
+ compare?: (a: T, b: T) => boolean;
11
+ on?: {
12
+ change?: (value: T) => void;
13
+ /** Providing this renders a close button on every tab that doesn't opt out with `closable: false`. */
14
+ close?: (value: T) => void;
15
+ };
16
+ };
17
+ exports: {};
18
+ bindings: "";
19
+ slots: {};
20
+ events: {};
21
+ };
22
+ declare class __sveltets_Render<T> {
23
+ props(): ReturnType<typeof $$render<T>>['props'];
24
+ events(): ReturnType<typeof $$render<T>>['events'];
25
+ slots(): ReturnType<typeof $$render<T>>['slots'];
26
+ bindings(): "";
27
+ exports(): {};
28
+ }
29
+ interface $$IsomorphicComponent {
30
+ 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']>> & {
31
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
32
+ } & ReturnType<__sveltets_Render<T>['exports']>;
33
+ <T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
34
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
35
+ }
36
+ /**
37
+ * DocumentTabs — browser-style tabs for an editor: the active tab is taller than its neighbours and
38
+ * sits flush on the container's bottom rule, so it reads as continuous with the panel below while the
39
+ * inactive ones stay behind that line. Each tab carries an optional icon, an unsaved-changes dot, a
40
+ * `tone` for a tab of a different kind (a live preview), and its own `closable` opt-out. A closable or
41
+ * dirty tab reserves the trailing slot permanently: the dot sits there at rest, the close button takes
42
+ * its place on hover, and a tab's width never depends on which of them is showing. Arrow keys / Home /
43
+ * End move between tabs (roving tabindex), Delete closes the focused one.
44
+ *
45
+ * `overflow="scroll"` gives the strip its own scroll container with edge fades, chevrons and
46
+ * wheel-to-scroll. Leave it `'visible'` where the container already scrolls the strip — two nested
47
+ * scrollers fight each other.
48
+ *
49
+ * The active tab dips `--sc-kit--document-tabs--baseline-overlap` below the strip to cover the bottom
50
+ * rule of the bar it sits in. A container that clips (any `overflow` other than `visible`) has to
51
+ * leave that last pixel inside its own clip region, or the overlap is cut off.
52
+ *
53
+ * ### CSS Custom Properties
54
+ * | Property | Description | Default |
55
+ * |---|---|---|
56
+ * | `--sc-kit--document-tabs--height` | Inactive tab height | `29px` |
57
+ * | `--sc-kit--document-tabs--height--active` | Active tab height | `32px` |
58
+ * | `--sc-kit--document-tabs--padding-inline` | Tab horizontal padding — the same in both states, so activating a tab shifts nothing sideways | `10px` |
59
+ * | `--sc-kit--document-tabs--min-width` | Minimum tab width | `112px` |
60
+ * | `--sc-kit--document-tabs--max-width` | Maximum tab width — the label ellipsizes past it | `200px` |
61
+ * | `--sc-kit--document-tabs--gap` | Gap between tabs | `1px` |
62
+ * | `--sc-kit--document-tabs--tab--gap` | Gap between a tab's icon, label and trailing slot | `8px` |
63
+ * | `--sc-kit--document-tabs--radius` | Top corner radius | `--sc-kit--radius--sm` |
64
+ * | `--sc-kit--document-tabs--border-width` | Tab border width | `1px` |
65
+ * | `--sc-kit--document-tabs--border-color` | Tab border color | `--sc-kit--color--border` |
66
+ * | `--sc-kit--document-tabs--baseline-overlap` | How far the active tab dips below the strip to cover its container's bottom rule | `1px` |
67
+ * | `--sc-kit--document-tabs--background` | Inactive tab background | `--sc-kit--color--bg--app` |
68
+ * | `--sc-kit--document-tabs--background--active` | Active tab background — matches the pane below | `--sc-kit--color--bg--panel` |
69
+ * | `--sc-kit--document-tabs--background--hover` | Inactive tab hover background | `--sc-kit--color--bg--hover` |
70
+ * | `--sc-kit--document-tabs--color` | Inactive tab label color | `--sc-kit--color--text--secondary` |
71
+ * | `--sc-kit--document-tabs--color--active` | Active tab label color | `--sc-kit--color--text--primary` |
72
+ * | `--sc-kit--document-tabs--color--hover` | Inactive tab hover label color | `--sc-kit--color--text--primary` |
73
+ * | `--sc-kit--document-tabs--color--disabled` | Disabled tab label color | `--sc-kit--color--text--muted` |
74
+ * | `--sc-kit--document-tabs--font-size` | Label font size | `--sc-kit--font-size--sm` |
75
+ * | `--sc-kit--document-tabs--font-weight` | Label font weight | `--sc-kit--font-weight--regular` |
76
+ * | `--sc-kit--document-tabs--scroll-btn--background` | Fade behind the overflow chevrons | `--sc-kit--color--bg--panel` |
77
+ */
78
+ declare const Cmp: $$IsomorphicComponent;
79
+ type Cmp<T> = InstanceType<typeof Cmp<T>>;
80
+ export default Cmp;
@@ -0,0 +1,210 @@
1
+ <script lang="ts" generics="T">import { IconSlot } from '../icon';
2
+ import { DocumentTabsLocalization } from './document-tabs-localization';
3
+ import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
4
+ const { tab, active, closable, on } = $props();
5
+ const localization = new DocumentTabsLocalization();
6
+ const ariaLabel = $derived(tab.dirty ? `${tab.label} — ${localization.unsavedChanges}` : undefined);
7
+ const handleClick = () => {
8
+ if (tab.disabled || active) {
9
+ return;
10
+ }
11
+ on?.select?.(tab.value);
12
+ };
13
+ const handleClose = () => {
14
+ on?.close?.(tab.value);
15
+ };
16
+ </script>
17
+
18
+ <div
19
+ class="document-tab"
20
+ class:document-tab--active={active}
21
+ class:document-tab--disabled={tab.disabled}
22
+ class:document-tab--hoverable={!active && !tab.disabled}
23
+ class:document-tab--dirty={tab.dirty}
24
+ class:document-tab--closable={closable}
25
+ class:document-tab--tone-gray={tab.tone === 'gray'}
26
+ class:document-tab--tone-blue={tab.tone === 'blue'}
27
+ class:document-tab--tone-green={tab.tone === 'green'}
28
+ class:document-tab--tone-red={tab.tone === 'red'}
29
+ class:document-tab--tone-orange={tab.tone === 'orange'}
30
+ class:document-tab--tone-teal={tab.tone === 'teal'}
31
+ role="presentation">
32
+ <button
33
+ type="button"
34
+ class="document-tab__button"
35
+ role="tab"
36
+ aria-selected={active}
37
+ aria-label={ariaLabel}
38
+ tabindex={active ? 0 : -1}
39
+ disabled={tab.disabled}
40
+ title={tab.tooltip}
41
+ onclick={handleClick}
42
+ onkeydown={on?.keydown}>
43
+ {#if tab.icon}
44
+ <span class="document-tab__icon"><IconSlot icon={tab.icon} fallbacks={{ size: 'sm' }} /></span>
45
+ {/if}
46
+ <span class="document-tab__label">{tab.label}</span>
47
+ </button>
48
+ {#if tab.dirty || closable}
49
+ <span class="document-tab__trailing">
50
+ <span class="document-tab__dot" class:document-tab__dot--hidden={!tab.dirty}></span>
51
+ {#if closable}
52
+ <button type="button" class="document-tab__close" tabindex={active ? 0 : -1} aria-label={localization.close} onclick={handleClose}>
53
+ <IconSlot icon={IconDismiss} fallbacks={{ size: 'sm' }} />
54
+ </button>
55
+ {/if}
56
+ </span>
57
+ {/if}
58
+ </div>
59
+
60
+ <style>.document-tab {
61
+ --_document-tab--height: var(--sc-kit--document-tabs--height, 1.8125rem);
62
+ --_document-tab--padding-inline: var(--sc-kit--document-tabs--padding-inline, 0.625rem);
63
+ --_document-tab--min-width: var(--sc-kit--document-tabs--min-width, 7rem);
64
+ --_document-tab--max-width: var(--sc-kit--document-tabs--max-width, 12.5rem);
65
+ --_document-tab--gap: var(--sc-kit--document-tabs--tab--gap, 0.5rem);
66
+ --_document-tab--radius: var(--sc-kit--document-tabs--radius, var(--sc-kit--radius--sm));
67
+ --_document-tab--border-width: var(--sc-kit--document-tabs--border-width, 1px);
68
+ --_document-tab--border-color: var(--sc-kit--document-tabs--border-color, var(--sc-kit--color--border));
69
+ --_document-tab--baseline-overlap: var(--sc-kit--document-tabs--baseline-overlap, 1px);
70
+ --_document-tab--background: var(--_document-tab--tone-background, var(--sc-kit--document-tabs--background, var(--sc-kit--color--bg--app)));
71
+ --_document-tab--background-hover: var(
72
+ --_document-tab--tone-background-hover,
73
+ var(--sc-kit--document-tabs--background--hover, var(--sc-kit--color--bg--hover))
74
+ );
75
+ --_document-tab--color: var(--sc-kit--document-tabs--color, var(--sc-kit--color--text--secondary));
76
+ --_document-tab--font-size: var(--sc-kit--document-tabs--font-size, var(--sc-kit--font-size--sm));
77
+ --_document-tab--font-weight: var(--sc-kit--document-tabs--font-weight, var(--sc-kit--font-weight--regular));
78
+ --_document-tab--cursor: pointer;
79
+ display: inline-flex;
80
+ align-items: center;
81
+ gap: var(--_document-tab--gap);
82
+ block-size: var(--_document-tab--height);
83
+ min-inline-size: var(--_document-tab--min-width);
84
+ max-inline-size: var(--_document-tab--max-width);
85
+ flex-shrink: 0;
86
+ padding-inline: var(--_document-tab--padding-inline);
87
+ margin-block-end: var(--_document-tab--baseline-overlap);
88
+ background: var(--_document-tab--background);
89
+ border: var(--_document-tab--border-width) solid var(--_document-tab--border-color);
90
+ border-block-end: 0;
91
+ border-start-start-radius: var(--_document-tab--radius);
92
+ border-start-end-radius: var(--_document-tab--radius);
93
+ color: var(--_document-tab--color);
94
+ transition: color var(--sc-kit--duration--base) var(--sc-kit--ease--default), background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
95
+ }
96
+ .document-tab--tone-gray {
97
+ --_document-tab--tone-background: var(--sc-kit--tone--gray--bg);
98
+ --_document-tab--tone-background-hover: color-mix(in srgb, var(--sc-kit--tone--gray--fg) 10%, var(--sc-kit--tone--gray--bg));
99
+ }
100
+ .document-tab--tone-blue {
101
+ --_document-tab--tone-background: var(--sc-kit--tone--blue--bg);
102
+ --_document-tab--tone-background-hover: color-mix(in srgb, var(--sc-kit--tone--blue--fg) 10%, var(--sc-kit--tone--blue--bg));
103
+ }
104
+ .document-tab--tone-green {
105
+ --_document-tab--tone-background: var(--sc-kit--tone--green--bg);
106
+ --_document-tab--tone-background-hover: color-mix(in srgb, var(--sc-kit--tone--green--fg) 10%, var(--sc-kit--tone--green--bg));
107
+ }
108
+ .document-tab--tone-red {
109
+ --_document-tab--tone-background: var(--sc-kit--tone--red--bg);
110
+ --_document-tab--tone-background-hover: color-mix(in srgb, var(--sc-kit--tone--red--fg) 10%, var(--sc-kit--tone--red--bg));
111
+ }
112
+ .document-tab--tone-orange {
113
+ --_document-tab--tone-background: var(--sc-kit--tone--orange--bg);
114
+ --_document-tab--tone-background-hover: color-mix(in srgb, var(--sc-kit--tone--orange--fg) 10%, var(--sc-kit--tone--orange--bg));
115
+ }
116
+ .document-tab--tone-teal {
117
+ --_document-tab--tone-background: var(--sc-kit--tone--teal--bg);
118
+ --_document-tab--tone-background-hover: color-mix(in srgb, var(--sc-kit--tone--teal--fg) 10%, var(--sc-kit--tone--teal--bg));
119
+ }
120
+ .document-tab--active {
121
+ --_document-tab--height: var(--sc-kit--document-tabs--height--active, 2rem);
122
+ --_document-tab--background: var(--_document-tab--tone-background, var(--sc-kit--document-tabs--background--active, var(--sc-kit--color--bg--panel)));
123
+ --_document-tab--color: var(--sc-kit--document-tabs--color--active, var(--sc-kit--color--text--primary));
124
+ margin-block-end: 0;
125
+ }
126
+ .document-tab--hoverable:hover {
127
+ --_document-tab--background: var(--_document-tab--background-hover);
128
+ --_document-tab--color: var(--sc-kit--document-tabs--color--hover, var(--sc-kit--color--text--primary));
129
+ }
130
+ .document-tab--disabled {
131
+ --_document-tab--color: var(--sc-kit--document-tabs--color--disabled, var(--sc-kit--color--text--muted));
132
+ --_document-tab--cursor: default;
133
+ }
134
+ .document-tab--closable:hover, .document-tab--closable:focus-within {
135
+ --_document-tab--dot-visibility: hidden;
136
+ --_document-tab--close-display: inline-flex;
137
+ }
138
+ .document-tab__button {
139
+ display: inline-flex;
140
+ align-items: center;
141
+ gap: var(--_document-tab--gap);
142
+ flex: 1;
143
+ min-inline-size: 0;
144
+ block-size: 100%;
145
+ padding: 0;
146
+ background: none;
147
+ border: none;
148
+ color: inherit;
149
+ font-family: inherit;
150
+ font-size: var(--_document-tab--font-size);
151
+ font-weight: var(--_document-tab--font-weight);
152
+ line-height: var(--sc-kit--leading--tight);
153
+ cursor: var(--_document-tab--cursor);
154
+ }
155
+ .document-tab__button:focus-visible {
156
+ outline: 2px solid var(--sc-kit--color--border--focus);
157
+ outline-offset: -2px;
158
+ }
159
+ .document-tab__icon {
160
+ display: inline-flex;
161
+ flex-shrink: 0;
162
+ }
163
+ .document-tab__label {
164
+ text-overflow: ellipsis;
165
+ max-width: 100%;
166
+ white-space: nowrap;
167
+ overflow: hidden;
168
+ }
169
+ .document-tab__trailing {
170
+ position: relative;
171
+ display: inline-flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ flex-shrink: 0;
175
+ inline-size: 1.125rem;
176
+ block-size: 1.125rem;
177
+ }
178
+ .document-tab__dot {
179
+ display: inline-flex;
180
+ visibility: var(--_document-tab--dot-visibility, visible);
181
+ inline-size: 0.375rem;
182
+ block-size: 0.375rem;
183
+ background: currentcolor;
184
+ border-radius: 50%;
185
+ }
186
+ .document-tab__dot--hidden {
187
+ visibility: hidden;
188
+ }
189
+ .document-tab__close {
190
+ display: var(--_document-tab--close-display, none);
191
+ position: absolute;
192
+ inset: 0;
193
+ align-items: center;
194
+ justify-content: center;
195
+ padding: 1px;
196
+ background: none;
197
+ border: none;
198
+ border-radius: var(--sc-kit--radius--sm);
199
+ color: var(--sc-kit--color--text--secondary);
200
+ cursor: pointer;
201
+ transition: color var(--sc-kit--duration--base) var(--sc-kit--ease--default), background-color var(--sc-kit--duration--base) var(--sc-kit--ease--default);
202
+ }
203
+ .document-tab__close:hover {
204
+ background: var(--sc-kit--color--bg--hover);
205
+ color: var(--sc-kit--color--text--primary);
206
+ }
207
+ .document-tab__close:focus-visible {
208
+ outline: 2px solid var(--sc-kit--color--border--focus);
209
+ outline-offset: 1px;
210
+ }</style>
@@ -0,0 +1,34 @@
1
+ import type { DocumentTab } from './types';
2
+ declare function $$render<T>(): {
3
+ props: {
4
+ tab: DocumentTab<T>;
5
+ active: boolean;
6
+ closable: boolean;
7
+ on?: {
8
+ select?: (value: T) => void;
9
+ close?: (value: T) => void;
10
+ keydown?: (event: KeyboardEvent) => 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 DocumentTab: $$IsomorphicComponent;
33
+ type DocumentTab<T> = InstanceType<typeof DocumentTab<T>>;
34
+ export default DocumentTab;
@@ -0,0 +1,6 @@
1
+ export declare class DocumentTabsLocalization {
2
+ get close(): string;
3
+ get unsavedChanges(): string;
4
+ get scrollBack(): string;
5
+ get scrollForward(): string;
6
+ }
@@ -0,0 +1,33 @@
1
+ import { AppLocale } from '../../core/locale';
2
+ const loc = {
3
+ close: {
4
+ en: 'Close tab',
5
+ no: 'Lukk fane'
6
+ },
7
+ unsavedChanges: {
8
+ en: 'unsaved changes',
9
+ no: 'ulagrede endringer'
10
+ },
11
+ scrollBack: {
12
+ en: 'Scroll back',
13
+ no: 'Rull tilbake'
14
+ },
15
+ scrollForward: {
16
+ en: 'Scroll forward',
17
+ no: 'Rull fremover'
18
+ }
19
+ };
20
+ export class DocumentTabsLocalization {
21
+ get close() {
22
+ return loc.close[AppLocale.current];
23
+ }
24
+ get unsavedChanges() {
25
+ return loc.unsavedChanges[AppLocale.current];
26
+ }
27
+ get scrollBack() {
28
+ return loc.scrollBack[AppLocale.current];
29
+ }
30
+ get scrollForward() {
31
+ return loc.scrollForward[AppLocale.current];
32
+ }
33
+ }
@@ -0,0 +1,3 @@
1
+ export { default as DocumentTabs } from './cmp.document-tabs.svelte';
2
+ export { DOCUMENT_TAB_TONES } from './types';
3
+ export type { DocumentTab, DocumentTabsOverflow, DocumentTabTone } from './types';
@@ -0,0 +1,2 @@
1
+ export { default as DocumentTabs } from './cmp.document-tabs.svelte';
2
+ export { DOCUMENT_TAB_TONES } from './types';
@@ -0,0 +1,20 @@
1
+ import type { IconProp } from '../icon';
2
+ export declare const DOCUMENT_TAB_TONES: readonly ["gray", "blue", "green", "red", "orange", "teal"];
3
+ export type DocumentTabTone = (typeof DOCUMENT_TAB_TONES)[number];
4
+ export type DocumentTabsOverflow = 'visible' | 'scroll';
5
+ export type DocumentTab<TValue = unknown> = {
6
+ value: TValue;
7
+ label: string;
8
+ /** Leading glyph — a file-type mark, typically. */
9
+ icon?: IconProp;
10
+ /** Unsaved changes: a dot in the trailing slot, which the close button takes over on hover. */
11
+ dirty?: boolean;
12
+ /** Set `false` to keep one tab open while its neighbours stay closable. @default true (whenever `on.close` is provided) */
13
+ closable?: boolean;
14
+ /** Tints the tab so it reads as a different kind of thing from the documents beside it — a live preview, a diff, a terminal. */
15
+ tone?: DocumentTabTone;
16
+ /** Full text for a label that truncates — the complete file path, typically. */
17
+ tooltip?: string;
18
+ /** Non-interactive — click does nothing. */
19
+ disabled?: boolean;
20
+ };
@@ -0,0 +1 @@
1
+ export const DOCUMENT_TAB_TONES = ['gray', 'blue', 'green', 'red', 'orange', 'teal'];
@@ -1,9 +1,25 @@
1
1
  <script lang="ts">import { slideHorizontally, transitionDuration } from '../../core/transitions';
2
2
  import PagePanel from './cmp.page-panel.svelte';
3
3
  import { untrack } from 'svelte';
4
- const { panelState, children, sidebarLeft, sidebarRight, sidebarLeftIsValid = true, sidebarRightIsValid = true, title, actionsLeft, actionsCenter, actionsRight, balanceHeaderCenter = false, toggle } = $props();
4
+ const { panelState, children, sidebarLeft, sidebarRight, sidebarLeftIsValid = true, sidebarRightIsValid = true, title, actionsLeft, actionsCenter, actionsRight, balanceHeaderCenter = false, alignActionsLeftToContent = false, toggle } = $props();
5
5
  let leftPanel = $state(null);
6
6
  let rightPanel = $state(null);
7
+ let sidebarLeftWidth = $state(0);
8
+ const sidebarLeftMounted = (node) => {
9
+ const measure = () => {
10
+ const divider = parseFloat(getComputedStyle(node).borderInlineEndWidth) || 0;
11
+ sidebarLeftWidth = Math.max(0, node.offsetWidth - divider);
12
+ };
13
+ const observer = new ResizeObserver(measure);
14
+ observer.observe(node);
15
+ measure();
16
+ return {
17
+ destroy: () => {
18
+ observer.disconnect();
19
+ sidebarLeftWidth = 0;
20
+ }
21
+ };
22
+ };
7
23
  $effect(() => {
8
24
  const hasLeft = !!sidebarLeft;
9
25
  untrack(() => {
@@ -69,10 +85,14 @@ $effect(() => {
69
85
  actionsCenter={actionsCenter}
70
86
  actionsRight={actionsRight}
71
87
  balanceHeaderCenter={balanceHeaderCenter}
72
- collapseButtons={collapseButtons}>
88
+ collapseButtons={collapseButtons}
89
+ --_--page-panel--content-start="{alignActionsLeftToContent ? sidebarLeftWidth : 0}px">
73
90
  <div class="page-panel-sidebars">
74
91
  {#if sidebarLeft && !leftCollapsed}
75
- <div class="page-panel-sidebars__sidebar page-panel-sidebars__sidebar--left" transition:slideHorizontally|local={{ duration: transitionDuration.slow }}>
92
+ <div
93
+ class="page-panel-sidebars__sidebar page-panel-sidebars__sidebar--left"
94
+ use:sidebarLeftMounted
95
+ transition:slideHorizontally|local={{ duration: transitionDuration.slow }}>
76
96
  {@render sidebarLeft()}
77
97
  </div>
78
98
  {/if}
@@ -97,6 +117,12 @@ Pass a `SidebarsToggle` instance as `toggle` to drive an external "collapse both
97
117
  top-bar button) without `bind:this` or a local collapsed flag — read `toggleBoth` / `allCollapsed`
98
118
  off that same instance.
99
119
 
120
+ `alignActionsLeftToContent` starts the header's scrolling left track at the content column rather
121
+ than right after the collapse button: whatever sits in `actionsLeft` begins exactly where the
122
+ content pane begins, and follows that edge as the sidebar resizes, collapses, or isn't there at all.
123
+ The track starts on the sidebar's divider rather than after it, so a bordered child lands on that
124
+ line instead of doubling it. The collapse button stays put outside that track.
125
+
100
126
  ### CSS Custom Properties
101
127
  | Property | Description | Default |
102
128
  |---|---|---|
@@ -14,6 +14,8 @@ type Props = {
14
14
  actionsRight?: Snippet;
15
15
  /** Lay the inner panel header out as a 3-column grid so `actionsCenter` is centered against the panel width. @default false */
16
16
  balanceHeaderCenter?: boolean;
17
+ /** Start the header's left track at the content column instead of right after the collapse button, so `actionsLeft` begins where the content pane begins and follows the sidebar as it resizes or collapses. @default false */
18
+ alignActionsLeftToContent?: boolean;
17
19
  /** Shared collapse target for an external "collapse both sidebars" control (e.g. `PageLayout`'s `editorControls.panelsToggle`). */
18
20
  toggle?: SidebarsToggle;
19
21
  };
@@ -26,6 +28,12 @@ type Props = {
26
28
  * top-bar button) without `bind:this` or a local collapsed flag — read `toggleBoth` / `allCollapsed`
27
29
  * off that same instance.
28
30
  *
31
+ * `alignActionsLeftToContent` starts the header's scrolling left track at the content column rather
32
+ * than right after the collapse button: whatever sits in `actionsLeft` begins exactly where the
33
+ * content pane begins, and follows that edge as the sidebar resizes, collapses, or isn't there at all.
34
+ * The track starts on the sidebar's divider rather than after it, so a bordered child lands on that
35
+ * line instead of doubling it. The collapse button stays put outside that track.
36
+ *
29
37
  * ### CSS Custom Properties
30
38
  * | Property | Description | Default |
31
39
  * |---|---|---|
@@ -145,44 +145,50 @@ $effect(() => {
145
145
  {:else}
146
146
  {#if balanceHeaderCenter || leftCollapseIds.length > 0 || title || actionsLeft}
147
147
  <div class="page-panel__header-left" class:page-panel__header-left--balanced={balanceHeaderCenter}>
148
- {#if canScrollHeaderLeftBack}
149
- <button
150
- type="button"
151
- class="page-panel__header-scroll-btn page-panel__header-scroll-btn--back"
152
- onclick={scrollHeaderLeftBack}
153
- aria-label={localization.scrollBack}>
154
- <Icon src={IconChevronLeft} size="sm" />
155
- </button>
148
+ {#if leftCollapseIds.length > 0}
149
+ <div class="page-panel__header-collapse">
150
+ {#each leftCollapseIds as cid (cid)}
151
+ <PanelCollapseButton panelState={panelState} panelId={cid} />
152
+ {/each}
153
+ </div>
156
154
  {/if}
157
- <div
158
- class="page-panel__header-left-track"
159
- class:page-panel__header-left-track--mask-back={canScrollHeaderLeftBack && !canScrollHeaderLeftForward}
160
- class:page-panel__header-left-track--mask-forward={canScrollHeaderLeftForward && !canScrollHeaderLeftBack}
161
- class:page-panel__header-left-track--mask-both={canScrollHeaderLeftBack && canScrollHeaderLeftForward}
162
- use:headerLeftTrackMounted
163
- use:horizontalWheelScroll
164
- onscroll={(e) => updateHeaderLeftScrollState(e.currentTarget)}>
165
- {#each leftCollapseIds as cid (cid)}
166
- <PanelCollapseButton panelState={panelState} panelId={cid} />
167
- {/each}
168
- {#if typeof title === 'function'}
169
- {@render title()}
170
- {:else if title}
171
- <span class="page-panel__title">{title}</span>
155
+ <div class="page-panel__header-track-wrap">
156
+ {#if canScrollHeaderLeftBack}
157
+ <button
158
+ type="button"
159
+ class="page-panel__header-scroll-btn page-panel__header-scroll-btn--back"
160
+ onclick={scrollHeaderLeftBack}
161
+ aria-label={localization.scrollBack}>
162
+ <Icon src={IconChevronLeft} size="sm" />
163
+ </button>
172
164
  {/if}
173
- {#if actionsLeft}
174
- {@render actionsLeft()}
165
+ <div
166
+ class="page-panel__header-left-track"
167
+ class:page-panel__header-left-track--mask-back={canScrollHeaderLeftBack && !canScrollHeaderLeftForward}
168
+ class:page-panel__header-left-track--mask-forward={canScrollHeaderLeftForward && !canScrollHeaderLeftBack}
169
+ class:page-panel__header-left-track--mask-both={canScrollHeaderLeftBack && canScrollHeaderLeftForward}
170
+ use:headerLeftTrackMounted
171
+ use:horizontalWheelScroll
172
+ onscroll={(e) => updateHeaderLeftScrollState(e.currentTarget)}>
173
+ {#if typeof title === 'function'}
174
+ {@render title()}
175
+ {:else if title}
176
+ <span class="page-panel__title">{title}</span>
177
+ {/if}
178
+ {#if actionsLeft}
179
+ {@render actionsLeft()}
180
+ {/if}
181
+ </div>
182
+ {#if canScrollHeaderLeftForward}
183
+ <button
184
+ type="button"
185
+ class="page-panel__header-scroll-btn page-panel__header-scroll-btn--forward"
186
+ onclick={scrollHeaderLeftForward}
187
+ aria-label={localization.scrollForward}>
188
+ <Icon src={IconChevronRight} size="sm" />
189
+ </button>
175
190
  {/if}
176
191
  </div>
177
- {#if canScrollHeaderLeftForward}
178
- <button
179
- type="button"
180
- class="page-panel__header-scroll-btn page-panel__header-scroll-btn--forward"
181
- onclick={scrollHeaderLeftForward}
182
- aria-label={localization.scrollForward}>
183
- <Icon src={IconChevronRight} size="sm" />
184
- </button>
185
- {/if}
186
192
  </div>
187
193
  {/if}
188
194
  {#if balanceHeaderCenter || actionsCenter}
@@ -274,6 +280,7 @@ to min / max; Enter / Space toggle collapsed.
274
280
  --_page-panel--footer-justify: var(--sc-kit--page-panel--footer--justify, flex-end);
275
281
  --_page-panel--header-background: var(--sc-kit--page-panel--header--background, var(--sc-kit--color--bg--panel));
276
282
  --_page-panel--header-padding-inline: var(--sc-kit--page-panel--header--padding-inline, 1.5rem);
283
+ --_page-panel--content-start: var(--_--page-panel--content-start, 0px);
277
284
  --_page-panel--title-font-size: var(--sc-kit--page-panel--title--font-size, 0.875rem);
278
285
  --_page-panel--resizer-color: var(--sc-kit--page-panel--resizer-color, var(--sc-kit--color--border));
279
286
  --_page-panel--height: var(--_--page-layout--panel--height, 100%);
@@ -334,7 +341,8 @@ to min / max; Enter / Space toggle collapsed.
334
341
  font-size: var(--_page-panel--title-font-size);
335
342
  }
336
343
  .page-panel__header-left {
337
- display: flex;
344
+ display: grid;
345
+ grid-template-columns: minmax(min-content, max(0px, var(--_page-panel--content-start) - var(--_page-panel--header-padding-inline))) minmax(0, 1fr);
338
346
  align-self: stretch;
339
347
  align-items: center;
340
348
  min-width: 0;
@@ -346,12 +354,29 @@ to min / max; Enter / Space toggle collapsed.
346
354
  .page-panel__header-left:hover {
347
355
  --_scroll-btn-opacity: 1;
348
356
  }
357
+ .page-panel__header-collapse {
358
+ grid-column: 1;
359
+ display: flex;
360
+ align-items: center;
361
+ padding-inline-end: 1rem;
362
+ }
363
+ .page-panel__header-track-wrap {
364
+ grid-column: 2;
365
+ position: relative;
366
+ display: flex;
367
+ align-self: stretch;
368
+ align-items: stretch;
369
+ min-width: 0;
370
+ }
349
371
  .page-panel__header-left-track {
350
372
  --_mask-size: 1.5rem;
351
373
  display: flex;
374
+ align-self: stretch;
352
375
  align-items: center;
353
376
  gap: 1rem;
354
377
  min-width: 0;
378
+ padding-block-end: 1px;
379
+ margin-block-end: -1px;
355
380
  overflow-x: auto;
356
381
  scrollbar-width: none;
357
382
  }
@@ -369,13 +394,12 @@ to min / max; Enter / Space toggle collapsed.
369
394
  }
370
395
  .page-panel__header-scroll-btn {
371
396
  position: absolute;
372
- top: 0;
373
- bottom: 0;
397
+ inset-block: 0;
374
398
  z-index: 1;
375
399
  display: flex;
376
400
  align-items: center;
377
401
  justify-content: center;
378
- padding: 0 0.25rem;
402
+ padding-inline: 0.25rem;
379
403
  background: transparent;
380
404
  backdrop-filter: blur(2px);
381
405
  border: none;
@@ -388,13 +412,13 @@ to min / max; Enter / Space toggle collapsed.
388
412
  color: var(--sc-kit--color--text--primary);
389
413
  }
390
414
  .page-panel__header-scroll-btn--back {
391
- left: 0;
392
- padding-right: 0.625rem;
415
+ inset-inline-start: 0;
416
+ padding-inline-end: 0.625rem;
393
417
  background: linear-gradient(to right, var(--_page-panel--header-background) 60%, transparent);
394
418
  }
395
419
  .page-panel__header-scroll-btn--forward {
396
- right: 0;
397
- padding-left: 0.625rem;
420
+ inset-inline-end: 0;
421
+ padding-inline-start: 0.625rem;
398
422
  background: linear-gradient(to left, var(--_page-panel--header-background) 60%, transparent);
399
423
  }
400
424
  .page-panel__header-center {