@streamscloud/kit 0.40.1 → 0.40.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/dist/ui/page-layout/cmp.page-layout.svelte +3 -1
- package/dist/ui/page-layout/cmp.page-layout.svelte.d.ts +1 -0
- package/dist/ui/page-layout/cmp.page-panel.svelte +120 -12
- package/dist/ui/page-layout/page-layout-localization.d.ts +2 -0
- package/dist/ui/page-layout/page-layout-localization.js +14 -0
- package/dist/ui/textarea/cmp.textarea.svelte +39 -5
- package/package.json +1 -1
|
@@ -71,6 +71,7 @@ sticky panel headers.
|
|
|
71
71
|
| `--sc-kit--page-layout--color` | Page text color | `--sc-kit--color--text--primary` |
|
|
72
72
|
| `--sc-kit--page-layout--border-color` | Header divider color | `--sc-kit--color--border` |
|
|
73
73
|
| `--sc-kit--page-layout--title--font-size` | Title font size | `18px` |
|
|
74
|
+
| `--sc-kit--page-layout--header--padding-inline` | Header inline padding | `24px` |
|
|
74
75
|
-->
|
|
75
76
|
<style>.page-layout {
|
|
76
77
|
--_page-layout--background: var(--sc-kit--page-layout--background, var(--sc-kit--color--bg--panel));
|
|
@@ -78,6 +79,7 @@ sticky panel headers.
|
|
|
78
79
|
--_page-layout--color: var(--sc-kit--page-layout--color, var(--sc-kit--color--text--primary));
|
|
79
80
|
--_page-layout--border-color: var(--sc-kit--page-layout--border-color, var(--sc-kit--color--border));
|
|
80
81
|
--_page-layout--title-font-size: var(--sc-kit--page-layout--title--font-size, 1.125rem);
|
|
82
|
+
--_page-layout--header-padding-inline: var(--sc-kit--page-layout--header--padding-inline, 1.5rem);
|
|
81
83
|
display: flex;
|
|
82
84
|
flex-direction: column;
|
|
83
85
|
height: 100%;
|
|
@@ -90,7 +92,7 @@ sticky panel headers.
|
|
|
90
92
|
align-items: center;
|
|
91
93
|
justify-content: space-between;
|
|
92
94
|
height: 4.1875rem;
|
|
93
|
-
padding: 1.25rem
|
|
95
|
+
padding: 1.25rem var(--_page-layout--header-padding-inline) 0.875rem;
|
|
94
96
|
background: var(--_page-layout--header-background);
|
|
95
97
|
border-bottom: 1px solid var(--_page-layout--border-color);
|
|
96
98
|
flex-shrink: 0;
|
|
@@ -28,6 +28,7 @@ type Props = {
|
|
|
28
28
|
* | `--sc-kit--page-layout--color` | Page text color | `--sc-kit--color--text--primary` |
|
|
29
29
|
* | `--sc-kit--page-layout--border-color` | Header divider color | `--sc-kit--color--border` |
|
|
30
30
|
* | `--sc-kit--page-layout--title--font-size` | Title font size | `18px` |
|
|
31
|
+
* | `--sc-kit--page-layout--header--padding-inline` | Header inline padding | `24px` |
|
|
31
32
|
*/
|
|
32
33
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
33
34
|
type Cmp = ReturnType<typeof Cmp>;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
<script lang="ts">import {
|
|
1
|
+
<script lang="ts">import { horizontalWheelScroll } from '../../core/actions';
|
|
2
|
+
import { slideHorizontally, transitionDuration } from '../../core/transitions';
|
|
3
|
+
import { Icon } from '../icon';
|
|
2
4
|
import PanelCollapseButton from './cmp.panel-collapse-button.svelte';
|
|
3
5
|
import { PageLayoutLocalization } from './page-layout-localization';
|
|
6
|
+
import IconChevronLeft from '@fluentui/svg-icons/icons/chevron_left_20_regular.svg?raw';
|
|
7
|
+
import IconChevronRight from '@fluentui/svg-icons/icons/chevron_right_20_regular.svg?raw';
|
|
4
8
|
import { untrack } from 'svelte';
|
|
5
9
|
const { panelState, children, id, header, footer, title, actionsLeft, actionsCenter, actionsRight, collapseButtons, hideHeader = false, balanceHeaderCenter = false, resizing = null, borders = [], on } = $props();
|
|
6
10
|
const localization = new PageLayoutLocalization();
|
|
7
11
|
const isFlex = untrack(() => !resizing);
|
|
12
|
+
const HEADER_SCROLL_STEP = 160;
|
|
8
13
|
let panelRef = $state(undefined);
|
|
14
|
+
let headerLeftTrack = $state(undefined);
|
|
15
|
+
let canScrollHeaderLeftBack = $state(false);
|
|
16
|
+
let canScrollHeaderLeftForward = $state(false);
|
|
9
17
|
const { id: panelId, entry } = untrack(() => panelState.register({
|
|
10
18
|
id,
|
|
11
19
|
initialWidth: resizing?.initialWidth ?? null,
|
|
@@ -15,6 +23,26 @@ const canResize = $derived(resizing && [resizing.initialWidth, resizing.minWidth
|
|
|
15
23
|
const widthCss = $derived(entry.width !== null ? `${entry.width}px` : undefined);
|
|
16
24
|
const leftCollapseIds = $derived((collapseButtons ?? []).filter((cid) => panelState.get(cid)?.collapseDirection === 'left'));
|
|
17
25
|
const rightCollapseIds = $derived((collapseButtons ?? []).filter((cid) => panelState.get(cid)?.collapseDirection === 'right'));
|
|
26
|
+
const updateHeaderLeftScrollState = (node) => {
|
|
27
|
+
const { scrollLeft, scrollWidth, clientWidth } = node;
|
|
28
|
+
canScrollHeaderLeftBack = scrollLeft > 0;
|
|
29
|
+
canScrollHeaderLeftForward = scrollLeft < scrollWidth - clientWidth - 1;
|
|
30
|
+
};
|
|
31
|
+
const headerLeftTrackMounted = (node) => {
|
|
32
|
+
headerLeftTrack = node;
|
|
33
|
+
const observer = new ResizeObserver(() => updateHeaderLeftScrollState(node));
|
|
34
|
+
observer.observe(node);
|
|
35
|
+
updateHeaderLeftScrollState(node);
|
|
36
|
+
return {
|
|
37
|
+
destroy: () => observer.disconnect()
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const scrollHeaderLeftBack = () => {
|
|
41
|
+
headerLeftTrack?.scrollBy({ left: -HEADER_SCROLL_STEP, behavior: 'smooth' });
|
|
42
|
+
};
|
|
43
|
+
const scrollHeaderLeftForward = () => {
|
|
44
|
+
headerLeftTrack?.scrollBy({ left: HEADER_SCROLL_STEP, behavior: 'smooth' });
|
|
45
|
+
};
|
|
18
46
|
$effect(() => {
|
|
19
47
|
const collapsed = entry.collapsed;
|
|
20
48
|
untrack(() => {
|
|
@@ -117,16 +145,43 @@ $effect(() => {
|
|
|
117
145
|
{:else}
|
|
118
146
|
{#if balanceHeaderCenter || leftCollapseIds.length > 0 || title || actionsLeft}
|
|
119
147
|
<div class="page-panel__header-left" class:page-panel__header-left--balanced={balanceHeaderCenter}>
|
|
120
|
-
{#
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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>
|
|
127
156
|
{/if}
|
|
128
|
-
|
|
129
|
-
|
|
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>
|
|
172
|
+
{/if}
|
|
173
|
+
{#if actionsLeft}
|
|
174
|
+
{@render actionsLeft()}
|
|
175
|
+
{/if}
|
|
176
|
+
</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>
|
|
130
185
|
{/if}
|
|
131
186
|
</div>
|
|
132
187
|
{/if}
|
|
@@ -282,13 +337,66 @@ to min / max; Enter / Space toggle collapsed.
|
|
|
282
337
|
display: flex;
|
|
283
338
|
align-self: stretch;
|
|
284
339
|
align-items: center;
|
|
285
|
-
gap: 1rem;
|
|
286
340
|
min-width: 0;
|
|
287
|
-
|
|
341
|
+
position: relative;
|
|
288
342
|
}
|
|
289
343
|
.page-panel__header-left--balanced {
|
|
290
344
|
justify-self: start;
|
|
291
345
|
}
|
|
346
|
+
.page-panel__header-left:hover {
|
|
347
|
+
--_scroll-btn-opacity: 1;
|
|
348
|
+
}
|
|
349
|
+
.page-panel__header-left-track {
|
|
350
|
+
--_mask-size: 1.5rem;
|
|
351
|
+
display: flex;
|
|
352
|
+
align-items: center;
|
|
353
|
+
gap: 1rem;
|
|
354
|
+
min-width: 0;
|
|
355
|
+
overflow-x: auto;
|
|
356
|
+
scrollbar-width: none;
|
|
357
|
+
}
|
|
358
|
+
.page-panel__header-left-track::-webkit-scrollbar {
|
|
359
|
+
display: none;
|
|
360
|
+
}
|
|
361
|
+
.page-panel__header-left-track--mask-back {
|
|
362
|
+
mask-image: linear-gradient(to right, transparent 0%, black var(--_mask-size), black 100%);
|
|
363
|
+
}
|
|
364
|
+
.page-panel__header-left-track--mask-forward {
|
|
365
|
+
mask-image: linear-gradient(to right, black 0%, black calc(100% - var(--_mask-size)), transparent 100%);
|
|
366
|
+
}
|
|
367
|
+
.page-panel__header-left-track--mask-both {
|
|
368
|
+
mask-image: linear-gradient(to right, transparent 0%, black var(--_mask-size), black calc(100% - var(--_mask-size)), transparent 100%);
|
|
369
|
+
}
|
|
370
|
+
.page-panel__header-scroll-btn {
|
|
371
|
+
position: absolute;
|
|
372
|
+
top: 0;
|
|
373
|
+
bottom: 0;
|
|
374
|
+
z-index: 1;
|
|
375
|
+
display: flex;
|
|
376
|
+
align-items: center;
|
|
377
|
+
justify-content: center;
|
|
378
|
+
padding: 0 0.25rem;
|
|
379
|
+
background: transparent;
|
|
380
|
+
backdrop-filter: blur(2px);
|
|
381
|
+
border: none;
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
color: var(--sc-kit--color--text--secondary);
|
|
384
|
+
opacity: var(--_scroll-btn-opacity, 0);
|
|
385
|
+
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
386
|
+
}
|
|
387
|
+
.page-panel__header-scroll-btn:hover {
|
|
388
|
+
color: var(--sc-kit--color--text--primary);
|
|
389
|
+
}
|
|
390
|
+
.page-panel__header-scroll-btn--back {
|
|
391
|
+
left: 0;
|
|
392
|
+
padding-right: 0.625rem;
|
|
393
|
+
background: linear-gradient(to right, var(--_page-panel--header-background) 60%, transparent);
|
|
394
|
+
}
|
|
395
|
+
.page-panel__header-scroll-btn--forward {
|
|
396
|
+
right: 0;
|
|
397
|
+
padding-left: 0.625rem;
|
|
398
|
+
background: linear-gradient(to left, var(--_page-panel--header-background) 60%, transparent);
|
|
399
|
+
}
|
|
292
400
|
.page-panel__header-center {
|
|
293
401
|
display: flex;
|
|
294
402
|
align-items: center;
|
|
@@ -19,6 +19,14 @@ const loc = {
|
|
|
19
19
|
close: {
|
|
20
20
|
en: 'Close',
|
|
21
21
|
no: 'Lukk'
|
|
22
|
+
},
|
|
23
|
+
scrollBack: {
|
|
24
|
+
en: 'Scroll back',
|
|
25
|
+
no: 'Rull tilbake'
|
|
26
|
+
},
|
|
27
|
+
scrollForward: {
|
|
28
|
+
en: 'Scroll forward',
|
|
29
|
+
no: 'Rull fremover'
|
|
22
30
|
}
|
|
23
31
|
};
|
|
24
32
|
export class PageLayoutLocalization {
|
|
@@ -37,4 +45,10 @@ export class PageLayoutLocalization {
|
|
|
37
45
|
get close() {
|
|
38
46
|
return loc.close[AppLocale.current];
|
|
39
47
|
}
|
|
48
|
+
get scrollBack() {
|
|
49
|
+
return loc.scrollBack[AppLocale.current];
|
|
50
|
+
}
|
|
51
|
+
get scrollForward() {
|
|
52
|
+
return loc.scrollForward[AppLocale.current];
|
|
53
|
+
}
|
|
40
54
|
}
|
|
@@ -13,14 +13,14 @@ const resize = () => {
|
|
|
13
13
|
if (!textareaEl || !autoresize) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
textareaEl.style.
|
|
16
|
+
textareaEl.style.overflowY = 'hidden';
|
|
17
17
|
textareaEl.style.height = 'auto';
|
|
18
18
|
const styles = getComputedStyle(textareaEl);
|
|
19
19
|
const lineHeight = parseFloat(styles.lineHeight) || parseFloat(styles.fontSize) * 1.4;
|
|
20
20
|
const paddingY = parseFloat(styles.paddingTop) + parseFloat(styles.paddingBottom);
|
|
21
21
|
const max = lineHeight * maxRows + paddingY;
|
|
22
22
|
textareaEl.style.height = Math.min(textareaEl.scrollHeight, max) + 'px';
|
|
23
|
-
textareaEl.style.
|
|
23
|
+
textareaEl.style.overflowY = '';
|
|
24
24
|
};
|
|
25
25
|
const initTextarea = (node) => {
|
|
26
26
|
textareaEl = node;
|
|
@@ -139,7 +139,9 @@ const focusTextarea = () => textareaEl?.focus();
|
|
|
139
139
|
onfocus={() => on?.focus?.()}></textarea>
|
|
140
140
|
|
|
141
141
|
{#if emoji}
|
|
142
|
-
<
|
|
142
|
+
<span class="textarea__emoji">
|
|
143
|
+
<InputEmojiPicker size={size} on={{ select: handleEmojiSelect }} />
|
|
144
|
+
</span>
|
|
143
145
|
{/if}
|
|
144
146
|
</div>
|
|
145
147
|
|
|
@@ -202,7 +204,8 @@ what the field shows. Blank lines inside the text are left alone. Typing is unto
|
|
|
202
204
|
align-items: flex-end;
|
|
203
205
|
width: var(--_ta--width);
|
|
204
206
|
max-width: 100%;
|
|
205
|
-
padding: var(--_ta--padding-block)
|
|
207
|
+
padding-block: var(--_ta--padding-block);
|
|
208
|
+
padding-inline-start: var(--_ta--padding-inline);
|
|
206
209
|
background: var(--_ta--background);
|
|
207
210
|
border: 1px solid var(--_ta--border-color);
|
|
208
211
|
border-radius: var(--_ta--border-radius);
|
|
@@ -262,7 +265,9 @@ what the field shows. Blank lines inside the text are left alone. Typing is unto
|
|
|
262
265
|
flex: 1;
|
|
263
266
|
width: 100%;
|
|
264
267
|
min-width: 0;
|
|
265
|
-
padding: 0;
|
|
268
|
+
padding-block: 0;
|
|
269
|
+
padding-inline-start: 0;
|
|
270
|
+
padding-inline-end: var(--_ta--padding-inline);
|
|
266
271
|
margin: 0;
|
|
267
272
|
border: none;
|
|
268
273
|
outline: none;
|
|
@@ -272,10 +277,39 @@ what the field shows. Blank lines inside the text are left alone. Typing is unto
|
|
|
272
277
|
font-size: inherit;
|
|
273
278
|
line-height: inherit;
|
|
274
279
|
resize: none;
|
|
280
|
+
overflow-y: auto;
|
|
281
|
+
--_cross-browser-scrollbar--thumb-color: transparent;
|
|
282
|
+
--_cross-browser-scrollbar--track-color: transparent;
|
|
283
|
+
}
|
|
284
|
+
.textarea__el:hover {
|
|
285
|
+
--_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border--strong);
|
|
286
|
+
--_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
|
|
287
|
+
}
|
|
288
|
+
.textarea__el::-webkit-scrollbar {
|
|
289
|
+
width: 6px;
|
|
290
|
+
height: 6px;
|
|
291
|
+
}
|
|
292
|
+
.textarea__el::-webkit-scrollbar-track {
|
|
293
|
+
background: var(--_cross-browser-scrollbar--track-color);
|
|
294
|
+
border-radius: 100vw;
|
|
295
|
+
}
|
|
296
|
+
.textarea__el::-webkit-scrollbar-thumb {
|
|
297
|
+
background: var(--_cross-browser-scrollbar--thumb-color);
|
|
298
|
+
border-radius: 100vw;
|
|
299
|
+
}
|
|
300
|
+
@supports (scrollbar-color: transparent transparent) {
|
|
301
|
+
.textarea__el {
|
|
302
|
+
scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
|
|
303
|
+
scrollbar-width: thin;
|
|
304
|
+
}
|
|
275
305
|
}
|
|
276
306
|
.textarea__el::placeholder {
|
|
277
307
|
color: var(--_ta--placeholder-color);
|
|
278
308
|
}
|
|
279
309
|
.textarea__el:disabled {
|
|
280
310
|
cursor: default;
|
|
311
|
+
}
|
|
312
|
+
.textarea__emoji {
|
|
313
|
+
flex-shrink: 0;
|
|
314
|
+
margin-inline-end: var(--_ta--padding-inline);
|
|
281
315
|
}</style>
|