@casinogate/ui 1.5.2 → 1.5.3
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 +5 -0
- package/dist/components/select/components/select.content.svelte +19 -1
- package/dist/components/select/components/select.content.svelte.d.ts +2 -2
- package/dist/components/select/exports.d.ts +1 -0
- package/dist/components/select/exports.js +1 -0
- package/dist/components/select/index.d.ts +1 -1
- package/dist/components/select/select.async.svelte +204 -0
- package/dist/components/select/select.async.svelte.d.ts +4 -0
- package/dist/components/select/select.stories.svelte +46 -0
- package/dist/components/select/select.svelte +17 -48
- package/dist/components/select/styles.js +6 -3
- package/dist/components/select/types.d.ts +18 -2
- package/dist/components/select/utils/get-item-key.d.ts +2 -0
- package/dist/components/select/utils/get-item-key.js +7 -0
- package/dist/components/select/utils/get-label-from-value.d.ts +2 -0
- package/dist/components/select/utils/get-label-from-value.js +23 -0
- package/dist/components/select/utils/index.d.ts +2 -0
- package/dist/components/select/utils/index.js +2 -0
- package/package.json +1 -1
package/dist/assets/css/root.css
CHANGED
|
@@ -1371,6 +1371,11 @@
|
|
|
1371
1371
|
padding-right: calc(var(--cgui-spacing) * 14);
|
|
1372
1372
|
}
|
|
1373
1373
|
}
|
|
1374
|
+
.cgui\:data-\[highlighted\]\:bg-surface-lightest {
|
|
1375
|
+
&[data-highlighted] {
|
|
1376
|
+
background-color: var(--cg-ui-palette-neutral-10);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1374
1379
|
.cgui\:data-\[orientation\=horizontal\]\:h-px {
|
|
1375
1380
|
&[data-orientation="horizontal"] {
|
|
1376
1381
|
height: 1px;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { cn } from '../../../internal/utils/common.js';
|
|
3
3
|
import { Select as SelectPrimitive } from 'bits-ui';
|
|
4
|
+
import type { Attachment } from 'svelte/attachments';
|
|
4
5
|
import { SelectStylesContext } from '../styles.js';
|
|
5
6
|
import type { SelectContentProps } from '../types.js';
|
|
6
7
|
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
children,
|
|
12
13
|
side = 'bottom',
|
|
13
14
|
forceMount = false,
|
|
15
|
+
maxContentHeight,
|
|
14
16
|
...restProps
|
|
15
17
|
}: SelectContentProps = $props();
|
|
16
18
|
|
|
@@ -24,8 +26,24 @@
|
|
|
24
26
|
side,
|
|
25
27
|
...restProps,
|
|
26
28
|
});
|
|
29
|
+
|
|
30
|
+
const contentStyle = $derived.by(() => {
|
|
31
|
+
return {
|
|
32
|
+
...(maxContentHeight ? { '--cg-ui-max-content-height': maxContentHeight } : {}),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const contentMount: Attachment<HTMLDivElement> = (el) => {
|
|
37
|
+
$effect.pre(() => {
|
|
38
|
+
if (!el) return;
|
|
39
|
+
|
|
40
|
+
Object.entries(contentStyle).forEach(([key, value]) => {
|
|
41
|
+
el.style.setProperty(key, value);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
};
|
|
27
45
|
</script>
|
|
28
46
|
|
|
29
|
-
<SelectPrimitive.Content bind:ref {...attrs}>
|
|
47
|
+
<SelectPrimitive.Content bind:ref {...attrs} {@attach contentMount}>
|
|
30
48
|
{@render children?.()}
|
|
31
49
|
</SelectPrimitive.Content>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare const Select: import("svelte").Component<
|
|
1
|
+
import type { SelectContentProps } from '../types.js';
|
|
2
|
+
declare const Select: import("svelte").Component<SelectContentProps, {}, "ref">;
|
|
3
3
|
type Select = ReturnType<typeof Select>;
|
|
4
4
|
export default Select;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export * as SelectPrimitive from './exports-primitive.js';
|
|
2
2
|
export * as Select from './exports.js';
|
|
3
|
-
export type { SelectContentProps, SelectGroupHeadingProps, SelectGroupProps, SelectItem, SelectItemData, SelectItemGroup, SelectItemProps, SelectRootProps, SelectTriggerProps, SelectViewportProps, } from './types.js';
|
|
3
|
+
export type { SelectAsyncCallback, SelectAsyncCallbackParams, SelectAsyncCallbackResult, SelectAsyncProps, SelectContentProps, SelectGroupHeadingProps, SelectGroupProps, SelectItem, SelectItemData, SelectItemGroup, SelectItemProps, SelectRootProps, SelectTriggerProps, SelectViewportProps, } from './types.js';
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Spinner } from '../spinner/index.js';
|
|
3
|
+
import Content from './components/select.content.svelte';
|
|
4
|
+
import GroupHeading from './components/select.group-heading.svelte';
|
|
5
|
+
import Group from './components/select.group.svelte';
|
|
6
|
+
import Item from './components/select.item.svelte';
|
|
7
|
+
import Portal from './components/select.portal.svelte';
|
|
8
|
+
import Root from './components/select.root.svelte';
|
|
9
|
+
import Trigger from './components/select.trigger.svelte';
|
|
10
|
+
import Viewport from './components/select.viewport.svelte';
|
|
11
|
+
import type { SelectAsyncProps, SelectData, SelectItem, SelectItemGroup } from './types.js';
|
|
12
|
+
import { getItemKey, getLabelFromValue } from './utils/index.js';
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
value = $bindable(''),
|
|
16
|
+
open = $bindable(false),
|
|
17
|
+
empty = 'No results found',
|
|
18
|
+
maxContentHeight,
|
|
19
|
+
item,
|
|
20
|
+
trigger,
|
|
21
|
+
side,
|
|
22
|
+
sideOffset,
|
|
23
|
+
align,
|
|
24
|
+
alignOffset,
|
|
25
|
+
avoidCollisions,
|
|
26
|
+
collisionPadding,
|
|
27
|
+
customAnchor,
|
|
28
|
+
placeholder: placeholderProp,
|
|
29
|
+
loading,
|
|
30
|
+
pageSize = 10,
|
|
31
|
+
callback,
|
|
32
|
+
...restProps
|
|
33
|
+
}: SelectAsyncProps = $props();
|
|
34
|
+
|
|
35
|
+
let data = $state<SelectData>([]);
|
|
36
|
+
let isLoading = $state(false);
|
|
37
|
+
let hasMore = $state(true);
|
|
38
|
+
let currentPage = $state(1);
|
|
39
|
+
|
|
40
|
+
let error = $state<string | null>(null);
|
|
41
|
+
|
|
42
|
+
const placeholder = $derived.by(() => {
|
|
43
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
44
|
+
const label = getLabelFromValue(value, data);
|
|
45
|
+
return label ?? value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
49
|
+
const labels = value.map((v) => getLabelFromValue(v, data) ?? v);
|
|
50
|
+
return labels.join(', ');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (placeholderProp) return placeholderProp;
|
|
54
|
+
|
|
55
|
+
return '';
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const fetchData = async (page: number, append = false) => {
|
|
59
|
+
isLoading = true;
|
|
60
|
+
error = null;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const result = await callback({ page, pageSize });
|
|
64
|
+
|
|
65
|
+
if (append) {
|
|
66
|
+
data = [...data, ...(result.data as any)];
|
|
67
|
+
} else {
|
|
68
|
+
data = result.data;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
currentPage = page;
|
|
72
|
+
hasMore = result.hasMore;
|
|
73
|
+
isLoading = false;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
error = err instanceof Error ? err.message : 'Failed to fetch data';
|
|
76
|
+
} finally {
|
|
77
|
+
isLoading = false;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
$effect(() => {
|
|
82
|
+
if (open && data.length === 0 && !isLoading) {
|
|
83
|
+
fetchData(currentPage);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
$effect(() => {
|
|
88
|
+
return () => {
|
|
89
|
+
currentPage = 1;
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const loadMore = () => {
|
|
94
|
+
if (!isLoading && hasMore) {
|
|
95
|
+
const nextPage = currentPage + 1;
|
|
96
|
+
fetchData(nextPage, true);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleScroll = (event: Event) => {
|
|
101
|
+
const viewport = event.target as HTMLElement;
|
|
102
|
+
const scrollBottom = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;
|
|
103
|
+
|
|
104
|
+
if (scrollBottom < 100 && hasMore && !isLoading) {
|
|
105
|
+
loadMore();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const hasResults = $derived.by(() => {
|
|
110
|
+
return data.length > 0;
|
|
111
|
+
});
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
{#snippet itemsString(item: string)}
|
|
115
|
+
<Item value={item} label={item} />
|
|
116
|
+
{/snippet}
|
|
117
|
+
|
|
118
|
+
{#snippet itemBasic(item: SelectItem)}
|
|
119
|
+
<Item value={item.value} label={item.label} />
|
|
120
|
+
{/snippet}
|
|
121
|
+
|
|
122
|
+
{#snippet itemGroup(group: SelectItemGroup)}
|
|
123
|
+
<Group>
|
|
124
|
+
<GroupHeading>
|
|
125
|
+
{group.group}
|
|
126
|
+
</GroupHeading>
|
|
127
|
+
|
|
128
|
+
{#each group.items as item (getItemKey(item))}
|
|
129
|
+
{#if typeof item === 'string'}
|
|
130
|
+
{@render itemsString(item)}
|
|
131
|
+
{:else}
|
|
132
|
+
{@render itemBasic(item)}
|
|
133
|
+
{/if}
|
|
134
|
+
{/each}
|
|
135
|
+
</Group>
|
|
136
|
+
{/snippet}
|
|
137
|
+
|
|
138
|
+
<Root bind:value={value as never} bind:open {...restProps as any}>
|
|
139
|
+
{#if trigger}
|
|
140
|
+
<Trigger>
|
|
141
|
+
{#snippet child({ props })}
|
|
142
|
+
{@render trigger?.({ props, label: placeholder })}
|
|
143
|
+
{/snippet}
|
|
144
|
+
</Trigger>
|
|
145
|
+
{:else}
|
|
146
|
+
<Trigger>
|
|
147
|
+
{placeholder}
|
|
148
|
+
</Trigger>
|
|
149
|
+
{/if}
|
|
150
|
+
|
|
151
|
+
<Portal>
|
|
152
|
+
<Content
|
|
153
|
+
{maxContentHeight}
|
|
154
|
+
{side}
|
|
155
|
+
{sideOffset}
|
|
156
|
+
{align}
|
|
157
|
+
{alignOffset}
|
|
158
|
+
{avoidCollisions}
|
|
159
|
+
{collisionPadding}
|
|
160
|
+
{customAnchor}
|
|
161
|
+
>
|
|
162
|
+
<Viewport onscroll={handleScroll}>
|
|
163
|
+
{#if error}
|
|
164
|
+
<div class="cgui:p-4 cgui:text-center cgui:text-body-2 cgui:text-fg-darkest cgui:text-destructive">
|
|
165
|
+
{error}
|
|
166
|
+
</div>
|
|
167
|
+
{:else if isLoading && !hasResults}
|
|
168
|
+
{#if loading}
|
|
169
|
+
{@render loading?.()}
|
|
170
|
+
{:else}
|
|
171
|
+
<div class="cgui:p-4 cgui:flex cgui:items-center cgui:h-full cgui:justify-center cgui:text-fg-darkest">
|
|
172
|
+
<Spinner />
|
|
173
|
+
</div>
|
|
174
|
+
{/if}
|
|
175
|
+
{:else if hasResults}
|
|
176
|
+
{#each data as item (getItemKey(item))}
|
|
177
|
+
{#if typeof item === 'string'}
|
|
178
|
+
{@render itemsString(item)}
|
|
179
|
+
{:else if 'group' in item}
|
|
180
|
+
{@render itemGroup(item)}
|
|
181
|
+
{:else}
|
|
182
|
+
{@render itemBasic(item)}
|
|
183
|
+
{/if}
|
|
184
|
+
{/each}
|
|
185
|
+
{#if isLoading}
|
|
186
|
+
<div
|
|
187
|
+
class="cgui:p-2 cgui:flex cgui:items-center cgui:justify-center cgui:text-center cgui:text-body-2 cgui:text-fg-darkest"
|
|
188
|
+
>
|
|
189
|
+
<Spinner />
|
|
190
|
+
</div>
|
|
191
|
+
{/if}
|
|
192
|
+
{:else if typeof empty === 'string'}
|
|
193
|
+
<div
|
|
194
|
+
class="cgui:p-4 cgui:flex cgui:items-center cgui:h-full cgui:justify-center cgui:text-center cgui:text-body-2 cgui:text-fg-regular"
|
|
195
|
+
>
|
|
196
|
+
{empty}
|
|
197
|
+
</div>
|
|
198
|
+
{:else}
|
|
199
|
+
{@render empty?.()}
|
|
200
|
+
{/if}
|
|
201
|
+
</Viewport>
|
|
202
|
+
</Content>
|
|
203
|
+
</Portal>
|
|
204
|
+
</Root>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { Parameters } from '@storybook/sveltekit';
|
|
4
4
|
import type { ComponentProps } from 'svelte';
|
|
5
5
|
import { Select, SelectPrimitive } from './index.js';
|
|
6
|
+
import type { SelectAsyncCallback } from './types.js';
|
|
6
7
|
|
|
7
8
|
const parameters: Parameters = {
|
|
8
9
|
controls: {
|
|
@@ -104,6 +105,38 @@
|
|
|
104
105
|
[] as Array<{ label: string; items: typeof groupedItems }>
|
|
105
106
|
)
|
|
106
107
|
);
|
|
108
|
+
|
|
109
|
+
type CatData = {
|
|
110
|
+
id: string;
|
|
111
|
+
url: string;
|
|
112
|
+
width: number;
|
|
113
|
+
height: number;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const fetchCatData: SelectAsyncCallback = async (params) => {
|
|
117
|
+
const { page, pageSize } = params;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const res = await fetch(`https://api.thecatapi.com/v1/images/search?limit=${pageSize}&page=${page}`, {
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/json',
|
|
123
|
+
'x-api-key': 'live_V1XvjJ84eY5LPP29tbKPJXDiRFPg0WgkDjRtCsocndgnNrVtiUDP25W9rp3QuwbX',
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const data = (await res.json()) as CatData[];
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
data: data.map((item) => ({
|
|
131
|
+
value: item.id,
|
|
132
|
+
label: item.id,
|
|
133
|
+
})),
|
|
134
|
+
hasMore: data.length > 0,
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
107
140
|
</script>
|
|
108
141
|
|
|
109
142
|
<Story name="Basic" {args} {parameters}>
|
|
@@ -215,3 +248,16 @@
|
|
|
215
248
|
</div>
|
|
216
249
|
{/snippet}
|
|
217
250
|
</Story>
|
|
251
|
+
|
|
252
|
+
<Story name="Async" {args} {parameters}>
|
|
253
|
+
{#snippet template(args: Args)}
|
|
254
|
+
<Select.Async
|
|
255
|
+
{...args}
|
|
256
|
+
type="single"
|
|
257
|
+
side="top"
|
|
258
|
+
maxContentHeight="150px"
|
|
259
|
+
callback={fetchCatData}
|
|
260
|
+
placeholder="Select Item"
|
|
261
|
+
/>
|
|
262
|
+
{/snippet}
|
|
263
|
+
</Story>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { SelectItem,
|
|
2
|
+
import type { SelectItem, SelectItemGroup, SelectProps } from './types.js';
|
|
3
|
+
import { getItemKey, getLabelFromValue } from './utils/index.js';
|
|
3
4
|
|
|
4
|
-
import type { Attachment } from 'svelte/attachments';
|
|
5
5
|
import Content from './components/select.content.svelte';
|
|
6
6
|
import GroupHeading from './components/select.group-heading.svelte';
|
|
7
7
|
import Group from './components/select.group.svelte';
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
empty = 'No results found',
|
|
19
19
|
|
|
20
20
|
maxContentHeight,
|
|
21
|
-
|
|
22
21
|
data,
|
|
23
22
|
item,
|
|
24
23
|
trigger,
|
|
@@ -35,33 +34,14 @@
|
|
|
35
34
|
...restProps
|
|
36
35
|
}: SelectProps = $props();
|
|
37
36
|
|
|
38
|
-
const getLabelFromValue = (searchValue: string): string | null => {
|
|
39
|
-
for (const item of data) {
|
|
40
|
-
if (typeof item === 'string') {
|
|
41
|
-
if (item === searchValue) return item;
|
|
42
|
-
} else if ('group' in item) {
|
|
43
|
-
for (const groupItem of item.items) {
|
|
44
|
-
if (typeof groupItem === 'string') {
|
|
45
|
-
if (groupItem === searchValue) return groupItem;
|
|
46
|
-
} else if (groupItem.value === searchValue) {
|
|
47
|
-
return groupItem.label;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
} else if (item.value === searchValue) {
|
|
51
|
-
return item.label;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
37
|
const placeholder = $derived.by(() => {
|
|
58
38
|
if (typeof value === 'string' && value.trim() !== '') {
|
|
59
|
-
const label = getLabelFromValue(value);
|
|
39
|
+
const label = getLabelFromValue(value, data);
|
|
60
40
|
return label ?? value;
|
|
61
41
|
}
|
|
62
42
|
|
|
63
43
|
if (Array.isArray(value) && value.length > 0) {
|
|
64
|
-
const labels = value.map((v) => getLabelFromValue(v) ?? v);
|
|
44
|
+
const labels = value.map((v) => getLabelFromValue(v, data) ?? v);
|
|
65
45
|
return labels.join(', ');
|
|
66
46
|
}
|
|
67
47
|
|
|
@@ -70,31 +50,9 @@
|
|
|
70
50
|
return '';
|
|
71
51
|
});
|
|
72
52
|
|
|
73
|
-
const getItemKey = (item: SelectItemData) => {
|
|
74
|
-
if (typeof item === 'string') return item;
|
|
75
|
-
if ('group' in item) return item.group;
|
|
76
|
-
return item.value;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
53
|
const hasResults = $derived.by(() => {
|
|
80
54
|
return data.length > 0;
|
|
81
55
|
});
|
|
82
|
-
|
|
83
|
-
const contentStyle = $derived.by(() => {
|
|
84
|
-
return {
|
|
85
|
-
...(maxContentHeight ? { '--cg-ui-max-content-height': maxContentHeight } : {}),
|
|
86
|
-
};
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const contentMount: Attachment<HTMLDivElement> = (el) => {
|
|
90
|
-
$effect.pre(() => {
|
|
91
|
-
if (!el) return;
|
|
92
|
-
|
|
93
|
-
Object.entries(contentStyle).forEach(([key, value]) => {
|
|
94
|
-
el.style.setProperty(key, value);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
};
|
|
98
56
|
</script>
|
|
99
57
|
|
|
100
58
|
{#snippet itemsString(item: string)}
|
|
@@ -135,7 +93,16 @@
|
|
|
135
93
|
{/if}
|
|
136
94
|
|
|
137
95
|
<Portal>
|
|
138
|
-
<Content
|
|
96
|
+
<Content
|
|
97
|
+
{maxContentHeight}
|
|
98
|
+
{side}
|
|
99
|
+
{sideOffset}
|
|
100
|
+
{align}
|
|
101
|
+
{alignOffset}
|
|
102
|
+
{avoidCollisions}
|
|
103
|
+
{collisionPadding}
|
|
104
|
+
{customAnchor}
|
|
105
|
+
>
|
|
139
106
|
<Viewport>
|
|
140
107
|
{#if hasResults}
|
|
141
108
|
{#each data as item (getItemKey(item))}
|
|
@@ -148,7 +115,9 @@
|
|
|
148
115
|
{/if}
|
|
149
116
|
{/each}
|
|
150
117
|
{:else if typeof empty === 'string'}
|
|
151
|
-
<div
|
|
118
|
+
<div
|
|
119
|
+
class="cgui:p-4 cgui:flex cgui:items-center cgui:h-full cgui:justify-center cgui:text-center cgui:text-body-2 cgui:text-fg-regular"
|
|
120
|
+
>
|
|
152
121
|
{empty}
|
|
153
122
|
</div>
|
|
154
123
|
{:else}
|
|
@@ -9,20 +9,23 @@ export const selectVariants = tv({
|
|
|
9
9
|
'cgui:shadow-select cgui:bg-surface-white',
|
|
10
10
|
'cgui:rounded-sm',
|
|
11
11
|
'cgui:z-(--cg-ui-z-index-select)',
|
|
12
|
-
'cgui:max-h-(--bits-select-content-available-height) cgui:origin-(--bits-select-content-transform-origin)',
|
|
12
|
+
'cgui:max-h-(--bits-select-content-available-height) cgui:origin-(--bits-select-content-transform-origin) cgui:h-(--cg-ui-max-content-height)',
|
|
13
13
|
'cgui:data-[state=open]:animate-in cgui:data-[state=closed]:animate-out cgui:data-[state=closed]:fade-out-0 cgui:data-[state=open]:fade-in-0 cgui:data-[state=closed]:zoom-out-95 cgui:data-[state=open]:zoom-in-95 cgui:data-[side=bottom]:slide-in-from-top-2 cgui:data-[side=left]:slide-in-from-right-2 cgui:data-[side=right]:slide-in-from-left-2 cgui:data-[side=top]:slide-in-from-bottom-2 cgui:data-[side=bottom]:translate-y-1 cgui:data-[side=left]:-translate-x-1 cgui:data-[side=right]:translate-x-1 cgui:data-[side=top]:-translate-y-1',
|
|
14
14
|
],
|
|
15
15
|
item: [
|
|
16
16
|
'cgui:relative cgui:w-full',
|
|
17
17
|
'cgui:flex cgui:items-center cgui:justify-between cgui:gap-2',
|
|
18
18
|
'cgui:outline-hidden cgui:cursor-default cgui:select-none',
|
|
19
|
+
'cgui:transition-all cgui:duration-250 cgui:ease-in-out',
|
|
20
|
+
'cgui:rounded-xs',
|
|
19
21
|
'cgui:p-2',
|
|
20
|
-
'cgui:text-body cgui:text-fg-
|
|
22
|
+
'cgui:text-body cgui:text-fg-medium',
|
|
21
23
|
'cgui:data-[disabled]:pointer-events-none cgui:data-[disabled]:opacity-50',
|
|
22
24
|
'cgui:[&_svg]:shrink-0 cgui:[&_svg]:pointer-events-none',
|
|
25
|
+
'cgui:data-[highlighted]:bg-surface-lightest',
|
|
23
26
|
],
|
|
24
27
|
viewport: [
|
|
25
|
-
'
|
|
28
|
+
'cgui:scrollbar-track-transparent cgui:scrollbar-thumb-stroke-default cgui:scrollbar-thumb-rounded-full',
|
|
26
29
|
'cgui:h-(--bits-select-anchor-height) cgui:min-w-(--bits-select-anchor-width) cgui:w-full cgui:scroll-my-1 cgui:p-1',
|
|
27
30
|
],
|
|
28
31
|
group: [],
|
|
@@ -6,7 +6,9 @@ export type SelectRootProps = SelectRootPropsPrimitive & SelectVariantsProps;
|
|
|
6
6
|
export type SelectTriggerProps = SelectTriggerPropsPrimitive & {
|
|
7
7
|
hasChevron?: boolean;
|
|
8
8
|
};
|
|
9
|
-
export type SelectContentProps = WithoutChild<SelectContentPropsPrimitive
|
|
9
|
+
export type SelectContentProps = WithoutChild<SelectContentPropsPrimitive> & {
|
|
10
|
+
maxContentHeight?: string;
|
|
11
|
+
};
|
|
10
12
|
export type SelectViewportProps = SelectViewportPropsPrimitive;
|
|
11
13
|
export type SelectItemProps = WithoutChild<SelectItemPropsPrimitive>;
|
|
12
14
|
export type SelectGroupProps = SelectGroupPropsPrimitive;
|
|
@@ -37,7 +39,7 @@ export type SelectProps = SelectRootProps & {
|
|
|
37
39
|
label: string;
|
|
38
40
|
}]>;
|
|
39
41
|
empty?: Snippet | string;
|
|
40
|
-
maxContentHeight?:
|
|
42
|
+
maxContentHeight?: SelectContentProps['maxContentHeight'];
|
|
41
43
|
side?: SelectContentProps['side'];
|
|
42
44
|
sideOffset?: SelectContentProps['sideOffset'];
|
|
43
45
|
align?: SelectContentProps['align'];
|
|
@@ -46,3 +48,17 @@ export type SelectProps = SelectRootProps & {
|
|
|
46
48
|
collisionPadding?: SelectContentProps['collisionPadding'];
|
|
47
49
|
customAnchor?: SelectContentProps['customAnchor'];
|
|
48
50
|
};
|
|
51
|
+
export type SelectAsyncCallbackParams = {
|
|
52
|
+
page: number;
|
|
53
|
+
pageSize: number;
|
|
54
|
+
};
|
|
55
|
+
export type SelectAsyncCallbackResult = {
|
|
56
|
+
data: SelectData;
|
|
57
|
+
hasMore: boolean;
|
|
58
|
+
};
|
|
59
|
+
export type SelectAsyncCallback = (params: SelectAsyncCallbackParams) => Promise<SelectAsyncCallbackResult>;
|
|
60
|
+
export type SelectAsyncProps = Omit<SelectProps, 'data'> & {
|
|
61
|
+
loading?: Snippet;
|
|
62
|
+
pageSize?: number;
|
|
63
|
+
callback: SelectAsyncCallback;
|
|
64
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const getLabelFromValue = (searchValue, data) => {
|
|
2
|
+
for (const item of data) {
|
|
3
|
+
if (typeof item === 'string') {
|
|
4
|
+
if (item === searchValue)
|
|
5
|
+
return item;
|
|
6
|
+
}
|
|
7
|
+
else if ('group' in item) {
|
|
8
|
+
for (const groupItem of item.items) {
|
|
9
|
+
if (typeof groupItem === 'string') {
|
|
10
|
+
if (groupItem === searchValue)
|
|
11
|
+
return groupItem;
|
|
12
|
+
}
|
|
13
|
+
else if (groupItem.value === searchValue) {
|
|
14
|
+
return groupItem.label;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else if (item.value === searchValue) {
|
|
19
|
+
return item.label;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
};
|