flowbite-svelte 1.8.3 → 1.8.5

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.
@@ -6,10 +6,41 @@
6
6
  import { Button, ToolbarButton, type DatepickerProps, cn } from "..";
7
7
  import { datepicker } from "./theme";
8
8
 
9
- let { value = $bindable(), defaultDate = null, range = false, rangeFrom = $bindable(), rangeTo = $bindable(), availableFrom = null, availableTo = null, locale = "default", firstDayOfWeek = 0, dateFormat, placeholder = "Select date", disabled = false, required = false, inputClass = "", color = "primary", inline = false, autohide = true, showActionButtons = false, title = "", onselect, onclear, onapply, btnClass, inputmode = "none", classes, monthColor = "alternative", monthBtnSelected = "bg-primary-500 text-white", monthBtn = "text-gray-700 dark:text-gray-300", class: className }: DatepickerProps = $props();
9
+ let {
10
+ value = $bindable(),
11
+ defaultDate = null,
12
+ range = false,
13
+ rangeFrom = $bindable(),
14
+ rangeTo = $bindable(),
15
+ availableFrom = null,
16
+ availableTo = null,
17
+ locale = "default",
18
+ translationLocale = locale, // NEW: Separate locale for translations, defaults to locale for backward compatibility
19
+ firstDayOfWeek = 0,
20
+ dateFormat,
21
+ placeholder = "Select date",
22
+ disabled = false,
23
+ required = false,
24
+ inputClass = "",
25
+ color = "primary",
26
+ inline = false,
27
+ autohide = true,
28
+ showActionButtons = false,
29
+ title = "",
30
+ onselect,
31
+ onclear,
32
+ onapply,
33
+ btnClass,
34
+ inputmode = "none",
35
+ classes,
36
+ monthColor = "alternative",
37
+ monthBtnSelected = "bg-primary-500 text-white",
38
+ monthBtn = "text-gray-700 dark:text-gray-300",
39
+ class: className
40
+ }: DatepickerProps & { translationLocale?: string } = $props();
10
41
 
11
42
  const dateFormatDefault = { year: "numeric", month: "long", day: "numeric" };
12
- // const dateFormatOptions = $derived(dateFormat ?? dateFormatDefault);
43
+
13
44
  // Internal state
14
45
  let isOpen: boolean = $state(inline);
15
46
  let showMonthSelector: boolean = $state(false);
@@ -60,17 +91,18 @@
60
91
  return daysArray;
61
92
  }
62
93
 
94
+ // MODIFIED: Use translationLocale for weekday names
63
95
  const getWeekdayNames = (): string[] => {
64
- return Array.from({ length: 7 }, (_, i) => new Date(1970, 0, 5 + i + firstDayOfWeek).toLocaleDateString(locale, { weekday: "short" }));
96
+ return Array.from({ length: 7 }, (_, i) => new Date(1970, 0, 5 + i + firstDayOfWeek).toLocaleDateString(translationLocale, { weekday: "short" }));
65
97
  };
66
- let weekdays = getWeekdayNames();
98
+ let weekdays = $derived(getWeekdayNames());
67
99
 
100
+ // MODIFIED: Use translationLocale for month names
68
101
  const getMonthNames = (): string[] => {
69
- return Array.from({ length: 12 }, (_, i) => new Date(2000, i, 1).toLocaleDateString(locale, { month: "short" }));
102
+ return Array.from({ length: 12 }, (_, i) => new Date(2000, i, 1).toLocaleDateString(translationLocale, { month: "short" }));
70
103
  };
71
- let monthNames = getMonthNames();
104
+ let monthNames = $derived(getMonthNames());
72
105
 
73
- // const addMonth = (date: Date, increment: number): Date => new Date(date.getFullYear(), date.getMonth() + increment, 1);
74
106
  const addDay = (date: Date, increment: number): Date => new Date(date.getFullYear(), date.getMonth(), date.getDate() + increment);
75
107
 
76
108
  function changeMonth(increment: number) {
@@ -92,7 +124,6 @@
92
124
  showMonthSelector = !showMonthSelector;
93
125
  }
94
126
 
95
- // Helper function to check if a date is available for selection
96
127
  function isDateAvailable(date: Date): boolean {
97
128
  const dateOnly = new Date(date.getFullYear(), date.getMonth(), date.getDate());
98
129
 
@@ -110,7 +141,6 @@
110
141
  }
111
142
 
112
143
  function handleDaySelect(day: Date) {
113
- // Don't allow selection of unavailable dates
114
144
  if (!isDateAvailable(day)) return;
115
145
 
116
146
  if (range) {
@@ -145,6 +175,7 @@
145
175
  }
146
176
  }
147
177
 
178
+ // MODIFIED: Use locale for formatting (not translationLocale)
148
179
  const formatDate = (date?: Date): string => date?.toLocaleDateString(locale, dateFormat) ?? "";
149
180
  const isSameDate = (date1?: Date, date2?: Date): boolean => date1?.toDateString() === date2?.toDateString();
150
181
  const isToday = (day: Date): boolean => isSameDate(day, new Date());
@@ -189,9 +220,9 @@
189
220
  currentMonth = new Date(focusedDate.getFullYear(), focusedDate.getMonth(), 1);
190
221
  }
191
222
 
192
- // Focus the button for the focused date
223
+ // MODIFIED: Use translationLocale for aria-label
193
224
  setTimeout(() => {
194
- const focusedButton = calendarRef?.querySelector(`button[aria-label="${focusedDate!.toLocaleDateString(locale, { weekday: "long", year: "numeric", month: "long", day: "numeric" })}"]`) as HTMLButtonElement | null;
225
+ const focusedButton = calendarRef?.querySelector(`button[aria-label="${focusedDate!.toLocaleDateString(translationLocale, { weekday: "long", year: "numeric", month: "long", day: "numeric" })}"]`) as HTMLButtonElement | null;
195
226
  focusedButton?.focus();
196
227
  }, 0);
197
228
  }
@@ -271,8 +302,9 @@
271
302
  <!-- Regular Calendar View -->
272
303
  <div class={nav({ class: clsx(classes?.nav) })}>
273
304
  {@render navButton(false)}
305
+ <!-- MODIFIED: Use translationLocale for month/year display -->
274
306
  <Button type="button" class={cn(polite({ class: clsx(classes?.polite) }), "cursor-pointer rounded px-2 py-1 hover:bg-gray-100 dark:hover:bg-gray-700")} aria-live="polite" onclick={(event: MouseEvent) => toggleMonthSelector(event)}>
275
- {currentMonth.toLocaleString(locale, { month: "long", year: "numeric" })}
307
+ {currentMonth.toLocaleString(translationLocale, { month: "long", year: "numeric" })}
276
308
  </Button>
277
309
  {@render navButton(true)}
278
310
  </div>
@@ -295,7 +327,7 @@
295
327
  })}
296
328
  onclick={() => handleDaySelect(day)}
297
329
  onkeydown={handleCalendarKeydown}
298
- aria-label={day.toLocaleDateString(locale, { weekday: "long", year: "numeric", month: "long", day: "numeric" })}
330
+ aria-label={day.toLocaleDateString(translationLocale, { weekday: "long", year: "numeric", month: "long", day: "numeric" })}
299
331
  aria-selected={isSelected(day)}
300
332
  aria-disabled={!available}
301
333
  disabled={!available}
@@ -317,40 +349,3 @@
317
349
  </div>
318
350
  {/if}
319
351
  </div>
320
-
321
- <!--
322
- @component
323
- [Go to docs](https://flowbite-svelte.com/)
324
- ## Type
325
- [DatepickerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L487)
326
- ## Props
327
- @prop value = $bindable()
328
- @prop defaultDate = null
329
- @prop range = false
330
- @prop rangeFrom = $bindable()
331
- @prop rangeTo = $bindable()
332
- @prop availableFrom = null
333
- @prop availableTo = null
334
- @prop locale = "default"
335
- @prop firstDayOfWeek = 0
336
- @prop dateFormat
337
- @prop placeholder = "Select date"
338
- @prop disabled = false
339
- @prop required = false
340
- @prop inputClass = ""
341
- @prop color = "primary"
342
- @prop inline = false
343
- @prop autohide = true
344
- @prop showActionButtons = false
345
- @prop title = ""
346
- @prop onselect
347
- @prop onclear
348
- @prop onapply
349
- @prop btnClass
350
- @prop inputmode = "none"
351
- @prop classes
352
- @prop monthColor = "alternative"
353
- @prop monthBtnSelected = "bg-primary-500 text-white"
354
- @prop monthBtn = "text-gray-700 dark:text-gray-300"
355
- @prop class: className
356
- -->
@@ -1,39 +1,7 @@
1
1
  import { type DatepickerProps } from "..";
2
- /**
3
- * [Go to docs](https://flowbite-svelte.com/)
4
- * ## Type
5
- * [DatepickerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L487)
6
- * ## Props
7
- * @prop value = $bindable()
8
- * @prop defaultDate = null
9
- * @prop range = false
10
- * @prop rangeFrom = $bindable()
11
- * @prop rangeTo = $bindable()
12
- * @prop availableFrom = null
13
- * @prop availableTo = null
14
- * @prop locale = "default"
15
- * @prop firstDayOfWeek = 0
16
- * @prop dateFormat
17
- * @prop placeholder = "Select date"
18
- * @prop disabled = false
19
- * @prop required = false
20
- * @prop inputClass = ""
21
- * @prop color = "primary"
22
- * @prop inline = false
23
- * @prop autohide = true
24
- * @prop showActionButtons = false
25
- * @prop title = ""
26
- * @prop onselect
27
- * @prop onclear
28
- * @prop onapply
29
- * @prop btnClass
30
- * @prop inputmode = "none"
31
- * @prop classes
32
- * @prop monthColor = "alternative"
33
- * @prop monthBtnSelected = "bg-primary-500 text-white"
34
- * @prop monthBtn = "text-gray-700 dark:text-gray-300"
35
- * @prop class: className
36
- */
37
- declare const Datepicker: import("svelte").Component<DatepickerProps, {}, "value" | "rangeFrom" | "rangeTo">;
2
+ type $$ComponentProps = DatepickerProps & {
3
+ translationLocale?: string;
4
+ };
5
+ declare const Datepicker: import("svelte").Component<$$ComponentProps, {}, "value" | "rangeFrom" | "rangeTo">;
38
6
  type Datepicker = ReturnType<typeof Datepicker>;
39
7
  export default Datepicker;
@@ -1,26 +1,17 @@
1
1
  <script lang="ts">
2
- import { type ParamsType, type ModalProps, CloseButton, trapFocus } from "..";
3
- import { twMerge } from "tailwind-merge";
2
+ import { type ModalProps, type ParamsType, CloseButton, trapFocus } from "..";
4
3
  import clsx from "clsx";
5
4
  import { sineIn } from "svelte/easing";
6
5
  import { fade } from "svelte/transition";
7
6
  import { modal as modalTheme } from ".";
8
7
 
9
- let { children, oncancel, onclose, modal = true, autoclose = false, header, footer, title, open = $bindable(false), permanent = false, dismissable = true, closeBtnClass, headerClass, bodyClass, footerClass, outsideclose = true, size = "md", placement, class: className, params, transition = fade, ...restProps }: ModalProps = $props();
8
+ let { children, oncancel, onsubmit, modal = true, autoclose = false, header, footer, title, open = $bindable(false), permanent = false, dismissable = true, closeBtnClass, headerClass, bodyClass, footerClass, outsideclose = true, size = "md", placement, class: className, params, transition = fade, ...restProps }: ModalProps = $props();
10
9
 
11
10
  const paramsDefault = { duration: 100, easing: sineIn };
12
11
  const paramsOptions = $derived(params ?? paramsDefault);
13
12
 
14
13
  const { base, header: headerCls, footer: footerCls, body, closeBtn } = $derived(modalTheme({ placement, size }));
15
14
 
16
- const closeModal = () => {
17
- // Only close if not permanent
18
- if (!permanent) {
19
- open = false;
20
- onclose?.();
21
- }
22
- };
23
-
24
15
  function _oncancel(ev: Event & { currentTarget: HTMLDialogElement }) {
25
16
  // this event gets called when user presses ESC key
26
17
  // We'll handle ESC via the trapFocus action instead
@@ -35,21 +26,23 @@
35
26
  function _onclick(ev: Event & { currentTarget: HTMLDialogElement }) {
36
27
  if (ev.currentTarget instanceof HTMLDialogElement) {
37
28
  if (outsideclose && ev.target === ev.currentTarget && !permanent) {
38
- closeModal();
29
+ open = false;
39
30
  }
40
31
  if (autoclose && ev.target instanceof HTMLButtonElement && !permanent) {
41
- closeModal();
32
+ open = false;
42
33
  }
43
34
  }
44
35
  }
45
36
 
46
37
  let dlg: HTMLDialogElement | undefined = $state();
47
38
 
48
- $effect(() => {
49
- if (permanent && !open) {
50
- open = true;
51
- }
52
- });
39
+ // This prevents total dialog closing and we want only to prevent cancelling
40
+ // The close with user defined actions should be allowed
41
+ // $effect(() => {
42
+ // if (permanent && !open) {
43
+ // open = true;
44
+ // }
45
+ // });
53
46
 
54
47
  // Handler for Escape key that respects component state
55
48
  const handleEscape = () => {
@@ -57,32 +50,55 @@
57
50
  oncancel?.({ currentTarget: dlg } as any);
58
51
  // If oncancel prevented default, we don't close
59
52
  if (oncancel && event?.defaultPrevented) return;
60
- closeModal();
53
+ open = false;
61
54
  }
62
55
  };
56
+
57
+ function _onsubmit(ev: SubmitEvent) {
58
+ if (!dlg) return;
59
+
60
+ if (ev.submitter instanceof HTMLButtonElement || ev.submitter instanceof HTMLInputElement) {
61
+ dlg.returnValue = ev.submitter.value;
62
+ }
63
+
64
+ // @ts-ignore
65
+ onsubmit?.(ev); // forward event to user handle
66
+
67
+ if (!ev.defaultPrevented) {
68
+ // stop dialog.close() and trigger close with transition
69
+ ev.preventDefault();
70
+ open = false;
71
+ }
72
+ }
73
+
74
+ function init(dlg: HTMLDialogElement) {
75
+ modal ? dlg?.showModal() : dlg?.show();
76
+ return () => dlg?.close();
77
+ }
63
78
  </script>
64
79
 
65
80
  {#if open}
66
- <dialog use:trapFocus={{ onEscape: handleEscape }} bind:this={dlg} {...restProps} class={twMerge(base(), clsx(className))} tabindex="-1" oncancel={_oncancel} onclick={_onclick} transition:transition|global={paramsOptions as ParamsType} onintrostart={() => (modal ? dlg?.showModal() : dlg?.show())} onoutroend={() => dlg?.close()}>
81
+ <dialog use:trapFocus={{ onEscape: handleEscape }} {@attach init} onsubmit={_onsubmit} bind:this={dlg} {...restProps} class={base({ class: clsx(className) })} tabindex="-1" oncancel={_oncancel} onclick={_onclick} transition:transition|global={paramsOptions as ParamsType}>
67
82
  {#if title || header}
68
- <div class={twMerge(headerCls(), clsx(headerClass))}>
83
+ <div class={headerCls({ class: clsx(headerClass) })}>
69
84
  {#if title}
70
85
  <h3>{title}</h3>
86
+ <CloseButton onclick={() => (open = false)} class={clsx(closeBtnClass)} />
71
87
  {:else if header}
72
88
  {@render header()}
73
89
  {/if}
74
90
  </div>
75
91
  {/if}
76
- <div class={twMerge(body(), clsx(bodyClass))}>
92
+ <div class={body({ class: clsx(bodyClass) })}>
77
93
  {@render children?.()}
78
94
  </div>
79
95
  {#if footer}
80
- <div class={twMerge(footerCls(), clsx(footerClass))}>
96
+ <div class={footerCls({ class: clsx(footerClass) })}>
81
97
  {@render footer()}
82
98
  </div>
83
99
  {/if}
84
- {#if dismissable && !permanent}
85
- <CloseButton onclick={closeModal} class={twMerge(closeBtn(), clsx(closeBtnClass))} />
100
+ {#if dismissable && !permanent && !title}
101
+ <CloseButton onclick={() => (open = false)} class={closeBtn({ class: clsx(closeBtnClass) })} />
86
102
  {/if}
87
103
  </dialog>
88
104
  {/if}
@@ -95,7 +111,7 @@
95
111
  ## Props
96
112
  @prop children
97
113
  @prop oncancel
98
- @prop onclose
114
+ @prop onsubmit
99
115
  @prop modal = true
100
116
  @prop autoclose = false
101
117
  @prop header
@@ -6,7 +6,7 @@ import { type ModalProps } from "..";
6
6
  * ## Props
7
7
  * @prop children
8
8
  * @prop oncancel
9
- * @prop onclose
9
+ * @prop onsubmit
10
10
  * @prop modal = true
11
11
  * @prop autoclose = false
12
12
  * @prop header
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowbite-svelte",
3
- "version": "1.8.3",
3
+ "version": "1.8.5",
4
4
  "description": "Flowbite components for Svelte",
5
5
  "main": "dist/index.js",
6
6
  "author": {
@@ -12,35 +12,34 @@
12
12
  "homepage": "https://flowbite-svelte.com/",
13
13
  "license": "MIT",
14
14
  "devDependencies": {
15
- "@changesets/cli": "2.29.2",
15
+ "@changesets/cli": "2.29.5",
16
16
  "@docsearch/css": "^3.9.0",
17
17
  "@docsearch/js": "^3.9.0",
18
18
  "@eslint/compat": "^1.3.1",
19
- "@eslint/js": "^9.29.0",
19
+ "@eslint/js": "^9.30.1",
20
20
  "@flowbite-svelte-plugins/chart": "^0.2.4",
21
- "@flowbite-svelte-plugins/datatable": "^0.3.4",
22
- "@flowbite-svelte-plugins/texteditor": "^0.20.1",
23
- "@playwright/test": "^1.53.1",
21
+ "@flowbite-svelte-plugins/datatable": "^0.4.0",
22
+ "@flowbite-svelte-plugins/texteditor": "^0.24.0",
23
+ "@playwright/test": "^1.53.2",
24
24
  "@sveltejs/adapter-auto": "^6.0.1",
25
25
  "@sveltejs/adapter-vercel": "^5.7.2",
26
26
  "@sveltejs/kit": "^2.22.2",
27
- "@sveltejs/package": "2.3.11",
27
+ "@sveltejs/package": "2.3.12",
28
28
  "@sveltejs/vite-plugin-svelte": "^5.1.0",
29
29
  "@svitejs/changesets-changelog-github-compact": "^1.2.0",
30
30
  "@tailwindcss/vite": "^4.1.11",
31
31
  "@testing-library/jest-dom": "^6.6.3",
32
32
  "@testing-library/svelte": "^5.2.8",
33
33
  "@testing-library/user-event": "^14.6.1",
34
- "@tiptap/core": "^2.23.0",
34
+ "@tiptap/core": "3.0.0-beta.24",
35
35
  "dayjs": "^1.11.13",
36
36
  "deepmerge": "^4.3.1",
37
- "eslint": "^9.29.0",
37
+ "eslint": "^9.30.1",
38
38
  "eslint-config-prettier": "^10.1.5",
39
39
  "eslint-plugin-svelte": "^3.10.1",
40
40
  "flowbite-svelte-icons": "^2.2.1",
41
41
  "flowbite-typography": "^1.0.5",
42
- "globals": "^16.2.0",
43
- "highlight.js": "^11.11.1",
42
+ "globals": "^16.3.0",
44
43
  "jsdom": "^26.1.0",
45
44
  "katex": "^0.16.22",
46
45
  "lowlight": "^3.3.0",
@@ -52,7 +51,7 @@
52
51
  "prism-themes": "^1.9.0",
53
52
  "publint": "^0.3.12",
54
53
  "simple-datatables": "^10.0.0",
55
- "svelte": "^5.34.8",
54
+ "svelte": "^5.35.4",
56
55
  "svelte-check": "^4.2.2",
57
56
  "svelte-doc-llm": "^0.2.2",
58
57
  "svelte-lib-helpers": "^0.4.30",
@@ -61,9 +60,9 @@
61
60
  "tailwindcss": "^4.1.11",
62
61
  "tsx": "^4.20.3",
63
62
  "typescript": "^5.8.3",
64
- "typescript-eslint": "8.31.1",
63
+ "typescript-eslint": "8.36.0",
65
64
  "vite": "^6.3.5",
66
- "vite-plugin-devtools-json": "^0.2.0",
65
+ "vite-plugin-devtools-json": "^0.2.1",
67
66
  "vitest": "^3.2.4"
68
67
  },
69
68
  "peerDependencies": {
@@ -114,8 +113,8 @@
114
113
  "url": "git+https://github.com/themesberg/flowbite-svelte.git"
115
114
  },
116
115
  "dependencies": {
117
- "@floating-ui/dom": "^1.7.1",
118
- "@floating-ui/utils": "^0.2.9",
116
+ "@floating-ui/dom": "^1.7.2",
117
+ "@floating-ui/utils": "^0.2.10",
119
118
  "apexcharts": "^4.7.0",
120
119
  "clsx": "^2.1.1",
121
120
  "flowbite": "^3.1.2",