@marianmeres/stuic 1.32.0 → 1.34.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.
@@ -70,17 +70,19 @@ export class AlertConfirmPromptConfig {
70
70
  `.trim()
71
71
  };
72
72
  // main userland configuration
73
- static classDialog = "";
74
- static classIcon = "";
75
- static classTitle = "";
76
- static classContentBlock = "";
77
- static classContent = "";
78
- static classInputBox = "";
79
- static classInputField = "";
80
- static classMenu = "";
81
- // static classMenuLi = '';
82
- static classButton = "";
83
- static classSpinnerBox = "";
73
+ static class = {
74
+ dialog: "",
75
+ icon: "",
76
+ contentBlock: "",
77
+ title: "",
78
+ content: "",
79
+ inputBox: "",
80
+ inputField: "",
81
+ menu: "",
82
+ // menuLi: '',
83
+ button: "",
84
+ spinnerBox: ""
85
+ };
84
86
  // 'info' | 'success' | 'warn' | 'error'
85
87
  // userlang variant fine tuning
86
88
  static variant = {
@@ -200,56 +202,56 @@ onMount(() => {
200
202
  $:
201
203
  _dialogClass = twMerge(`
202
204
  ${AlertConfirmPromptConfig.preset.dialog}
203
- ${AlertConfirmPromptConfig.classDialog}
205
+ ${AlertConfirmPromptConfig.class.dialog}
204
206
  ${dialog?.class?.dialog || ""}
205
207
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.dialog || ""}
206
208
  `);
207
209
  $:
208
210
  _iconClass = twMerge(`
209
211
  ${AlertConfirmPromptConfig.preset.icon}
210
- ${AlertConfirmPromptConfig.classIcon}
212
+ ${AlertConfirmPromptConfig.class.icon}
211
213
  ${dialog?.class?.icon || ""}
212
214
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.icon || ""}
213
215
  `);
214
216
  $:
215
217
  _contentBlockClass = twMerge(`
216
218
  ${AlertConfirmPromptConfig.preset.contentBlock}
217
- ${AlertConfirmPromptConfig.classContentBlock}
219
+ ${AlertConfirmPromptConfig.class.contentBlock}
218
220
  ${dialog?.class?.contentBlock || ""}
219
221
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.contentBlock || ""}
220
222
  `);
221
223
  $:
222
224
  _titleClass = twMerge(`
223
225
  ${AlertConfirmPromptConfig.preset.title}
224
- ${AlertConfirmPromptConfig.classTitle}
226
+ ${AlertConfirmPromptConfig.class.title}
225
227
  ${dialog?.class?.title || ""}
226
228
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.title || ""}
227
229
  `);
228
230
  $:
229
231
  _contentClass = twMerge(`
230
232
  ${AlertConfirmPromptConfig.preset.content}
231
- ${AlertConfirmPromptConfig.classContent}
233
+ ${AlertConfirmPromptConfig.class.content}
232
234
  ${dialog?.class?.content || ""}
233
235
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.content || ""}
234
236
  `);
235
237
  $:
236
238
  _inputBoxClass = twMerge(`
237
239
  ${AlertConfirmPromptConfig.preset.inputBox}
238
- ${AlertConfirmPromptConfig.classInputBox}
240
+ ${AlertConfirmPromptConfig.class.inputBox}
239
241
  ${dialog?.class?.inputBox || ""}
240
242
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.inputBox || ""}
241
243
  `);
242
244
  $:
243
245
  _inputFieldClass = twMerge(`
244
246
  ${AlertConfirmPromptConfig.preset.inputField}
245
- ${AlertConfirmPromptConfig.classInputField}
247
+ ${AlertConfirmPromptConfig.class.inputField}
246
248
  ${dialog?.class?.inputField || ""}
247
249
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.inputField || ""}
248
250
  `);
249
251
  $:
250
252
  _menuClass = twMerge(`
251
253
  ${AlertConfirmPromptConfig.preset.menu}
252
- ${AlertConfirmPromptConfig.classMenu}
254
+ ${AlertConfirmPromptConfig.class.menu}
253
255
  ${dialog?.class?.menu || ""}
254
256
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.menu || ""}
255
257
  `);
@@ -260,14 +262,14 @@ $:
260
262
  $:
261
263
  _buttonClass = twMerge(`
262
264
  ${AlertConfirmPromptConfig.preset.button}
263
- ${AlertConfirmPromptConfig.classButton}
265
+ ${AlertConfirmPromptConfig.class.button}
264
266
  ${dialog?.class?.button || ""}
265
267
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.button || ""}
266
268
  `);
267
269
  $:
268
270
  _spinnerBoxClass = twMerge(`
269
271
  ${AlertConfirmPromptConfig.preset.spinnerBox}
270
- ${AlertConfirmPromptConfig.classSpinnerBox}
272
+ ${AlertConfirmPromptConfig.class.spinnerBox}
271
273
  ${dialog?.class?.spinnerBox || ""}
272
274
  ${AlertConfirmPromptConfig.variant?.[dialog?.variant]?.spinnerBox || ""}
273
275
  `);
@@ -14,16 +14,18 @@ export declare class AlertConfirmPromptConfig {
14
14
  button: string;
15
15
  spinnerBox: string;
16
16
  };
17
- static classDialog: string;
18
- static classIcon: string;
19
- static classTitle: string;
20
- static classContentBlock: string;
21
- static classContent: string;
22
- static classInputBox: string;
23
- static classInputField: string;
24
- static classMenu: string;
25
- static classButton: string;
26
- static classSpinnerBox: string;
17
+ static class: {
18
+ dialog: string;
19
+ icon: string;
20
+ contentBlock: string;
21
+ title: string;
22
+ content: string;
23
+ inputBox: string;
24
+ inputField: string;
25
+ menu: string;
26
+ button: string;
27
+ spinnerBox: string;
28
+ };
27
29
  static variant: {
28
30
  info: {
29
31
  dialog: string;
@@ -81,7 +83,7 @@ export declare class AlertConfirmPromptConfig {
81
83
  error: undefined;
82
84
  spinner: undefined;
83
85
  };
84
- static forceAsHtml: undefined;
86
+ static forceAsHtml: boolean | undefined;
85
87
  }
86
88
  declare const __propDef: {
87
89
  props: {
@@ -1,3 +1,4 @@
1
+ export declare function iconFeatherAlertTriangle(props: any): string;
1
2
  export declare const acpDefaultIcons: {
2
3
  info: () => string;
3
4
  success: () => string;
@@ -1,23 +1,5 @@
1
1
  // taken from @marianmeres/icons-fns
2
- function iconFeatherAlertCircle(props) {
3
- // Backward compatible signature support: fn(cls, size, style)
4
- if (props === null || props === undefined)
5
- props = {};
6
- if (typeof props !== 'object')
7
- props = { class: props || '' };
8
- if (arguments.length > 1)
9
- props.size ??= arguments[1];
10
- if (arguments.length > 2)
11
- props.style ??= arguments[2];
12
- //
13
- const { size, class: cls, style, strokeWidth } = props;
14
- let attrs = Object.entries(props)
15
- .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
16
- .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
17
- .join(' ');
18
- return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>`;
19
- }
20
- function iconFeatherCheckCircle(props) {
2
+ export function iconFeatherAlertTriangle(props) {
21
3
  // Backward compatible signature support: fn(cls, size, style)
22
4
  if (props === null || props === undefined)
23
5
  props = {};
@@ -33,9 +15,9 @@ function iconFeatherCheckCircle(props) {
33
15
  .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
34
16
  .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
35
17
  .join(' ');
36
- return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>`;
18
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>`;
37
19
  }
38
- function iconFeatherHelpCircle(props) {
20
+ function iconFeatherAlertCircle(props) {
39
21
  // Backward compatible signature support: fn(cls, size, style)
40
22
  if (props === null || props === undefined)
41
23
  props = {};
@@ -51,9 +33,9 @@ function iconFeatherHelpCircle(props) {
51
33
  .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
52
34
  .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
53
35
  .join(' ');
54
- return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>`;
36
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>`;
55
37
  }
56
- function iconFeatherXCircle(props) {
38
+ function iconFeatherCheckCircle(props) {
57
39
  // Backward compatible signature support: fn(cls, size, style)
58
40
  if (props === null || props === undefined)
59
41
  props = {};
@@ -69,7 +51,7 @@ function iconFeatherXCircle(props) {
69
51
  .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
70
52
  .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
71
53
  .join(' ');
72
- return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>`;
54
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>`;
73
55
  }
74
56
  function iconFeatherXOctagon(props) {
75
57
  // Backward compatible signature support: fn(cls, size, style)
@@ -128,7 +110,7 @@ function iconFeatherRotateCw(props) {
128
110
  export const acpDefaultIcons = {
129
111
  info: () => iconFeatherInfo({}),
130
112
  success: () => iconFeatherCheckCircle({}),
131
- warn: () => iconFeatherAlertCircle({}),
113
+ warn: () => iconFeatherAlertTriangle({}),
132
114
  error: () => iconFeatherXOctagon({}),
133
115
  spinner: () => iconFeatherRotateCw({ size: 32, class: 'opacity-50' }),
134
116
  };
@@ -0,0 +1,316 @@
1
+ <script context="module">import { fade } from "svelte/transition";
2
+ import { twMerge } from "tailwind-merge";
3
+ import Thc from "../Thc/Thc.svelte";
4
+ import X from "../X/X.svelte";
5
+ import { notificationsDefaultIcons } from "./notifications-icons.js";
6
+ import { createClog } from "@marianmeres/clog";
7
+ const X_POSITIONS = ["left", "center", "right"];
8
+ const Y_POSITIONS = ["top", "center", "bottom"];
9
+ const DEFAULT = {
10
+ posX: "center",
11
+ posXMobile: "center",
12
+ posY: "top",
13
+ posYMobile: "bottom"
14
+ };
15
+ const XMAP = { left: "sm:items-start", center: "sm:items-center", right: "sm:items-end" };
16
+ const XMAP_M = { left: "items-start", center: "items-center", right: "items-end" };
17
+ const YMAP = { top: "sm:items-start", center: "sm:items-center", bottom: "sm:items-end" };
18
+ const YMAP_M = { top: "items-start", center: "items-center", bottom: "items-end" };
19
+ export class NotificationsConfig {
20
+ static preset = {
21
+ wrap: `
22
+ fixed z-10
23
+ `,
24
+ wrapInner: `
25
+ p-4 space-y-4
26
+ `,
27
+ notification: {
28
+ box: `
29
+ relative flex
30
+ pointer-events-auto
31
+ w-full max-w-lg
32
+ rounded-md
33
+ shadow-lg
34
+ bg-gray-700 text-white
35
+ `,
36
+ count: `
37
+ absolute -top-2 -right-2
38
+ w-auto h-auto
39
+ flex items-center justify-center
40
+ px-2 py-1 rounded-full
41
+ leading-none text-xs
42
+ bg-black text-white
43
+ `,
44
+ icon: `
45
+ flex items-start justify-center
46
+ pt-4 pr-0 pb-4 pl-4
47
+ text-gray-200
48
+ `,
49
+ content: `
50
+ flex-1
51
+ flex flex-col justify-center
52
+ text-sm
53
+ pl-4 pr-1 py-3
54
+ `,
55
+ button: `
56
+ flex flex-col items-center justify-center
57
+ leading-none
58
+ px-3
59
+ hover:bg-black/20
60
+ group
61
+ rounded-tr-md rounded-br-md
62
+ `,
63
+ x: `
64
+ opacity-75 group-hover:opacity-100
65
+ `
66
+ }
67
+ };
68
+ static presetByType = {
69
+ info: {
70
+ box: ``,
71
+ count: ``,
72
+ icon: ``,
73
+ content: ``,
74
+ button: ``,
75
+ x: ``
76
+ },
77
+ success: {
78
+ box: ``,
79
+ // e.g. bg-green-800
80
+ count: ``,
81
+ icon: ``,
82
+ content: ``,
83
+ button: ``,
84
+ x: ``
85
+ },
86
+ warn: {
87
+ box: ``,
88
+ // e.g. bg-yellow-800
89
+ count: ``,
90
+ icon: ``,
91
+ content: ``,
92
+ button: ``,
93
+ x: ``
94
+ },
95
+ error: {
96
+ box: `bg-red-700`,
97
+ // e.g. bg-red-800
98
+ count: ``,
99
+ icon: ``,
100
+ content: ``,
101
+ button: ``,
102
+ x: ``
103
+ }
104
+ };
105
+ static class = {
106
+ wrap: "",
107
+ wrapInner: "",
108
+ notification: {
109
+ box: ``,
110
+ count: ``,
111
+ icon: ``,
112
+ content: ``,
113
+ button: ``,
114
+ x: ``
115
+ }
116
+ };
117
+ static classByType = {
118
+ info: {
119
+ box: ``,
120
+ count: ``,
121
+ icon: ``,
122
+ content: ``,
123
+ button: ``,
124
+ x: ``
125
+ },
126
+ success: {
127
+ box: ``,
128
+ count: ``,
129
+ icon: ``,
130
+ content: ``,
131
+ button: ``,
132
+ x: ``
133
+ },
134
+ warn: {
135
+ box: ``,
136
+ count: ``,
137
+ icon: ``,
138
+ content: ``,
139
+ button: ``,
140
+ x: ``
141
+ },
142
+ error: {
143
+ box: ``,
144
+ count: ``,
145
+ icon: ``,
146
+ content: ``,
147
+ button: ``,
148
+ x: ``
149
+ }
150
+ };
151
+ static iconFn = {
152
+ info: void 0,
153
+ success: void 0,
154
+ warn: void 0,
155
+ error: void 0
156
+ };
157
+ // conveniently hoisted THC option, since all content is rendered via THC
158
+ static forceAsHtml = void 0;
159
+ }
160
+ </script>
161
+
162
+ <script>const clog = createClog("Notifications");
163
+ export let notifications;
164
+ let _class = "";
165
+ export { _class as class };
166
+ export let ariaCloseLabel = "Discard";
167
+ export let posX = DEFAULT.posX;
168
+ export let posXMobile = DEFAULT.posXMobile;
169
+ export let posY = DEFAULT.posY;
170
+ export let posYMobile = DEFAULT.posYMobile;
171
+ let x, y, xMobile, yMobile;
172
+ $:
173
+ x = X_POSITIONS.includes(posX) ? posX : DEFAULT.posX;
174
+ $:
175
+ xMobile = X_POSITIONS.includes(posXMobile) ? posXMobile : DEFAULT.posXMobile;
176
+ $:
177
+ y = Y_POSITIONS.includes(posY) ? posY : DEFAULT.posY;
178
+ $:
179
+ yMobile = Y_POSITIONS.includes(posYMobile) ? posYMobile : DEFAULT.posYMobile;
180
+ $:
181
+ _wrapClass = twMerge(`
182
+ ${NotificationsConfig.preset.wrap}
183
+ ${NotificationsConfig.class.wrap}
184
+ flex flex-row inset-0
185
+ pointer-events-none bg-transparent
186
+ ${YMAP_M[yMobile]} ${YMAP[y]}
187
+ `);
188
+ $:
189
+ _wrapInnerClass = twMerge(`
190
+ ${NotificationsConfig.preset.wrapInner}
191
+ ${NotificationsConfig.class.wrapInner}
192
+ flex flex-col w-full
193
+ pointer-events-none bg-transparent
194
+ ${XMAP_M[xMobile]} ${XMAP[x]}
195
+ `);
196
+ const _boxClass = (n) => twMerge(`
197
+ ${NotificationsConfig?.preset?.notification?.box}
198
+ ${NotificationsConfig?.class?.notification?.box}
199
+ ${NotificationsConfig?.presetByType?.[n.type]?.box || ""}
200
+ ${NotificationsConfig?.classByType?.[n.type]?.box || ""}
201
+ ${n.class?.box || ""}
202
+ `);
203
+ const _countClass = (n) => twMerge(`
204
+ ${NotificationsConfig?.preset?.notification?.count}
205
+ ${NotificationsConfig?.class?.notification?.count}
206
+ ${NotificationsConfig?.presetByType?.[n.type]?.count || ""}
207
+ ${NotificationsConfig?.classByType?.[n.type]?.count || ""}
208
+ ${n.class?.count || ""}
209
+ `);
210
+ const _iconClass = (n) => twMerge(`
211
+ ${NotificationsConfig?.preset?.notification?.icon}
212
+ ${NotificationsConfig?.class?.notification?.icon}
213
+ ${NotificationsConfig?.presetByType?.[n.type]?.icon || ""}
214
+ ${NotificationsConfig?.classByType?.[n.type]?.icon || ""}
215
+ ${n.class?.icon || ""}
216
+ `);
217
+ const _contentClass = (n) => twMerge(`
218
+ ${NotificationsConfig?.preset?.notification?.content}
219
+ ${NotificationsConfig?.class?.notification?.content}
220
+ ${NotificationsConfig?.presetByType?.[n.type]?.content || ""}
221
+ ${NotificationsConfig?.classByType?.[n.type]?.content || ""}
222
+ ${n.class?.content || ""}
223
+ `);
224
+ const _buttonClass = (n) => twMerge(`
225
+ ${NotificationsConfig?.preset?.notification?.button}
226
+ ${NotificationsConfig?.class?.notification?.button}
227
+ ${NotificationsConfig?.presetByType?.[n.type]?.button || ""}
228
+ ${NotificationsConfig?.classByType?.[n.type]?.button || ""}
229
+ ${n.class?.button || ""}
230
+ `);
231
+ const _xClass = (n) => twMerge(`
232
+ ${NotificationsConfig?.preset?.notification?.x}
233
+ ${NotificationsConfig?.class?.notification?.x}
234
+ ${NotificationsConfig?.presetByType?.[n.type]?.x || ""}
235
+ ${NotificationsConfig?.classByType?.[n.type]?.x || ""}
236
+ ${n.class?.x || ""}
237
+ `);
238
+ const _iconFn = (n) => {
239
+ let iconFn = false;
240
+ if (n?.iconFn === true) {
241
+ iconFn = // either runtime config
242
+ NotificationsConfig.iconFn[n?.type] || // or fixed default
243
+ notificationsDefaultIcons[n?.type];
244
+ } else if (n?.iconFn) {
245
+ iconFn = n.iconFn;
246
+ } else {
247
+ iconFn = false;
248
+ }
249
+ return iconFn;
250
+ };
251
+ const _isFn = (v) => typeof v === "function";
252
+ </script>
253
+
254
+ <!-- {#if $notifications.length} -->
255
+ <div class={_wrapClass} aria-live="assertive">
256
+ <div class={_wrapInnerClass}>
257
+ {#if $notifications.length}
258
+ {#each $notifications as n}
259
+ {@const iconFn = _iconFn(n)}
260
+ <!-- use your own component -->
261
+ {#if n?.component}
262
+ <svelte:component
263
+ this={n.component.component || n.component}
264
+ {...n.component.props || {}}
265
+ notification={n}
266
+ {notifications}
267
+ />
268
+ {:else}
269
+ <!-- svelte-ignore
270
+ a11y-click-events-have-key-events
271
+ a11y-no-noninteractive-element-interactions
272
+ a11y-mouse-events-have-key-events -->
273
+ <div
274
+ transition:fade|global={{ duration: 200 }}
275
+ class={_boxClass(n)}
276
+ class:cursor-pointer={typeof n.onClick === 'function'}
277
+ data-notification-type={n.type}
278
+ data-notification-multiple={n.count > 1 ? true : undefined}
279
+ role="alert"
280
+ on:mouseover={() => notifications.event(n.id, notifications.EVENT.MOUSEOVER)}
281
+ on:mouseout={() => notifications.event(n.id, notifications.EVENT.MOUSEOUT)}
282
+ on:click={() => notifications.event(n.id, notifications.EVENT.CLICK)}
283
+ >
284
+ {#if n.count > 1}
285
+ <div class={_countClass(n)}>
286
+ {n.count}
287
+ </div>
288
+ {/if}
289
+
290
+ {#if _isFn(iconFn)}
291
+ <div class={_iconClass(n)}>{@html iconFn()}</div>
292
+ {/if}
293
+
294
+ <div class={_contentClass(n)}>
295
+ <Thc
296
+ thc={n.content}
297
+ forceAsHtml={n.forceAsHtml ?? NotificationsConfig.forceAsHtml}
298
+ notification={n}
299
+ {notifications}
300
+ />
301
+ </div>
302
+
303
+ <button
304
+ class={_buttonClass(n)}
305
+ aria-label={ariaCloseLabel}
306
+ on:click|preventDefault|stopPropagation={() => notifications.remove(n.id)}
307
+ >
308
+ <X class={_xClass(n)} />
309
+ </button>
310
+ </div>
311
+ {/if}
312
+ {/each}
313
+ {/if}
314
+ </div>
315
+ </div>
316
+ <!-- {/if} -->
@@ -0,0 +1,61 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { NotificationType, createNotificationsStore } from './notifications.js';
3
+ interface NotifClasses {
4
+ box: string;
5
+ count: string;
6
+ icon: string;
7
+ content: string;
8
+ button: string;
9
+ x: string;
10
+ }
11
+ export declare class NotificationsConfig {
12
+ static preset: {
13
+ wrap: string;
14
+ wrapInner: string;
15
+ notification: {
16
+ box: string;
17
+ count: string;
18
+ icon: string;
19
+ content: string;
20
+ button: string;
21
+ x: string;
22
+ };
23
+ };
24
+ static presetByType: Record<NotificationType, NotifClasses>;
25
+ static class: {
26
+ wrap: string;
27
+ wrapInner: string;
28
+ notification: {
29
+ box: string;
30
+ count: string;
31
+ icon: string;
32
+ content: string;
33
+ button: string;
34
+ x: string;
35
+ };
36
+ };
37
+ static classByType: Record<NotificationType, NotifClasses>;
38
+ static iconFn: Record<NotificationType, undefined | (() => string)>;
39
+ static forceAsHtml: boolean | undefined;
40
+ }
41
+ declare const __propDef: {
42
+ props: {
43
+ notifications: ReturnType<typeof createNotificationsStore>;
44
+ class?: string | undefined;
45
+ ariaCloseLabel?: string | undefined;
46
+ posX?: "center" | "left" | "right" | undefined;
47
+ posXMobile?: "center" | "left" | "right" | undefined;
48
+ posY?: "center" | "top" | "bottom" | undefined;
49
+ posYMobile?: "center" | "top" | "bottom" | undefined;
50
+ };
51
+ events: {
52
+ [evt: string]: CustomEvent<any>;
53
+ };
54
+ slots: {};
55
+ };
56
+ export type NotificationsProps = typeof __propDef.props;
57
+ export type NotificationsEvents = typeof __propDef.events;
58
+ export type NotificationsSlots = typeof __propDef.slots;
59
+ export default class Notifications extends SvelteComponent<NotificationsProps, NotificationsEvents, NotificationsSlots> {
60
+ }
61
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { NotificationType } from './notifications.js';
2
+ export declare function iconFeatherAlertTriangle(props: any): string;
3
+ export declare const notificationsDefaultIcons: Record<NotificationType, () => string>;
@@ -0,0 +1,115 @@
1
+ // taken from @marianmeres/icons-fns
2
+ export function iconFeatherAlertTriangle(props) {
3
+ // Backward compatible signature support: fn(cls, size, style)
4
+ if (props === null || props === undefined)
5
+ props = {};
6
+ if (typeof props !== 'object')
7
+ props = { class: props || '' };
8
+ if (arguments.length > 1)
9
+ props.size ??= arguments[1];
10
+ if (arguments.length > 2)
11
+ props.style ??= arguments[2];
12
+ //
13
+ const { size, class: cls, style, strokeWidth } = props;
14
+ let attrs = Object.entries(props)
15
+ .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
16
+ .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
17
+ .join(' ');
18
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>`;
19
+ }
20
+ function iconFeatherAlertCircle(props) {
21
+ // Backward compatible signature support: fn(cls, size, style)
22
+ if (props === null || props === undefined)
23
+ props = {};
24
+ if (typeof props !== 'object')
25
+ props = { class: props || '' };
26
+ if (arguments.length > 1)
27
+ props.size ??= arguments[1];
28
+ if (arguments.length > 2)
29
+ props.style ??= arguments[2];
30
+ //
31
+ const { size, class: cls, style, strokeWidth } = props;
32
+ let attrs = Object.entries(props)
33
+ .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
34
+ .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
35
+ .join(' ');
36
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>`;
37
+ }
38
+ function iconFeatherCheckCircle(props) {
39
+ // Backward compatible signature support: fn(cls, size, style)
40
+ if (props === null || props === undefined)
41
+ props = {};
42
+ if (typeof props !== 'object')
43
+ props = { class: props || '' };
44
+ if (arguments.length > 1)
45
+ props.size ??= arguments[1];
46
+ if (arguments.length > 2)
47
+ props.style ??= arguments[2];
48
+ //
49
+ const { size, class: cls, style, strokeWidth } = props;
50
+ let attrs = Object.entries(props)
51
+ .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
52
+ .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
53
+ .join(' ');
54
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>`;
55
+ }
56
+ function iconFeatherXOctagon(props) {
57
+ // Backward compatible signature support: fn(cls, size, style)
58
+ if (props === null || props === undefined)
59
+ props = {};
60
+ if (typeof props !== 'object')
61
+ props = { class: props || '' };
62
+ if (arguments.length > 1)
63
+ props.size ??= arguments[1];
64
+ if (arguments.length > 2)
65
+ props.style ??= arguments[2];
66
+ //
67
+ const { size, class: cls, style, strokeWidth } = props;
68
+ let attrs = Object.entries(props)
69
+ .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
70
+ .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
71
+ .join(' ');
72
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>`;
73
+ }
74
+ function iconFeatherInfo(props) {
75
+ // Backward compatible signature support: fn(cls, size, style)
76
+ if (props === null || props === undefined)
77
+ props = {};
78
+ if (typeof props !== 'object')
79
+ props = { class: props || '' };
80
+ if (arguments.length > 1)
81
+ props.size ??= arguments[1];
82
+ if (arguments.length > 2)
83
+ props.style ??= arguments[2];
84
+ //
85
+ const { size, class: cls, style, strokeWidth } = props;
86
+ let attrs = Object.entries(props)
87
+ .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
88
+ .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
89
+ .join(' ');
90
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>`;
91
+ }
92
+ function iconFeatherRotateCw(props) {
93
+ // Backward compatible signature support: fn(cls, size, style)
94
+ if (props === null || props === undefined)
95
+ props = {};
96
+ if (typeof props !== 'object')
97
+ props = { class: props || '' };
98
+ if (arguments.length > 1)
99
+ props.size ??= arguments[1];
100
+ if (arguments.length > 2)
101
+ props.style ??= arguments[2];
102
+ //
103
+ const { size, class: cls, style, strokeWidth } = props;
104
+ let attrs = Object.entries(props)
105
+ .filter(([k, v]) => !/^class|size|style|strokeWidth$/.test(k))
106
+ .reduce((m, [k, v]) => [...m, `${k}="${v}"`], [])
107
+ .join(' ');
108
+ return `<svg ${style ? `style="${style}" ` : ''}${cls ? `class="${cls}" ` : ''}width="${size || 24}" height="${size || 24}" stroke-width="${strokeWidth ?? 2}" ${attrs ? `${attrs} ` : ''}viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>`;
109
+ }
110
+ export const notificationsDefaultIcons = {
111
+ info: () => iconFeatherInfo({}),
112
+ success: () => iconFeatherCheckCircle({}),
113
+ warn: () => iconFeatherAlertTriangle({}),
114
+ error: () => iconFeatherXOctagon({}),
115
+ };
@@ -0,0 +1,79 @@
1
+ import type { THC } from '../Thc/Thc.svelte';
2
+ export type NotificationsSortOrder = 'asc' | 'desc';
3
+ export type NotificationOnEventHandler = (eventName: string, self: Notification, all: Notification[], data: any) => void;
4
+ export type NotificationType = 'info' | 'success' | 'warn' | 'error' | string;
5
+ interface ComponentWrap {
6
+ component: Function;
7
+ props?: any;
8
+ }
9
+ export interface NotificationInput extends Record<string, any> {
10
+ id: any;
11
+ type: NotificationType;
12
+ content: THC;
13
+ on: NotificationOnEventHandler;
14
+ onClick: (self: Notification, all: Notification[], data: any) => void;
15
+ ttl: number;
16
+ count: number;
17
+ component?: Function | ComponentWrap;
18
+ iconFn: (() => string) | boolean;
19
+ class?: Partial<{
20
+ box: string;
21
+ count: string;
22
+ icon: string;
23
+ content: string;
24
+ button: string;
25
+ x: string;
26
+ }>;
27
+ forceAsHtml: boolean | undefined;
28
+ }
29
+ export interface Notification extends NotificationInput {
30
+ created: Date;
31
+ }
32
+ export type NotificationCreateParam = string | Partial<NotificationInput>;
33
+ export interface NotiticationsCreateStoreOptions {
34
+ maxCapacity: number;
35
+ defaultType: string;
36
+ defaultTtl: number;
37
+ sortOrder?: NotificationsSortOrder;
38
+ defaultIcons?: Record<NotificationType, () => string> | boolean;
39
+ logger: (...v: any) => void;
40
+ }
41
+ export declare const NOTIFICATION_EVENT: {
42
+ CLICK: string;
43
+ CREATE: string;
44
+ REMOVE: string;
45
+ AUTO_DISPOSE: string;
46
+ MOUSEOVER: string;
47
+ MOUSEOUT: string;
48
+ };
49
+ export declare const createNotificationsStore: (initial?: NotificationCreateParam[], opts?: Partial<NotiticationsCreateStoreOptions>) => {
50
+ subscribe: (cb: CallableFunction) => () => void;
51
+ get: () => Notification[];
52
+ add: (notif: NotificationCreateParam[] | NotificationCreateParam) => void;
53
+ event: (id: string, eventName: string, data?: any) => boolean;
54
+ find: (id: string) => Notification | null;
55
+ remove: (id: string) => boolean;
56
+ options: {
57
+ maxCapacity?: number | undefined;
58
+ defaultType?: string | undefined;
59
+ defaultTtl?: number | undefined;
60
+ sortOrder?: NotificationsSortOrder | undefined;
61
+ defaultIcons?: boolean | Record<string, () => string> | undefined;
62
+ logger?: ((...v: any) => void) | undefined;
63
+ };
64
+ EVENT: {
65
+ CLICK: string;
66
+ CREATE: string;
67
+ REMOVE: string;
68
+ AUTO_DISPOSE: string;
69
+ MOUSEOVER: string;
70
+ MOUSEOUT: string;
71
+ };
72
+ setMaxCapacity: (v: number) => void;
73
+ setSortOrder: (v: string) => void;
74
+ info: (content: THC, n?: Partial<NotificationInput>) => void;
75
+ success: (content: THC, n?: Partial<NotificationInput>) => void;
76
+ warn: (content: THC, n?: Partial<NotificationInput>) => void;
77
+ error: (content: THC, n?: Partial<NotificationInput>) => void;
78
+ };
79
+ export {};
@@ -0,0 +1,225 @@
1
+ import { createClog } from '@marianmeres/clog';
2
+ import { createStore } from '@marianmeres/store';
3
+ import { createTicker } from '@marianmeres/ticker';
4
+ const isFn = (v) => typeof v === 'function';
5
+ const DEFAULT_OPTIONS = {
6
+ maxCapacity: 5,
7
+ defaultTtl: 10,
8
+ defaultType: 'info',
9
+ sortOrder: 'asc',
10
+ defaultIcons: true,
11
+ logger: createClog('notifications'),
12
+ };
13
+ export const NOTIFICATION_EVENT = {
14
+ CLICK: 'click',
15
+ CREATE: 'create',
16
+ // `remove` programatically, or e.g. by clicking on X
17
+ REMOVE: 'remove',
18
+ // triggered when auto disposed by ttl expiration
19
+ AUTO_DISPOSE: 'auto_dispose',
20
+ // usefull for detecting interacion (so internally may notify as "seen")
21
+ MOUSEOVER: 'mouseover',
22
+ MOUSEOUT: 'mouseout',
23
+ };
24
+ // https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
25
+ const _strHash = (str) => `${str || ''}`.split('').reduce((a, b) => {
26
+ a = (a << 5) - a + b.charCodeAt(0);
27
+ return a & a;
28
+ }, 0);
29
+ const _id = (type, content) => {
30
+ const str = content?.component
31
+ ? 'component'
32
+ : content?.html || content?.text || content;
33
+ return ['id', type, _strHash(str)].join('-');
34
+ };
35
+ export const createNotificationsStore = (initial = [], opts = {}) => {
36
+ if (!Array.isArray(initial))
37
+ initial = [initial];
38
+ // merge provided with defaults
39
+ opts = { ...DEFAULT_OPTIONS, ...(opts || {}) };
40
+ const _log = (...args) => isFn(opts.logger) && opts.logger.apply(null, [...args]);
41
+ const _setOption = (k, v) => {
42
+ // _log(`INFO: setting option '${k} = ${v}'`);
43
+ if (/^maxCapacity|defaultTtl$/.test(k)) {
44
+ v = parseInt(v, 10);
45
+ if (isNaN(v) || v < 0) {
46
+ _log(`WARN: invalid '${k}' option, falling back to default`);
47
+ opts[k] = DEFAULT_OPTIONS[k];
48
+ }
49
+ else {
50
+ opts[k] = v;
51
+ }
52
+ }
53
+ else {
54
+ opts[k] = v;
55
+ }
56
+ };
57
+ // sanitize options
58
+ ['maxCapacity', 'defaultTtl'].forEach((k) => _setOption(k, opts[k]));
59
+ const _factory = (notification) => {
60
+ let notif = typeof notification === 'string' ? { id: 0, content: notification } : notification;
61
+ // ignore invalid (empty) notifs
62
+ if (!notif.content) {
63
+ _log(`WARN: ignoring empty notification`);
64
+ return null;
65
+ }
66
+ notif.type ||= opts.defaultType; //
67
+ notif.id ||= _id(notif.type, [notif.content].join());
68
+ notif.created = new Date(notif.created || Date.now());
69
+ notif.count ??= 1;
70
+ //
71
+ if (notif.ttl === undefined)
72
+ notif.ttl = opts.defaultTtl;
73
+ //
74
+ if (notif.iconFn === undefined) {
75
+ if (typeof opts.defaultIcons === 'boolean') {
76
+ notif.iconFn = opts.defaultIcons;
77
+ }
78
+ else {
79
+ notif.iconFn = opts.defaultIcons?.[notif.type];
80
+ }
81
+ }
82
+ return notif;
83
+ };
84
+ const _findIndexById = (notifs, id) => notifs.findIndex((n) => n.id === id);
85
+ const _removeByIdx = (notifs, idx) => {
86
+ if (idx > -1) {
87
+ notifs.splice(idx, 1);
88
+ notifs = [...notifs];
89
+ }
90
+ return notifs;
91
+ };
92
+ const _add = (notifs, notification, onAddHook = null) => {
93
+ const notif = _factory(notification);
94
+ // return early on invalid
95
+ if (!notif)
96
+ return notifs;
97
+ const _isDesc = opts.sortOrder === 'desc';
98
+ const idx = _findIndexById(notifs, notif.id);
99
+ if (idx > -1) {
100
+ notifs[idx].count++;
101
+ notifs[idx].created = new Date(Math.max(notifs[idx].created.valueOf(), notif.created.valueOf()));
102
+ }
103
+ else {
104
+ notifs.push(notif);
105
+ notifs.sort((a, b) => {
106
+ let _a = a.created.valueOf();
107
+ let _b = b.created.valueOf();
108
+ return _isDesc ? _b - _a : _a - _b;
109
+ });
110
+ }
111
+ if (isFn(onAddHook))
112
+ onAddHook?.(notif);
113
+ // keep only `maxCapacity` in the queue
114
+ if (opts.maxCapacity && notifs.length > opts.maxCapacity) {
115
+ notifs = _isDesc
116
+ ? notifs.slice(0, opts.maxCapacity)
117
+ : notifs.slice(-1 * opts.maxCapacity);
118
+ }
119
+ return [...notifs];
120
+ };
121
+ // main internal store
122
+ let notifs = [];
123
+ initial.forEach((n) => (notifs = _add(notifs, n)));
124
+ const _store = createStore(notifs);
125
+ // auto dispose feature
126
+ const ticker = createTicker(1_000);
127
+ const _tickerInit = () => {
128
+ const _tickerUnsub = ticker.start().subscribe((ts) => {
129
+ if (ts) {
130
+ const { disposed, kept } = _store.get().reduce((memo, n) => {
131
+ if (n.ttl) {
132
+ const expiry = n.created.valueOf() + n.ttl * 1000;
133
+ expiry >= Date.now() ? memo.kept.push(n) : memo.disposed.push(n);
134
+ }
135
+ else {
136
+ memo.kept.push(n);
137
+ }
138
+ return memo;
139
+ }, { disposed: [], kept: [] });
140
+ if (disposed.length) {
141
+ disposed.forEach((n) => event(n.id, NOTIFICATION_EVENT.AUTO_DISPOSE));
142
+ _store.set(kept);
143
+ }
144
+ }
145
+ });
146
+ return () => {
147
+ ticker.stop();
148
+ _tickerUnsub();
149
+ };
150
+ };
151
+ //
152
+ const findById = (id) => {
153
+ const notifs = _store.get();
154
+ const idx = _findIndexById(notifs, id);
155
+ return idx > -1 ? notifs[idx] : null;
156
+ };
157
+ const event = (id, eventName, data = null) => {
158
+ const n = findById(id);
159
+ if (n) {
160
+ if (isFn(n.on)) {
161
+ n.on(eventName, n, _store.get(), data);
162
+ }
163
+ if (eventName === NOTIFICATION_EVENT.CLICK && isFn(n.onClick)) {
164
+ n.onClick(n, _store.get(), data);
165
+ }
166
+ return true;
167
+ }
168
+ return false;
169
+ };
170
+ // we need to keep track of subscriptions count, so we can do the cleanup
171
+ let _subsCount = 0;
172
+ let _tickerDestroy;
173
+ const subscribe = (cb) => {
174
+ if (!_subsCount++)
175
+ _tickerDestroy = _tickerInit();
176
+ const unsub = _store.subscribe(cb);
177
+ return () => {
178
+ if (!--_subsCount)
179
+ _tickerDestroy();
180
+ unsub();
181
+ };
182
+ };
183
+ const add = (notif) => {
184
+ if (!Array.isArray(notif))
185
+ notif = [notif];
186
+ let notifs = _store.get();
187
+ notif.forEach((n) => (notifs = _add(notifs, n, (_n) => event(_n.id, NOTIFICATION_EVENT.CREATE))));
188
+ _store.set(notifs);
189
+ };
190
+ return {
191
+ subscribe,
192
+ //
193
+ get: () => _store.get(),
194
+ //
195
+ add,
196
+ //
197
+ event,
198
+ //
199
+ find: findById,
200
+ //
201
+ remove: (id) => {
202
+ let notifs = _store.get();
203
+ const idx = _findIndexById(notifs, id);
204
+ if (idx > -1) {
205
+ const notif = notifs[idx];
206
+ event(id, NOTIFICATION_EVENT.REMOVE);
207
+ _store.set(_removeByIdx(notifs, idx));
208
+ return true;
209
+ }
210
+ return false;
211
+ },
212
+ //
213
+ options: { ...opts },
214
+ //
215
+ EVENT: NOTIFICATION_EVENT,
216
+ // some options setters (for playground mostly)
217
+ setMaxCapacity: (v) => _setOption('maxCapacity', v),
218
+ setSortOrder: (v) => _setOption('sortOrder', v),
219
+ // sugar
220
+ info: (content, n) => add({ ...(n || {}), type: 'info', content }),
221
+ success: (content, n) => add({ ...(n || {}), type: 'success', content }),
222
+ warn: (content, n) => add({ ...(n || {}), type: 'warn', content }),
223
+ error: (content, n) => add({ ...(n || {}), type: 'error', content }),
224
+ };
225
+ };
@@ -11,8 +11,8 @@ export class SwitchConfig {
11
11
  hover:brightness-[1.05] active:brightness-[0.95]
12
12
  disabled:!cursor-not-allowed disabled:!opacity-50 disabled:hover:brightness-100
13
13
 
14
- bg-zinc-300 dark:bg-zinc-700
15
- data-[checked=true]:bg-zinc-700 dark:data-[checked=true]:bg-zinc-300
14
+ bg-gray-400 dark:bg-gray-400
15
+ data-[checked=true]:bg-stuic-primary dark:data-[checked=true]:bg-stuic-primary
16
16
  `.trim();
17
17
  static presetsSize = {
18
18
  xs: "h-4 w-7",
@@ -30,8 +30,8 @@ export class SwitchConfig {
30
30
  translate-x-1 rounded-full
31
31
  transition-all duration-100
32
32
  shadow
33
- bg-white dark:bg-black
34
- text-black dark:text-white
33
+ bg-white dark:bg-white
34
+ text-black dark:text-black
35
35
  `.trim();
36
36
  static presetsSizeDot = {
37
37
  // size + translate-x = width
@@ -11,7 +11,7 @@ export let thc;
11
11
  {:else if thc?.html}
12
12
  {@html thc.html}
13
13
  {:else if thc?.component}
14
- <svelte:component this={thc.component} {...thc?.props || {}} />
14
+ <svelte:component this={thc.component} {...thc?.props || {}} {...$$restProps || {}} />
15
15
  {:else}
16
16
  <!-- cast to string as the last resort -->
17
17
  {thc}
@@ -12,6 +12,7 @@ interface WithHtml {
12
12
  export type THC = string | WithText | WithHtml | WithComponent;
13
13
  declare const __propDef: {
14
14
  props: {
15
+ [x: string]: any;
15
16
  forceAsHtml?: boolean | undefined;
16
17
  thc: THC;
17
18
  };
@@ -1,16 +1,15 @@
1
1
  <script>import { twMerge } from "tailwind-merge";
2
2
  let _class = "";
3
3
  export { _class as class };
4
- export let size = "1rem";
4
+ export let strokeWidth = 2;
5
5
  </script>
6
6
 
7
7
  <svg
8
8
  fill="none"
9
9
  viewBox="0 0 24 24"
10
- stroke-width="1.5"
10
+ stroke-width={strokeWidth}
11
11
  stroke="currentColor"
12
- style="width:{size};height:{size};"
13
- class={twMerge(`inline ${_class}`)}
12
+ class={twMerge(`inline size-6 ${_class}`)}
14
13
  >
15
14
  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
16
15
  </svg>
@@ -2,7 +2,7 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  class?: string | undefined;
5
- size?: string | undefined;
5
+ strokeWidth?: 1 | 2 | 3 | 4 | 1.5 | 2.5 | 3.5 | undefined;
6
6
  };
7
7
  events: {
8
8
  [evt: string]: CustomEvent<any>;
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ export { default as FieldCheckbox } from './components/Input/FieldCheckbox.svelt
13
13
  export { default as FieldRadios } from './components/Input/FieldRadios.svelte';
14
14
  export { default as FieldSelect } from './components/Input/FieldSelect.svelte';
15
15
  export { default as Fieldset } from './components/Input/Fieldset.svelte';
16
+ export { createNotificationsStore, NOTIFICATION_EVENT, type NotiticationsCreateStoreOptions, type NotificationCreateParam, type Notification, type NotificationInput, type NotificationType, type NotificationOnEventHandler, type NotificationsSortOrder, } from './components/Notifications/notifications.js';
17
+ export { default as Notifications } from './components/Notifications/Notifications.svelte';
16
18
  export { default as Popover } from './components/Popover/Popover.svelte';
17
19
  export { default as Switch, SwitchConfig } from './components/Switch/Switch.svelte';
18
20
  export { default as Thc } from './components/Thc/Thc.svelte';
package/dist/index.js CHANGED
@@ -23,6 +23,9 @@ export { default as FieldRadios } from './components/Input/FieldRadios.svelte';
23
23
  export { default as FieldSelect } from './components/Input/FieldSelect.svelte';
24
24
  export { default as Fieldset } from './components/Input/Fieldset.svelte';
25
25
  //
26
+ export { createNotificationsStore, NOTIFICATION_EVENT, } from './components/Notifications/notifications.js';
27
+ export { default as Notifications } from './components/Notifications/Notifications.svelte';
28
+ //
26
29
  export { default as Popover } from './components/Popover/Popover.svelte';
27
30
  //
28
31
  export { default as Switch, SwitchConfig } from './components/Switch/Switch.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "1.32.0",
3
+ "version": "1.34.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run package && node ./scripts/date.js",
@@ -65,6 +65,7 @@
65
65
  "@marianmeres/clog": "^1.0.1",
66
66
  "@marianmeres/store": "^1.5.0",
67
67
  "@marianmeres/switch-store": "^1.3.1",
68
+ "@marianmeres/ticker": "^1.5.0",
68
69
  "esm-env": "^1.0.0",
69
70
  "tailwind-merge": "^2.1.0"
70
71
  }