@functionalcms/svelte-components 5.4.4 → 5.5.1

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.
@@ -1,269 +1,268 @@
1
- <script lang="ts">
2
- import {
3
- type Field,
4
- type SelectField,
5
- type CollectionField,
6
- type TextareaField,
7
- type InputField,
8
- type FilePickerField,
9
- type PhoneInputField,
10
- type TimePickerField,
11
- type ButtonConfig,
12
- type HiddenField,
13
- type DatePickerField,
14
- SubmitResult,
15
- } from './form.js';
16
- import {
17
- Button,
18
- Label,
19
- Fileupload,
20
- Select,
21
- MultiSelect,
22
- Textarea,
23
- Toggle,
24
- Input,
25
- FloatingLabelInput,
26
- Timepicker,
27
- PhoneInput,
28
- Datepicker
29
- } from 'flowbite-svelte';
30
- import { fade } from 'svelte/transition';
31
-
32
- interface Props {
33
- action: string;
34
- class?: string;
35
- successMessage?: string;
36
- fields?: Array<Field>;
37
- onMessageSubmitted?: (result: SubmitResult) => void;
38
- buttons: Array<ButtonConfig>;
39
- }
40
-
41
- let {
42
- action,
43
- fields = [],
44
- class: css = '',
45
- successMessage,
46
- onMessageSubmitted,
47
- buttons = [
48
- {
49
- label: 'Submit',
50
- type: 'submit',
51
- action: undefined
52
- }
53
- ]
54
- }: Props = $props();
55
-
56
- let form: HTMLFormElement;
57
- let showMessage = $state(false);
58
- let isSendingMessage = $state(false);
59
- let loading = $state(false);
60
-
61
- const handleSubmit = async (event: Event) => {
62
- event.preventDefault();
63
- loading = true;
64
- try {
65
- if (!isSendingMessage) {
66
- isSendingMessage = true;
67
- const formData = new FormData(form);
68
- const response = await fetch(action, {
69
- method: 'POST',
70
- body: formData
71
- });
72
- showMessage = response.status === 200;
73
- onMessageSubmitted?.(showMessage ? SubmitResult.Success : SubmitResult.Error);
74
- setTimeout(() => {
75
- showMessage = false;
76
- }, 3000);
77
- }
78
- } catch (error) {
79
- console.error('Error submitting form:', error);
80
- } finally {
81
- isSendingMessage = false;
82
- }
83
- loading = false;
84
- };
85
- </script>
86
-
87
- {#snippet renderInput(field: InputField)}
88
- {#if field.isFloat}
89
- <FloatingLabelInput
90
- id={field.id}
91
- name={field.id}
92
- required={field.required}
93
- readonly={field.readonly}
94
- disabled={field.disabled}
95
- class={field.class}
96
- size={field.size == 'sm' ? 'small' : 'default'}
97
- type={field.type}
98
- variant={field.floatVariant}
99
- placeholder={field.placeholder}
100
- bind:value={field.value}
101
- >
102
- {field.label}
103
- </FloatingLabelInput>
104
- {:else}
105
- <Label for={field.id} class={field.labelClass}>{field.label}</Label>
106
- <Input
107
- id={field.id}
108
- name={field.id}
109
- required={field.required}
110
- readonly={field.readonly}
111
- disabled={field.disabled}
112
- class={field.class}
113
- size={field.size}
114
- type={field.type}
115
- placeholder={field.placeholder}
116
- bind:value={field.value}
117
- />
118
- {/if}
119
- {/snippet}
120
-
121
- {#snippet renderTextarea(field: TextareaField)}
122
- <Label for={field.id} class={field.labelClass}>{field.label}</Label>
123
- <Textarea
124
- id={field.id}
125
- name={field.id}
126
- required={field.required}
127
- readonly={field.readonly}
128
- disabled={field.disabled}
129
- class={field.class}
130
- rows={field.rows}
131
- cols={field.cols}
132
- placeholder={field.placeholder}
133
- bind:value={field.value}
134
- />
135
- {/snippet}
136
-
137
- {#snippet renderSelect(field: SelectField)}
138
- <Label class={field.labelClass}
139
- >{field.label}
140
- {#if field.type === 'single'}
141
- <Select
142
- id={field.id}
143
- name={field.id}
144
- required={field.required}
145
- readonly={field.readonly}
146
- disabled={field.disabled}
147
- class={field.class}
148
- items={field.options}
149
- bind:value={field.value}
150
- />
151
- {:else}
152
- <MultiSelect class={field.class} items={field.options} bind:value={field.value} />
153
- {/if}
154
- </Label>
155
- {/snippet}
156
-
157
- {#snippet renderCollection(field: CollectionField)}
158
- {#if field.type === 'checkbox'}{:else if field.type === 'radio'}{:else if field.type === 'toggle'}
159
- <Toggle
160
- id={field.id}
161
- name={field.id}
162
- required={field.required}
163
- readonly={field.readonly}
164
- disabled={field.disabled}
165
- class={field.class}
166
- checked={field.checked}
167
- >
168
- {field.label}
169
- </Toggle>
170
- {/if}
171
- {/snippet}
172
-
173
- {#snippet renderFile(field: FilePickerField)}
174
- <Label class={field.labelClass}>{field.label}</Label>
175
- <Fileupload
176
- id={field.id}
177
- name={field.id}
178
- required={field.required}
179
- readonly={field.readonly}
180
- disabled={field.disabled}
181
- class={field.class}
182
- multiple={field.multiple}
183
- />
184
- {/snippet}
185
-
186
- {#snippet renderPhoneInput(field: PhoneInputField)}
187
- <Label class={field.labelClass}>{field.label}</Label>
188
- <PhoneInput
189
- id={field.id}
190
- name={field.id}
191
- required={field.required}
192
- readonly={field.readonly}
193
- disabled={field.disabled}
194
- class={field.class}
195
- placeholder={field.placeholder}
196
- />
197
- {/snippet}
198
-
199
- {#snippet renderTimePicker(field: TimePickerField)}
200
- <Label for={field.id} class={field.labelClass}>{field.label}</Label>
201
- <Timepicker id={field.id} required={field.required} disabled={field.disabled} />
202
- {/snippet}
203
-
204
- {#snippet renderDatePicker(field: DatePickerField)}
205
- <Label for={field.id} class={field.labelClass}>{field.label}</Label>
206
- <Datepicker required={field.required} bind:value={field.value} />
207
- <input type="hidden" name={field.id} bind:value={field.value} />
208
- {/snippet}
209
-
210
- {#snippet renderHidden(field: HiddenField)}
211
- <input type="hidden" id={field.id} name={field.id} bind:value={field.value} />
212
- {/snippet}
213
-
214
- {#if showMessage}
215
- <div class="alert alert-success" transition:fade={{ duration: 2000 }}>
216
- {successMessage}
217
- </div>
218
- {:else}
219
- <form method="POST" {action} class={css} bind:this={form}>
220
- <fieldset>
221
- {#each fields as field}
222
- {#if field.typeOfField === 'input'}
223
- {@render renderInput(field)}
224
- {:else if field.typeOfField === 'textarea'}
225
- {@render renderTextarea(field)}
226
- {:else if field.typeOfField === 'select'}
227
- {@render renderSelect(field)}
228
- {:else if field.typeOfField === 'collection'}
229
- {@render renderCollection(field)}
230
- {:else if field.typeOfField === 'file'}
231
- {@render renderFile(field)}
232
- {:else if field.typeOfField === 'phoneInput'}
233
- {@render renderPhoneInput(field)}
234
- {:else if field.typeOfField === 'timepicker'}
235
- {@render renderTimePicker(field)}
236
- {:else if field.typeOfField === 'hidden'}
237
- {@render renderHidden(field)}
238
- {:else if field.typeOfField === 'date'}
239
- {@render renderDatePicker(field)}
240
- {/if}
241
- {/each}
242
-
243
- <div class="buttons">
244
- {#each buttons as button}
245
- {#if button.type === 'submit'}
246
- <Button
247
- type="button"
248
- class={button.class}
249
- color={button.color}
250
- onclick={handleSubmit}
251
- {loading}
252
- >
253
- {button.label}
254
- </Button>
255
- {:else}
256
- <Button
257
- type={button.type}
258
- class={button.class}
259
- color={button.color}
260
- onclick={button.action}
261
- >
262
- {button.label}
263
- </Button>
264
- {/if}
265
- {/each}
266
- </div>
267
- </fieldset>
268
- </form>
269
- {/if}
1
+ <script lang="ts">
2
+ import {
3
+ type Field,
4
+ type SelectField,
5
+ type CollectionField,
6
+ type TextareaField,
7
+ type InputField,
8
+ type FilePickerField,
9
+ type PhoneInputField,
10
+ type TimePickerField,
11
+ type ButtonConfig,
12
+ type HiddenField,
13
+ type DatePickerField,
14
+ SubmitResult,
15
+ } from './form.js';
16
+ import {
17
+ Button,
18
+ Label,
19
+ Fileupload,
20
+ Select,
21
+ MultiSelect,
22
+ Textarea,
23
+ Toggle,
24
+ Input,
25
+ FloatingLabelInput,
26
+ Timepicker,
27
+ PhoneInput,
28
+ Datepicker
29
+ } from 'flowbite-svelte';
30
+ import { fade } from 'svelte/transition';
31
+
32
+ interface Props {
33
+ action: string;
34
+ class?: string;
35
+ successMessage?: string;
36
+ fields?: Array<Field>;
37
+ onMessageSubmitted?: (result: SubmitResult) => void;
38
+ buttons: Array<ButtonConfig>;
39
+ }
40
+
41
+ let {
42
+ action,
43
+ fields = [],
44
+ class: css = '',
45
+ successMessage,
46
+ onMessageSubmitted,
47
+ buttons = [
48
+ {
49
+ label: 'Submit',
50
+ type: 'submit',
51
+ action: undefined
52
+ }
53
+ ]
54
+ }: Props = $props();
55
+
56
+ let form: HTMLFormElement;
57
+ let showMessage = $state(false);
58
+ let isSendingMessage = $state(false);
59
+ let loading = $state(false);
60
+
61
+ const handleSubmit = async (event: Event) => {
62
+ event.preventDefault();
63
+ loading = true;
64
+ try {
65
+ if (!isSendingMessage) {
66
+ isSendingMessage = true;
67
+ const formData = new FormData(form);
68
+ const response = await fetch(action, {
69
+ method: 'POST',
70
+ body: formData
71
+ });
72
+ showMessage = response.status === 200;
73
+ onMessageSubmitted?.(showMessage ? SubmitResult.Success : SubmitResult.Error);
74
+ setTimeout(() => {
75
+ showMessage = false;
76
+ }, 3000);
77
+ }
78
+ } catch (error) {
79
+ console.error('Error submitting form:', error);
80
+ } finally {
81
+ isSendingMessage = false;
82
+ }
83
+ loading = false;
84
+ };
85
+ </script>
86
+
87
+ {#snippet renderInput(field: InputField)}
88
+ {#if field.isFloat}
89
+ <FloatingLabelInput
90
+ id={field.id}
91
+ name={field.id}
92
+ required={field.required}
93
+ readonly={field.readonly}
94
+ disabled={field.disabled}
95
+ class={field.class}
96
+ size={field.size == 'sm' ? 'small' : 'default'}
97
+ type={field.type}
98
+ variant={field.floatVariant}
99
+ placeholder={field.placeholder}
100
+ bind:value={field.value}
101
+ >
102
+ {field.label}
103
+ </FloatingLabelInput>
104
+ {:else}
105
+ <Label for={field.id} class={field.labelClass}>{field.label}</Label>
106
+ <Input
107
+ id={field.id}
108
+ name={field.id}
109
+ required={field.required}
110
+ readonly={field.readonly}
111
+ disabled={field.disabled}
112
+ class={field.class}
113
+ size={field.size}
114
+ type={field.type}
115
+ placeholder={field.placeholder}
116
+ bind:value={field.value}
117
+ />
118
+ {/if}
119
+ {/snippet}
120
+
121
+ {#snippet renderTextarea(field: TextareaField)}
122
+ <Label for={field.id} class={field.labelClass}>{field.label}</Label>
123
+ <Textarea
124
+ id={field.id}
125
+ name={field.id}
126
+ required={field.required}
127
+ readonly={field.readonly}
128
+ disabled={field.disabled}
129
+ class={field.class}
130
+ rows={field.rows}
131
+ cols={field.cols}
132
+ placeholder={field.placeholder}
133
+ bind:value={field.value}
134
+ />
135
+ {/snippet}
136
+
137
+ {#snippet renderSelect(field: SelectField)}
138
+ <Label class={field.labelClass}
139
+ >{field.label}
140
+ {#if field.type === 'single'}
141
+ <Select
142
+ id={field.id}
143
+ name={field.id}
144
+ required={field.required}
145
+ disabled={field.disabled}
146
+ class={field.class}
147
+ items={field.options.map(option => ({ name: option.label, value: option.value }))}
148
+ bind:value={field.value}
149
+ />
150
+ {:else}
151
+ <MultiSelect class={field.class} items={field.options} bind:value={field.value} />
152
+ {/if}
153
+ </Label>
154
+ {/snippet}
155
+
156
+ {#snippet renderCollection(field: CollectionField)}
157
+ {#if field.type === 'checkbox'}{:else if field.type === 'radio'}{:else if field.type === 'toggle'}
158
+ <Toggle
159
+ id={field.id}
160
+ name={field.id}
161
+ required={field.required}
162
+ readonly={field.readonly}
163
+ disabled={field.disabled}
164
+ class={field.class}
165
+ checked={field.checked}
166
+ >
167
+ {field.label}
168
+ </Toggle>
169
+ {/if}
170
+ {/snippet}
171
+
172
+ {#snippet renderFile(field: FilePickerField)}
173
+ <Label class={field.labelClass}>{field.label}</Label>
174
+ <Fileupload
175
+ id={field.id}
176
+ name={field.id}
177
+ required={field.required}
178
+ readonly={field.readonly}
179
+ disabled={field.disabled}
180
+ class={field.class}
181
+ multiple={field.multiple}
182
+ />
183
+ {/snippet}
184
+
185
+ {#snippet renderPhoneInput(field: PhoneInputField)}
186
+ <Label class={field.labelClass}>{field.label}</Label>
187
+ <PhoneInput
188
+ id={field.id}
189
+ name={field.id}
190
+ required={field.required}
191
+ readonly={field.readonly}
192
+ disabled={field.disabled}
193
+ class={field.class}
194
+ placeholder={field.placeholder}
195
+ />
196
+ {/snippet}
197
+
198
+ {#snippet renderTimePicker(field: TimePickerField)}
199
+ <Label for={field.id} class={field.labelClass}>{field.label}</Label>
200
+ <Timepicker id={field.id} required={field.required} disabled={field.disabled} />
201
+ {/snippet}
202
+
203
+ {#snippet renderDatePicker(field: DatePickerField)}
204
+ <Label for={field.id} class={field.labelClass}>{field.label}</Label>
205
+ <Datepicker required={field.required} bind:value={field.value} />
206
+ <input type="hidden" name={field.id} bind:value={field.value} />
207
+ {/snippet}
208
+
209
+ {#snippet renderHidden(field: HiddenField)}
210
+ <input type="hidden" id={field.id} name={field.id} bind:value={field.value} />
211
+ {/snippet}
212
+
213
+ {#if showMessage}
214
+ <div class="alert alert-success" transition:fade={{ duration: 2000 }}>
215
+ {successMessage}
216
+ </div>
217
+ {:else}
218
+ <form method="POST" {action} class={css} bind:this={form}>
219
+ <fieldset>
220
+ {#each fields as field}
221
+ {#if field.typeOfField === 'input'}
222
+ {@render renderInput(field)}
223
+ {:else if field.typeOfField === 'textarea'}
224
+ {@render renderTextarea(field)}
225
+ {:else if field.typeOfField === 'select'}
226
+ {@render renderSelect(field)}
227
+ {:else if field.typeOfField === 'collection'}
228
+ {@render renderCollection(field)}
229
+ {:else if field.typeOfField === 'file'}
230
+ {@render renderFile(field)}
231
+ {:else if field.typeOfField === 'phoneInput'}
232
+ {@render renderPhoneInput(field)}
233
+ {:else if field.typeOfField === 'timepicker'}
234
+ {@render renderTimePicker(field)}
235
+ {:else if field.typeOfField === 'hidden'}
236
+ {@render renderHidden(field)}
237
+ {:else if field.typeOfField === 'date'}
238
+ {@render renderDatePicker(field)}
239
+ {/if}
240
+ {/each}
241
+
242
+ <div class="buttons">
243
+ {#each buttons as button}
244
+ {#if button.type === 'submit'}
245
+ <Button
246
+ type="button"
247
+ class={button.class}
248
+ color={button.color}
249
+ onclick={handleSubmit}
250
+ {loading}
251
+ >
252
+ {button.label}
253
+ </Button>
254
+ {:else}
255
+ <Button
256
+ type={button.type}
257
+ class={button.class}
258
+ color={button.color}
259
+ onclick={button.action}
260
+ >
261
+ {button.label}
262
+ </Button>
263
+ {/if}
264
+ {/each}
265
+ </div>
266
+ </fieldset>
267
+ </form>
268
+ {/if}
@@ -1,20 +1,20 @@
1
- <script lang="ts">
2
- interface Props {
3
- fonts: Array<{ name: string; values: string }>;
4
- }
5
-
6
- let { fonts = [] }: Partial<Props> = $props();
7
-
8
- const fontQuery = $derived(() =>
9
- fonts.map((font) => `family=${font.name}:${font.values}`).join('&')
10
- );
11
- </script>
12
-
13
- <svelte:head>
14
- <link rel="preconnect" href="https://fonts.googleapis.com" />
15
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
16
- <link
17
- rel="stylesheet"
18
- href={`https://fonts.googleapis.com/css2?${fontQuery}&display=swap`}
19
- />
20
- </svelte:head>
1
+ <script lang="ts">
2
+ interface Props {
3
+ fonts: Array<{ name: string; values: string }>;
4
+ }
5
+
6
+ let { fonts = [] }: Partial<Props> = $props();
7
+
8
+ const fontQuery = $derived(() =>
9
+ fonts.map((font) => `family=${font.name}:${font.values}`).join('&')
10
+ );
11
+ </script>
12
+
13
+ <svelte:head>
14
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
15
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
16
+ <link
17
+ rel="stylesheet"
18
+ href={`https://fonts.googleapis.com/css2?${fontQuery}&display=swap`}
19
+ />
20
+ </svelte:head>
@@ -1,28 +1,28 @@
1
- <script lang="ts">
2
- interface MetaProps {
3
- title: string;
4
- description: string;
5
- keywords: string;
6
- robots: string;
7
- charset: string;
8
- viewport: string;
9
- }
10
-
11
- let {
12
- title = '',
13
- description = '',
14
- keywords = '',
15
- robots = 'index, follow',
16
- charset = 'UTF-8',
17
- viewport = 'width=device-width, initial-scale=1.0'
18
- }: Partial<MetaProps> = $props();
19
- </script>
20
-
21
- <svelte:head>
22
- <title>{title}</title>
23
- <meta name="description" content={description} />
24
- <meta name="keywords" content={keywords} />
25
- <meta name="robots" content={robots} />
26
- <meta name="viewport" content={viewport} />
27
- <meta {charset} />
28
- </svelte:head>
1
+ <script lang="ts">
2
+ interface MetaProps {
3
+ title: string;
4
+ description: string;
5
+ keywords: string;
6
+ robots: string;
7
+ charset: string;
8
+ viewport: string;
9
+ }
10
+
11
+ let {
12
+ title = '',
13
+ description = '',
14
+ keywords = '',
15
+ robots = 'index, follow',
16
+ charset = 'UTF-8',
17
+ viewport = 'width=device-width, initial-scale=1.0'
18
+ }: Partial<MetaProps> = $props();
19
+ </script>
20
+
21
+ <svelte:head>
22
+ <title>{title}</title>
23
+ <meta name="description" content={description} />
24
+ <meta name="keywords" content={keywords} />
25
+ <meta name="robots" content={robots} />
26
+ <meta name="viewport" content={viewport} />
27
+ <meta {charset} />
28
+ </svelte:head>