@r2digisolutions/ui 0.28.3 → 0.29.0

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.
@@ -251,94 +251,89 @@
251
251
  <div class="min-w-full">
252
252
  <!-- HEADER -->
253
253
  <div
254
- class="sticky top-0 z-10 border-b border-neutral-200/80 bg-neutral-50/95 text-[11px] tracking-wide text-neutral-500 uppercase backdrop-blur-xl dark:border-neutral-800/80 dark:bg-neutral-950/95 dark:text-neutral-400"
254
+ class="sticky top-0 z-10 grid w-max items-center gap-0 border-b border-neutral-200/80 bg-neutral-50/95 text-[11px] tracking-wide text-neutral-500 uppercase backdrop-blur-xl dark:border-neutral-800/80 dark:bg-neutral-950/95 dark:text-neutral-400"
255
+ style={`grid-template-columns:${gridTemplate}`}
255
256
  >
256
- <div class="grid w-max items-center gap-0" style={`grid-template-columns:${gridTemplate}`}>
257
- {#if controller.multiSelect}
258
- <div
259
- class={`sticky top-0 left-0 z-20 flex items-center justify-center border-r border-neutral-200/60 bg-neutral-50/95 px-2 ${
260
- density === 'compact' ? 'py-1.5' : 'py-2.5'
261
- } backdrop-blur-xl dark:border-neutral-800/70 dark:bg-neutral-950/95`}
262
- >
263
- <input
264
- type="checkbox"
265
- bind:this={selectAllEl}
266
- onchange={handleToggleAll}
267
- class="h-3.5 w-3.5 rounded border-neutral-300 bg-neutral-50 text-purple-500 focus:ring-purple-500 dark:border-neutral-600 dark:bg-neutral-900"
268
- data-stop-row-toggle="true"
269
- />
270
- </div>
271
- {/if}
257
+ {#if controller.multiSelect}
258
+ <div
259
+ class={`sticky top-0 left-0 z-20 flex items-center justify-center border-r border-neutral-200/60 bg-neutral-50/95 px-2 ${
260
+ density === 'compact' ? 'py-1.5' : 'py-2.5'
261
+ } backdrop-blur-xl dark:border-neutral-800/70 dark:bg-neutral-950/95`}
262
+ >
263
+ <input
264
+ type="checkbox"
265
+ bind:this={selectAllEl}
266
+ onchange={handleToggleAll}
267
+ class="h-3.5 w-3.5 rounded border-neutral-300 bg-neutral-50 text-purple-500 focus:ring-purple-500 dark:border-neutral-600 dark:bg-neutral-900"
268
+ data-stop-row-toggle="true"
269
+ />
270
+ </div>
271
+ {/if}
272
272
 
273
- {#each controller.mainColumns as col (col.id)}
274
- {@const sticky = stickyOffsets[col.id as keyof T]}
273
+ {#each controller.mainColumns as col (col.id)}
274
+ {@const sticky = stickyOffsets[col.id as keyof T]}
275
+ <div
276
+ role="columnheader"
277
+ tabindex="0"
278
+ class={`relative flex items-center border-r border-neutral-200/60 px-3 ${
279
+ density === 'compact' ? 'py-1.5' : 'py-2.5'
280
+ } text-left text-[11px] font-semibold text-neutral-600 dark:border-neutral-800/70 dark:text-neutral-300 ${
281
+ col.sticky === 'left'
282
+ ? 'z-10 bg-neutral-50/95 shadow-[1px_0_0_rgba(15,23,42,0.15)] backdrop-blur-xl dark:bg-neutral-950/95'
283
+ : ''
284
+ }`}
285
+ style={col.sticky === 'left' && sticky?.left !== undefined
286
+ ? `position: sticky; left: ${sticky.left}px; top: 0;`
287
+ : ''}
288
+ ondblclick={() => col.sortable && controller.toggleSort(col.id as keyof T)}
289
+ >
290
+ <button
291
+ type="button"
292
+ class="flex w-full items-center justify-between gap-1"
293
+ onclick={() => col.sortable && controller.toggleSort(col.id as keyof T)}
294
+ data-stop-row-toggle="true"
295
+ >
296
+ {#if headerCell}
297
+ {@render headerCell(col)}
298
+ {:else}
299
+ <span class="line-clamp-1">{col.label}</span>
300
+ {/if}
301
+ {#if col.sortable}
302
+ <span
303
+ class={`text-[9px] ${
304
+ controller.sortColumn === col.id
305
+ ? 'text-purple-500'
306
+ : 'text-neutral-300 dark:text-neutral-600'
307
+ }`}
308
+ >
309
+ {#if controller.sortColumn === col.id}
310
+ {controller.sortDirection === 'asc' ? '▲' : '▼'}
311
+ {:else}
312
+
313
+ {/if}
314
+ </span>
315
+ {/if}
316
+ </button>
275
317
  <div
276
318
  role="columnheader"
277
319
  tabindex="0"
278
- class={`relative flex items-center border-r border-neutral-200/60 px-3 ${
279
- density === 'compact' ? 'py-1.5' : 'py-2.5'
280
- } text-left text-[11px] font-semibold text-neutral-600 dark:border-neutral-800/70 dark:text-neutral-300 ${
281
- col.sticky === 'left'
282
- ? 'z-10 bg-neutral-50/95 shadow-[1px_0_0_rgba(15,23,42,0.15)] backdrop-blur-xl dark:bg-neutral-950/95'
283
- : ''
284
- }`}
285
- style={col.sticky === 'left' && sticky?.left !== undefined
286
- ? `position: sticky; left: ${sticky.left}px; top: 0;`
287
- : ''}
288
- ondblclick={() => col.sortable && controller.toggleSort(col.id as keyof T)}
320
+ class="absolute inset-y-1 right-0 flex w-2 cursor-col-resize items-center justify-end"
321
+ onmousedown={(e) =>
322
+ onResizeDown(e, col.id as keyof T, e.currentTarget.parentElement as HTMLDivElement)}
323
+ data-stop-row-toggle="true"
289
324
  >
290
- <button
291
- type="button"
292
- class="flex w-full items-center justify-between gap-1"
293
- onclick={() => col.sortable && controller.toggleSort(col.id as keyof T)}
294
- data-stop-row-toggle="true"
295
- >
296
- {#if headerCell}
297
- {@render headerCell(col)}
298
- {:else}
299
- <span class="line-clamp-1">{col.label}</span>
300
- {/if}
301
- {#if col.sortable}
302
- <span
303
- class={`text-[9px] ${
304
- controller.sortColumn === col.id
305
- ? 'text-purple-500'
306
- : 'text-neutral-300 dark:text-neutral-600'
307
- }`}
308
- >
309
- {#if controller.sortColumn === col.id}
310
- {controller.sortDirection === 'asc' ? '▲' : '▼'}
311
- {:else}
312
-
313
- {/if}
314
- </span>
315
- {/if}
316
- </button>
317
325
  <div
318
- role="columnheader"
319
- tabindex="0"
320
- class="absolute inset-y-1 right-0 flex w-2 cursor-col-resize items-center justify-end"
321
- onmousedown={(e) =>
322
- onResizeDown(
323
- e,
324
- col.id as keyof T,
325
- e.currentTarget.parentElement as HTMLDivElement
326
- )}
327
- data-stop-row-toggle="true"
328
- >
329
- <div
330
- class="h-6 w-[2px] rounded-full bg-neutral-200 hover:bg-neutral-400 dark:bg-neutral-700 dark:hover:bg-neutral-400"
331
- ></div>
332
- </div>
326
+ class="h-6 w-[2px] rounded-full bg-neutral-200 hover:bg-neutral-400 dark:bg-neutral-700 dark:hover:bg-neutral-400"
327
+ ></div>
333
328
  </div>
334
- {/each}
329
+ </div>
330
+ {/each}
335
331
 
336
- <div
337
- class={`sticky top-0 right-0 z-20 flex items-center justify-end border-l border-neutral-200/60 bg-neutral-50/95 px-2 ${
338
- density === 'compact' ? 'py-1.5' : 'py-2.5'
339
- } backdrop-blur-xl dark:border-neutral-800/70 dark:bg-neutral-950/95`}
340
- ></div>
341
- </div>
332
+ <div
333
+ class={`sticky top-0 right-0 z-20 flex items-center justify-end border-l border-neutral-200/60 bg-neutral-50/95 px-2 ${
334
+ density === 'compact' ? 'py-1.5' : 'py-2.5'
335
+ } backdrop-blur-xl dark:border-neutral-800/70 dark:bg-neutral-950/95`}
336
+ ></div>
342
337
  </div>
343
338
 
344
339
  <!-- BODY -->
@@ -0,0 +1,329 @@
1
+ <script lang="ts">
2
+ import type { Option, Props } from './type.js';
3
+ import { BoxSelect, Check, ChevronDown, Save, Search, X, Plus } from 'lucide-svelte';
4
+ import { SvelteMap } from 'svelte/reactivity';
5
+ import Tag from '../../ui/Tag/Tag.svelte';
6
+ import Dialog from '../../ui/Dialog/Dialog.svelte';
7
+ import DialogHeader from '../../ui/Dialog/DialogHeader.svelte';
8
+ import DialogTitle from '../../ui/Dialog/DialogTitle.svelte';
9
+ import DialogDescription from '../../ui/Dialog/DialogDescription.svelte';
10
+ import Input from '../../ui/Input/Input.svelte';
11
+ import DialogContent from '../../ui/Dialog/DialogContent.svelte';
12
+ import Checkbox from '../../ui/Checkbox/Checkbox.svelte';
13
+ import DialogFooter from '../../ui/Dialog/DialogFooter.svelte';
14
+ import Button from '../../ui/Button/Button.svelte';
15
+ import { i18n } from '../../../settings/index.js';
16
+ import NoContent from '../../ui/NoContent/NoContent.svelte';
17
+ import Field from '../../ui/Field/Field.svelte';
18
+
19
+ let {
20
+ options,
21
+ value = $bindable(),
22
+ label,
23
+ name,
24
+ placeholder,
25
+ multiple = true,
26
+ onConfirm,
27
+ onCancel,
28
+ required,
29
+ errors = [],
30
+ ...props
31
+ }: Props = $props();
32
+
33
+ let open = $state(false);
34
+ let search = $state('');
35
+
36
+ let selected_items = $state(new SvelteMap<string, Option>());
37
+ let pending_selection = $state(new SvelteMap<string, Option>());
38
+
39
+ $effect(() => {
40
+ selected_items = new SvelteMap((value ?? []).map((v) => [v.value, v] as [string, Option]));
41
+ });
42
+
43
+ function openDialog() {
44
+ pending_selection = new SvelteMap(selected_items);
45
+ search = '';
46
+ open = true;
47
+ }
48
+
49
+ function toggleItem(item: Option) {
50
+ if (!multiple) {
51
+ pending_selection = new SvelteMap([[item.value, item]]);
52
+ confirmSelection();
53
+ return;
54
+ }
55
+
56
+ const next = new SvelteMap(pending_selection);
57
+
58
+ if (next.has(item.value)) {
59
+ next.delete(item.value);
60
+ } else {
61
+ next.set(item.value, item);
62
+ }
63
+
64
+ pending_selection = next;
65
+ }
66
+
67
+ function confirmSelection() {
68
+ value = [...pending_selection.values()];
69
+ selected_items = new SvelteMap(pending_selection);
70
+ open = false;
71
+ onConfirm?.(value);
72
+ }
73
+
74
+ function cancelSelection() {
75
+ pending_selection = new SvelteMap(selected_items);
76
+ open = false;
77
+ onCancel?.();
78
+ }
79
+
80
+ function removeSelected(v: string) {
81
+ const next = new SvelteMap(selected_items);
82
+ next.delete(v);
83
+ selected_items = next;
84
+ value = [...selected_items.values()];
85
+ }
86
+
87
+ const filtered_options = $derived.by(() => {
88
+ if (!search) return options;
89
+ const term = search.toLowerCase();
90
+ return options.filter((option) => option.label.toLowerCase().includes(term));
91
+ });
92
+
93
+ const single_selected: Option | undefined = $derived.by(() => {
94
+ if (!selected_items.size) return;
95
+ return selected_items.values().next().value;
96
+ });
97
+
98
+ const footerMessage = $derived.by(() => {
99
+ if (multiple) {
100
+ if (pending_selection.size === 0) return 'No hay elementos seleccionados.';
101
+ if (pending_selection.size === 1) return '1 elemento seleccionado.';
102
+ return `${pending_selection.size} elementos seleccionados.`;
103
+ } else {
104
+ if (single_selected) return `Seleccionado: ${single_selected.label}`;
105
+ return '';
106
+ }
107
+ });
108
+ </script>
109
+
110
+ <div class={['flex w-full flex-col gap-2', props.parentClass].join(' ')}>
111
+ <Field {name} {label} {errors} {required}>
112
+ <button
113
+ type="button"
114
+ onclick={openDialog}
115
+ class="flex h-11 w-full items-center gap-2 rounded-2xl border border-neutral-200/70 bg-neutral-50/80 px-3.5 text-left text-sm text-neutral-800 shadow-xs transition-all hover:border-neutral-300 hover:bg-neutral-50 focus-visible:ring-2 focus-visible:ring-indigo-500/60 focus-visible:outline-none dark:border-neutral-700/70 dark:bg-neutral-900/70 dark:text-neutral-100 dark:hover:border-neutral-500 dark:hover:bg-neutral-900"
116
+ >
117
+ <BoxSelect class="h-4 w-4 text-neutral-400 dark:text-neutral-500" />
118
+
119
+ <span class="truncate text-[13px] text-neutral-600 dark:text-neutral-300">
120
+ {#if multiple}
121
+ {#if selected_items.size > 0}
122
+ {selected_items.size === 1
123
+ ? Array.from(selected_items.values())[0].label
124
+ : `${selected_items.size} seleccionados`}
125
+ {:else}
126
+ {placeholder}
127
+ {/if}
128
+ {:else if selected_items.size > 0}
129
+ {single_selected?.label ?? placeholder}
130
+ {:else}
131
+ {placeholder}
132
+ {/if}
133
+ </span>
134
+
135
+ {#if multiple && selected_items.size > 0}
136
+ <span
137
+ class="ml-1 rounded-full bg-neutral-200/80 px-2 py-[2px] text-[11px] text-neutral-700 tabular-nums dark:bg-neutral-800/90 dark:text-neutral-200"
138
+ >
139
+ {selected_items.size}
140
+ </span>
141
+ {/if}
142
+
143
+ <ChevronDown
144
+ class={[
145
+ 'ml-auto h-4 w-4 text-neutral-400 transition-transform dark:text-neutral-500',
146
+ open ? 'rotate-180' : ''
147
+ ].join(' ')}
148
+ />
149
+ </button>
150
+ </Field>
151
+
152
+ {#if multiple}
153
+ <div class="flex flex-wrap gap-1.5">
154
+ {#each Array.from(selected_items.values()) as item, index (item.value)}
155
+ <input type="hidden" name="{name}[{index}]" value={item.value} />
156
+ <Tag
157
+ onclose={() => removeSelected(item.value)}
158
+ variant="solid"
159
+ color="indigo"
160
+ class="rounded-full bg-indigo-500/10 text-[11px] text-indigo-700 ring-1 ring-indigo-500/30 dark:bg-indigo-500/15 dark:text-indigo-200"
161
+ >
162
+ {item.label}
163
+ </Tag>
164
+ {/each}
165
+ </div>
166
+ {:else}
167
+ <input type="hidden" {name} value={single_selected?.value ?? ''} />
168
+ {/if}
169
+ </div>
170
+
171
+ <Dialog {open} onclose={cancelSelection}>
172
+ <DialogHeader>
173
+ <DialogTitle>{label}</DialogTitle>
174
+ {#if placeholder}
175
+ <DialogDescription>{placeholder}</DialogDescription>
176
+ {/if}
177
+
178
+ <div class="mt-4 space-y-2">
179
+ <div class="relative">
180
+ <Input
181
+ type="search"
182
+ class="w-full"
183
+ placeholder="Buscar..."
184
+ name="search"
185
+ autofocus
186
+ value={search}
187
+ oninput={(e) => (search = e.currentTarget.value)}
188
+ />
189
+ <div class="top absolute left-2">
190
+ <Search class="h-4 w-4 text-neutral-400" />
191
+ </div>
192
+ </div>
193
+
194
+ {#if multiple}
195
+ <div
196
+ class="flex items-center justify-between text-[11px] text-neutral-500 dark:text-neutral-400"
197
+ >
198
+ <span>
199
+ Seleccionados:
200
+ <span class="ml-1 font-semibold text-neutral-700 dark:text-neutral-200">
201
+ {pending_selection.size}
202
+ </span>
203
+ {#if options.length}
204
+ <span class="ml-1 text-neutral-400">/ {options.length}</span>
205
+ {/if}
206
+ </span>
207
+ {#if pending_selection.size > 0}
208
+ <button
209
+ type="button"
210
+ class="inline-flex items-center gap-1 rounded-full px-2 py-[2px] text-[11px] text-neutral-500 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-800"
211
+ onclick={() => (pending_selection = new SvelteMap())}
212
+ >
213
+ <X class="h-3 w-3" />
214
+ Limpiar
215
+ </button>
216
+ {/if}
217
+ </div>
218
+ {/if}
219
+ </div>
220
+ </DialogHeader>
221
+
222
+ <DialogContent class="max-h-[70dvh] gap-1 py-2">
223
+ {#each filtered_options as item (item.value)}
224
+ {#if multiple}
225
+ <label class="flex flex-row gap-1">
226
+ <Checkbox
227
+ class={[
228
+ 'group flex items-center gap-3 rounded-2xl border px-3.5 py-2.5 text-sm transition-all',
229
+ pending_selection.has(item.value)
230
+ ? 'border-indigo-500/70 bg-indigo-500/8 shadow-sm shadow-indigo-500/20 dark:border-indigo-400/70 dark:bg-indigo-500/10'
231
+ : 'border-transparent bg-neutral-100/80 hover:bg-neutral-200/80 dark:bg-neutral-900/70 dark:hover:bg-neutral-800'
232
+ ]}
233
+ value={item.value}
234
+ label={item.label}
235
+ checked={pending_selection.has(item.value)}
236
+ onchange={() => toggleItem(item)}
237
+ />
238
+ <div class="flex flex-col">
239
+ <span>{item.label}</span>
240
+ {#if item.description}
241
+ <span
242
+ class="block text-[11px] text-neutral-500 group-hover:text-neutral-600 dark:text-neutral-400 dark:group-hover:text-neutral-300"
243
+ >
244
+ {item.description}
245
+ </span>
246
+ {/if}
247
+ </div>
248
+ </label>
249
+ {:else}
250
+ <button
251
+ type="button"
252
+ onclick={() => toggleItem(item)}
253
+ class={[
254
+ 'flex w-full cursor-pointer items-center gap-3 rounded-2xl border px-3.5 py-2.5 text-sm transition-all',
255
+ pending_selection.has(item.value)
256
+ ? 'border-indigo-500/70 bg-indigo-500/8 shadow-sm shadow-indigo-500/20 dark:border-indigo-400/70 dark:bg-indigo-500/10'
257
+ : 'border-transparent bg-neutral-100/80 hover:bg-neutral-200/80 dark:bg-neutral-900/70 dark:hover:bg-neutral-800'
258
+ ].join(' ')}
259
+ >
260
+ <!-- icono left -->
261
+ <div
262
+ class={[
263
+ 'flex h-7 w-7 items-center justify-center rounded-full border text-neutral-500 transition-all',
264
+ pending_selection.has(item.value)
265
+ ? 'border-transparent bg-linear-to-tr from-indigo-500 via-violet-500 to-blue-500 text-white shadow-sm shadow-indigo-500/40'
266
+ : 'border-neutral-300 bg-white/90 dark:border-neutral-600 dark:bg-neutral-900/90'
267
+ ].join(' ')}
268
+ >
269
+ {#if pending_selection.has(item.value)}
270
+ <Check class="h-3.5 w-3.5" />
271
+ {:else}
272
+ <Plus class="h-3.5 w-3.5" />
273
+ {/if}
274
+ </div>
275
+
276
+ <div class="flex flex-1 flex-col text-left">
277
+ <span class="text-[13px] font-medium text-neutral-800 dark:text-neutral-100">
278
+ {item.label}
279
+ </span>
280
+ {#if item.description}
281
+ <span class="text-[11px] text-neutral-500 dark:text-neutral-400">
282
+ {item.description}
283
+ </span>
284
+ {/if}
285
+ </div>
286
+
287
+ {#if pending_selection.has(item.value)}
288
+ <div
289
+ class="inline-flex items-center gap-1 rounded-full bg-neutral-900/90 px-2.5 py-[3px] text-[10px] font-medium text-neutral-50 shadow-sm shadow-black/40 dark:bg-neutral-50/95 dark:text-neutral-900"
290
+ >
291
+ <Check class="h-3 w-3" />
292
+ <span>Seleccionado</span>
293
+ </div>
294
+ {/if}
295
+ </button>
296
+ {/if}
297
+ {:else}
298
+ <NoContent
299
+ icon={Search}
300
+ title="No se encontraron resultados"
301
+ subtitle="Prueba con otros términos o limpia el buscador."
302
+ />
303
+ {/each}
304
+ </DialogContent>
305
+
306
+ <DialogFooter class="flex items-center justify-between gap-2">
307
+ {#if footerMessage}
308
+ <div class="text-[11px] text-neutral-500 dark:text-neutral-400">
309
+ {footerMessage}
310
+ </div>
311
+ {/if}
312
+
313
+ <div class="ml-auto flex gap-2">
314
+ <Button variant="secondary" onclick={cancelSelection}>
315
+ <X class="h-4 w-4" />
316
+ {i18n.t('common.cancel')}
317
+ </Button>
318
+ <Button variant="primary" onclick={confirmSelection}>
319
+ <Save class="h-4 w-4" />
320
+ {i18n.t('common.confirm')}
321
+ {#if multiple}
322
+ <span class="ml-1 text-[11px] tabular-nums">
323
+ ({pending_selection.size})
324
+ </span>
325
+ {/if}
326
+ </Button>
327
+ </div>
328
+ </DialogFooter>
329
+ </Dialog>
@@ -0,0 +1,4 @@
1
+ import type { Props } from './type.js';
2
+ declare const SelectMultiple: import("svelte").Component<Props, {}, "value">;
3
+ type SelectMultiple = ReturnType<typeof SelectMultiple>;
4
+ export default SelectMultiple;
@@ -0,0 +1,23 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassValue } from 'svelte/elements';
3
+ export interface Option {
4
+ label: string;
5
+ value: string;
6
+ description?: string;
7
+ [key: string]: any;
8
+ }
9
+ export interface Props {
10
+ parentClass?: ClassValue;
11
+ class?: ClassValue;
12
+ required?: boolean;
13
+ multiple?: boolean;
14
+ onConfirm?: (selected: Option[]) => void;
15
+ onCancel?: () => void;
16
+ item?: Snippet<[option: Option]>;
17
+ options: Option[];
18
+ value?: Option[];
19
+ label: string;
20
+ name: string;
21
+ placeholder: string;
22
+ errors?: string[];
23
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -2,6 +2,7 @@ import DataTable from './DataTable/DataTable.svelte';
2
2
  import TabsStepper from './TabsStepper/TabsStepper.svelte';
3
3
  import DataTableShell from './DataTableShell/DataTableShell.svelte';
4
4
  import { DataTableController } from './DataTableShell/core/DataTableController.svelte.js';
5
+ import SelectMultiple from './SelectMultiple/SelectMultiple.svelte';
5
6
  export * from './DataTable/core/types.js';
6
7
  export * from './TabsStepper/core/types.js';
7
- export { DataTable, TabsStepper, DataTableShell, DataTableController };
8
+ export { DataTable, TabsStepper, DataTableShell, DataTableController, SelectMultiple };
@@ -2,6 +2,7 @@ import DataTable from './DataTable/DataTable.svelte';
2
2
  import TabsStepper from './TabsStepper/TabsStepper.svelte';
3
3
  import DataTableShell from './DataTableShell/DataTableShell.svelte';
4
4
  import { DataTableController } from './DataTableShell/core/DataTableController.svelte.js';
5
+ import SelectMultiple from './SelectMultiple/SelectMultiple.svelte';
5
6
  export * from './DataTable/core/types.js';
6
7
  export * from './TabsStepper/core/types.js';
7
- export { DataTable, TabsStepper, DataTableShell, DataTableController };
8
+ export { DataTable, TabsStepper, DataTableShell, DataTableController, SelectMultiple };
@@ -30,7 +30,8 @@
30
30
  white: `${type}-white text-black`,
31
31
  info: `${type}-blue-500 text-whit`,
32
32
  outline: `${type}-gray-200 text-gray-800`,
33
- default: `${type}-gray-800 text-white`
33
+ default: `${type}-gray-800 text-white`,
34
+ indigo: `${type}-indigo-500 text-white`
34
35
  });
35
36
 
36
37
  const variants: Record<typeof variant, string> = $derived({
@@ -4,7 +4,7 @@ export interface Props {
4
4
  onclick?(): void;
5
5
  href?: string;
6
6
  variant?: 'solid' | 'outline';
7
- color?: 'primary' | 'secondary' | 'danger' | 'white' | 'teal' | 'info' | 'outline' | 'default';
7
+ color?: 'primary' | 'secondary' | 'danger' | 'white' | 'teal' | 'info' | 'outline' | 'default' | 'indigo';
8
8
  shadow?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
9
9
  onclose?(): void;
10
10
  class?: ClassValue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@r2digisolutions/ui",
3
- "version": "0.28.3",
3
+ "version": "0.29.0",
4
4
  "private": false,
5
5
  "packageManager": "bun@1.3.4",
6
6
  "publishConfig": {
@@ -82,7 +82,7 @@
82
82
  "tailwindcss": "4.1.17",
83
83
  "typescript": "5.9.3",
84
84
  "typescript-eslint": "8.48.1",
85
- "vite": "7.2.6",
85
+ "vite": "7.2.7",
86
86
  "vitest": "4.0.15"
87
87
  },
88
88
  "dependencies": {