@rkosafo/cai.components 0.0.75 → 0.0.78
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/README.md +8 -8
- package/dist/baseEditor/index.svelte +32 -32
- package/dist/builders/filters/FilterBuilder.svelte +641 -641
- package/dist/forms/FormCheckbox/FormCheckbox.svelte +53 -53
- package/dist/forms/FormClEditor/ClEdito.svelte +68 -68
- package/dist/forms/FormDatepicker/FormDatepicker.svelte +159 -159
- package/dist/forms/FormFileUpload/FormFileUplad.svelte +134 -134
- package/dist/forms/FormInput/FormInput.svelte +87 -87
- package/dist/forms/FormRadio/FormRadio.svelte +53 -53
- package/dist/forms/FormSelect/FormSelect.svelte +88 -88
- package/dist/forms/FormTextarea/FormTextarea.svelte +78 -78
- package/dist/forms/button-toggle/ButtonToggle.svelte +119 -0
- package/dist/forms/button-toggle/ButtonToggle.svelte.d.ts +139 -0
- package/dist/forms/button-toggle/ButtonToggleGroup.svelte +0 -0
- package/dist/forms/button-toggle/ButtonToggleGroup.svelte.d.ts +26 -0
- package/dist/forms/button-toggle/CheckIcon.svelte +28 -0
- package/dist/forms/button-toggle/CheckIcon.svelte.d.ts +4 -0
- package/dist/forms/button-toggle/index.d.ts +4 -0
- package/dist/forms/button-toggle/index.js +4 -0
- package/dist/forms/button-toggle/theme.d.ts +347 -0
- package/dist/forms/button-toggle/theme.js +129 -0
- package/dist/forms/checkbox/Checkbox.svelte +82 -82
- package/dist/forms/checkbox/CheckboxButton.svelte +92 -92
- package/dist/forms/datepicker/Datepicker.svelte +707 -707
- package/dist/forms/form/Form.svelte +69 -69
- package/dist/forms/input/Input.svelte +363 -363
- package/dist/forms/label/Label.svelte +38 -38
- package/dist/forms/radio/Radio.svelte +48 -48
- package/dist/forms/radio/RadioButton.svelte +22 -22
- package/dist/forms/select/Select.svelte +56 -56
- package/dist/forms/textarea/Textarea.svelte +165 -165
- package/dist/forms/toggle/Toggle.svelte +70 -0
- package/dist/forms/toggle/Toggle.svelte.d.ts +3 -0
- package/dist/forms/toggle/index.d.ts +2 -0
- package/dist/forms/toggle/index.js +2 -0
- package/dist/forms/toggle/theme.d.ts +280 -0
- package/dist/forms/toggle/theme.js +97 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/layout/Chat/CategorySelector.svelte +52 -52
- package/dist/layout/Chat/ChatEntry.svelte +246 -246
- package/dist/layout/Chat/ChatEntrySkeleton.svelte +81 -81
- package/dist/layout/Chat/ChatHeader.svelte +172 -172
- package/dist/layout/Chat/ChatInput.svelte +207 -207
- package/dist/layout/Chat/DraggableWindow.svelte +230 -230
- package/dist/layout/Chat/PreviewPage.svelte +182 -182
- package/dist/layout/Chat/RichText.svelte +216 -216
- package/dist/layout/ComponentCanvas/Canvas.svelte +40 -40
- package/dist/layout/ComponentCanvas/ComponentRenderer.svelte +85 -85
- package/dist/layout/TF/Content/Content.svelte +21 -21
- package/dist/layout/TF/Header/Header.svelte +166 -166
- package/dist/layout/TF/Sidebar/Sidebar.svelte +148 -148
- package/dist/layout/TF/Wrapper/Wrapper.svelte +17 -17
- package/dist/layout/mailing/MailPaginator.svelte +36 -36
- package/dist/layout/mailing/MailSidebar.svelte +39 -39
- package/dist/layout/mailing/MailToolBar.svelte +174 -174
- package/dist/layout/mailing/MailingContent.svelte +10 -10
- package/dist/layout/mailing/MailingHeader.svelte +55 -55
- package/dist/layout/mailing/MailingMessageCard.svelte +112 -112
- package/dist/layout/mailing/MailingMessageViewer.svelte +87 -87
- package/dist/layout/mailing/MailingModule.svelte +448 -448
- package/dist/styles/docs.css +615 -615
- package/dist/styles/tf-layout.css +185 -185
- package/dist/themes/ThemeProvider.svelte +20 -20
- package/dist/themes/themes.d.ts +3 -0
- package/dist/themes/themes.js +3 -0
- package/dist/types/index.d.ts +57 -1
- package/dist/typography/heading/Heading.svelte +35 -35
- package/dist/ui/accordion/Accordion.svelte +49 -49
- package/dist/ui/accordion/AccordionItem.svelte +173 -173
- package/dist/ui/alert/Alert.svelte +83 -83
- package/dist/ui/alertDialog/AlertDialog.svelte +40 -40
- package/dist/ui/avatar/Avatar.svelte +77 -77
- package/dist/ui/box/Box.svelte +28 -28
- package/dist/ui/breadcrumb/Breadcrumb.svelte +39 -39
- package/dist/ui/buttons/ActionButton.svelte +234 -234
- package/dist/ui/buttons/Button.svelte +102 -102
- package/dist/ui/buttons/GradientButton.svelte +59 -59
- package/dist/ui/datatable/Datatable.svelte +525 -525
- package/dist/ui/drawer/Drawer.svelte +300 -300
- package/dist/ui/dropdown/Dropdown.svelte +36 -36
- package/dist/ui/dropdown/DropdownDivider.svelte +11 -11
- package/dist/ui/dropdown/DropdownGroup.svelte +14 -14
- package/dist/ui/dropdown/DropdownHeader.svelte +14 -14
- package/dist/ui/dropdown/DropdownItem.svelte +52 -52
- package/dist/ui/footer/Footer.svelte +15 -15
- package/dist/ui/footer/FooterBrand.svelte +37 -37
- package/dist/ui/footer/FooterCopyright.svelte +45 -45
- package/dist/ui/footer/FooterIcon.svelte +22 -22
- package/dist/ui/footer/FooterLink.svelte +33 -33
- package/dist/ui/footer/FooterLinkGroup.svelte +13 -13
- package/dist/ui/icons/IconifyIcon.svelte +7 -7
- package/dist/ui/indicator/Indicator.svelte +42 -42
- package/dist/ui/modal/Modal.svelte +265 -265
- package/dist/ui/modal/theme.d.ts +26 -26
- package/dist/ui/modal/theme.js +25 -25
- package/dist/ui/notificationList/NotificationList.svelte +123 -123
- package/dist/ui/pageLoader/PageLoader.svelte +14 -14
- package/dist/ui/paginate/Paginate.svelte +96 -96
- package/dist/ui/speedDial/SpeedDial.svelte +77 -0
- package/dist/ui/speedDial/SpeedDial.svelte.d.ts +21 -0
- package/dist/ui/speedDial/SpeedDialButton.svelte +75 -0
- package/dist/ui/speedDial/SpeedDialButton.svelte.d.ts +20 -0
- package/dist/ui/speedDial/SpeedDialTrigger.svelte +79 -0
- package/dist/ui/speedDial/SpeedDialTrigger.svelte.d.ts +18 -0
- package/dist/ui/speedDial/index.d.ts +4 -0
- package/dist/ui/speedDial/index.js +4 -0
- package/dist/ui/speedDial/theme.d.ts +75 -0
- package/dist/ui/speedDial/theme.js +35 -0
- package/dist/ui/tab/Tab.svelte +67 -67
- package/dist/ui/table/Table.svelte +396 -396
- package/dist/ui/tableLoader/TableLoader.svelte +24 -24
- package/dist/ui/toast/Toast.svelte +337 -337
- package/dist/ui/toast/Toast.svelte.d.ts +10 -10
- package/dist/ui/toast/index.d.ts +1 -2
- package/dist/ui/toast/index.js +3 -1
- package/dist/ui/toolbar/Toolbar.svelte +59 -59
- package/dist/ui/toolbar/ToolbarButton.svelte +56 -56
- package/dist/ui/toolbar/ToolbarGroup.svelte +43 -43
- package/dist/ui/tooltip/Tooltip.svelte +51 -51
- package/dist/utils/Popper.svelte +257 -257
- package/dist/utils/closeButton/CloseButton.svelte +88 -88
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.js +13 -3
- package/dist/utils/singleSelection.svelte.js +48 -48
- package/dist/youtube/index.svelte +12 -12
- package/package.json +2 -1
|
@@ -1,265 +1,265 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import clsx, { type ClassValue } from 'clsx';
|
|
3
|
-
import { sineIn } from 'svelte/easing';
|
|
4
|
-
import { fade } from 'svelte/transition';
|
|
5
|
-
import { modal as modalStyle } from './index.js';
|
|
6
|
-
import { getTheme, warnThemeDeprecation } from '../../themes/themeUtils.js';
|
|
7
|
-
import { createDismissableContext } from '../../utils/dismissable.js';
|
|
8
|
-
import { CloseButton } from '../../utils/closeButton/index.js';
|
|
9
|
-
import type { ModalProps, ParamsType, TransitionFunc } from '../../types/index.js';
|
|
10
|
-
import { trapFocus } from '../../utils/action.js';
|
|
11
|
-
|
|
12
|
-
let {
|
|
13
|
-
children,
|
|
14
|
-
onaction = () => true,
|
|
15
|
-
oncancel,
|
|
16
|
-
onsubmit,
|
|
17
|
-
ontoggle,
|
|
18
|
-
form = false,
|
|
19
|
-
modal = true,
|
|
20
|
-
autoclose = false,
|
|
21
|
-
focustrap = false,
|
|
22
|
-
header,
|
|
23
|
-
footer,
|
|
24
|
-
title,
|
|
25
|
-
open = $bindable(false),
|
|
26
|
-
permanent = false,
|
|
27
|
-
dismissable = true,
|
|
28
|
-
closeBtnClass,
|
|
29
|
-
headerClass,
|
|
30
|
-
bodyClass,
|
|
31
|
-
footerClass,
|
|
32
|
-
outsideclose = true,
|
|
33
|
-
size = 'md',
|
|
34
|
-
placement,
|
|
35
|
-
class: className,
|
|
36
|
-
classes,
|
|
37
|
-
params,
|
|
38
|
-
transition = fade,
|
|
39
|
-
fullscreen = false,
|
|
40
|
-
...restProps
|
|
41
|
-
}: ModalProps = $props();
|
|
42
|
-
|
|
43
|
-
// form, header, footer, body, close
|
|
44
|
-
warnThemeDeprecation(
|
|
45
|
-
'Modal',
|
|
46
|
-
{ headerClass, bodyClass, footerClass, closeBtnClass },
|
|
47
|
-
{ bodyClass: 'body', headerClass: 'header', footerClass: 'footer', closeBtnClass: 'close' }
|
|
48
|
-
);
|
|
49
|
-
const styling = $derived(
|
|
50
|
-
classes ?? { header: headerClass, body: bodyClass, footer: footerClass, close: closeBtnClass }
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const theme = getTheme('modal');
|
|
54
|
-
|
|
55
|
-
const paramsDefault = { duration: 100, easing: sineIn };
|
|
56
|
-
const paramsOptions = $derived(params ?? paramsDefault);
|
|
57
|
-
|
|
58
|
-
const {
|
|
59
|
-
base,
|
|
60
|
-
form: formCls,
|
|
61
|
-
header: headerCls,
|
|
62
|
-
footer: footerCls,
|
|
63
|
-
body,
|
|
64
|
-
close: closeCls
|
|
65
|
-
} = $derived(modalStyle({ placement, size }));
|
|
66
|
-
|
|
67
|
-
const close = (dlg: HTMLDialogElement) => (open = false);
|
|
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
|
-
}
|
|
76
|
-
|
|
77
|
-
// this event gets called when user canceled the dialog:
|
|
78
|
-
// pressesed ESC key, clicked outside, pressed submit button with no 'value' like close button
|
|
79
|
-
oncancel?.(ev);
|
|
80
|
-
if (ev.defaultPrevented) return;
|
|
81
|
-
|
|
82
|
-
ev.preventDefault(); // prevent anyway, we need clean close
|
|
83
|
-
if (!permanent) close(ev.currentTarget);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function _onclick(ev: Event & { currentTarget: HTMLDialogElement }) {
|
|
87
|
-
const dlg: HTMLDialogElement = ev.currentTarget;
|
|
88
|
-
if (outsideclose && ev.target === dlg) {
|
|
89
|
-
return cancel(dlg);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (autoclose && ev.target instanceof HTMLButtonElement && !permanent) {
|
|
93
|
-
return close(dlg);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function _onsubmit(ev: SubmitEvent & { currentTarget: HTMLDialogElement }) {
|
|
98
|
-
// When dialog contains the <form method="dialog"> and when child with type="submit" was pressed
|
|
99
|
-
|
|
100
|
-
onsubmit?.(ev);
|
|
101
|
-
if (ev.defaultPrevented) return;
|
|
102
|
-
|
|
103
|
-
ev.preventDefault(); // stop dialog.close()
|
|
104
|
-
|
|
105
|
-
const dlg: HTMLDialogElement = ev.currentTarget;
|
|
106
|
-
|
|
107
|
-
if (ev.submitter instanceof HTMLButtonElement || ev.submitter instanceof HTMLInputElement) {
|
|
108
|
-
dlg.returnValue = ev.submitter.value;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!dlg.returnValue) {
|
|
112
|
-
return cancel(dlg); // if no action - treat that as cancel
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// MAIN APPROACH: Use only the first nested form's data
|
|
116
|
-
let formData: FormData;
|
|
117
|
-
const nestedForms = dlg.querySelectorAll('form');
|
|
118
|
-
|
|
119
|
-
if (nestedForms.length > 0) {
|
|
120
|
-
// Use the first nested form (assuming it contains the input fields)
|
|
121
|
-
formData = new FormData(nestedForms[0]);
|
|
122
|
-
} else {
|
|
123
|
-
// Fallback: use the target form if no nested forms found
|
|
124
|
-
formData = new FormData(ev.target as HTMLFormElement);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ALTERNATIVE APPROACH: Combine data from all nested forms (uncomment to use)
|
|
128
|
-
|
|
129
|
-
const combinedFormData = new FormData();
|
|
130
|
-
const allForms = dlg.querySelectorAll('form');
|
|
131
|
-
|
|
132
|
-
allForms.forEach((form) => {
|
|
133
|
-
new FormData(form).forEach((value, key) => {
|
|
134
|
-
combinedFormData.append(key, value);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// Use the alternative approach
|
|
139
|
-
formData = combinedFormData;
|
|
140
|
-
|
|
141
|
-
// explicit false from onaction blocks the form closing
|
|
142
|
-
if (
|
|
143
|
-
typeof onaction === 'function' &&
|
|
144
|
-
onaction({ action: dlg.returnValue, data: formData }) === false
|
|
145
|
-
)
|
|
146
|
-
return;
|
|
147
|
-
|
|
148
|
-
close(dlg);
|
|
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();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const focusTrap = (node: HTMLElement) => (focustrap ? trapFocus(node) : undefined);
|
|
162
|
-
|
|
163
|
-
let ref: HTMLDialogElement | undefined = $state(undefined);
|
|
164
|
-
|
|
165
|
-
function close_handler(ev: MouseEvent) {
|
|
166
|
-
if (form) {
|
|
167
|
-
// dialog/form mechanism will close the dialog
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
ref?.dispatchEvent(new Event('cancel', { bubbles: true, cancelable: true }));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
createDismissableContext(close_handler);
|
|
175
|
-
</script>
|
|
176
|
-
|
|
177
|
-
{#snippet content()}
|
|
178
|
-
{#if title || header}
|
|
179
|
-
<div class={headerCls({ class: clsx(theme?.header, styling.header) })}>
|
|
180
|
-
{#if title}
|
|
181
|
-
<h3>{title}</h3>
|
|
182
|
-
{#if dismissable && !permanent}
|
|
183
|
-
<CloseButton type="submit" formnovalidate class={clsx(styling.close)} />
|
|
184
|
-
{/if}
|
|
185
|
-
{:else if header}
|
|
186
|
-
{@render header()}
|
|
187
|
-
{/if}
|
|
188
|
-
</div>
|
|
189
|
-
{/if}
|
|
190
|
-
<div class={body({ class: clsx(theme?.body, styling.body) })}>
|
|
191
|
-
{@render children?.()}
|
|
192
|
-
</div>
|
|
193
|
-
{#if footer}
|
|
194
|
-
<div class={footerCls({ class: clsx(theme?.footer, styling.footer) })}>
|
|
195
|
-
{@render footer()}
|
|
196
|
-
</div>
|
|
197
|
-
{/if}
|
|
198
|
-
{#if dismissable && !permanent && !title}
|
|
199
|
-
<CloseButton
|
|
200
|
-
type="submit"
|
|
201
|
-
formnovalidate
|
|
202
|
-
class={closeCls({ class: clsx(theme?.close, styling.close) })}
|
|
203
|
-
/>
|
|
204
|
-
{/if}
|
|
205
|
-
{/snippet}
|
|
206
|
-
|
|
207
|
-
{#if open}
|
|
208
|
-
<dialog
|
|
209
|
-
{@attach init}
|
|
210
|
-
bind:this={ref}
|
|
211
|
-
use:focusTrap
|
|
212
|
-
class={base({ fullscreen, class: clsx(theme?.base, className) })}
|
|
213
|
-
tabindex="-1"
|
|
214
|
-
onsubmit={_onsubmit}
|
|
215
|
-
oncancel={_oncancel}
|
|
216
|
-
onclick={_onclick}
|
|
217
|
-
ontoggle={_ontoggle}
|
|
218
|
-
transition:transition|global={paramsOptions as ParamsType}
|
|
219
|
-
{...restProps}
|
|
220
|
-
>
|
|
221
|
-
{#if form}
|
|
222
|
-
<form method="dialog" class={formCls({ class: clsx(theme?.form) })}>
|
|
223
|
-
{@render content()}
|
|
224
|
-
</form>
|
|
225
|
-
{:else}
|
|
226
|
-
{@render content()}
|
|
227
|
-
{/if}
|
|
228
|
-
</dialog>
|
|
229
|
-
{/if}
|
|
230
|
-
|
|
231
|
-
<!--
|
|
232
|
-
@component
|
|
233
|
-
[Go to docs](https://flowbite-svelte.com/)
|
|
234
|
-
## Type
|
|
235
|
-
[ModalProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1038)
|
|
236
|
-
## Props
|
|
237
|
-
@prop children
|
|
238
|
-
@prop onaction = ()
|
|
239
|
-
@prop oncancel
|
|
240
|
-
@prop onsubmit
|
|
241
|
-
@prop ontoggle
|
|
242
|
-
@prop form = false
|
|
243
|
-
@prop modal = true
|
|
244
|
-
@prop autoclose = false
|
|
245
|
-
@prop focustrap = false
|
|
246
|
-
@prop header
|
|
247
|
-
@prop footer
|
|
248
|
-
@prop title
|
|
249
|
-
@prop open = $bindable(false)
|
|
250
|
-
@prop permanent = false
|
|
251
|
-
@prop dismissable = true
|
|
252
|
-
@prop closeBtnClass
|
|
253
|
-
@prop headerClass
|
|
254
|
-
@prop bodyClass
|
|
255
|
-
@prop footerClass
|
|
256
|
-
@prop outsideclose = true
|
|
257
|
-
@prop size = "md"
|
|
258
|
-
@prop placement
|
|
259
|
-
@prop class: className
|
|
260
|
-
@prop classes
|
|
261
|
-
@prop params
|
|
262
|
-
@prop transition = fade
|
|
263
|
-
@prop fullscreen = false
|
|
264
|
-
@prop ...restProps
|
|
265
|
-
-->
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import clsx, { type ClassValue } from 'clsx';
|
|
3
|
+
import { sineIn } from 'svelte/easing';
|
|
4
|
+
import { fade } from 'svelte/transition';
|
|
5
|
+
import { modal as modalStyle } from './index.js';
|
|
6
|
+
import { getTheme, warnThemeDeprecation } from '../../themes/themeUtils.js';
|
|
7
|
+
import { createDismissableContext } from '../../utils/dismissable.js';
|
|
8
|
+
import { CloseButton } from '../../utils/closeButton/index.js';
|
|
9
|
+
import type { ModalProps, ParamsType, TransitionFunc } from '../../types/index.js';
|
|
10
|
+
import { trapFocus } from '../../utils/action.js';
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
children,
|
|
14
|
+
onaction = () => true,
|
|
15
|
+
oncancel,
|
|
16
|
+
onsubmit,
|
|
17
|
+
ontoggle,
|
|
18
|
+
form = false,
|
|
19
|
+
modal = true,
|
|
20
|
+
autoclose = false,
|
|
21
|
+
focustrap = false,
|
|
22
|
+
header,
|
|
23
|
+
footer,
|
|
24
|
+
title,
|
|
25
|
+
open = $bindable(false),
|
|
26
|
+
permanent = false,
|
|
27
|
+
dismissable = true,
|
|
28
|
+
closeBtnClass,
|
|
29
|
+
headerClass,
|
|
30
|
+
bodyClass,
|
|
31
|
+
footerClass,
|
|
32
|
+
outsideclose = true,
|
|
33
|
+
size = 'md',
|
|
34
|
+
placement,
|
|
35
|
+
class: className,
|
|
36
|
+
classes,
|
|
37
|
+
params,
|
|
38
|
+
transition = fade,
|
|
39
|
+
fullscreen = false,
|
|
40
|
+
...restProps
|
|
41
|
+
}: ModalProps = $props();
|
|
42
|
+
|
|
43
|
+
// form, header, footer, body, close
|
|
44
|
+
warnThemeDeprecation(
|
|
45
|
+
'Modal',
|
|
46
|
+
{ headerClass, bodyClass, footerClass, closeBtnClass },
|
|
47
|
+
{ bodyClass: 'body', headerClass: 'header', footerClass: 'footer', closeBtnClass: 'close' }
|
|
48
|
+
);
|
|
49
|
+
const styling = $derived(
|
|
50
|
+
classes ?? { header: headerClass, body: bodyClass, footer: footerClass, close: closeBtnClass }
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const theme = getTheme('modal');
|
|
54
|
+
|
|
55
|
+
const paramsDefault = { duration: 100, easing: sineIn };
|
|
56
|
+
const paramsOptions = $derived(params ?? paramsDefault);
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
base,
|
|
60
|
+
form: formCls,
|
|
61
|
+
header: headerCls,
|
|
62
|
+
footer: footerCls,
|
|
63
|
+
body,
|
|
64
|
+
close: closeCls
|
|
65
|
+
} = $derived(modalStyle({ placement, size }));
|
|
66
|
+
|
|
67
|
+
const close = (dlg: HTMLDialogElement) => (open = false);
|
|
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
|
+
}
|
|
76
|
+
|
|
77
|
+
// this event gets called when user canceled the dialog:
|
|
78
|
+
// pressesed ESC key, clicked outside, pressed submit button with no 'value' like close button
|
|
79
|
+
oncancel?.(ev);
|
|
80
|
+
if (ev.defaultPrevented) return;
|
|
81
|
+
|
|
82
|
+
ev.preventDefault(); // prevent anyway, we need clean close
|
|
83
|
+
if (!permanent) close(ev.currentTarget);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function _onclick(ev: Event & { currentTarget: HTMLDialogElement }) {
|
|
87
|
+
const dlg: HTMLDialogElement = ev.currentTarget;
|
|
88
|
+
if (outsideclose && ev.target === dlg) {
|
|
89
|
+
return cancel(dlg);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (autoclose && ev.target instanceof HTMLButtonElement && !permanent) {
|
|
93
|
+
return close(dlg);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function _onsubmit(ev: SubmitEvent & { currentTarget: HTMLDialogElement }) {
|
|
98
|
+
// When dialog contains the <form method="dialog"> and when child with type="submit" was pressed
|
|
99
|
+
|
|
100
|
+
onsubmit?.(ev);
|
|
101
|
+
if (ev.defaultPrevented) return;
|
|
102
|
+
|
|
103
|
+
ev.preventDefault(); // stop dialog.close()
|
|
104
|
+
|
|
105
|
+
const dlg: HTMLDialogElement = ev.currentTarget;
|
|
106
|
+
|
|
107
|
+
if (ev.submitter instanceof HTMLButtonElement || ev.submitter instanceof HTMLInputElement) {
|
|
108
|
+
dlg.returnValue = ev.submitter.value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!dlg.returnValue) {
|
|
112
|
+
return cancel(dlg); // if no action - treat that as cancel
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// MAIN APPROACH: Use only the first nested form's data
|
|
116
|
+
let formData: FormData;
|
|
117
|
+
const nestedForms = dlg.querySelectorAll('form');
|
|
118
|
+
|
|
119
|
+
if (nestedForms.length > 0) {
|
|
120
|
+
// Use the first nested form (assuming it contains the input fields)
|
|
121
|
+
formData = new FormData(nestedForms[0]);
|
|
122
|
+
} else {
|
|
123
|
+
// Fallback: use the target form if no nested forms found
|
|
124
|
+
formData = new FormData(ev.target as HTMLFormElement);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ALTERNATIVE APPROACH: Combine data from all nested forms (uncomment to use)
|
|
128
|
+
|
|
129
|
+
const combinedFormData = new FormData();
|
|
130
|
+
const allForms = dlg.querySelectorAll('form');
|
|
131
|
+
|
|
132
|
+
allForms.forEach((form) => {
|
|
133
|
+
new FormData(form).forEach((value, key) => {
|
|
134
|
+
combinedFormData.append(key, value);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Use the alternative approach
|
|
139
|
+
formData = combinedFormData;
|
|
140
|
+
|
|
141
|
+
// explicit false from onaction blocks the form closing
|
|
142
|
+
if (
|
|
143
|
+
typeof onaction === 'function' &&
|
|
144
|
+
onaction({ action: dlg.returnValue, data: formData }) === false
|
|
145
|
+
)
|
|
146
|
+
return;
|
|
147
|
+
|
|
148
|
+
close(dlg);
|
|
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();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const focusTrap = (node: HTMLElement) => (focustrap ? trapFocus(node) : undefined);
|
|
162
|
+
|
|
163
|
+
let ref: HTMLDialogElement | undefined = $state(undefined);
|
|
164
|
+
|
|
165
|
+
function close_handler(ev: MouseEvent) {
|
|
166
|
+
if (form) {
|
|
167
|
+
// dialog/form mechanism will close the dialog
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
ref?.dispatchEvent(new Event('cancel', { bubbles: true, cancelable: true }));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
createDismissableContext(close_handler);
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
{#snippet content()}
|
|
178
|
+
{#if title || header}
|
|
179
|
+
<div class={headerCls({ class: clsx(theme?.header, styling.header) })}>
|
|
180
|
+
{#if title}
|
|
181
|
+
<h3>{title}</h3>
|
|
182
|
+
{#if dismissable && !permanent}
|
|
183
|
+
<CloseButton type="submit" formnovalidate class={clsx(styling.close)} />
|
|
184
|
+
{/if}
|
|
185
|
+
{:else if header}
|
|
186
|
+
{@render header()}
|
|
187
|
+
{/if}
|
|
188
|
+
</div>
|
|
189
|
+
{/if}
|
|
190
|
+
<div class={body({ class: clsx(theme?.body, styling.body) })}>
|
|
191
|
+
{@render children?.()}
|
|
192
|
+
</div>
|
|
193
|
+
{#if footer}
|
|
194
|
+
<div class={footerCls({ class: clsx(theme?.footer, styling.footer) })}>
|
|
195
|
+
{@render footer()}
|
|
196
|
+
</div>
|
|
197
|
+
{/if}
|
|
198
|
+
{#if dismissable && !permanent && !title}
|
|
199
|
+
<CloseButton
|
|
200
|
+
type="submit"
|
|
201
|
+
formnovalidate
|
|
202
|
+
class={closeCls({ class: clsx(theme?.close, styling.close) })}
|
|
203
|
+
/>
|
|
204
|
+
{/if}
|
|
205
|
+
{/snippet}
|
|
206
|
+
|
|
207
|
+
{#if open}
|
|
208
|
+
<dialog
|
|
209
|
+
{@attach init}
|
|
210
|
+
bind:this={ref}
|
|
211
|
+
use:focusTrap
|
|
212
|
+
class={base({ fullscreen, class: clsx(theme?.base, className) })}
|
|
213
|
+
tabindex="-1"
|
|
214
|
+
onsubmit={_onsubmit}
|
|
215
|
+
oncancel={_oncancel}
|
|
216
|
+
onclick={_onclick}
|
|
217
|
+
ontoggle={_ontoggle}
|
|
218
|
+
transition:transition|global={paramsOptions as ParamsType}
|
|
219
|
+
{...restProps}
|
|
220
|
+
>
|
|
221
|
+
{#if form}
|
|
222
|
+
<form method="dialog" class={formCls({ class: clsx(theme?.form) })}>
|
|
223
|
+
{@render content()}
|
|
224
|
+
</form>
|
|
225
|
+
{:else}
|
|
226
|
+
{@render content()}
|
|
227
|
+
{/if}
|
|
228
|
+
</dialog>
|
|
229
|
+
{/if}
|
|
230
|
+
|
|
231
|
+
<!--
|
|
232
|
+
@component
|
|
233
|
+
[Go to docs](https://flowbite-svelte.com/)
|
|
234
|
+
## Type
|
|
235
|
+
[ModalProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L1038)
|
|
236
|
+
## Props
|
|
237
|
+
@prop children
|
|
238
|
+
@prop onaction = ()
|
|
239
|
+
@prop oncancel
|
|
240
|
+
@prop onsubmit
|
|
241
|
+
@prop ontoggle
|
|
242
|
+
@prop form = false
|
|
243
|
+
@prop modal = true
|
|
244
|
+
@prop autoclose = false
|
|
245
|
+
@prop focustrap = false
|
|
246
|
+
@prop header
|
|
247
|
+
@prop footer
|
|
248
|
+
@prop title
|
|
249
|
+
@prop open = $bindable(false)
|
|
250
|
+
@prop permanent = false
|
|
251
|
+
@prop dismissable = true
|
|
252
|
+
@prop closeBtnClass
|
|
253
|
+
@prop headerClass
|
|
254
|
+
@prop bodyClass
|
|
255
|
+
@prop footerClass
|
|
256
|
+
@prop outsideclose = true
|
|
257
|
+
@prop size = "md"
|
|
258
|
+
@prop placement
|
|
259
|
+
@prop class: className
|
|
260
|
+
@prop classes
|
|
261
|
+
@prop params
|
|
262
|
+
@prop transition = fade
|
|
263
|
+
@prop fullscreen = false
|
|
264
|
+
@prop ...restProps
|
|
265
|
+
-->
|
package/dist/ui/modal/theme.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Classes } from
|
|
2
|
-
import { type VariantProps } from
|
|
1
|
+
import type { Classes } from '../../themes/themeUtils.js';
|
|
2
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
3
3
|
export type ModalVariants = VariantProps<typeof modal> & Classes<typeof modal>;
|
|
4
4
|
export declare const modal: import("tailwind-variants").TVReturnType<{
|
|
5
5
|
fullscreen: {
|
|
@@ -8,31 +8,31 @@ export declare const modal: import("tailwind-variants").TVReturnType<{
|
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
10
|
placement: {
|
|
11
|
-
|
|
11
|
+
'top-left': {
|
|
12
12
|
base: string;
|
|
13
13
|
};
|
|
14
|
-
|
|
14
|
+
'top-center': {
|
|
15
15
|
base: string;
|
|
16
16
|
};
|
|
17
|
-
|
|
17
|
+
'top-right': {
|
|
18
18
|
base: string;
|
|
19
19
|
};
|
|
20
|
-
|
|
20
|
+
'center-left': {
|
|
21
21
|
base: string;
|
|
22
22
|
};
|
|
23
23
|
center: {
|
|
24
24
|
base: string;
|
|
25
25
|
};
|
|
26
|
-
|
|
26
|
+
'center-right': {
|
|
27
27
|
base: string;
|
|
28
28
|
};
|
|
29
|
-
|
|
29
|
+
'bottom-left': {
|
|
30
30
|
base: string;
|
|
31
31
|
};
|
|
32
|
-
|
|
32
|
+
'bottom-center': {
|
|
33
33
|
base: string;
|
|
34
34
|
};
|
|
35
|
-
|
|
35
|
+
'bottom-right': {
|
|
36
36
|
base: string;
|
|
37
37
|
};
|
|
38
38
|
};
|
|
@@ -70,31 +70,31 @@ export declare const modal: import("tailwind-variants").TVReturnType<{
|
|
|
70
70
|
};
|
|
71
71
|
};
|
|
72
72
|
placement: {
|
|
73
|
-
|
|
73
|
+
'top-left': {
|
|
74
74
|
base: string;
|
|
75
75
|
};
|
|
76
|
-
|
|
76
|
+
'top-center': {
|
|
77
77
|
base: string;
|
|
78
78
|
};
|
|
79
|
-
|
|
79
|
+
'top-right': {
|
|
80
80
|
base: string;
|
|
81
81
|
};
|
|
82
|
-
|
|
82
|
+
'center-left': {
|
|
83
83
|
base: string;
|
|
84
84
|
};
|
|
85
85
|
center: {
|
|
86
86
|
base: string;
|
|
87
87
|
};
|
|
88
|
-
|
|
88
|
+
'center-right': {
|
|
89
89
|
base: string;
|
|
90
90
|
};
|
|
91
|
-
|
|
91
|
+
'bottom-left': {
|
|
92
92
|
base: string;
|
|
93
93
|
};
|
|
94
|
-
|
|
94
|
+
'bottom-center': {
|
|
95
95
|
base: string;
|
|
96
96
|
};
|
|
97
|
-
|
|
97
|
+
'bottom-right': {
|
|
98
98
|
base: string;
|
|
99
99
|
};
|
|
100
100
|
};
|
|
@@ -132,31 +132,31 @@ export declare const modal: import("tailwind-variants").TVReturnType<{
|
|
|
132
132
|
};
|
|
133
133
|
};
|
|
134
134
|
placement: {
|
|
135
|
-
|
|
135
|
+
'top-left': {
|
|
136
136
|
base: string;
|
|
137
137
|
};
|
|
138
|
-
|
|
138
|
+
'top-center': {
|
|
139
139
|
base: string;
|
|
140
140
|
};
|
|
141
|
-
|
|
141
|
+
'top-right': {
|
|
142
142
|
base: string;
|
|
143
143
|
};
|
|
144
|
-
|
|
144
|
+
'center-left': {
|
|
145
145
|
base: string;
|
|
146
146
|
};
|
|
147
147
|
center: {
|
|
148
148
|
base: string;
|
|
149
149
|
};
|
|
150
|
-
|
|
150
|
+
'center-right': {
|
|
151
151
|
base: string;
|
|
152
152
|
};
|
|
153
|
-
|
|
153
|
+
'bottom-left': {
|
|
154
154
|
base: string;
|
|
155
155
|
};
|
|
156
|
-
|
|
156
|
+
'bottom-center': {
|
|
157
157
|
base: string;
|
|
158
158
|
};
|
|
159
|
-
|
|
159
|
+
'bottom-right': {
|
|
160
160
|
base: string;
|
|
161
161
|
};
|
|
162
162
|
};
|