@x33025/sveltely 0.1.0 → 0.1.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.
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<script lang="ts">
|
|
9
9
|
import Dropdown from './Dropdown.svelte';
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const allItems = [
|
|
12
12
|
{
|
|
13
13
|
type: 'group' as const,
|
|
14
14
|
label: 'Publishing',
|
|
@@ -48,7 +48,35 @@
|
|
|
48
48
|
}
|
|
49
49
|
];
|
|
50
50
|
|
|
51
|
+
const filterEntries = (entries: typeof allItems, query: string): typeof allItems => {
|
|
52
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
53
|
+
if (!normalizedQuery) return allItems;
|
|
54
|
+
|
|
55
|
+
return entries.flatMap((entry) => {
|
|
56
|
+
if (entry.type === 'group') {
|
|
57
|
+
const nextItems = filterEntries(entry.items, normalizedQuery);
|
|
58
|
+
return nextItems.length > 0 ? [{ ...entry, items: nextItems }] : [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (entry.type === 'submenu') {
|
|
62
|
+
const nextItems = filterEntries(entry.items, normalizedQuery);
|
|
63
|
+
const matches = entry.label.toLowerCase().includes(normalizedQuery);
|
|
64
|
+
if (!matches && nextItems.length === 0) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return [{ ...entry, items: matches ? entry.items : nextItems }];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return entry.label.toLowerCase().includes(normalizedQuery) ? [entry] : [];
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let items = $state(allItems);
|
|
51
75
|
let value = $state<string | null>('draft');
|
|
76
|
+
|
|
77
|
+
const handleSearch = async (query: string) => {
|
|
78
|
+
items = filterEntries(allItems, query);
|
|
79
|
+
};
|
|
52
80
|
</script>
|
|
53
81
|
|
|
54
|
-
<Dropdown {items} bind:value placeholder="Choose status"
|
|
82
|
+
<Dropdown {items} bind:value placeholder="Choose status" onSearch={handleSearch} />
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
disabled?: boolean;
|
|
22
22
|
closeOnSelect?: boolean;
|
|
23
23
|
showCheck?: boolean;
|
|
24
|
-
searchable?: boolean;
|
|
25
24
|
searchPlaceholder?: string;
|
|
26
25
|
placement?: Anchor;
|
|
26
|
+
onSearch?: (query: string) => void | Promise<void>;
|
|
27
27
|
onSelect?: (item: DropdownItem<T>) => void;
|
|
28
28
|
} & StyleProps;
|
|
29
29
|
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
disabled = false,
|
|
44
44
|
closeOnSelect = true,
|
|
45
45
|
showCheck = true,
|
|
46
|
-
searchable = false,
|
|
47
46
|
searchPlaceholder = 'Search',
|
|
48
47
|
placement = 'bottom',
|
|
48
|
+
onSearch,
|
|
49
49
|
onSelect,
|
|
50
50
|
...styleProps
|
|
51
51
|
}: Props = $props();
|
|
@@ -53,6 +53,9 @@
|
|
|
53
53
|
const dropdownStyle = $derived.by(() => surfaceStyle(styleProps, 'dropdown'));
|
|
54
54
|
|
|
55
55
|
let query = $state('');
|
|
56
|
+
let lastSearchedQuery = '';
|
|
57
|
+
|
|
58
|
+
const searchEnabled = $derived(!!onSearch);
|
|
56
59
|
|
|
57
60
|
const flattenItems = (
|
|
58
61
|
entries: DropdownEntry<T>[],
|
|
@@ -69,25 +72,6 @@
|
|
|
69
72
|
return [{ ...entry, disabled: nextDisabled || entry.disabled }];
|
|
70
73
|
});
|
|
71
74
|
|
|
72
|
-
const filterEntries = (entries: DropdownEntry<T>[]): DropdownEntry<T>[] =>
|
|
73
|
-
entries.flatMap((entry) => {
|
|
74
|
-
if (isGroup(entry)) {
|
|
75
|
-
const nextItems = filterEntries(entry.items);
|
|
76
|
-
return nextItems.length > 0 ? [{ ...entry, items: nextItems }] : [];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (isSubmenu(entry)) {
|
|
80
|
-
const nextItems = filterEntries(entry.items);
|
|
81
|
-
const matches = entry.label.toLowerCase().includes(normalizedQuery);
|
|
82
|
-
if (!matches && nextItems.length === 0) {
|
|
83
|
-
return [];
|
|
84
|
-
}
|
|
85
|
-
return [{ ...entry, items: matches ? entry.items : nextItems }];
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return entry.label.toLowerCase().includes(normalizedQuery) ? [entry] : [];
|
|
89
|
-
});
|
|
90
|
-
|
|
91
75
|
const findFirstRenderableLabel = (entries: DropdownEntry<T>[]): string | null => {
|
|
92
76
|
for (const entry of entries) {
|
|
93
77
|
if (isGroup(entry)) {
|
|
@@ -100,8 +84,6 @@
|
|
|
100
84
|
return null;
|
|
101
85
|
};
|
|
102
86
|
|
|
103
|
-
const normalizedQuery = $derived(query.trim().toLowerCase());
|
|
104
|
-
|
|
105
87
|
const flatItems = $derived.by(() => flattenItems(items));
|
|
106
88
|
|
|
107
89
|
const selectedItem = $derived.by(
|
|
@@ -110,14 +92,12 @@
|
|
|
110
92
|
|
|
111
93
|
const triggerText = $derived(selectedItem?.label ?? placeholder);
|
|
112
94
|
|
|
113
|
-
const filteredItems = $derived
|
|
114
|
-
normalizedQuery ? filterEntries(items) : items
|
|
115
|
-
);
|
|
95
|
+
const filteredItems = $derived(items);
|
|
116
96
|
|
|
117
97
|
const firstRenderableLabel = $derived.by(() => findFirstRenderableLabel(filteredItems));
|
|
118
98
|
|
|
119
99
|
const itemRadiusSourceEnabled = $derived.by(() => {
|
|
120
|
-
if (
|
|
100
|
+
if (searchEnabled) return false;
|
|
121
101
|
const firstEntry = filteredItems[0];
|
|
122
102
|
if (!firstEntry) return false;
|
|
123
103
|
if (isGroup(firstEntry)) {
|
|
@@ -143,6 +123,13 @@
|
|
|
143
123
|
disabled || inheritedDisabled || !!entry.disabled;
|
|
144
124
|
|
|
145
125
|
const isRadiusSource = (label: string) => itemRadiusSourceEnabled && label === firstRenderableLabel;
|
|
126
|
+
|
|
127
|
+
$effect(() => {
|
|
128
|
+
if (!onSearch) return;
|
|
129
|
+
if (query === lastSearchedQuery) return;
|
|
130
|
+
lastSearchedQuery = query;
|
|
131
|
+
void onSearch(query);
|
|
132
|
+
});
|
|
146
133
|
</script>
|
|
147
134
|
|
|
148
135
|
<Floating
|
|
@@ -174,9 +161,9 @@
|
|
|
174
161
|
|
|
175
162
|
<div
|
|
176
163
|
class="dropdown-content vstack"
|
|
177
|
-
style={`${dropdownStyle} --dropdown-item-radius: ${
|
|
164
|
+
style={`${dropdownStyle} --dropdown-item-radius: ${searchEnabled ? 'var(--sveltely-border-radius)' : 'var(--sveltely-border-radius-nested)'};`}
|
|
178
165
|
>
|
|
179
|
-
{#if
|
|
166
|
+
{#if searchEnabled}
|
|
180
167
|
<SearchInput
|
|
181
168
|
bind:value={query}
|
|
182
169
|
placeholder={searchPlaceholder}
|
|
@@ -11,9 +11,9 @@ declare function $$render<T extends string | number = string>(): {
|
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
closeOnSelect?: boolean;
|
|
13
13
|
showCheck?: boolean;
|
|
14
|
-
searchable?: boolean;
|
|
15
14
|
searchPlaceholder?: string;
|
|
16
15
|
placement?: Anchor;
|
|
16
|
+
onSearch?: (query: string) => void | Promise<void>;
|
|
17
17
|
onSelect?: (item: DropdownItem<T>) => void;
|
|
18
18
|
} & StyleProps;
|
|
19
19
|
exports: {};
|