@rkosafo/cai.components 0.0.79 → 0.0.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/modal/Modal.svelte +97 -52
- package/dist/ui/pageLoader/PageLoader2.svelte +99 -0
- package/dist/ui/pageLoader/PageLoader2.svelte.d.ts +24 -0
- package/dist/ui/pageLoader/index.d.ts +2 -1
- package/dist/ui/pageLoader/index.js +2 -1
- package/dist/ui/toast/Toast.svelte +120 -281
- package/dist/ui/toast/Toast.svelte.d.ts +27 -52
- package/dist/ui/toast/index.d.ts +2 -1
- package/dist/ui/toast/index.js +1 -3
- package/package.json +1 -1
- package/dist/forms/FormSelect/index.d.ts +0 -1
- package/dist/forms/FormSelect/index.js +0 -1
- package/dist/forms/input/index.d.ts +0 -4
- package/dist/forms/input/index.js +0 -5
- package/dist/forms/input/theme.d.ts +0 -301
- package/dist/forms/input/theme.js +0 -100
- package/dist/layout/TF/Sidebar/Sidebar.svelte +0 -148
- package/dist/layout/TF/Sidebar/Sidebar.svelte.d.ts +0 -4
- package/dist/layout/TF/Sidebar/index.d.ts +0 -1
- package/dist/layout/TF/Sidebar/index.js +0 -1
- package/dist/layout/TF/Wrapper/index.d.ts +0 -1
- package/dist/layout/TF/Wrapper/index.js +0 -1
- package/dist/ui/accordion/theme.d.ts +0 -96
- package/dist/ui/accordion/theme.js +0 -59
- package/dist/ui/speedDial/SpeedDial.svelte +0 -77
- package/dist/ui/speedDial/SpeedDial.svelte.d.ts +0 -21
- package/dist/ui/tableLoader/TableLoader.svelte +0 -24
- package/dist/ui/tableLoader/TableLoader.svelte.d.ts +0 -13
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
let openModalCount = 0;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
1
5
|
<script lang="ts">
|
|
2
6
|
import clsx, { type ClassValue } from 'clsx';
|
|
3
7
|
import { sineIn } from 'svelte/easing';
|
|
@@ -64,57 +68,52 @@
|
|
|
64
68
|
close: closeCls
|
|
65
69
|
} = $derived(modalStyle({ placement, size }));
|
|
66
70
|
|
|
67
|
-
const close = (
|
|
68
|
-
// @ts-expect-error: dlg.requestClose may not be supported
|
|
69
|
-
const cancel = (dlg: HTMLDialogElement) =>
|
|
70
|
-
typeof dlg.requestClose === 'function' ? dlg.requestClose() : close();
|
|
71
|
-
|
|
72
|
-
function _oncancel(ev: Event & { currentTarget: HTMLDialogElement }) {
|
|
73
|
-
if (ev.target !== ev.currentTarget) {
|
|
74
|
-
return; // ignore if not on dialog
|
|
75
|
-
}
|
|
71
|
+
const close = () => (open = false);
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
// pressesed ESC key, clicked outside, pressed submit button with no 'value' like close button
|
|
73
|
+
function _oncancel(ev: Event) {
|
|
79
74
|
oncancel?.(ev);
|
|
80
75
|
if (ev.defaultPrevented) return;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (!permanent) close(ev.currentTarget);
|
|
76
|
+
ev.preventDefault();
|
|
77
|
+
if (!permanent) close();
|
|
84
78
|
}
|
|
85
79
|
|
|
86
|
-
function _onclick(ev:
|
|
87
|
-
const
|
|
88
|
-
if (outsideclose &&
|
|
89
|
-
|
|
80
|
+
function _onclick(ev: MouseEvent) {
|
|
81
|
+
const target = ev.target as HTMLElement;
|
|
82
|
+
if (outsideclose && target.dataset.modalBackdrop === 'true') {
|
|
83
|
+
const cancelEvent = new Event('cancel', { cancelable: true });
|
|
84
|
+
oncancel?.(cancelEvent);
|
|
85
|
+
if (!cancelEvent.defaultPrevented && !permanent) {
|
|
86
|
+
close();
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
if (autoclose &&
|
|
93
|
-
|
|
91
|
+
if (autoclose && target instanceof HTMLButtonElement && !permanent) {
|
|
92
|
+
close();
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
|
|
97
|
-
function _onsubmit(ev: SubmitEvent & { currentTarget:
|
|
96
|
+
function _onsubmit(ev: SubmitEvent & { currentTarget: HTMLFormElement }) {
|
|
98
97
|
// When dialog contains the <form method="dialog"> and when child with type="submit" was pressed
|
|
99
98
|
|
|
100
99
|
onsubmit?.(ev);
|
|
101
100
|
if (ev.defaultPrevented) return;
|
|
102
101
|
|
|
103
|
-
ev.preventDefault();
|
|
104
|
-
|
|
105
|
-
const dlg: HTMLDialogElement = ev.currentTarget;
|
|
102
|
+
ev.preventDefault();
|
|
103
|
+
const panel: HTMLElement = ref as HTMLElement;
|
|
106
104
|
|
|
107
105
|
if (ev.submitter instanceof HTMLButtonElement || ev.submitter instanceof HTMLInputElement) {
|
|
108
|
-
|
|
106
|
+
panel.dataset.returnValue = ev.submitter.value;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
const returnValue = panel.dataset.returnValue ?? '';
|
|
110
|
+
if (!returnValue) {
|
|
111
|
+
return _oncancel(new Event('cancel', { cancelable: true }));
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
// MAIN APPROACH: Use only the first nested form's data
|
|
116
115
|
let formData: FormData;
|
|
117
|
-
const nestedForms =
|
|
116
|
+
const nestedForms = panel.querySelectorAll('form');
|
|
118
117
|
|
|
119
118
|
if (nestedForms.length > 0) {
|
|
120
119
|
// Use the first nested form (assuming it contains the input fields)
|
|
@@ -127,7 +126,7 @@
|
|
|
127
126
|
// ALTERNATIVE APPROACH: Combine data from all nested forms (uncomment to use)
|
|
128
127
|
|
|
129
128
|
const combinedFormData = new FormData();
|
|
130
|
-
const allForms =
|
|
129
|
+
const allForms = panel.querySelectorAll('form');
|
|
131
130
|
|
|
132
131
|
allForms.forEach((form) => {
|
|
133
132
|
new FormData(form).forEach((value, key) => {
|
|
@@ -141,26 +140,40 @@
|
|
|
141
140
|
// explicit false from onaction blocks the form closing
|
|
142
141
|
if (
|
|
143
142
|
typeof onaction === 'function' &&
|
|
144
|
-
onaction({ action:
|
|
143
|
+
onaction({ action: returnValue, data: formData }) === false
|
|
145
144
|
)
|
|
146
145
|
return;
|
|
147
146
|
|
|
148
|
-
close(
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function _ontoggle(ev: ToggleEvent & { currentTarget: HTMLDialogElement }) {
|
|
152
|
-
ontoggle?.(ev);
|
|
153
|
-
open = ev.newState === 'open'; // for cases when toggle by other means
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function init(dlg: HTMLDialogElement) {
|
|
157
|
-
modal ? dlg.showModal() : dlg.show();
|
|
158
|
-
return () => dlg.close();
|
|
147
|
+
close();
|
|
159
148
|
}
|
|
160
149
|
|
|
161
150
|
const focusTrap = (node: HTMLElement) => (focustrap ? trapFocus(node) : undefined);
|
|
162
151
|
|
|
163
|
-
let ref:
|
|
152
|
+
let ref: HTMLDivElement | undefined = $state(undefined);
|
|
153
|
+
|
|
154
|
+
let previousOpen = false;
|
|
155
|
+
$effect(() => {
|
|
156
|
+
if (open && !previousOpen) {
|
|
157
|
+
openModalCount += 1;
|
|
158
|
+
if (modal) document.body.style.overflow = 'hidden';
|
|
159
|
+
ontoggle?.({ newState: 'open', oldState: 'closed' } as ToggleEvent);
|
|
160
|
+
}
|
|
161
|
+
if (!open && previousOpen) {
|
|
162
|
+
openModalCount = Math.max(0, openModalCount - 1);
|
|
163
|
+
if (modal && openModalCount === 0) document.body.style.overflow = '';
|
|
164
|
+
ontoggle?.({ newState: 'closed', oldState: 'open' } as ToggleEvent);
|
|
165
|
+
}
|
|
166
|
+
previousOpen = open;
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
$effect(() => {
|
|
170
|
+
return () => {
|
|
171
|
+
if (previousOpen) {
|
|
172
|
+
openModalCount = Math.max(0, openModalCount - 1);
|
|
173
|
+
if (modal && openModalCount === 0) document.body.style.overflow = '';
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
});
|
|
164
177
|
|
|
165
178
|
function close_handler(ev: MouseEvent) {
|
|
166
179
|
if (form) {
|
|
@@ -168,7 +181,9 @@
|
|
|
168
181
|
return;
|
|
169
182
|
}
|
|
170
183
|
|
|
171
|
-
|
|
184
|
+
const cancelEvent = new Event('cancel', { cancelable: true });
|
|
185
|
+
oncancel?.(cancelEvent);
|
|
186
|
+
if (!cancelEvent.defaultPrevented && !permanent) close();
|
|
172
187
|
}
|
|
173
188
|
|
|
174
189
|
createDismissableContext(close_handler);
|
|
@@ -180,7 +195,14 @@
|
|
|
180
195
|
{#if title}
|
|
181
196
|
<h3>{title}</h3>
|
|
182
197
|
{#if dismissable && !permanent}
|
|
183
|
-
<CloseButton
|
|
198
|
+
<CloseButton
|
|
199
|
+
type={form ? 'submit' : 'button'}
|
|
200
|
+
formnovalidate
|
|
201
|
+
class={clsx(styling.close)}
|
|
202
|
+
onclick={() => {
|
|
203
|
+
if (!form) close();
|
|
204
|
+
}}
|
|
205
|
+
/>
|
|
184
206
|
{/if}
|
|
185
207
|
{:else if header}
|
|
186
208
|
{@render header()}
|
|
@@ -197,24 +219,46 @@
|
|
|
197
219
|
{/if}
|
|
198
220
|
{#if dismissable && !permanent && !title}
|
|
199
221
|
<CloseButton
|
|
200
|
-
type=
|
|
222
|
+
type={form ? 'submit' : 'button'}
|
|
201
223
|
formnovalidate
|
|
202
224
|
class={closeCls({ class: clsx(theme?.close, styling.close) })}
|
|
225
|
+
onclick={() => {
|
|
226
|
+
if (!form) close();
|
|
227
|
+
}}
|
|
203
228
|
/>
|
|
204
229
|
{/if}
|
|
205
230
|
{/snippet}
|
|
206
231
|
|
|
207
232
|
{#if open}
|
|
208
|
-
<
|
|
209
|
-
{@attach init}
|
|
233
|
+
<div
|
|
210
234
|
bind:this={ref}
|
|
211
235
|
use:focusTrap
|
|
212
|
-
class=
|
|
236
|
+
class="fixed inset-0 z-40 flex items-center justify-center p-4"
|
|
237
|
+
onclick={_onclick}
|
|
238
|
+
onkeydown={(ev) => {
|
|
239
|
+
if (ev.key === 'Escape' && !permanent) {
|
|
240
|
+
_oncancel(new Event('cancel', { cancelable: true }));
|
|
241
|
+
}
|
|
242
|
+
}}
|
|
243
|
+
tabindex="-1"
|
|
244
|
+
aria-modal={modal ? 'true' : undefined}
|
|
245
|
+
role="dialog"
|
|
246
|
+
>
|
|
247
|
+
{#if modal}
|
|
248
|
+
<div class="absolute inset-0 bg-black/80" data-modal-backdrop="true"></div>
|
|
249
|
+
{/if}
|
|
250
|
+
<div
|
|
251
|
+
class={base({
|
|
252
|
+
fullscreen,
|
|
253
|
+
class: clsx(
|
|
254
|
+
theme?.base,
|
|
255
|
+
className,
|
|
256
|
+
'relative z-10',
|
|
257
|
+
!modal && 'shadow-xl'
|
|
258
|
+
)
|
|
259
|
+
})}
|
|
213
260
|
tabindex="-1"
|
|
214
261
|
onsubmit={_onsubmit}
|
|
215
|
-
oncancel={_oncancel}
|
|
216
|
-
onclick={_onclick}
|
|
217
|
-
ontoggle={_ontoggle}
|
|
218
262
|
transition:transition|global={paramsOptions as ParamsType}
|
|
219
263
|
{...restProps}
|
|
220
264
|
>
|
|
@@ -225,7 +269,8 @@
|
|
|
225
269
|
{:else}
|
|
226
270
|
{@render content()}
|
|
227
271
|
{/if}
|
|
228
|
-
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
229
274
|
{/if}
|
|
230
275
|
|
|
231
276
|
<!--
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
|
|
5
|
+
interface PageLoader2Props extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/** Optional message below the spinner */
|
|
7
|
+
message?: string;
|
|
8
|
+
/** Use full-screen overlay (fixed, centered, dimmed background) */
|
|
9
|
+
fullScreen?: boolean;
|
|
10
|
+
/** Loader size in pixels */
|
|
11
|
+
size?: number;
|
|
12
|
+
/** Overlay background classes when fullScreen is true */
|
|
13
|
+
overlayColor?: string;
|
|
14
|
+
/** Static outer ring border color class */
|
|
15
|
+
ringBaseColor?: string;
|
|
16
|
+
/** Rotating outer ring accent classes */
|
|
17
|
+
ringOuterColor?: string;
|
|
18
|
+
/** Rotating inner ring accent classes */
|
|
19
|
+
ringInnerColor?: string;
|
|
20
|
+
/** Center dot and bounce dots color class */
|
|
21
|
+
dotColor?: string;
|
|
22
|
+
/** Message text color class */
|
|
23
|
+
textColor?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
message,
|
|
28
|
+
fullScreen = true,
|
|
29
|
+
size = 56,
|
|
30
|
+
overlayColor = 'bg-background/80',
|
|
31
|
+
ringBaseColor = 'border-muted',
|
|
32
|
+
ringOuterColor = 'border-t-accent border-r-accent/60',
|
|
33
|
+
ringInnerColor = 'border-b-primary border-l-primary/40',
|
|
34
|
+
dotColor = 'bg-accent',
|
|
35
|
+
textColor = 'text-muted-foreground',
|
|
36
|
+
class: className,
|
|
37
|
+
...restProps
|
|
38
|
+
}: PageLoader2Props = $props();
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<div
|
|
42
|
+
{...restProps}
|
|
43
|
+
class={clsx(
|
|
44
|
+
'flex flex-col items-center justify-center gap-6',
|
|
45
|
+
fullScreen && ['fixed inset-0 z-50 backdrop-blur-sm', overlayColor],
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
role="status"
|
|
49
|
+
aria-live="polite"
|
|
50
|
+
aria-label={message ?? 'Loading'}
|
|
51
|
+
>
|
|
52
|
+
<div class="relative" style={`width: ${size}px; height: ${size}px;`} aria-hidden="true">
|
|
53
|
+
<div class={clsx('absolute inset-0 rounded-full border-2', ringBaseColor)}></div>
|
|
54
|
+
<div
|
|
55
|
+
class={clsx(
|
|
56
|
+
'absolute inset-0 rounded-full border-2 border-transparent animate-[spin_0.8s_linear_infinite]',
|
|
57
|
+
ringOuterColor
|
|
58
|
+
)}
|
|
59
|
+
></div>
|
|
60
|
+
<div
|
|
61
|
+
class={clsx(
|
|
62
|
+
'absolute inset-1 rounded-full border-2 border-transparent animate-[spin_1.2s_linear_infinite_reverse]',
|
|
63
|
+
ringInnerColor
|
|
64
|
+
)}
|
|
65
|
+
></div>
|
|
66
|
+
<div class="absolute inset-0 flex items-center justify-center">
|
|
67
|
+
<div class={clsx('h-2 w-2 rounded-full animate-pulse', dotColor)}></div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
{#if message}
|
|
72
|
+
<div class="flex flex-col items-center gap-2">
|
|
73
|
+
<p class={clsx('text-sm font-medium', textColor)}>{message}</p>
|
|
74
|
+
<div class="flex gap-1.5" aria-hidden="true">
|
|
75
|
+
<span
|
|
76
|
+
class={clsx(
|
|
77
|
+
'h-1.5 w-1.5 rounded-full animate-[page-loader-bounce_1.4s_ease-in-out_infinite_both]',
|
|
78
|
+
dotColor
|
|
79
|
+
)}
|
|
80
|
+
style="animation-delay: 0ms;"
|
|
81
|
+
></span>
|
|
82
|
+
<span
|
|
83
|
+
class={clsx(
|
|
84
|
+
'h-1.5 w-1.5 rounded-full animate-[page-loader-bounce_1.4s_ease-in-out_infinite_both]',
|
|
85
|
+
dotColor
|
|
86
|
+
)}
|
|
87
|
+
style="animation-delay: 160ms;"
|
|
88
|
+
></span>
|
|
89
|
+
<span
|
|
90
|
+
class={clsx(
|
|
91
|
+
'h-1.5 w-1.5 rounded-full animate-[page-loader-bounce_1.4s_ease-in-out_infinite_both]',
|
|
92
|
+
dotColor
|
|
93
|
+
)}
|
|
94
|
+
style="animation-delay: 320ms;"
|
|
95
|
+
></span>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
{/if}
|
|
99
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
interface PageLoader2Props extends HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
/** Optional message below the spinner */
|
|
4
|
+
message?: string;
|
|
5
|
+
/** Use full-screen overlay (fixed, centered, dimmed background) */
|
|
6
|
+
fullScreen?: boolean;
|
|
7
|
+
/** Loader size in pixels */
|
|
8
|
+
size?: number;
|
|
9
|
+
/** Overlay background classes when fullScreen is true */
|
|
10
|
+
overlayColor?: string;
|
|
11
|
+
/** Static outer ring border color class */
|
|
12
|
+
ringBaseColor?: string;
|
|
13
|
+
/** Rotating outer ring accent classes */
|
|
14
|
+
ringOuterColor?: string;
|
|
15
|
+
/** Rotating inner ring accent classes */
|
|
16
|
+
ringInnerColor?: string;
|
|
17
|
+
/** Center dot and bounce dots color class */
|
|
18
|
+
dotColor?: string;
|
|
19
|
+
/** Message text color class */
|
|
20
|
+
textColor?: string;
|
|
21
|
+
}
|
|
22
|
+
declare const PageLoader2: import("svelte").Component<PageLoader2Props, {}, "">;
|
|
23
|
+
type PageLoader2 = ReturnType<typeof PageLoader2>;
|
|
24
|
+
export default PageLoader2;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { default as PageLoader } from
|
|
1
|
+
export { default as PageLoader } from './PageLoader.svelte';
|
|
2
|
+
export { default as PageLoader2 } from './PageLoader2.svelte';
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { default as PageLoader } from
|
|
1
|
+
export { default as PageLoader } from './PageLoader.svelte';
|
|
2
|
+
export { default as PageLoader2 } from './PageLoader2.svelte';
|