flowbite-svelte 1.10.1 → 1.10.2

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,7 +1,8 @@
1
1
  <script lang="ts" generics="T">
2
2
  import { Badge, CloseButton, type MultiSelectProps, type SelectOptionType, cn } from "../..";
3
- import { multiselect, type MultiSelectTheme } from ".";
3
+ import { multiSelect, type MultiSelectTheme } from ".";
4
4
  import { getTheme } from "../../theme/themeUtils";
5
+ import { onMount, onDestroy } from "svelte"; // Import onMount and onDestroy
5
6
 
6
7
  // Consider reusing that component - https://svelecte.vercel.app/
7
8
 
@@ -33,7 +34,12 @@
33
34
  let activeIndex: number | null = $state(null);
34
35
  let activeItem = $derived(activeIndex !== null ? items[((activeIndex % items.length) + items.length) % items.length] : null);
35
36
 
36
- const selectOption = (select: SelectOptionType<any>) => {
37
+ let multiSelectContainer: HTMLDivElement; // Reference to the main div
38
+
39
+ const selectOption = (select: SelectOptionType<any>, event: MouseEvent) => {
40
+ // Prevent the click from propagating to the parent div
41
+ event.stopPropagation();
42
+
37
43
  if (disabled) return;
38
44
  if (select.disabled) return;
39
45
 
@@ -49,6 +55,7 @@
49
55
  if (JSON.stringify(oldValue) !== JSON.stringify(value)) {
50
56
  triggerChange();
51
57
  }
58
+ // Don't close the dropdown here
52
59
  };
53
60
 
54
61
  const clearAll = (e: MouseEvent) => {
@@ -94,11 +101,23 @@
94
101
  };
95
102
 
96
103
  const closeDropdown = () => !disabled && (show = false);
97
- const toggleDropdown = () => !disabled && (show = !show);
104
+ const toggleDropdown = (event: MouseEvent) => {
105
+ if (disabled) return;
106
+ // Prevent immediate closing if the click originated from within the component itself
107
+ // This is useful if the click triggers a re-render and focus is lost momentarily.
108
+ if (multiSelectContainer && multiSelectContainer.contains(event.target as Node)) {
109
+ show = !show;
110
+ } else {
111
+ show = false; // Close if clicked outside
112
+ }
113
+ };
98
114
 
99
115
  // Handle blur event for validation
100
116
  const handleBlur = (event: FocusEvent) => {
101
- closeDropdown();
117
+ // We'll rely more on the global click listener for closing, but keep this for standard blur behavior
118
+ if (event.currentTarget && (event.currentTarget as HTMLElement).contains && !(event.currentTarget as HTMLElement).contains(event.relatedTarget as Node)) {
119
+ closeDropdown();
120
+ }
102
121
  if (onblur) {
103
122
  onblur(event);
104
123
  }
@@ -112,7 +131,7 @@
112
131
  show = true;
113
132
  activeIndex = 0;
114
133
  } else {
115
- if (activeItem !== null) selectOption(activeItem);
134
+ if (activeItem !== null) selectOption(activeItem, new MouseEvent("click")); // Pass a dummy MouseEvent
116
135
  }
117
136
  }
118
137
 
@@ -133,8 +152,11 @@
133
152
 
134
153
  function handleKeyDown(event: KeyboardEvent) {
135
154
  if (disabled) return;
155
+ // Do not prevent default for tab key, allow it to move focus
156
+ if (event.key !== "Tab") {
157
+ event.preventDefault();
158
+ }
136
159
  event.stopPropagation();
137
- event.preventDefault();
138
160
 
139
161
  const actions = {
140
162
  Escape: closeDropdown,
@@ -148,17 +170,30 @@
148
170
  }
149
171
  }
150
172
 
151
- const { base, dropdown, dropdownitem, closebutton, select } = multiselect({ disabled });
173
+ // Global click listener for closing the dropdown when clicking outside
174
+ onMount(() => {
175
+ const handleClickOutside = (event: MouseEvent) => {
176
+ if (multiSelectContainer && !multiSelectContainer.contains(event.target as Node)) {
177
+ closeDropdown();
178
+ }
179
+ };
180
+ document.addEventListener("click", handleClickOutside);
181
+
182
+ return () => {
183
+ document.removeEventListener("click", handleClickOutside);
184
+ };
185
+ });
186
+
187
+ const { base, dropdown, dropdownitem, closebutton, select } = multiSelect({ disabled });
152
188
  </script>
153
189
 
154
- <!-- Hidden select for form submission -->
155
190
  <select {name} {form} {required} {autocomplete} {value} hidden multiple {onchange}>
156
191
  {#each items as item}
157
192
  <option value={item.value} disabled={item.disabled}>{item.name}</option>
158
193
  {/each}
159
194
  </select>
160
195
 
161
- <div {...restProps} onclick={toggleDropdown} onblur={handleBlur} onkeydown={handleKeyDown} tabindex="0" role="listbox" class={cn(base({ size }), className, (theme as MultiSelectTheme)?.base)}>
196
+ <div bind:this={multiSelectContainer} {...restProps} onclick={toggleDropdown} onblur={handleBlur} onkeydown={handleKeyDown} tabindex="0" role="listbox" class={cn(base({ size }), className, (theme as MultiSelectTheme)?.base)}>
162
197
  {#if !selectItems.length}
163
198
  <span class="text-gray-400">{placeholder}</span>
164
199
  {/if}
@@ -189,7 +224,7 @@
189
224
  <div role="presentation" class={cn(dropdown(), dropdownClass)}>
190
225
  {#each items as item (item.name)}
191
226
  <div
192
- onclick={() => selectOption(item)}
227
+ onclick={(e) => selectOption(item, e)}
193
228
  role="presentation"
194
229
  class={dropdownitem({
195
230
  selected: selectItems.includes(item),
@@ -1,3 +1,3 @@
1
1
  export { default as Select } from "./Select.svelte";
2
2
  export { default as MultiSelect } from "./MultiSelect.svelte";
3
- export { select, multiselect, type SelectTheme, type MultiSelectTheme } from "./theme";
3
+ export { select, multiSelect, type SelectTheme, type MultiSelectTheme } from "./theme";
@@ -1,3 +1,3 @@
1
1
  export { default as Select } from "./Select.svelte";
2
2
  export { default as MultiSelect } from "./MultiSelect.svelte";
3
- export { select, multiselect } from "./theme";
3
+ export { select, multiSelect } from "./theme";
@@ -93,8 +93,8 @@ export declare const select: import("tailwind-variants").TVReturnType<{
93
93
  }, undefined, unknown, unknown, undefined>>;
94
94
  export type SelectSlots = keyof typeof select.slots;
95
95
  export type SelectTheme = Partial<Record<SelectSlots, string>>;
96
- export type MultiSelectVariants = VariantProps<typeof multiselect>;
97
- export declare const multiselect: import("tailwind-variants").TVReturnType<{
96
+ export type MultiSelectVariants = VariantProps<typeof multiSelect>;
97
+ export declare const multiSelect: import("tailwind-variants").TVReturnType<{
98
98
  size: {
99
99
  sm: string;
100
100
  md: string;
@@ -191,5 +191,5 @@ export declare const multiselect: import("tailwind-variants").TVReturnType<{
191
191
  dropdownitem: string;
192
192
  closebutton: string;
193
193
  }, undefined, unknown, unknown, undefined>>;
194
- export type MultiSelectSlots = keyof typeof multiselect.slots;
194
+ export type MultiSelectSlots = keyof typeof multiSelect.slots;
195
195
  export type MultiSelectTheme = Partial<Record<MultiSelectSlots, string>>;
@@ -31,7 +31,7 @@ export const select = tv({
31
31
  size: "md"
32
32
  }
33
33
  });
34
- export const multiselect = tv({
34
+ export const multiSelect = tv({
35
35
  slots: {
36
36
  base: "relative border border-gray-300 flex items-center rounded-lg gap-2 dark:border-gray-600 ring-primary-500 dark:ring-primary-500 focus-visible:outline-hidden",
37
37
  select: "flex flex-wrap gap-2",
@@ -64,6 +64,16 @@ export const multiselect = tv({
64
64
  }
65
65
  }
66
66
  },
67
+ // Add compoundVariants here
68
+ compoundVariants: [
69
+ {
70
+ selected: true,
71
+ active: true,
72
+ class: {
73
+ dropdownitem: "bg-primary-200 dark:bg-primary-600 text-primary-700 dark:text-primary-100 font-semibold" // Adjust colors as needed
74
+ }
75
+ }
76
+ ],
67
77
  defaultVariants: {
68
78
  underline: false,
69
79
  size: "md"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowbite-svelte",
3
- "version": "1.10.1",
3
+ "version": "1.10.2",
4
4
  "description": "Flowbite components for Svelte",
5
5
  "main": "dist/index.js",
6
6
  "author": {