@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.
Files changed (72) hide show
  1. package/dist/actions/LoaderOverlay.svelte +10 -3
  2. package/dist/components/Library/Accordion/Accordion.demo.svelte +21 -0
  3. package/dist/components/Library/Accordion/Accordion.demo.svelte.d.ts +9 -0
  4. package/dist/components/Library/Accordion/Accordion.svelte +78 -0
  5. package/dist/components/Library/Accordion/Accordion.svelte.d.ts +14 -0
  6. package/dist/components/Library/Accordion/Content.svelte +57 -0
  7. package/dist/components/Library/Accordion/Content.svelte.d.ts +8 -0
  8. package/dist/components/Library/Accordion/Header.svelte +98 -0
  9. package/dist/components/Library/Accordion/Header.svelte.d.ts +10 -0
  10. package/dist/components/Library/Accordion/context.d.ts +9 -0
  11. package/dist/components/Library/Accordion/context.js +6 -0
  12. package/dist/components/Library/Accordion/index.d.ts +9 -0
  13. package/dist/components/Library/Accordion/index.js +7 -0
  14. package/dist/components/Library/AnimatedNumber/AnimatedNumber.demo.svelte +1 -1
  15. package/dist/components/Library/ArticleEditor/ArticleEditor.svelte +1 -2
  16. package/dist/components/Library/ArticleEditor/Blocks/Table.svelte +1 -1
  17. package/dist/components/Library/ChipInput/ChipInput.demo.svelte +1 -1
  18. package/dist/components/Library/Dropdown/Dropdown.demo.svelte +5 -3
  19. package/dist/components/Library/Grid/Grid.demo.svelte +58 -0
  20. package/dist/components/Library/Grid/Grid.demo.svelte.d.ts +25 -0
  21. package/dist/components/Library/Grid/Grid.svelte +120 -26
  22. package/dist/components/Library/Grid/Grid.svelte.d.ts +38 -10
  23. package/dist/components/Library/Grid/GridItem.svelte +8 -13
  24. package/dist/components/Library/Image/Image.svelte +25 -4
  25. package/dist/components/Library/ImageMask/ImageMask.demo.svelte +1 -1
  26. package/dist/components/Library/Label/Label.demo.svelte +1 -1
  27. package/dist/components/Library/Label/Label.svelte +5 -13
  28. package/dist/components/Library/Notifications/Notifications.demo.svelte +63 -0
  29. package/dist/components/Library/Notifications/Notifications.demo.svelte.d.ts +9 -0
  30. package/dist/components/Library/Notifications/Notifications.svelte +155 -0
  31. package/dist/components/Library/Notifications/Notifications.svelte.d.ts +35 -0
  32. package/dist/components/Library/Notifications/index.d.ts +2 -0
  33. package/dist/components/Library/Notifications/index.js +1 -0
  34. package/dist/components/Library/Notifications/types.d.ts +8 -0
  35. package/dist/components/Library/Notifications/types.js +1 -0
  36. package/dist/components/Library/NumberField/NumberField.svelte +25 -19
  37. package/dist/components/Library/Pagination/Pagination.svelte +6 -18
  38. package/dist/components/Library/ScrollView/ScrollView.svelte +76 -4
  39. package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +9 -2
  40. package/dist/components/Library/ScrollView/index.d.ts +1 -1
  41. package/dist/components/Library/SearchField/SearchField.demo.svelte +1 -1
  42. package/dist/components/Library/SegmentedPicker/SegmentedPicker.demo.svelte +1 -1
  43. package/dist/components/Library/Slider/Slider.demo.svelte +1 -1
  44. package/dist/components/Library/Table/Table.demo.svelte +9 -1
  45. package/dist/components/Library/Table/Table.svelte +315 -112
  46. package/dist/components/Library/Table/Table.svelte.d.ts +7 -1
  47. package/dist/components/Library/TextField/TextField.svelte +20 -14
  48. package/dist/components/Library/TokenSearchField/TokenSearchField.demo.svelte +1 -1
  49. package/dist/components/Local/ColorStyleControls.svelte +25 -64
  50. package/dist/components/Local/ComponentGrid.svelte +103 -22
  51. package/dist/components/Local/ComponentGrid.svelte.d.ts +2 -1
  52. package/dist/components/Local/ComponentPage.svelte +74 -0
  53. package/dist/components/Local/ComponentPage.svelte.d.ts +13 -0
  54. package/dist/components/Local/HeroCard.svelte +10 -5
  55. package/dist/components/Local/LayoutStyleControls.svelte +6 -6
  56. package/dist/index.d.ts +8 -3
  57. package/dist/index.js +4 -1
  58. package/dist/style/index.css +1 -2
  59. package/dist/style/layout.d.ts +11 -26
  60. package/dist/style/layout.js +31 -53
  61. package/dist/style.css +4 -1
  62. package/dist/viewport/geometry.d.ts +8 -0
  63. package/dist/viewport/geometry.js +43 -0
  64. package/dist/viewport/index.d.ts +4 -0
  65. package/dist/viewport/index.js +4 -0
  66. package/dist/viewport/layout.d.ts +4 -0
  67. package/dist/viewport/layout.js +138 -0
  68. package/dist/viewport/placement.d.ts +4 -0
  69. package/dist/viewport/placement.js +14 -0
  70. package/dist/viewport/types.d.ts +81 -0
  71. package/dist/viewport/types.js +1 -0
  72. 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
- <div
64
- class="grid"
65
- class:grid-dense={dense}
66
- style={rootStyle}
67
- {...forwardedProps}
68
- use:loaderAction={loaderOptions}
69
- >
70
- {#if children}
71
- {@render children()}
72
- {/if}
73
- </div>
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
- display: grid;
78
- width: 100%;
179
+ .grid-engine-item {
180
+ position: absolute;
181
+ left: 0;
182
+ top: 0;
79
183
  min-width: 0;
80
- min-height: 100%;
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 Props = {
6
- children?: Snippet;
7
- columns?: number | string;
8
- rows?: number | string;
9
- autoRows?: number | string;
10
- dense?: boolean;
11
- } & LayoutProps & StyleProps & LoaderProps;
12
- type $$ComponentProps = Props & Record<string, unknown>;
13
- declare const Grid: import("svelte").Component<$$ComponentProps, {}, "">;
14
- type Grid = ReturnType<typeof Grid>;
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 class="grid-item" style={rootStyle} {...forwardedProps} use:loaderAction={loaderOptions}>
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
- busy;
36
- imageLoaded = false;
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={() => (imageLoaded = true)}
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))" align="start" gap={7.5}>
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"
@@ -19,7 +19,7 @@
19
19
  let enabled = $state(true);
20
20
  </script>
21
21
 
22
- <HStack gap={10} align="center">
22
+ <HStack gap={10}>
23
23
  <Label label="Name" icon={UserIcon} orientation="top">
24
24
  <TextField bind:value={name} placeholder="Sveltely" />
25
25
  </Label>
@@ -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,2 @@
1
+ export { default } from './Notifications.svelte';
2
+ export type { NotificationItem, NotificationTone } from './types';
@@ -0,0 +1 @@
1
+ export { default } from './Notifications.svelte';
@@ -0,0 +1,8 @@
1
+ export type NotificationTone = 'default' | 'info' | 'success' | 'warning' | 'danger';
2
+ export type NotificationItem = {
3
+ id?: string | number;
4
+ title?: string;
5
+ message?: string;
6
+ description?: string;
7
+ tone?: NotificationTone;
8
+ };
@@ -0,0 +1 @@
1
+ export {};