@x33025/sveltely 0.1.24 → 0.1.28
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/actions/LoaderOverlay.svelte +10 -3
- package/dist/components/Library/Accordion/Accordion.demo.svelte +21 -0
- package/dist/components/Library/Accordion/Accordion.demo.svelte.d.ts +9 -0
- package/dist/components/Library/Accordion/Accordion.svelte +78 -0
- package/dist/components/Library/Accordion/Accordion.svelte.d.ts +14 -0
- package/dist/components/Library/Accordion/Content.svelte +57 -0
- package/dist/components/Library/Accordion/Content.svelte.d.ts +8 -0
- package/dist/components/Library/Accordion/Header.svelte +98 -0
- package/dist/components/Library/Accordion/Header.svelte.d.ts +10 -0
- package/dist/components/Library/Accordion/context.d.ts +9 -0
- package/dist/components/Library/Accordion/context.js +6 -0
- package/dist/components/Library/Accordion/index.d.ts +9 -0
- package/dist/components/Library/Accordion/index.js +7 -0
- package/dist/components/Library/AnimatedNumber/AnimatedNumber.demo.svelte +1 -1
- package/dist/components/Library/ArticleEditor/ArticleEditor.svelte +1 -2
- package/dist/components/Library/ArticleEditor/Blocks/Table.svelte +1 -1
- package/dist/components/Library/ChipInput/ChipInput.demo.svelte +1 -1
- package/dist/components/Library/Dropdown/Dropdown.demo.svelte +5 -3
- package/dist/components/Library/Grid/Grid.demo.svelte +58 -0
- package/dist/components/Library/Grid/Grid.demo.svelte.d.ts +25 -0
- package/dist/components/Library/Grid/Grid.svelte +120 -26
- package/dist/components/Library/Grid/Grid.svelte.d.ts +38 -10
- package/dist/components/Library/Grid/GridItem.svelte +8 -13
- package/dist/components/Library/Image/Image.svelte +25 -4
- package/dist/components/Library/ImageMask/ImageMask.demo.svelte +1 -1
- package/dist/components/Library/Label/Label.demo.svelte +1 -1
- package/dist/components/Library/Label/Label.svelte +5 -13
- package/dist/components/Library/Notifications/Notifications.demo.svelte +63 -0
- package/dist/components/Library/Notifications/Notifications.demo.svelte.d.ts +9 -0
- package/dist/components/Library/Notifications/Notifications.svelte +155 -0
- package/dist/components/Library/Notifications/Notifications.svelte.d.ts +35 -0
- package/dist/components/Library/Notifications/index.d.ts +2 -0
- package/dist/components/Library/Notifications/index.js +1 -0
- package/dist/components/Library/Notifications/types.d.ts +8 -0
- package/dist/components/Library/Notifications/types.js +1 -0
- package/dist/components/Library/NumberField/NumberField.svelte +25 -19
- package/dist/components/Library/Pagination/Pagination.svelte +6 -18
- package/dist/components/Library/ScrollView/ScrollView.svelte +76 -4
- package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +9 -2
- package/dist/components/Library/ScrollView/index.d.ts +1 -1
- package/dist/components/Library/SearchField/SearchField.demo.svelte +1 -1
- package/dist/components/Library/SegmentedPicker/SegmentedPicker.demo.svelte +1 -1
- package/dist/components/Library/Slider/Slider.demo.svelte +1 -1
- package/dist/components/Library/Table/Table.demo.svelte +9 -1
- package/dist/components/Library/Table/Table.svelte +315 -112
- package/dist/components/Library/Table/Table.svelte.d.ts +7 -1
- package/dist/components/Library/TextField/TextField.svelte +20 -14
- package/dist/components/Library/TokenSearchField/TokenSearchField.demo.svelte +1 -1
- package/dist/components/Local/ColorStyleControls.svelte +25 -64
- package/dist/components/Local/ComponentGrid.svelte +103 -22
- package/dist/components/Local/ComponentGrid.svelte.d.ts +2 -1
- package/dist/components/Local/ComponentPage.svelte +74 -0
- package/dist/components/Local/ComponentPage.svelte.d.ts +13 -0
- package/dist/components/Local/HeroCard.svelte +10 -5
- package/dist/components/Local/LayoutStyleControls.svelte +6 -6
- package/dist/index.d.ts +8 -3
- package/dist/index.js +4 -1
- package/dist/style/index.css +1 -2
- package/dist/style/layout.d.ts +11 -26
- package/dist/style/layout.js +31 -53
- package/dist/style.css +4 -1
- package/dist/viewport/geometry.d.ts +8 -0
- package/dist/viewport/geometry.js +43 -0
- package/dist/viewport/index.d.ts +4 -0
- package/dist/viewport/index.js +4 -0
- package/dist/viewport/layout.d.ts +4 -0
- package/dist/viewport/layout.js +138 -0
- package/dist/viewport/placement.d.ts +4 -0
- package/dist/viewport/placement.js +14 -0
- package/dist/viewport/types.d.ts +81 -0
- package/dist/viewport/types.js +1 -0
- package/package.json +1 -1
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
<script lang="ts">
|
|
1
|
+
<script lang="ts" generics="T">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import { loader as loaderAction } from '../../../actions/loader';
|
|
4
4
|
import { extractLoaderProps, resolveLoaderOptions, type LoaderProps } from '../../../style/loader';
|
|
5
5
|
import { extractLayoutProps, layoutStyle, type LayoutProps } from '../../../style/layout';
|
|
6
6
|
import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
|
|
7
|
+
import {
|
|
8
|
+
fixedGridLayout,
|
|
9
|
+
type LayoutKey,
|
|
10
|
+
type ViewportGeometry,
|
|
11
|
+
type ViewportOverscan
|
|
12
|
+
} from '../../../viewport';
|
|
13
|
+
import ScrollView from '../ScrollView';
|
|
7
14
|
|
|
8
15
|
type Props = {
|
|
9
16
|
children?: Snippet;
|
|
17
|
+
items?: T[];
|
|
18
|
+
item?: Snippet<[item: T, index: number]>;
|
|
10
19
|
columns?: number | string;
|
|
11
20
|
rows?: number | string;
|
|
12
21
|
autoRows?: number | string;
|
|
22
|
+
columnWidth?: number;
|
|
23
|
+
rowHeight?: number;
|
|
24
|
+
columnGap?: number;
|
|
25
|
+
rowGap?: number;
|
|
26
|
+
overscan?: ViewportOverscan;
|
|
27
|
+
key?: LayoutKey<T>;
|
|
13
28
|
dense?: boolean;
|
|
14
29
|
} & LayoutProps &
|
|
15
30
|
StyleProps &
|
|
@@ -17,9 +32,17 @@
|
|
|
17
32
|
|
|
18
33
|
let {
|
|
19
34
|
children,
|
|
35
|
+
items,
|
|
36
|
+
item,
|
|
20
37
|
columns = 1,
|
|
21
38
|
rows,
|
|
22
39
|
autoRows,
|
|
40
|
+
columnWidth,
|
|
41
|
+
rowHeight,
|
|
42
|
+
columnGap = 0,
|
|
43
|
+
rowGap = 0,
|
|
44
|
+
overscan = 0,
|
|
45
|
+
key,
|
|
23
46
|
dense = false,
|
|
24
47
|
...restProps
|
|
25
48
|
}: Props & Record<string, unknown> = $props();
|
|
@@ -47,8 +70,26 @@
|
|
|
47
70
|
const gridAutoRows = $derived(
|
|
48
71
|
autoRows === undefined ? undefined : typeof autoRows === 'number' ? `${autoRows}rem` : autoRows
|
|
49
72
|
);
|
|
73
|
+
const engineEnabled = $derived(
|
|
74
|
+
items !== undefined &&
|
|
75
|
+
item !== undefined &&
|
|
76
|
+
typeof columns === 'number' &&
|
|
77
|
+
columnWidth !== undefined &&
|
|
78
|
+
rowHeight !== undefined
|
|
79
|
+
);
|
|
50
80
|
const rootStyle = $derived.by(() =>
|
|
51
81
|
[
|
|
82
|
+
'display: grid;',
|
|
83
|
+
'width: 100%;',
|
|
84
|
+
'min-width: 0;',
|
|
85
|
+
'min-height: 100%;',
|
|
86
|
+
'align-self: stretch;',
|
|
87
|
+
'align-items: stretch;',
|
|
88
|
+
'gap: var(--grid-gap, 0px);',
|
|
89
|
+
'border-radius: var(--grid-border-radius, 0px);',
|
|
90
|
+
'padding: var(--grid-padding-y, 0px) var(--grid-padding-x, 0px);',
|
|
91
|
+
'font-size: var(--grid-font-size, inherit);',
|
|
92
|
+
dense ? 'grid-auto-flow: dense;' : '',
|
|
52
93
|
`grid-template-columns: ${templateColumns};`,
|
|
53
94
|
templateRows ? `grid-template-rows: ${templateRows};` : '',
|
|
54
95
|
gridAutoRows ? `grid-auto-rows: ${gridAutoRows};` : '',
|
|
@@ -58,35 +99,88 @@
|
|
|
58
99
|
.filter(Boolean)
|
|
59
100
|
.join(' ')
|
|
60
101
|
);
|
|
102
|
+
const engineRootStyle = $derived.by(() =>
|
|
103
|
+
[
|
|
104
|
+
'width: 100%;',
|
|
105
|
+
'height: 100%;',
|
|
106
|
+
'min-width: 0;',
|
|
107
|
+
'min-height: 0;',
|
|
108
|
+
'align-self: stretch;',
|
|
109
|
+
'border-radius: var(--grid-border-radius, 0px);',
|
|
110
|
+
'font-size: var(--grid-font-size, inherit);',
|
|
111
|
+
layoutStyle(layoutProps),
|
|
112
|
+
surfaceStyle(styleProps, 'grid')
|
|
113
|
+
]
|
|
114
|
+
.filter(Boolean)
|
|
115
|
+
.join(' ')
|
|
116
|
+
);
|
|
117
|
+
let viewport = $state<ViewportGeometry>({
|
|
118
|
+
offset: { x: 0, y: 0 },
|
|
119
|
+
viewport: { width: 0, height: 0 }
|
|
120
|
+
});
|
|
121
|
+
const engineLayout = $derived.by(() =>
|
|
122
|
+
engineEnabled
|
|
123
|
+
? fixedGridLayout({
|
|
124
|
+
items: items ?? [],
|
|
125
|
+
viewport,
|
|
126
|
+
columnWidth: columnWidth ?? 1,
|
|
127
|
+
rowHeight: rowHeight ?? 1,
|
|
128
|
+
columns: typeof columns === 'number' ? columns : 1,
|
|
129
|
+
columnGap,
|
|
130
|
+
rowGap,
|
|
131
|
+
overscan,
|
|
132
|
+
key
|
|
133
|
+
})
|
|
134
|
+
: { content: { width: 0, height: 0 }, visible: [] }
|
|
135
|
+
);
|
|
136
|
+
const updateViewport = (geometry: {
|
|
137
|
+
offset: { x: number; y: number };
|
|
138
|
+
viewport: { width: number; height: number };
|
|
139
|
+
}) => {
|
|
140
|
+
viewport = {
|
|
141
|
+
offset: geometry.offset,
|
|
142
|
+
viewport: geometry.viewport
|
|
143
|
+
};
|
|
144
|
+
};
|
|
61
145
|
</script>
|
|
62
146
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
147
|
+
{#if engineEnabled}
|
|
148
|
+
<div style={engineRootStyle} {...forwardedProps} use:loaderAction={loaderOptions}>
|
|
149
|
+
{#snippet layout()}
|
|
150
|
+
{#each engineLayout.visible as placed (placed.key)}
|
|
151
|
+
<div
|
|
152
|
+
class="grid-engine-item"
|
|
153
|
+
style:width={`${placed.rect.width}px`}
|
|
154
|
+
style:height={`${placed.rect.height}px`}
|
|
155
|
+
style:transform={`translate3d(${placed.rect.x}px, ${placed.rect.y}px, 0)`}
|
|
156
|
+
>
|
|
157
|
+
{@render item?.(placed.item, placed.index)}
|
|
158
|
+
</div>
|
|
159
|
+
{/each}
|
|
160
|
+
{/snippet}
|
|
161
|
+
|
|
162
|
+
<ScrollView
|
|
163
|
+
axis="both"
|
|
164
|
+
contentSize={engineLayout.content}
|
|
165
|
+
contentStyles={{ paddingX: 0, paddingY: 0 }}
|
|
166
|
+
onScroll={updateViewport}
|
|
167
|
+
{layout}
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
{:else}
|
|
171
|
+
<div style={rootStyle} {...forwardedProps} use:loaderAction={loaderOptions}>
|
|
172
|
+
{#if children}
|
|
173
|
+
{@render children()}
|
|
174
|
+
{/if}
|
|
175
|
+
</div>
|
|
176
|
+
{/if}
|
|
74
177
|
|
|
75
178
|
<style>
|
|
76
|
-
.grid {
|
|
77
|
-
|
|
78
|
-
|
|
179
|
+
.grid-engine-item {
|
|
180
|
+
position: absolute;
|
|
181
|
+
left: 0;
|
|
182
|
+
top: 0;
|
|
79
183
|
min-width: 0;
|
|
80
|
-
min-height:
|
|
81
|
-
align-self: stretch;
|
|
82
|
-
align-items: stretch;
|
|
83
|
-
gap: var(--grid-gap, 0px);
|
|
84
|
-
border-radius: var(--grid-border-radius, 0px);
|
|
85
|
-
padding: var(--grid-padding-y, 0px) var(--grid-padding-x, 0px);
|
|
86
|
-
font-size: var(--grid-font-size, inherit);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.grid-dense {
|
|
90
|
-
grid-auto-flow: dense;
|
|
184
|
+
min-height: 0;
|
|
91
185
|
}
|
|
92
186
|
</style>
|
|
@@ -2,14 +2,42 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
import { type LoaderProps } from '../../../style/loader';
|
|
3
3
|
import { type LayoutProps } from '../../../style/layout';
|
|
4
4
|
import { type StyleProps } from '../../../style/surface';
|
|
5
|
-
type
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
import { type LayoutKey, type ViewportOverscan } from '../../../viewport';
|
|
6
|
+
declare function $$render<T>(): {
|
|
7
|
+
props: {
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
items?: T[];
|
|
10
|
+
item?: Snippet<[item: T, index: number]>;
|
|
11
|
+
columns?: number | string;
|
|
12
|
+
rows?: number | string;
|
|
13
|
+
autoRows?: number | string;
|
|
14
|
+
columnWidth?: number;
|
|
15
|
+
rowHeight?: number;
|
|
16
|
+
columnGap?: number;
|
|
17
|
+
rowGap?: number;
|
|
18
|
+
overscan?: ViewportOverscan;
|
|
19
|
+
key?: LayoutKey<T>;
|
|
20
|
+
dense?: boolean;
|
|
21
|
+
} & LayoutProps & StyleProps & LoaderProps & Record<string, unknown>;
|
|
22
|
+
exports: {};
|
|
23
|
+
bindings: "";
|
|
24
|
+
slots: {};
|
|
25
|
+
events: {};
|
|
26
|
+
};
|
|
27
|
+
declare class __sveltets_Render<T> {
|
|
28
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
29
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
30
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
31
|
+
bindings(): "";
|
|
32
|
+
exports(): {};
|
|
33
|
+
}
|
|
34
|
+
interface $$IsomorphicComponent {
|
|
35
|
+
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']>> & {
|
|
36
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
37
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
38
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
39
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
40
|
+
}
|
|
41
|
+
declare const Grid: $$IsomorphicComponent;
|
|
42
|
+
type Grid<T> = InstanceType<typeof Grid<T>>;
|
|
15
43
|
export default Grid;
|
|
@@ -38,6 +38,13 @@
|
|
|
38
38
|
const forwardedProps = $derived(extractedStyleProps.restProps);
|
|
39
39
|
const rootStyle = $derived.by(() =>
|
|
40
40
|
[
|
|
41
|
+
'width: 100%;',
|
|
42
|
+
'min-width: 0;',
|
|
43
|
+
'min-height: 0;',
|
|
44
|
+
'height: 100%;',
|
|
45
|
+
'border-radius: var(--grid-item-border-radius, 0px);',
|
|
46
|
+
'padding: var(--grid-item-padding-y, 0px) var(--grid-item-padding-x, 0px);',
|
|
47
|
+
'font-size: var(--grid-item-font-size, inherit);',
|
|
41
48
|
column !== undefined ? `grid-column: ${column};` : '',
|
|
42
49
|
row !== undefined ? `grid-row: ${row};` : '',
|
|
43
50
|
column === undefined && columnSpan !== undefined
|
|
@@ -55,20 +62,8 @@
|
|
|
55
62
|
);
|
|
56
63
|
</script>
|
|
57
64
|
|
|
58
|
-
<div
|
|
65
|
+
<div style={rootStyle} {...forwardedProps} use:loaderAction={loaderOptions}>
|
|
59
66
|
{#if children}
|
|
60
67
|
{@render children()}
|
|
61
68
|
{/if}
|
|
62
69
|
</div>
|
|
63
|
-
|
|
64
|
-
<style>
|
|
65
|
-
.grid-item {
|
|
66
|
-
width: 100%;
|
|
67
|
-
min-width: 0;
|
|
68
|
-
min-height: 0;
|
|
69
|
-
height: 100%;
|
|
70
|
-
border-radius: var(--grid-item-border-radius, 0px);
|
|
71
|
-
padding: var(--grid-item-padding-y, 0px) var(--grid-item-padding-x, 0px);
|
|
72
|
-
font-size: var(--grid-item-font-size, inherit);
|
|
73
|
-
}
|
|
74
|
-
</style>
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
const loadedImageSources = new Set<string>();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
1
5
|
<script lang="ts">
|
|
2
6
|
import { ImageOffIcon } from '@lucide/svelte';
|
|
3
7
|
import type { Snippet } from 'svelte';
|
|
@@ -29,12 +33,28 @@
|
|
|
29
33
|
|
|
30
34
|
let imageLoaded = $state(false);
|
|
31
35
|
let imageFailed = $state(false);
|
|
36
|
+
let imageElement = $state<HTMLImageElement | null>(null);
|
|
37
|
+
let previousSrc = $state<string | null>(null);
|
|
32
38
|
|
|
33
39
|
$effect(() => {
|
|
34
|
-
src;
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
const nextSrc = src ?? null;
|
|
41
|
+
if (nextSrc === previousSrc) return;
|
|
42
|
+
|
|
43
|
+
previousSrc = nextSrc;
|
|
37
44
|
imageFailed = false;
|
|
45
|
+
imageLoaded = nextSrc ? loadedImageSources.has(nextSrc) : false;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const markImageLoaded = (loadedSrc: string | null | undefined) => {
|
|
49
|
+
if (loadedSrc) loadedImageSources.add(loadedSrc);
|
|
50
|
+
imageLoaded = true;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
$effect(() => {
|
|
54
|
+
if (!imageElement || !src || imageLoaded || imageFailed) return;
|
|
55
|
+
if (imageElement.complete && imageElement.naturalWidth > 0) {
|
|
56
|
+
markImageLoaded(src);
|
|
57
|
+
}
|
|
38
58
|
});
|
|
39
59
|
|
|
40
60
|
const canRenderImage = $derived(Boolean(src) && !busy && !imageFailed);
|
|
@@ -53,6 +73,7 @@
|
|
|
53
73
|
>
|
|
54
74
|
{#if canRenderImage}
|
|
55
75
|
<img
|
|
76
|
+
bind:this={imageElement}
|
|
56
77
|
{src}
|
|
57
78
|
{alt}
|
|
58
79
|
{loading}
|
|
@@ -63,7 +84,7 @@
|
|
|
63
84
|
: 'object-contain'} {imageLoaded
|
|
64
85
|
? 'opacity-100'
|
|
65
86
|
: 'opacity-0'} transition-opacity duration-150"
|
|
66
|
-
onload={() => (
|
|
87
|
+
onload={() => markImageLoaded(src)}
|
|
67
88
|
onerror={() => (imageFailed = true)}
|
|
68
89
|
/>
|
|
69
90
|
{/if}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
let clearRevision = $state(0);
|
|
22
22
|
</script>
|
|
23
23
|
|
|
24
|
-
<Grid columns="repeat(auto-fit, minmax(min(100%, 18rem), 1fr))"
|
|
24
|
+
<Grid columns="repeat(auto-fit, minmax(min(100%, 18rem), 1fr))" gap={7.5}>
|
|
25
25
|
<ImageMask
|
|
26
26
|
src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?auto=format&fit=crop&w=700&q=80"
|
|
27
27
|
alt="Red sneaker"
|
|
@@ -23,14 +23,10 @@
|
|
|
23
23
|
iconSize,
|
|
24
24
|
iconColor,
|
|
25
25
|
orientation = 'top',
|
|
26
|
+
grow,
|
|
27
|
+
fit,
|
|
26
28
|
width,
|
|
27
29
|
height,
|
|
28
|
-
minWidth,
|
|
29
|
-
minHeight,
|
|
30
|
-
maxWidth,
|
|
31
|
-
maxHeight,
|
|
32
|
-
align,
|
|
33
|
-
justify,
|
|
34
30
|
fontSize,
|
|
35
31
|
paddingX,
|
|
36
32
|
paddingY,
|
|
@@ -44,14 +40,10 @@
|
|
|
44
40
|
}: Props = $props();
|
|
45
41
|
|
|
46
42
|
const layoutProps = $derived({
|
|
43
|
+
grow,
|
|
44
|
+
fit,
|
|
47
45
|
width,
|
|
48
|
-
height
|
|
49
|
-
minWidth,
|
|
50
|
-
minHeight,
|
|
51
|
-
maxWidth,
|
|
52
|
-
maxHeight,
|
|
53
|
-
align,
|
|
54
|
-
justify
|
|
46
|
+
height
|
|
55
47
|
});
|
|
56
48
|
const styleProps = $derived({
|
|
57
49
|
fontSize,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export const demo = {
|
|
3
|
+
name: 'Notifications',
|
|
4
|
+
description: 'Animated notification stack driven by an items array.',
|
|
5
|
+
columnSpan: 2
|
|
6
|
+
};
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import Button from '../Button';
|
|
11
|
+
import HStack from '../HStack';
|
|
12
|
+
import VStack from '../VStack';
|
|
13
|
+
import Notifications, { type NotificationItem } from './index';
|
|
14
|
+
|
|
15
|
+
const tones: NotificationItem['tone'][] = ['info', 'success', 'warning'];
|
|
16
|
+
|
|
17
|
+
let nextId = 3;
|
|
18
|
+
let items = $state<NotificationItem[]>([
|
|
19
|
+
{
|
|
20
|
+
id: 1,
|
|
21
|
+
title: 'Build complete',
|
|
22
|
+
message: 'The package finished compiling.',
|
|
23
|
+
tone: 'success'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 2,
|
|
27
|
+
title: 'New deploy queued',
|
|
28
|
+
message: 'Production will update after checks pass.',
|
|
29
|
+
tone: 'info'
|
|
30
|
+
}
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
function addNotification() {
|
|
34
|
+
const id = nextId++;
|
|
35
|
+
const tone = tones[id % tones.length];
|
|
36
|
+
items = [
|
|
37
|
+
{
|
|
38
|
+
id,
|
|
39
|
+
title: `Notification ${id}`,
|
|
40
|
+
message: 'Inserted at the top of the array.',
|
|
41
|
+
tone
|
|
42
|
+
},
|
|
43
|
+
...items
|
|
44
|
+
].slice(0, 4);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function clearNotifications() {
|
|
48
|
+
items = [];
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<VStack width="100%" gap={7.5}>
|
|
53
|
+
<HStack gap={5}>
|
|
54
|
+
<Button label="Add" onclick={addNotification} />
|
|
55
|
+
<Button label="Clear" onclick={clearNotifications} />
|
|
56
|
+
</HStack>
|
|
57
|
+
|
|
58
|
+
<Notifications bind:items width="100%" animationDuration={220}>
|
|
59
|
+
{#snippet empty()}
|
|
60
|
+
No notifications
|
|
61
|
+
{/snippet}
|
|
62
|
+
</Notifications>
|
|
63
|
+
</VStack>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const demo: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
columnSpan: number;
|
|
5
|
+
};
|
|
6
|
+
import Notifications from './index';
|
|
7
|
+
declare const Notifications: import("svelte").Component<Record<string, never>, {}, "">;
|
|
8
|
+
type Notifications = ReturnType<typeof Notifications>;
|
|
9
|
+
export default Notifications;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script lang="ts" generics="TItem extends NotificationItem = NotificationItem">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { flip } from 'svelte/animate';
|
|
4
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
5
|
+
import { fade, fly } from 'svelte/transition';
|
|
6
|
+
import { extractLayoutProps, layoutStyle, type LayoutProps } from '../../../style/layout';
|
|
7
|
+
import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
|
|
8
|
+
import type { NotificationItem } from './types';
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
items?: TItem[];
|
|
12
|
+
children?: Snippet<[TItem, number]>;
|
|
13
|
+
empty?: Snippet;
|
|
14
|
+
key?: (item: TItem, index: number) => string | number;
|
|
15
|
+
animationDuration?: number;
|
|
16
|
+
} & LayoutProps &
|
|
17
|
+
StyleProps &
|
|
18
|
+
Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'class' | 'style'>;
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
items = $bindable<TItem[]>([]),
|
|
22
|
+
children,
|
|
23
|
+
empty,
|
|
24
|
+
key,
|
|
25
|
+
animationDuration = 180,
|
|
26
|
+
...restProps
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const extractedLayoutProps = $derived.by(() => extractLayoutProps(restProps));
|
|
30
|
+
const layoutProps = $derived(extractedLayoutProps.layoutProps);
|
|
31
|
+
const afterLayoutProps = $derived(extractedLayoutProps.restProps);
|
|
32
|
+
const extractedStyleProps = $derived.by(() => extractStyleProps(afterLayoutProps));
|
|
33
|
+
const styleProps = $derived(extractedStyleProps.styleProps);
|
|
34
|
+
const forwardedProps = $derived(extractedStyleProps.restProps);
|
|
35
|
+
const rootStyle = $derived.by(() =>
|
|
36
|
+
[layoutStyle(layoutProps), surfaceStyle(styleProps, 'notifications')].filter(Boolean).join(' ')
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function itemKey(item: TItem, index: number) {
|
|
40
|
+
return key?.(item, index) ?? item.id ?? index;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function itemTitle(item: TItem) {
|
|
44
|
+
return item.title ?? String(item.id ?? 'Notification');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function itemMessage(item: TItem) {
|
|
48
|
+
return item.message ?? item.description ?? '';
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<div class="notifications" style={rootStyle} {...forwardedProps}>
|
|
53
|
+
{#each items as item, index (itemKey(item, index))}
|
|
54
|
+
<div
|
|
55
|
+
class="notification"
|
|
56
|
+
data-tone={item.tone ?? 'default'}
|
|
57
|
+
in:fly={{ y: -8, duration: animationDuration }}
|
|
58
|
+
out:fade={{ duration: animationDuration * 0.75 }}
|
|
59
|
+
animate:flip={{ duration: animationDuration }}
|
|
60
|
+
>
|
|
61
|
+
{#if children}
|
|
62
|
+
{@render children(item, index)}
|
|
63
|
+
{:else}
|
|
64
|
+
<p class="notification-title">{itemTitle(item)}</p>
|
|
65
|
+
{#if itemMessage(item)}
|
|
66
|
+
<p class="notification-message">{itemMessage(item)}</p>
|
|
67
|
+
{/if}
|
|
68
|
+
{/if}
|
|
69
|
+
</div>
|
|
70
|
+
{:else}
|
|
71
|
+
{#if empty}
|
|
72
|
+
<div class="notifications-empty">
|
|
73
|
+
{@render empty()}
|
|
74
|
+
</div>
|
|
75
|
+
{/if}
|
|
76
|
+
{/each}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<style>
|
|
80
|
+
.notifications {
|
|
81
|
+
--notifications-font-size: var(--sveltely-font-size);
|
|
82
|
+
--notifications-padding-x: 0px;
|
|
83
|
+
--notifications-padding-y: 0px;
|
|
84
|
+
--notifications-gap: var(--sveltely-gap);
|
|
85
|
+
--notifications-border-radius: var(--sveltely-border-radius);
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
min-width: 0;
|
|
88
|
+
min-height: 0;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
align-items: stretch;
|
|
91
|
+
gap: var(--notifications-gap);
|
|
92
|
+
border-radius: var(--notifications-border-radius);
|
|
93
|
+
padding: var(--notifications-padding-y) var(--notifications-padding-x);
|
|
94
|
+
font-size: var(--notifications-font-size);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.notification {
|
|
98
|
+
position: relative;
|
|
99
|
+
min-width: min(100%, 16rem);
|
|
100
|
+
border: 1px solid var(--sveltely-border-color);
|
|
101
|
+
border-radius: var(--sveltely-border-radius);
|
|
102
|
+
background: var(--sveltely-background-color);
|
|
103
|
+
box-shadow: var(--sveltely-shadow);
|
|
104
|
+
color: var(--sveltely-text-primary-color);
|
|
105
|
+
padding: calc(var(--sveltely-padding-y) * 0.83) var(--sveltely-padding-x);
|
|
106
|
+
overflow: hidden;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.notification::before {
|
|
110
|
+
position: absolute;
|
|
111
|
+
inset-block: 0;
|
|
112
|
+
inset-inline-start: 0;
|
|
113
|
+
width: 3px;
|
|
114
|
+
background: var(--notification-accent, var(--sveltely-control-active-color));
|
|
115
|
+
content: '';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.notification[data-tone='info'] {
|
|
119
|
+
--notification-accent: #2563eb;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.notification[data-tone='success'] {
|
|
123
|
+
--notification-accent: #16a34a;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.notification[data-tone='warning'] {
|
|
127
|
+
--notification-accent: #d97706;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.notification[data-tone='danger'] {
|
|
131
|
+
--notification-accent: #dc2626;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.notification-title,
|
|
135
|
+
.notification-message {
|
|
136
|
+
margin: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.notification-title {
|
|
140
|
+
font-weight: 600;
|
|
141
|
+
line-height: 1.35;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.notification-message {
|
|
145
|
+
margin-top: 0.125rem;
|
|
146
|
+
color: var(--sveltely-text-secondary-color);
|
|
147
|
+
font-size: 0.875em;
|
|
148
|
+
line-height: 1.45;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.notifications-empty {
|
|
152
|
+
color: var(--sveltely-text-secondary-color);
|
|
153
|
+
font-size: 0.875em;
|
|
154
|
+
}
|
|
155
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type LayoutProps } from '../../../style/layout';
|
|
4
|
+
import { type StyleProps } from '../../../style/surface';
|
|
5
|
+
import type { NotificationItem } from './types';
|
|
6
|
+
declare function $$render<TItem extends NotificationItem = NotificationItem>(): {
|
|
7
|
+
props: {
|
|
8
|
+
items?: TItem[];
|
|
9
|
+
children?: Snippet<[TItem, number]>;
|
|
10
|
+
empty?: Snippet;
|
|
11
|
+
key?: (item: TItem, index: number) => string | number;
|
|
12
|
+
animationDuration?: number;
|
|
13
|
+
} & LayoutProps & StyleProps & Omit<HTMLAttributes<HTMLDivElement>, "style" | "class" | "children">;
|
|
14
|
+
exports: {};
|
|
15
|
+
bindings: "items";
|
|
16
|
+
slots: {};
|
|
17
|
+
events: {};
|
|
18
|
+
};
|
|
19
|
+
declare class __sveltets_Render<TItem extends NotificationItem = NotificationItem> {
|
|
20
|
+
props(): ReturnType<typeof $$render<TItem>>['props'];
|
|
21
|
+
events(): ReturnType<typeof $$render<TItem>>['events'];
|
|
22
|
+
slots(): ReturnType<typeof $$render<TItem>>['slots'];
|
|
23
|
+
bindings(): "items";
|
|
24
|
+
exports(): {};
|
|
25
|
+
}
|
|
26
|
+
interface $$IsomorphicComponent {
|
|
27
|
+
new <TItem extends NotificationItem = NotificationItem>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TItem>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TItem>['props']>, ReturnType<__sveltets_Render<TItem>['events']>, ReturnType<__sveltets_Render<TItem>['slots']>> & {
|
|
28
|
+
$$bindings?: ReturnType<__sveltets_Render<TItem>['bindings']>;
|
|
29
|
+
} & ReturnType<__sveltets_Render<TItem>['exports']>;
|
|
30
|
+
<TItem extends NotificationItem = NotificationItem>(internal: unknown, props: ReturnType<__sveltets_Render<TItem>['props']> & {}): ReturnType<__sveltets_Render<TItem>['exports']>;
|
|
31
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
32
|
+
}
|
|
33
|
+
declare const Notifications: $$IsomorphicComponent;
|
|
34
|
+
type Notifications<TItem extends NotificationItem = NotificationItem> = InstanceType<typeof Notifications<TItem>>;
|
|
35
|
+
export default Notifications;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Notifications.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|