@svadmin/ui 0.5.4 → 0.5.9
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/package.json +1 -1
- package/src/app.css +23 -5
- package/src/components/ComboboxField.svelte +5 -3
- package/src/components/ConfirmDialog.svelte +2 -2
- package/src/components/EditPage.svelte +2 -1
- package/src/components/FieldRenderer.svelte +1 -1
- package/src/components/PasswordInput.svelte +1 -1
- package/src/components/Sidebar.svelte +1 -1
- package/src/components/StepsForm.svelte +83 -5
- package/src/components/fieldComponentMap.ts +5 -5
- package/src/components/fields/MultiSelectField.svelte +4 -3
- package/src/components/fields/SelectField.svelte +1 -1
- package/src/components/ui/alert-dialog/alert-dialog-content.svelte +6 -2
- package/src/components/ui/button/button.svelte +6 -6
- package/src/components/ui/card/card-title.svelte +1 -1
- package/src/components/ui/input/input.svelte +2 -2
- package/src/components/ui/select/select.svelte +1 -1
- package/src/components/ui/tooltip/tooltip-content.svelte +2 -2
package/package.json
CHANGED
package/src/app.css
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/* Typography — Inter font via Bunny Fonts (global CDN, works in China) */
|
|
2
|
+
@import url('https://fonts.bunny.net/css?family=inter:400,500,600,700&display=swap');
|
|
3
|
+
|
|
1
4
|
/* @svadmin/ui — Design Tokens (Shadcn New York style)
|
|
2
5
|
* Auto-imported by AdminApp. Can be overridden by consumer's CSS.
|
|
3
6
|
*
|
|
@@ -12,8 +15,8 @@
|
|
|
12
15
|
/* Auto-register svadmin source files for Tailwind 4 content scanning.
|
|
13
16
|
* This ensures all utility classes used in @svadmin/ui and @svadmin/core
|
|
14
17
|
* components are generated without manual @source in consumer projects. */
|
|
15
|
-
@source "
|
|
16
|
-
@source "
|
|
18
|
+
@source "@svadmin/ui/src/**/*.{svelte,ts,js}";
|
|
19
|
+
@source "@svadmin/core/src/**/*.{svelte,ts,js}";
|
|
17
20
|
|
|
18
21
|
@layer base {
|
|
19
22
|
:root {
|
|
@@ -86,10 +89,25 @@
|
|
|
86
89
|
--sidebar-border: oklch(1 0 0 / 10%);
|
|
87
90
|
--sidebar-ring: oklch(0.556 0 0);
|
|
88
91
|
}
|
|
89
|
-
}
|
|
90
92
|
|
|
91
|
-
/*
|
|
92
|
-
|
|
93
|
+
/* Emulate new-york style shadows on inputs and buttons */
|
|
94
|
+
[data-slot="button"],
|
|
95
|
+
[data-slot="input"],
|
|
96
|
+
[data-slot="select-trigger"],
|
|
97
|
+
[data-slot="select"],
|
|
98
|
+
[data-slot="textarea"] {
|
|
99
|
+
@apply shadow-sm;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[data-slot="card"] {
|
|
103
|
+
@apply shadow-sm;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[data-slot="dialog-content"],
|
|
107
|
+
[data-slot="sheet-content"] {
|
|
108
|
+
@apply shadow-lg;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
93
111
|
|
|
94
112
|
body {
|
|
95
113
|
font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { t } from '@svadmin/core/i18n';
|
|
2
3
|
import { useSelect } from '@svadmin/core';
|
|
3
4
|
import type { Filter } from '@svadmin/core';
|
|
4
5
|
import { Button } from './ui/button/index.js';
|
|
@@ -57,7 +58,8 @@
|
|
|
57
58
|
onchange?.(null);
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
function handleSearchInput(
|
|
61
|
+
function handleSearchInput(e: Event) {
|
|
62
|
+
const v = (e.currentTarget as HTMLInputElement).value;
|
|
61
63
|
searchInputValue = v;
|
|
62
64
|
onSearchChange(v);
|
|
63
65
|
}
|
|
@@ -88,8 +90,8 @@
|
|
|
88
90
|
<div class="flex items-center border-b px-2">
|
|
89
91
|
<Search class="mr-1.5 h-3.5 w-3.5 shrink-0 opacity-50" />
|
|
90
92
|
<Command.Input
|
|
91
|
-
value={searchInputValue}
|
|
92
|
-
|
|
93
|
+
bind:value={searchInputValue}
|
|
94
|
+
oninput={handleSearchInput}
|
|
93
95
|
placeholder="Search..."
|
|
94
96
|
class="flex h-9 w-full bg-transparent py-2 text-sm outline-none placeholder:text-muted-foreground"
|
|
95
97
|
/>
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import { Button } from './ui/button/index.js';
|
|
4
4
|
import { t } from '@svadmin/core/i18n';
|
|
5
5
|
|
|
6
|
-
let { open = false, title, message = '',
|
|
6
|
+
let { open = $bindable(false), title, message = '',
|
|
7
7
|
confirmText, cancelText, variant = 'danger',
|
|
8
8
|
onconfirm, oncancel } = $props<{
|
|
9
|
-
open
|
|
9
|
+
open?: boolean;
|
|
10
10
|
title?: string;
|
|
11
11
|
message?: string;
|
|
12
12
|
confirmText?: string;
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
{#if multiVal.length > 0}
|
|
221
221
|
<div class="flex flex-wrap gap-1 mt-1">
|
|
222
222
|
{#each multiVal as v}
|
|
223
|
-
{@const label = field.options?.find(o => o.value === v)?.label ?? String(v)}
|
|
223
|
+
{@const label = field.options?.find((o: any) => o.value === v)?.label ?? String(v)}
|
|
224
224
|
<Badge variant="secondary" class="gap-1">
|
|
225
225
|
{label}
|
|
226
226
|
<button type="button" onclick={() => toggleMulti(v)} class="ml-0.5 rounded-sm hover:text-destructive hover:bg-destructive/10 transition-colors" aria-label={t('common.clear')}>×</button>
|
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
</Button>
|
|
245
245
|
{/snippet}
|
|
246
246
|
</DropdownMenu.Trigger>
|
|
247
|
-
<DropdownMenu.Content
|
|
247
|
+
<DropdownMenu.Content align="end" class="w-40">
|
|
248
248
|
{#each colorThemes as ct}
|
|
249
249
|
<DropdownMenu.Item onclick={() => setColorTheme(ct.id)} class="gap-2">
|
|
250
250
|
<span
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { fly } from 'svelte/transition';
|
|
3
3
|
import { useStepsForm } from '@svadmin/core';
|
|
4
4
|
import type { FieldDefinition } from '@svadmin/core';
|
|
5
|
-
import { getResource } from '@svadmin/core';
|
|
5
|
+
import { getResource, useOne } from '@svadmin/core';
|
|
6
6
|
import { navigate } from '@svadmin/core/router';
|
|
7
7
|
import { t } from '@svadmin/core/i18n';
|
|
8
8
|
import { Button } from './ui/button/index.js';
|
|
@@ -49,13 +49,88 @@
|
|
|
49
49
|
.filter((f): f is FieldDefinition => f != null && f.key !== primaryKey) ?? []
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
+
const flatStepsFields = steps.flatMap(s => s.fields)
|
|
53
|
+
.map(key => resource.fields.find(f => f.key === key))
|
|
54
|
+
.filter((f): f is FieldDefinition => f != null);
|
|
55
|
+
|
|
56
|
+
const existingQuery = mode === 'edit' && id != null
|
|
57
|
+
? stepsForm.query as { data?: { data: Record<string, unknown> }, isLoading: boolean }
|
|
58
|
+
: null;
|
|
59
|
+
|
|
60
|
+
let formData = $state<Record<string, unknown>>({});
|
|
61
|
+
let fieldErrors = $state<Record<string, string>>({});
|
|
62
|
+
let initialized = $state(false);
|
|
63
|
+
|
|
64
|
+
function getDefaultForType(field: FieldDefinition): unknown {
|
|
65
|
+
switch (field.type) {
|
|
66
|
+
case 'text': case 'textarea': case 'richtext': case 'image': return '';
|
|
67
|
+
case 'number': return 0;
|
|
68
|
+
case 'boolean': return false;
|
|
69
|
+
case 'tags': case 'images': case 'multiselect': return [];
|
|
70
|
+
case 'select': return field.options?.[0]?.value ?? '';
|
|
71
|
+
case 'json': return {};
|
|
72
|
+
default: return '';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
$effect.pre(() => {
|
|
77
|
+
if (initialized) return;
|
|
78
|
+
if (mode === 'create') {
|
|
79
|
+
const defaults: Record<string, unknown> = {};
|
|
80
|
+
for (const field of flatStepsFields) {
|
|
81
|
+
defaults[field.key] = field.defaultValue ?? getDefaultForType(field);
|
|
82
|
+
}
|
|
83
|
+
formData = defaults;
|
|
84
|
+
initialized = true;
|
|
85
|
+
} else if (existingQuery && existingQuery.data?.data) {
|
|
86
|
+
formData = { ...existingQuery.data.data };
|
|
87
|
+
initialized = true;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
function handleFieldChange(key: string, val: unknown) {
|
|
92
|
+
formData[key] = val;
|
|
93
|
+
if (fieldErrors[key]) delete fieldErrors[key];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function validateCurrentStep(): boolean {
|
|
97
|
+
const errors: Record<string, string> = {};
|
|
98
|
+
let valid = true;
|
|
99
|
+
for (const field of currentFields) {
|
|
100
|
+
const value = formData[field.key];
|
|
101
|
+
if (field.required && (value === undefined || value === null || value === '')) {
|
|
102
|
+
errors[field.key] = t('validation.required');
|
|
103
|
+
valid = false;
|
|
104
|
+
} else if (field.validate) {
|
|
105
|
+
const msg = field.validate(value);
|
|
106
|
+
if (msg) {
|
|
107
|
+
errors[field.key] = msg;
|
|
108
|
+
valid = false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
fieldErrors = errors;
|
|
113
|
+
return valid;
|
|
114
|
+
}
|
|
115
|
+
|
|
52
116
|
function handleSubmit() {
|
|
117
|
+
if (!validateCurrentStep()) return;
|
|
118
|
+
|
|
53
119
|
if (!isLastStep) {
|
|
54
120
|
next();
|
|
55
121
|
return;
|
|
56
122
|
}
|
|
57
|
-
|
|
123
|
+
|
|
124
|
+
const cleanData: Record<string, unknown> = {};
|
|
125
|
+
for (const field of flatStepsFields) {
|
|
126
|
+
const value = formData[field.key];
|
|
127
|
+
if (value !== undefined) cleanData[field.key] = value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
stepsForm.onFinish(cleanData, { redirect: 'list' });
|
|
58
131
|
}
|
|
132
|
+
|
|
133
|
+
const isLoadingData = $derived(mode === 'edit' && !initialized);
|
|
59
134
|
</script>
|
|
60
135
|
|
|
61
136
|
<div class="space-y-6">
|
|
@@ -103,12 +178,15 @@
|
|
|
103
178
|
<Card.CardContent>
|
|
104
179
|
{#key currentStep}
|
|
105
180
|
<form class="space-y-5" onsubmit={(e) => { e.preventDefault(); handleSubmit(); }} in:fly={{ x: 30, duration: 250 }}>
|
|
106
|
-
{#each currentFields as field}
|
|
181
|
+
{#each currentFields as field (field.key)}
|
|
107
182
|
<FieldRenderer
|
|
108
183
|
{field}
|
|
109
|
-
value={
|
|
110
|
-
onchange={(v) =>
|
|
184
|
+
value={formData[field.key]}
|
|
185
|
+
onchange={(v) => handleFieldChange(field.key, v)}
|
|
111
186
|
/>
|
|
187
|
+
{#if fieldErrors[field.key]}
|
|
188
|
+
<p class="text-destructive text-[0.8125rem] mt-1">{fieldErrors[field.key]}</p>
|
|
189
|
+
{/if}
|
|
112
190
|
{/each}
|
|
113
191
|
|
|
114
192
|
<div class="flex items-center justify-between pt-4">
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import type { Component } from 'svelte';
|
|
6
6
|
|
|
7
7
|
export interface FieldComponentMap {
|
|
8
|
-
[fieldType: string]: Component<
|
|
8
|
+
[fieldType: string]: Component<any>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// Default display component map (read-only field display)
|
|
12
12
|
// Dynamically imports components only when first used
|
|
13
|
-
const displayCache = new Map<string, Component<
|
|
13
|
+
const displayCache = new Map<string, Component<any>>();
|
|
14
14
|
|
|
15
15
|
// Pre-import all field display components (tree-shaken by bundler)
|
|
16
16
|
import BooleanField from './fields/BooleanField.svelte';
|
|
@@ -52,13 +52,13 @@ export const builtinDisplayComponents: FieldComponentMap = {
|
|
|
52
52
|
// ─── Custom component registry ─────────────────────────────────
|
|
53
53
|
// Users can register custom field display components at runtime
|
|
54
54
|
|
|
55
|
-
const customDisplayComponents = new Map<string, Component<
|
|
55
|
+
const customDisplayComponents = new Map<string, Component<any>>();
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Register a custom field display component for a given field type.
|
|
59
59
|
* The component will be used by ShowPage and any other display contexts.
|
|
60
60
|
*/
|
|
61
|
-
export function registerDisplayComponent(fieldType: string, component: Component<
|
|
61
|
+
export function registerDisplayComponent(fieldType: string, component: Component<any>): void {
|
|
62
62
|
customDisplayComponents.set(fieldType, component);
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -67,7 +67,7 @@ export function registerDisplayComponent(fieldType: string, component: Component
|
|
|
67
67
|
* Custom components take precedence over built-in ones.
|
|
68
68
|
* Returns undefined if no component is registered for the type.
|
|
69
69
|
*/
|
|
70
|
-
export function getDisplayComponent(fieldType: string): Component<
|
|
70
|
+
export function getDisplayComponent(fieldType: string): Component<any> | undefined {
|
|
71
71
|
return customDisplayComponents.get(fieldType) ?? builtinDisplayComponents[fieldType];
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Badge } from '../ui/badge/index.js';
|
|
3
3
|
|
|
4
|
-
let { value } = $props<{
|
|
4
|
+
let { value, options } = $props<{
|
|
5
5
|
value: (string | number)[] | null | undefined;
|
|
6
6
|
options?: { label: string; value: string | number }[];
|
|
7
7
|
}>();
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
{#if items.length > 0}
|
|
13
13
|
<div class="flex flex-wrap gap-1">
|
|
14
|
-
{#each items as
|
|
15
|
-
|
|
14
|
+
{#each items as v}
|
|
15
|
+
{@const label = options?.find((o: { label: string; value: string | number }) => o.value === v)?.label ?? String(v)}
|
|
16
|
+
<Badge variant="secondary" class="font-normal">{label}</Badge>
|
|
16
17
|
{/each}
|
|
17
18
|
</div>
|
|
18
19
|
{:else}
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
3
3
|
import type { WithoutChildrenOrChild } from "bits-ui";
|
|
4
4
|
import { cn } from "../../../utils.js";
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
ref = $bindable(null),
|
|
8
9
|
class: className,
|
|
10
|
+
children,
|
|
9
11
|
...restProps
|
|
10
|
-
}: WithoutChildrenOrChild<AlertDialogPrimitive.ContentProps> & { class?: string } = $props();
|
|
12
|
+
}: WithoutChildrenOrChild<AlertDialogPrimitive.ContentProps> & { children?: Snippet; class?: string } = $props();
|
|
11
13
|
</script>
|
|
12
14
|
|
|
13
15
|
<AlertDialogPrimitive.Portal>
|
|
@@ -21,5 +23,7 @@
|
|
|
21
23
|
className,
|
|
22
24
|
)}
|
|
23
25
|
{...restProps}
|
|
24
|
-
|
|
26
|
+
>
|
|
27
|
+
{@render children?.()}
|
|
28
|
+
</AlertDialogPrimitive.Content>
|
|
25
29
|
</AlertDialogPrimitive.Portal>
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
link: "text-primary underline-offset-4 hover:underline",
|
|
16
16
|
},
|
|
17
17
|
size: {
|
|
18
|
-
default: "h-
|
|
18
|
+
default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
|
|
19
19
|
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
20
|
-
sm: "h-
|
|
21
|
-
lg: "h-
|
|
22
|
-
icon: "size-
|
|
20
|
+
sm: "h-8 gap-1 rounded-[min(var(--radius-md),12px)] px-3 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3.5",
|
|
21
|
+
lg: "h-10 gap-2 px-4 has-data-[icon=inline-end]:pr-3.5 has-data-[icon=inline-start]:pl-3.5",
|
|
22
|
+
icon: "size-9",
|
|
23
23
|
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
24
|
-
"icon-sm": "size-
|
|
25
|
-
"icon-lg": "size-
|
|
24
|
+
"icon-sm": "size-8 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
25
|
+
"icon-lg": "size-10",
|
|
26
26
|
},
|
|
27
27
|
},
|
|
28
28
|
defaultVariants: {
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
|
-
<div bind:this={ref} data-slot="card-title" class={cn("
|
|
13
|
+
<div bind:this={ref} data-slot="card-title" class={cn("leading-none font-semibold tracking-tight group-data-[size=sm]/card:text-sm", className)} {...restProps}>
|
|
14
14
|
{@render children?.()}
|
|
15
15
|
</div>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
bind:this={ref}
|
|
26
26
|
data-slot={dataSlot}
|
|
27
27
|
class={cn(
|
|
28
|
-
"dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-
|
|
28
|
+
"dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-9 rounded-lg border bg-transparent px-3 py-1.5 text-base transition-colors file:h-7 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
29
29
|
className
|
|
30
30
|
)}
|
|
31
31
|
type="file"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
bind:this={ref}
|
|
39
39
|
data-slot={dataSlot}
|
|
40
40
|
class={cn(
|
|
41
|
-
"dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-
|
|
41
|
+
"dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-9 rounded-lg border bg-transparent px-3 py-1.5 text-base transition-colors file:h-7 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
42
42
|
className
|
|
43
43
|
)}
|
|
44
44
|
{type}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
bind:this={ref}
|
|
21
21
|
data-slot="select"
|
|
22
22
|
class={cn(
|
|
23
|
-
"dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-
|
|
23
|
+
"dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-9 w-full appearance-none rounded-lg border bg-transparent px-3 py-1.5 text-base transition-colors focus-visible:ring-3 aria-invalid:ring-3 md:text-sm placeholder:text-muted-foreground outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 bg-[length:16px] bg-[right_8px_center] bg-no-repeat",
|
|
24
24
|
"bg-[url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e\")]",
|
|
25
25
|
className
|
|
26
26
|
)}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
{sideOffset}
|
|
28
28
|
{side}
|
|
29
29
|
class={cn(
|
|
30
|
-
"data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-data-[slot=kbd]:pr-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm bg-
|
|
30
|
+
"data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-data-[slot=kbd]:pr-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm bg-primary text-primary-foreground z-50 w-fit max-w-xs origin-(--bits-tooltip-content-transform-origin)",
|
|
31
31
|
className
|
|
32
32
|
)}
|
|
33
33
|
{...restProps}
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
{#snippet child({ props })}
|
|
38
38
|
<div
|
|
39
39
|
class={cn(
|
|
40
|
-
"size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-
|
|
40
|
+
"size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-primary fill-primary z-50",
|
|
41
41
|
"data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%+2px)]",
|
|
42
42
|
"data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%+1px)]",
|
|
43
43
|
"data-[side=right]:translate-x-[calc(50%+2px)] data-[side=right]:translate-y-1/2",
|