@streamscloud/kit 0.70.0 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/infinite-scroll/cmp.infinite-scroll.svelte +3 -3
- package/dist/ui/infinite-scroll/cmp.infinite-scroll.svelte.d.ts +4 -2
- package/dist/ui/page-layout/cmp.page-layout.svelte +16 -3
- package/dist/ui/page-layout/cmp.page-layout.svelte.d.ts +9 -1
- package/dist/ui/page-layout/cmp.page-panel.svelte +15 -2
- package/dist/ui/page-layout/cmp.page-panel.svelte.d.ts +8 -1
- package/dist/ui/page-layout/index.d.ts +1 -1
- package/dist/ui/page-layout/page-loading.d.ts +5 -0
- package/dist/ui/page-layout/page-loading.js +7 -0
- package/dist/ui/page-layout/types.d.ts +8 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">import { Button } from '../button';
|
|
2
2
|
import { Spinner } from '../spinner';
|
|
3
3
|
import { InfiniteScrollLocalization } from './infinite-scroll-localization';
|
|
4
|
-
let { loadMore, container = null, rootMargin = '200px', children, loading, retry } = $props();
|
|
4
|
+
let { loadMore, container = null, rootMargin = '200px', children, showLoading = false, loading, retry } = $props();
|
|
5
5
|
const localization = new InfiniteScrollLocalization();
|
|
6
6
|
const source = $derived(typeof loadMore === 'function' ? { loadMore, failed: false, canLoadMore: true } : loadMore);
|
|
7
7
|
let isLoading = $state(false);
|
|
@@ -61,7 +61,7 @@ const onRetry = () => {
|
|
|
61
61
|
{#if isLoading}
|
|
62
62
|
{#if loading}
|
|
63
63
|
{@render loading()}
|
|
64
|
-
{:else}
|
|
64
|
+
{:else if showLoading}
|
|
65
65
|
<div class="infinite-scroll__loading-container">
|
|
66
66
|
<Spinner timeout={500} />
|
|
67
67
|
</div>
|
|
@@ -79,7 +79,7 @@ const onRetry = () => {
|
|
|
79
79
|
|
|
80
80
|
<!--
|
|
81
81
|
@component
|
|
82
|
-
Triggers an async `loadMore` callback when a sentinel element scrolls into view, using IntersectionObserver. Re-observes the sentinel on container resize so layout shifts inside the parent re-evaluate visibility (a known IntersectionObserver gotcha). Internal `isLoading` guard prevents overlapping fires while a load is in flight.
|
|
82
|
+
Triggers an async `loadMore` callback when a sentinel element scrolls into view, using IntersectionObserver. Re-observes the sentinel on container resize so layout shifts inside the parent re-evaluate visibility (a known IntersectionObserver gotcha). Internal `isLoading` guard prevents overlapping fires while a load is in flight. No loading indicator by default — opt in with `showLoading` or a `loading` snippet.
|
|
83
83
|
|
|
84
84
|
Pairs with `core/data-loaders` (`CursorDataLoader`, `PageDataLoader`, `CursorDataLoaderWithSearch`) — wire `loadMore={loader.loadMore}` and `{#each loader.items as item}` directly.
|
|
85
85
|
|
|
@@ -16,13 +16,15 @@ type Props = {
|
|
|
16
16
|
/** IntersectionObserver `rootMargin`. Default `'200px'` — fires the load a viewport-screen before reaching the bottom. */
|
|
17
17
|
rootMargin?: string;
|
|
18
18
|
children: Snippet;
|
|
19
|
-
/**
|
|
19
|
+
/** Shows the kit `Spinner` while a batch loads. Off by default — the list owner usually renders its own loading state. */
|
|
20
|
+
showLoading?: boolean;
|
|
21
|
+
/** Custom loading indicator snippet. Passing it turns the indicator on regardless of `showLoading`. */
|
|
20
22
|
loading?: Snippet;
|
|
21
23
|
/** Manual continue shown while `failed`, taking the callback that retries the failed page. Defaults to a kit `Button`. */
|
|
22
24
|
retry?: Snippet<[() => void]>;
|
|
23
25
|
};
|
|
24
26
|
/**
|
|
25
|
-
* Triggers an async `loadMore` callback when a sentinel element scrolls into view, using IntersectionObserver. Re-observes the sentinel on container resize so layout shifts inside the parent re-evaluate visibility (a known IntersectionObserver gotcha). Internal `isLoading` guard prevents overlapping fires while a load is in flight.
|
|
27
|
+
* Triggers an async `loadMore` callback when a sentinel element scrolls into view, using IntersectionObserver. Re-observes the sentinel on container resize so layout shifts inside the parent re-evaluate visibility (a known IntersectionObserver gotcha). Internal `isLoading` guard prevents overlapping fires while a load is in flight. No loading indicator by default — opt in with `showLoading` or a `loading` snippet.
|
|
26
28
|
*
|
|
27
29
|
* Pairs with `core/data-loaders` (`CursorDataLoader`, `PageDataLoader`, `CursorDataLoaderWithSearch`) — wire `loadMore={loader.loadMore}` and `{#each loader.items as item}` directly.
|
|
28
30
|
*
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
<script lang="ts">import
|
|
1
|
+
<script lang="ts">import { Spinner } from '../spinner';
|
|
2
|
+
import LayoutControlButton from './layout-control-button.svelte';
|
|
2
3
|
import { PageLayoutLocalization } from './page-layout-localization';
|
|
4
|
+
import { resolvePageLoading } from './page-loading';
|
|
3
5
|
import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
|
|
4
6
|
import IconWindow from '@fluentui/svg-icons/icons/window_20_regular.svg?raw';
|
|
5
7
|
import IconWindowMultiple from '@fluentui/svg-icons/icons/window_multiple_20_regular.svg?raw';
|
|
6
|
-
const { children, title, actions, navigationToggle, editorControls, scrollable = false, inert = false } = $props();
|
|
8
|
+
const { children, title, actions, navigationToggle, editorControls, scrollable = false, inert = false, loading = false } = $props();
|
|
7
9
|
const localization = new PageLayoutLocalization();
|
|
10
|
+
const { timeout: loadingTimeout, blocking: loadingBlocking } = $derived(resolvePageLoading(loading));
|
|
8
11
|
</script>
|
|
9
12
|
|
|
10
|
-
<div class="page-layout" class:page-layout--scrollable={scrollable} inert={inert}>
|
|
13
|
+
<div class="page-layout" class:page-layout--scrollable={scrollable} inert={inert || loadingBlocking}>
|
|
11
14
|
<div class="page-layout__header">
|
|
12
15
|
<div class="page-layout__title">
|
|
13
16
|
{#if typeof title === 'function'}
|
|
@@ -50,6 +53,9 @@ const localization = new PageLayoutLocalization();
|
|
|
50
53
|
<div class="page-layout__body">
|
|
51
54
|
{@render children()}
|
|
52
55
|
</div>
|
|
56
|
+
{#if loading}
|
|
57
|
+
<Spinner position="absolute-center" timeout={loadingTimeout} blocking={loadingBlocking} />
|
|
58
|
+
{/if}
|
|
53
59
|
</div>
|
|
54
60
|
|
|
55
61
|
<!--
|
|
@@ -63,6 +69,12 @@ outlined squares. Every entry takes `{ active?, onClick }`, and the group render
|
|
|
63
69
|
navigation → panels → close. Set `scrollable` to let the whole page scroll as one document with
|
|
64
70
|
sticky panel headers.
|
|
65
71
|
|
|
72
|
+
`loading` (`true` or `{ timeout, blocking }`) shows a delayed `Spinner` centered over the layout.
|
|
73
|
+
`blocking` makes the whole layout — header included — `inert` at once and dims it with the
|
|
74
|
+
`Spinner` overlay after the same `timeout`; `inert` alone blocks without a spinner. With
|
|
75
|
+
`scrollable` the layout itself scrolls, so the spinner and overlay stay at the top of the scroll.
|
|
76
|
+
A loading `PagePanel` inside a loading layout draws its own spinner and overlay on top.
|
|
77
|
+
|
|
66
78
|
### CSS Custom Properties
|
|
67
79
|
| Property | Description | Default |
|
|
68
80
|
|---|---|---|
|
|
@@ -84,6 +96,7 @@ sticky panel headers.
|
|
|
84
96
|
flex-direction: column;
|
|
85
97
|
height: 100%;
|
|
86
98
|
max-height: 100%;
|
|
99
|
+
position: relative;
|
|
87
100
|
background: var(--_page-layout--background);
|
|
88
101
|
color: var(--_page-layout--color);
|
|
89
102
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EditorControls, PageLayoutAction } from './types';
|
|
1
|
+
import type { EditorControls, PageLayoutAction, PageLoading } from './types';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
type Props = {
|
|
4
4
|
children: Snippet;
|
|
@@ -11,6 +11,8 @@ type Props = {
|
|
|
11
11
|
scrollable?: boolean;
|
|
12
12
|
/** Sets `inert` on the whole layout — header and body stop taking focus, clicks and assistive-tech interaction. */
|
|
13
13
|
inert?: boolean;
|
|
14
|
+
/** Shows a `Spinner` centered over the layout; `blocking` also makes the whole layout `inert` and dims it. @default false */
|
|
15
|
+
loading?: PageLoading;
|
|
14
16
|
};
|
|
15
17
|
/**
|
|
16
18
|
* PageLayout — top-level window chrome. Renders a fixed-height header (title + actions + an optional
|
|
@@ -22,6 +24,12 @@ type Props = {
|
|
|
22
24
|
* navigation → panels → close. Set `scrollable` to let the whole page scroll as one document with
|
|
23
25
|
* sticky panel headers.
|
|
24
26
|
*
|
|
27
|
+
* `loading` (`true` or `{ timeout, blocking }`) shows a delayed `Spinner` centered over the layout.
|
|
28
|
+
* `blocking` makes the whole layout — header included — `inert` at once and dims it with the
|
|
29
|
+
* `Spinner` overlay after the same `timeout`; `inert` alone blocks without a spinner. With
|
|
30
|
+
* `scrollable` the layout itself scrolls, so the spinner and overlay stay at the top of the scroll.
|
|
31
|
+
* A loading `PagePanel` inside a loading layout draws its own spinner and overlay on top.
|
|
32
|
+
*
|
|
25
33
|
* ### CSS Custom Properties
|
|
26
34
|
* | Property | Description | Default |
|
|
27
35
|
* |---|---|---|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<script lang="ts">import { horizontalWheelScroll } from '../../core/actions';
|
|
2
2
|
import { slideHorizontally, transitionDuration } from '../../core/transitions';
|
|
3
3
|
import { Icon } from '../icon';
|
|
4
|
+
import { Spinner } from '../spinner';
|
|
4
5
|
import PanelCollapseButton from './cmp.panel-collapse-button.svelte';
|
|
5
6
|
import { PageLayoutLocalization } from './page-layout-localization';
|
|
7
|
+
import { resolvePageLoading } from './page-loading';
|
|
6
8
|
import IconChevronLeft from '@fluentui/svg-icons/icons/chevron_left_20_regular.svg?raw';
|
|
7
9
|
import IconChevronRight from '@fluentui/svg-icons/icons/chevron_right_20_regular.svg?raw';
|
|
8
10
|
import { untrack } from 'svelte';
|
|
9
|
-
const { panelState, children, id, header, footer, title, actionsLeft, actionsCenter, actionsRight, collapseButtons, hideHeader = false, balanceHeaderCenter = false, resizing = null, borders = [], on } = $props();
|
|
11
|
+
const { panelState, children, id, header, footer, title, actionsLeft, actionsCenter, actionsRight, collapseButtons, hideHeader = false, balanceHeaderCenter = false, resizing = null, borders = [], loading = false, on } = $props();
|
|
10
12
|
const localization = new PageLayoutLocalization();
|
|
13
|
+
const { timeout: loadingTimeout, blocking: loadingBlocking } = $derived(resolvePageLoading(loading));
|
|
11
14
|
const isFlex = untrack(() => !resizing);
|
|
12
15
|
const HEADER_SCROLL_STEP = 160;
|
|
13
16
|
let panelRef = $state(undefined);
|
|
@@ -137,7 +140,8 @@ $effect(() => {
|
|
|
137
140
|
class:page-panel--border-top={borders.includes('top')}
|
|
138
141
|
class:page-panel--border-right={borders.includes('right')}
|
|
139
142
|
class:page-panel--border-bottom={borders.includes('bottom')}
|
|
140
|
-
class:page-panel--border-left={borders.includes('left')}
|
|
143
|
+
class:page-panel--border-left={borders.includes('left')}
|
|
144
|
+
inert={loadingBlocking}>
|
|
141
145
|
{#if !hideHeader}
|
|
142
146
|
<div class="page-panel__header" class:page-panel__header--balanced={balanceHeaderCenter}>
|
|
143
147
|
{#if header}
|
|
@@ -239,6 +243,10 @@ $effect(() => {
|
|
|
239
243
|
aria-valuenow={entry.width ?? 0}>
|
|
240
244
|
</button>
|
|
241
245
|
{/if}
|
|
246
|
+
|
|
247
|
+
{#if loading}
|
|
248
|
+
<Spinner position="absolute-center" timeout={loadingTimeout} blocking={loadingBlocking} />
|
|
249
|
+
{/if}
|
|
242
250
|
</div>
|
|
243
251
|
{/if}
|
|
244
252
|
|
|
@@ -250,6 +258,11 @@ instance, and configurable borders. Collapse uses the `slideHorizontally` transi
|
|
|
250
258
|
|
|
251
259
|
Pass the same `panelState` instance to every panel + collapse button that should coordinate.
|
|
252
260
|
|
|
261
|
+
`loading` (`true` or `{ timeout, blocking }`) shows a delayed `Spinner` centered over the panel.
|
|
262
|
+
`blocking` makes the whole panel — header and footer included — `inert` at once and dims it with
|
|
263
|
+
the `Spinner` overlay after the same `timeout`. Inside a `scrollable` `PageLayout` the panel is as
|
|
264
|
+
tall as its content, so the spinner sits in the middle of the whole panel, not of the viewport.
|
|
265
|
+
|
|
253
266
|
### Header modes
|
|
254
267
|
- `title` (string | Snippet) — renders in the left section; `actionsLeft` / `actionsRight` and
|
|
255
268
|
`collapseButtons` are placed alongside.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PanelState } from './panel-state.svelte';
|
|
2
|
-
import type { PanelBorder, PanelResizingSettings } from './types';
|
|
2
|
+
import type { PageLoading, PanelBorder, PanelResizingSettings } from './types';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
type Props = {
|
|
5
5
|
/** The shared `PanelState` instance this panel registers with. */
|
|
@@ -22,6 +22,8 @@ type Props = {
|
|
|
22
22
|
/** Resize / collapse config. `null` ⇒ the panel is `flex: 1` (no resizing). @default null */
|
|
23
23
|
resizing?: PanelResizingSettings | null;
|
|
24
24
|
borders?: PanelBorder[];
|
|
25
|
+
/** Shows a `Spinner` centered over the panel; `blocking` also makes the whole panel `inert` and dims it. @default false */
|
|
26
|
+
loading?: PageLoading;
|
|
25
27
|
on?: {
|
|
26
28
|
collapse?: (collapsed: boolean) => void;
|
|
27
29
|
};
|
|
@@ -33,6 +35,11 @@ type Props = {
|
|
|
33
35
|
*
|
|
34
36
|
* Pass the same `panelState` instance to every panel + collapse button that should coordinate.
|
|
35
37
|
*
|
|
38
|
+
* `loading` (`true` or `{ timeout, blocking }`) shows a delayed `Spinner` centered over the panel.
|
|
39
|
+
* `blocking` makes the whole panel — header and footer included — `inert` at once and dims it with
|
|
40
|
+
* the `Spinner` overlay after the same `timeout`. Inside a `scrollable` `PageLayout` the panel is as
|
|
41
|
+
* tall as its content, so the spinner sits in the middle of the whole panel, not of the viewport.
|
|
42
|
+
*
|
|
36
43
|
* ### Header modes
|
|
37
44
|
* - `title` (string | Snippet) — renders in the left section; `actionsLeft` / `actionsRight` and
|
|
38
45
|
* `collapseButtons` are placed alongside.
|
|
@@ -5,4 +5,4 @@ export { default as PagePanels } from './cmp.page-panels.svelte';
|
|
|
5
5
|
export { default as PanelCollapseButton } from './cmp.panel-collapse-button.svelte';
|
|
6
6
|
export { default as PanelContent } from './cmp.panel-content.svelte';
|
|
7
7
|
export { PanelEntry, PanelState, SidebarsToggle } from './panel-state.svelte';
|
|
8
|
-
export type { EditorControls, PageLayoutAction, PanelBorder, PanelResizingSettings } from './types';
|
|
8
|
+
export type { EditorControls, PageLayoutAction, PageLoading, PanelBorder, PanelResizingSettings } from './types';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const DEFAULT_LOADING_TIMEOUT = 500;
|
|
2
|
+
export const resolvePageLoading = (loading) => {
|
|
3
|
+
if (typeof loading === 'object') {
|
|
4
|
+
return { timeout: loading.timeout ?? DEFAULT_LOADING_TIMEOUT, blocking: loading.blocking === true };
|
|
5
|
+
}
|
|
6
|
+
return { timeout: DEFAULT_LOADING_TIMEOUT, blocking: false };
|
|
7
|
+
};
|
|
@@ -30,3 +30,11 @@ export type EditorControls = {
|
|
|
30
30
|
panelsToggle: PageLayoutAction;
|
|
31
31
|
close: PageLayoutAction;
|
|
32
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* `PageLayout` / `PagePanel` loading state. `true` shows a non-blocking `Spinner` after 500ms. The object form sets the delay in ms (`timeout`, default
|
|
35
|
+
* 500) and, with `blocking`, makes the whole component `inert` at once and dims it with the `Spinner` overlay after the same delay.
|
|
36
|
+
*/
|
|
37
|
+
export type PageLoading = boolean | {
|
|
38
|
+
timeout?: number;
|
|
39
|
+
blocking?: boolean;
|
|
40
|
+
};
|