@casinogate/ui 1.10.6 → 1.10.7
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/dist/assets/css/root.css
CHANGED
|
@@ -369,6 +369,9 @@
|
|
|
369
369
|
.cgui\:h-\(--cg-ui-max-content-height\) {
|
|
370
370
|
height: var(--cg-ui-max-content-height);
|
|
371
371
|
}
|
|
372
|
+
.cgui\:h-1 {
|
|
373
|
+
height: calc(var(--cgui-spacing) * 1);
|
|
374
|
+
}
|
|
372
375
|
.cgui\:h-3 {
|
|
373
376
|
height: calc(var(--cgui-spacing) * 3);
|
|
374
377
|
}
|
|
@@ -450,6 +453,9 @@
|
|
|
450
453
|
.cgui\:max-h-75 {
|
|
451
454
|
max-height: calc(var(--cgui-spacing) * 75);
|
|
452
455
|
}
|
|
456
|
+
.cgui\:max-h-80 {
|
|
457
|
+
max-height: calc(var(--cgui-spacing) * 80);
|
|
458
|
+
}
|
|
453
459
|
.cgui\:min-h-7\.5 {
|
|
454
460
|
min-height: calc(var(--cgui-spacing) * 7.5);
|
|
455
461
|
}
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { boolAttr } from '../../internal/utils/attrs.js';
|
|
6
6
|
import { cn } from '../../internal/utils/common.js';
|
|
7
|
-
import { watch } from 'runed';
|
|
7
|
+
import { Debounced, watch } from 'runed';
|
|
8
8
|
import type { AnyFn } from 'svelte-toolbelt';
|
|
9
9
|
import { onMountEffect } from 'svelte-toolbelt';
|
|
10
|
+
import type { Attachment } from 'svelte/attachments';
|
|
10
11
|
import { cubicInOut } from 'svelte/easing';
|
|
11
12
|
import { fly } from 'svelte/transition';
|
|
12
13
|
import { CommandApi, CommandPrimitive } from '../command/index.js';
|
|
@@ -49,13 +50,14 @@
|
|
|
49
50
|
|
|
50
51
|
let items = $state<CommandItem[]>(initialItems);
|
|
51
52
|
let isLoading = $state(false);
|
|
52
|
-
let
|
|
53
|
+
let isSearching = $state(false);
|
|
54
|
+
let hasMore = $state(false);
|
|
53
55
|
let currentPage = $state(1);
|
|
54
56
|
let error = $state<string | null>(null);
|
|
55
|
-
let viewportRef = $state<HTMLElement | null>(null);
|
|
56
|
-
let searchTimeout = $state<ReturnType<typeof setTimeout> | null>(null);
|
|
57
57
|
let lastSearch = $state('');
|
|
58
58
|
|
|
59
|
+
let listRef = $state<HTMLElement | null>(null);
|
|
60
|
+
|
|
59
61
|
const createCollection = (itemList: CommandItem[]): CommandCollection => {
|
|
60
62
|
const groupOrderMap = new Map<string, number>();
|
|
61
63
|
groups.forEach((g, index) => {
|
|
@@ -93,6 +95,7 @@
|
|
|
93
95
|
if (isLoading) return;
|
|
94
96
|
|
|
95
97
|
isLoading = true;
|
|
98
|
+
isSearching = !append;
|
|
96
99
|
error = null;
|
|
97
100
|
|
|
98
101
|
try {
|
|
@@ -107,53 +110,34 @@
|
|
|
107
110
|
currentPage = page;
|
|
108
111
|
hasMore = result.hasMore;
|
|
109
112
|
lastSearch = search;
|
|
110
|
-
|
|
111
|
-
// Check if viewport needs more items after render
|
|
112
|
-
requestAnimationFrame(() => {
|
|
113
|
-
checkNeedMoreItems();
|
|
114
|
-
});
|
|
115
113
|
} catch (err) {
|
|
116
114
|
error = err instanceof Error ? err.message : 'Failed to fetch data';
|
|
117
115
|
} finally {
|
|
118
116
|
isLoading = false;
|
|
119
|
-
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
// Auto-fill viewport if content doesn't overflow
|
|
123
|
-
const checkNeedMoreItems = () => {
|
|
124
|
-
if (!viewportRef || isLoading || !hasMore) return;
|
|
125
|
-
|
|
126
|
-
const { scrollHeight, clientHeight } = viewportRef;
|
|
127
|
-
|
|
128
|
-
// If content doesn't fill viewport, load more
|
|
129
|
-
if (scrollHeight <= clientHeight) {
|
|
130
|
-
loadMore();
|
|
117
|
+
isSearching = false;
|
|
131
118
|
}
|
|
132
119
|
};
|
|
133
120
|
|
|
134
121
|
// Debounced search handler
|
|
135
|
-
const
|
|
136
|
-
if (searchTimeout) {
|
|
137
|
-
clearTimeout(searchTimeout);
|
|
138
|
-
}
|
|
122
|
+
const debouncedSearchValue = new Debounced(() => searchValue, searchDebounce);
|
|
139
123
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}, searchDebounce);
|
|
124
|
+
const search = (search: string) => {
|
|
125
|
+
currentPage = 1;
|
|
126
|
+
fetchData(1, search, false);
|
|
144
127
|
};
|
|
145
128
|
|
|
146
129
|
// Watch for search value changes
|
|
147
130
|
watch(
|
|
148
|
-
() =>
|
|
131
|
+
() => debouncedSearchValue.current,
|
|
149
132
|
() => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
133
|
+
search(debouncedSearchValue.current);
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
lazy: true,
|
|
153
137
|
}
|
|
154
138
|
);
|
|
155
139
|
|
|
156
|
-
// Fetch on first open
|
|
140
|
+
// Fetch on first open
|
|
157
141
|
watch(
|
|
158
142
|
() => open,
|
|
159
143
|
() => {
|
|
@@ -164,11 +148,6 @@
|
|
|
164
148
|
fetchData(1, searchValue);
|
|
165
149
|
return;
|
|
166
150
|
}
|
|
167
|
-
|
|
168
|
-
// If already have items, check if viewport needs more after it renders
|
|
169
|
-
requestAnimationFrame(() => {
|
|
170
|
-
checkNeedMoreItems();
|
|
171
|
-
});
|
|
172
151
|
}
|
|
173
152
|
);
|
|
174
153
|
|
|
@@ -181,9 +160,6 @@
|
|
|
181
160
|
$effect(() => {
|
|
182
161
|
return () => {
|
|
183
162
|
currentPage = 1;
|
|
184
|
-
if (searchTimeout) {
|
|
185
|
-
clearTimeout(searchTimeout);
|
|
186
|
-
}
|
|
187
163
|
};
|
|
188
164
|
});
|
|
189
165
|
|
|
@@ -221,13 +197,21 @@
|
|
|
221
197
|
}
|
|
222
198
|
};
|
|
223
199
|
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
|
|
200
|
+
const anchorAttachment: Attachment = (el) => {
|
|
201
|
+
const observer = new IntersectionObserver(
|
|
202
|
+
(entries) => {
|
|
203
|
+
if (entries[0]?.isIntersecting) {
|
|
204
|
+
loadMore();
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{ threshold: 0 }
|
|
208
|
+
);
|
|
227
209
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
210
|
+
observer.observe(el);
|
|
211
|
+
|
|
212
|
+
return () => {
|
|
213
|
+
observer.disconnect();
|
|
214
|
+
};
|
|
231
215
|
};
|
|
232
216
|
|
|
233
217
|
const retry = () => {
|
|
@@ -236,9 +220,9 @@
|
|
|
236
220
|
};
|
|
237
221
|
|
|
238
222
|
const scrollToTop = () => {
|
|
239
|
-
if (!
|
|
223
|
+
if (!listRef) return;
|
|
240
224
|
|
|
241
|
-
|
|
225
|
+
listRef.scrollTo({
|
|
242
226
|
top: 0,
|
|
243
227
|
behavior: 'smooth',
|
|
244
228
|
});
|
|
@@ -343,14 +327,14 @@
|
|
|
343
327
|
|
|
344
328
|
<PopoverPrimitive.Portal disabled={portalDisabled}>
|
|
345
329
|
<PopoverPrimitive.Content class="cgui:overflow-hidden cgui:rounded-md">
|
|
346
|
-
<CommandPrimitive.Root shouldFilter={false}>
|
|
330
|
+
<CommandPrimitive.Root shouldFilter={false} class="cgui:max-h-80">
|
|
347
331
|
<CommandPrimitive.Input bind:value={searchValue} placeholder={searchPlaceholder} />
|
|
348
332
|
|
|
349
|
-
<CommandPrimitive.List class="cgui:rounded-inherit">
|
|
350
|
-
<CommandPrimitive.Viewport
|
|
333
|
+
<CommandPrimitive.List class="cgui:rounded-inherit" bind:ref={listRef} style={{ maxHeight: maxContentHeight }}>
|
|
334
|
+
<CommandPrimitive.Viewport class={cn('cgui:relative')}>
|
|
351
335
|
{#if error}
|
|
352
336
|
{@render errorState()}
|
|
353
|
-
{:else if
|
|
337
|
+
{:else if isSearching}
|
|
354
338
|
{#if loadingSnippet}
|
|
355
339
|
{@render loadingSnippet()}
|
|
356
340
|
{:else}
|
|
@@ -380,22 +364,29 @@
|
|
|
380
364
|
{/if}
|
|
381
365
|
{/each}
|
|
382
366
|
|
|
383
|
-
{#if
|
|
384
|
-
{
|
|
367
|
+
{#if hasMore}
|
|
368
|
+
{#key items.length}
|
|
369
|
+
<div class="cgui:h-1 cgui:w-full" {@attach anchorAttachment}></div>
|
|
370
|
+
{/key}
|
|
385
371
|
{/if}
|
|
386
372
|
|
|
387
|
-
{#if
|
|
388
|
-
|
|
389
|
-
class="cgui:w-full cgui:flex cgui:justify-center cgui:cursor-pointer cgui:items-center cgui:text-xs cgui:text-icon-default cgui:underline cgui:hover:no-underline"
|
|
390
|
-
onclick={scrollToTop}
|
|
391
|
-
>
|
|
392
|
-
<IconComponent.ChevronUp />
|
|
393
|
-
</button>
|
|
373
|
+
{#if isLoading}
|
|
374
|
+
{@render loadingMore()}
|
|
394
375
|
{/if}
|
|
395
376
|
{:else}
|
|
396
377
|
<CommandPrimitive.Empty>No results found</CommandPrimitive.Empty>
|
|
397
378
|
{/if}
|
|
398
379
|
</CommandPrimitive.Viewport>
|
|
380
|
+
|
|
381
|
+
{#if !hasMore && items.length > pageSize}
|
|
382
|
+
<button
|
|
383
|
+
type="button"
|
|
384
|
+
class="cgui:w-full cgui:flex cgui:justify-center cgui:cursor-pointer cgui:items-center cgui:text-xs cgui:text-icon-default cgui:underline cgui:hover:no-underline"
|
|
385
|
+
onclick={scrollToTop}
|
|
386
|
+
>
|
|
387
|
+
<IconComponent.ChevronUp />
|
|
388
|
+
</button>
|
|
389
|
+
{/if}
|
|
399
390
|
</CommandPrimitive.List>
|
|
400
391
|
</CommandPrimitive.Root>
|
|
401
392
|
</PopoverPrimitive.Content>
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
rounded = 'default',
|
|
30
30
|
fullWidth = true,
|
|
31
31
|
closeOnSelect = true,
|
|
32
|
+
maxContentHeight,
|
|
32
33
|
...restProps
|
|
33
34
|
}: ComboboxProps = $props();
|
|
34
35
|
|
|
@@ -126,7 +127,7 @@
|
|
|
126
127
|
<CommandPrimitive.Root>
|
|
127
128
|
<CommandPrimitive.Input bind:value={searchValue} placeholder={searchPlaceholder} />
|
|
128
129
|
|
|
129
|
-
<CommandPrimitive.List class="cgui:rounded-inherit">
|
|
130
|
+
<CommandPrimitive.List class="cgui:rounded-inherit" style={{ maxHeight: maxContentHeight }}>
|
|
130
131
|
<CommandPrimitive.Viewport>
|
|
131
132
|
<CommandPrimitive.Empty>No results found</CommandPrimitive.Empty>
|
|
132
133
|
|
|
@@ -14,6 +14,7 @@ export type ComboboxProps = PrimitivePopoverRootProps & ComboboxTriggerVariantsP
|
|
|
14
14
|
allowDeselect?: boolean;
|
|
15
15
|
triggerClass?: string;
|
|
16
16
|
closeOnSelect?: boolean;
|
|
17
|
+
maxContentHeight?: string;
|
|
17
18
|
trigger?: Snippet<[{
|
|
18
19
|
props: Record<string, unknown>;
|
|
19
20
|
displayValue: string;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { cn } from '../../internal/utils/common.js';
|
|
8
8
|
import { watch } from 'runed';
|
|
9
9
|
import { onMountEffect } from 'svelte-toolbelt';
|
|
10
|
+
import type { Attachment } from 'svelte/attachments';
|
|
10
11
|
import { cubicInOut } from 'svelte/easing';
|
|
11
12
|
import { fly } from 'svelte/transition';
|
|
12
13
|
import Content from './components/select.content.svelte';
|
|
@@ -208,15 +209,32 @@
|
|
|
208
209
|
}
|
|
209
210
|
};
|
|
210
211
|
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
212
|
+
const anchorAttachment: Attachment = (el) => {
|
|
213
|
+
const observer = new IntersectionObserver(
|
|
214
|
+
(entries) => {
|
|
215
|
+
if (entries[0]?.isIntersecting) {
|
|
216
|
+
loadMore();
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
{ threshold: 0 }
|
|
220
|
+
);
|
|
214
221
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
222
|
+
observer.observe(el);
|
|
223
|
+
|
|
224
|
+
return () => {
|
|
225
|
+
observer.disconnect();
|
|
226
|
+
};
|
|
218
227
|
};
|
|
219
228
|
|
|
229
|
+
// const handleScroll = (event: Event) => {
|
|
230
|
+
// const viewport = event.target as HTMLElement;
|
|
231
|
+
// const scrollBottom = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;
|
|
232
|
+
|
|
233
|
+
// if (scrollBottom < 100 && hasMore && !isLoading) {
|
|
234
|
+
// loadMore();
|
|
235
|
+
// }
|
|
236
|
+
// };
|
|
237
|
+
|
|
220
238
|
const retry = () => {
|
|
221
239
|
error = null;
|
|
222
240
|
fetchData(currentPage);
|
|
@@ -272,6 +290,12 @@
|
|
|
272
290
|
</div>
|
|
273
291
|
{/snippet}
|
|
274
292
|
|
|
293
|
+
{#if hasMore}
|
|
294
|
+
{#key items.length}
|
|
295
|
+
<div class="cgui:h-1 cgui:w-full" {@attach anchorAttachment}></div>
|
|
296
|
+
{/key}
|
|
297
|
+
{/if}
|
|
298
|
+
|
|
275
299
|
{#snippet loadingMore()}
|
|
276
300
|
<div class="cgui:p-2 cgui:flex cgui:items-center cgui:justify-center cgui:text-icon-default">
|
|
277
301
|
<Spinner />
|
|
@@ -302,7 +326,7 @@
|
|
|
302
326
|
|
|
303
327
|
<Portal>
|
|
304
328
|
<Content bind:ref={contentRef} class={cn(contentClass)} {...contentProps}>
|
|
305
|
-
<Viewport
|
|
329
|
+
<Viewport bind:ref={viewportRef}>
|
|
306
330
|
{#if error}
|
|
307
331
|
{@render errorState()}
|
|
308
332
|
{:else if isLoading && !hasResults}
|