@x33025/sveltely 0.0.44 → 0.0.46
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/components/AsyncButton.svelte +52 -0
- package/dist/components/AsyncButton.svelte.d.ts +15 -0
- package/dist/components/Popover/Popover.svelte +312 -0
- package/dist/components/Popover/Popover.svelte.d.ts +12 -0
- package/dist/components/Popover/index.d.ts +1 -0
- package/dist/components/Popover/index.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/style/index.css +4 -0
- package/dist/style.css +33 -0
- package/package.json +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
|
+
import Spinner from './Spinner.svelte';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
icon?: Component<{ class?: string }>;
|
|
7
|
+
label: string;
|
|
8
|
+
action: () => void | Promise<void>;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
class?: string;
|
|
11
|
+
type?: 'button' | 'submit' | 'reset';
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
icon,
|
|
16
|
+
label,
|
|
17
|
+
action,
|
|
18
|
+
disabled = false,
|
|
19
|
+
class: className = '',
|
|
20
|
+
type = 'button',
|
|
21
|
+
...props
|
|
22
|
+
}: Props & Record<string, unknown> = $props();
|
|
23
|
+
|
|
24
|
+
let pending = $state(false);
|
|
25
|
+
|
|
26
|
+
const handleClick = async () => {
|
|
27
|
+
if (disabled || pending) return;
|
|
28
|
+
pending = true;
|
|
29
|
+
try {
|
|
30
|
+
await action();
|
|
31
|
+
} finally {
|
|
32
|
+
pending = false;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<button
|
|
38
|
+
{type}
|
|
39
|
+
class="inline-flex items-center gap-2 disabled:cursor-not-allowed disabled:opacity-50 {className}"
|
|
40
|
+
disabled={disabled || pending}
|
|
41
|
+
aria-busy={pending}
|
|
42
|
+
onclick={handleClick}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
{#if pending}
|
|
46
|
+
<Spinner class="size-4" />
|
|
47
|
+
{:else if icon}
|
|
48
|
+
{@const Icon = icon}
|
|
49
|
+
<Icon class="size-4" />
|
|
50
|
+
{/if}
|
|
51
|
+
<span>{label}</span>
|
|
52
|
+
</button>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
type Props = {
|
|
3
|
+
icon?: Component<{
|
|
4
|
+
class?: string;
|
|
5
|
+
}>;
|
|
6
|
+
label: string;
|
|
7
|
+
action: () => void | Promise<void>;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
type?: 'button' | 'submit' | 'reset';
|
|
11
|
+
};
|
|
12
|
+
type $$ComponentProps = Props & Record<string, unknown>;
|
|
13
|
+
declare const AsyncButton: Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type AsyncButton = ReturnType<typeof AsyncButton>;
|
|
15
|
+
export default AsyncButton;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { tick } from 'svelte';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import { portalContent } from '../../actions/portal';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
trigger: Snippet;
|
|
8
|
+
children: Snippet;
|
|
9
|
+
open?: boolean;
|
|
10
|
+
class?: string;
|
|
11
|
+
align?: 'left' | 'center' | 'right';
|
|
12
|
+
anchor?: 'top' | 'bottom' | 'leading' | 'trailing';
|
|
13
|
+
}
|
|
14
|
+
type Anchor = 'top' | 'bottom' | 'leading' | 'trailing';
|
|
15
|
+
type Align = 'left' | 'center' | 'right';
|
|
16
|
+
type Point = { x: number; y: number };
|
|
17
|
+
type Placement = {
|
|
18
|
+
top: number;
|
|
19
|
+
left: number;
|
|
20
|
+
transform:
|
|
21
|
+
| 'none'
|
|
22
|
+
| 'translateX(-50%)'
|
|
23
|
+
| 'translateX(-100%)'
|
|
24
|
+
| 'translateY(-100%)'
|
|
25
|
+
| 'translate(-50%, -100%)'
|
|
26
|
+
| 'translate(-100%, -100%)';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
trigger,
|
|
31
|
+
children,
|
|
32
|
+
open = $bindable(false),
|
|
33
|
+
class: className = '',
|
|
34
|
+
align = 'left',
|
|
35
|
+
anchor = 'bottom'
|
|
36
|
+
}: Props = $props();
|
|
37
|
+
|
|
38
|
+
let triggerEl = $state<HTMLElement | null>(null);
|
|
39
|
+
let panelEl = $state<HTMLElement | null>(null);
|
|
40
|
+
let panelCoords = $state({ top: 0, left: 0 });
|
|
41
|
+
let panelTransform = $state('none');
|
|
42
|
+
let resolvedAnchor = $state<Anchor>('bottom');
|
|
43
|
+
|
|
44
|
+
const isInsideRect = (rect: DOMRect, x: number, y: number) =>
|
|
45
|
+
x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
46
|
+
|
|
47
|
+
const pointInPolygon = (point: Point, polygon: Point[]) => {
|
|
48
|
+
let inside = false;
|
|
49
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
50
|
+
const xi = polygon[i].x;
|
|
51
|
+
const yi = polygon[i].y;
|
|
52
|
+
const xj = polygon[j].x;
|
|
53
|
+
const yj = polygon[j].y;
|
|
54
|
+
const intersects =
|
|
55
|
+
yi > point.y !== yj > point.y &&
|
|
56
|
+
point.x < ((xj - xi) * (point.y - yi)) / (yj - yi + Number.EPSILON) + xi;
|
|
57
|
+
if (intersects) inside = !inside;
|
|
58
|
+
}
|
|
59
|
+
return inside;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getSafePolygon = (
|
|
63
|
+
triggerRect: DOMRect,
|
|
64
|
+
popoverRect: DOMRect,
|
|
65
|
+
currentAnchor: Anchor
|
|
66
|
+
): Point[] => {
|
|
67
|
+
if (currentAnchor === 'bottom') {
|
|
68
|
+
return [
|
|
69
|
+
{ x: triggerRect.left, y: triggerRect.top },
|
|
70
|
+
{ x: triggerRect.right, y: triggerRect.top },
|
|
71
|
+
{ x: popoverRect.right, y: popoverRect.top },
|
|
72
|
+
{ x: popoverRect.left, y: popoverRect.top }
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (currentAnchor === 'top') {
|
|
77
|
+
return [
|
|
78
|
+
{ x: triggerRect.left, y: triggerRect.bottom },
|
|
79
|
+
{ x: triggerRect.right, y: triggerRect.bottom },
|
|
80
|
+
{ x: popoverRect.right, y: popoverRect.bottom },
|
|
81
|
+
{ x: popoverRect.left, y: popoverRect.bottom }
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (currentAnchor === 'leading') {
|
|
86
|
+
return [
|
|
87
|
+
{ x: triggerRect.right, y: triggerRect.top },
|
|
88
|
+
{ x: triggerRect.right, y: triggerRect.bottom },
|
|
89
|
+
{ x: popoverRect.right, y: popoverRect.bottom },
|
|
90
|
+
{ x: popoverRect.right, y: popoverRect.top }
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return [
|
|
95
|
+
{ x: triggerRect.left, y: triggerRect.top },
|
|
96
|
+
{ x: triggerRect.left, y: triggerRect.bottom },
|
|
97
|
+
{ x: popoverRect.left, y: popoverRect.bottom },
|
|
98
|
+
{ x: popoverRect.left, y: popoverRect.top }
|
|
99
|
+
];
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const oppositeAnchor = (value: Anchor): Anchor => {
|
|
103
|
+
if (value === 'top') return 'bottom';
|
|
104
|
+
if (value === 'bottom') return 'top';
|
|
105
|
+
if (value === 'leading') return 'trailing';
|
|
106
|
+
return 'leading';
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const flippedAlign = (value: Align): Align => {
|
|
110
|
+
if (value === 'center') return 'center';
|
|
111
|
+
return value === 'left' ? 'right' : 'left';
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const getPlacement = (rect: DOMRect, nextAnchor: Anchor, nextAlign: Align): Placement => {
|
|
115
|
+
if (nextAnchor === 'top' || nextAnchor === 'bottom') {
|
|
116
|
+
const left =
|
|
117
|
+
nextAlign === 'left'
|
|
118
|
+
? rect.left + window.scrollX
|
|
119
|
+
: nextAlign === 'right'
|
|
120
|
+
? rect.right + window.scrollX
|
|
121
|
+
: rect.left + rect.width / 2 + window.scrollX;
|
|
122
|
+
const top =
|
|
123
|
+
nextAnchor === 'bottom' ? rect.bottom + window.scrollY : rect.top + window.scrollY;
|
|
124
|
+
if (nextAnchor === 'top' && nextAlign === 'center') {
|
|
125
|
+
return { top, left, transform: 'translate(-50%, -100%)' };
|
|
126
|
+
}
|
|
127
|
+
if (nextAnchor === 'top' && nextAlign === 'right') {
|
|
128
|
+
return { top, left, transform: 'translate(-100%, -100%)' };
|
|
129
|
+
}
|
|
130
|
+
if (nextAlign === 'center') {
|
|
131
|
+
return { top, left, transform: 'translateX(-50%)' };
|
|
132
|
+
}
|
|
133
|
+
if (nextAnchor === 'top') {
|
|
134
|
+
return { top, left, transform: 'translateY(-100%)' };
|
|
135
|
+
}
|
|
136
|
+
if (nextAlign === 'right') {
|
|
137
|
+
return { top, left, transform: 'translateX(-100%)' };
|
|
138
|
+
}
|
|
139
|
+
return { top, left, transform: 'none' };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (nextAnchor === 'leading') {
|
|
143
|
+
return {
|
|
144
|
+
top: rect.top + window.scrollY,
|
|
145
|
+
left: rect.left + window.scrollX,
|
|
146
|
+
transform: 'translateX(-100%)'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
top: rect.top + window.scrollY,
|
|
152
|
+
left: rect.right + window.scrollX,
|
|
153
|
+
transform: 'none'
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const getViewportRect = (placement: Placement, width: number, height: number) => {
|
|
158
|
+
let left = placement.left - window.scrollX;
|
|
159
|
+
let top = placement.top - window.scrollY;
|
|
160
|
+
|
|
161
|
+
if (placement.transform === 'translateX(-100%)') left -= width;
|
|
162
|
+
else if (placement.transform === 'translateX(-50%)') left -= width / 2;
|
|
163
|
+
else if (placement.transform === 'translateY(-100%)') top -= height;
|
|
164
|
+
else if (placement.transform === 'translate(-50%, -100%)') {
|
|
165
|
+
left -= width / 2;
|
|
166
|
+
top -= height;
|
|
167
|
+
} else if (placement.transform === 'translate(-100%, -100%)') {
|
|
168
|
+
left -= width;
|
|
169
|
+
top -= height;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { left, top, right: left + width, bottom: top + height };
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const overflowScore = (
|
|
176
|
+
rect: { left: number; top: number; right: number; bottom: number },
|
|
177
|
+
margin = 8
|
|
178
|
+
) => {
|
|
179
|
+
const leftOverflow = Math.max(0, margin - rect.left);
|
|
180
|
+
const rightOverflow = Math.max(0, rect.right - (window.innerWidth - margin));
|
|
181
|
+
const topOverflow = Math.max(0, margin - rect.top);
|
|
182
|
+
const bottomOverflow = Math.max(0, rect.bottom - (window.innerHeight - margin));
|
|
183
|
+
return leftOverflow + rightOverflow + topOverflow + bottomOverflow;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const positionPanel = () => {
|
|
187
|
+
if (!triggerEl || !panelEl) return;
|
|
188
|
+
const rect = triggerEl.getBoundingClientRect();
|
|
189
|
+
const width = panelEl.offsetWidth;
|
|
190
|
+
const height = panelEl.offsetHeight;
|
|
191
|
+
const candidates: Array<{ anchor: Anchor; align: Align }> = [
|
|
192
|
+
{ anchor, align },
|
|
193
|
+
{ anchor, align: flippedAlign(align) },
|
|
194
|
+
{ anchor: oppositeAnchor(anchor), align },
|
|
195
|
+
{ anchor: oppositeAnchor(anchor), align: flippedAlign(align) }
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
const unique = new Set<string>();
|
|
199
|
+
let bestPlacement: Placement | null = null;
|
|
200
|
+
let bestAnchor: Anchor | null = null;
|
|
201
|
+
let bestScore = Number.POSITIVE_INFINITY;
|
|
202
|
+
|
|
203
|
+
for (const candidate of candidates) {
|
|
204
|
+
const key = `${candidate.anchor}:${candidate.align}`;
|
|
205
|
+
if (unique.has(key)) continue;
|
|
206
|
+
unique.add(key);
|
|
207
|
+
|
|
208
|
+
const placement = getPlacement(rect, candidate.anchor, candidate.align);
|
|
209
|
+
const nextRect = getViewportRect(placement, width, height);
|
|
210
|
+
const score = overflowScore(nextRect);
|
|
211
|
+
if (score < bestScore) {
|
|
212
|
+
bestScore = score;
|
|
213
|
+
bestPlacement = placement;
|
|
214
|
+
bestAnchor = candidate.anchor;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!bestPlacement) return;
|
|
219
|
+
panelCoords = { top: bestPlacement.top, left: bestPlacement.left };
|
|
220
|
+
panelTransform = bestPlacement.transform;
|
|
221
|
+
if (bestAnchor) resolvedAnchor = bestAnchor;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
async function openPanel() {
|
|
225
|
+
open = true;
|
|
226
|
+
await tick();
|
|
227
|
+
positionPanel();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function closePanel() {
|
|
231
|
+
open = false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function toggle() {
|
|
235
|
+
if (open) {
|
|
236
|
+
closePanel();
|
|
237
|
+
} else {
|
|
238
|
+
void openPanel();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function handleOutsideClick(event: MouseEvent) {
|
|
243
|
+
if (!open) return;
|
|
244
|
+
const target = event.target as HTMLElement;
|
|
245
|
+
if (target.closest('[data-popover-root]') || target.closest('[data-popover]')) return;
|
|
246
|
+
closePanel();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function handleEscape(event: KeyboardEvent) {
|
|
250
|
+
if (!open) return;
|
|
251
|
+
if (event.key === 'Escape') closePanel();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function handlePointerMove(event: MouseEvent) {
|
|
255
|
+
if (!open || !triggerEl || !panelEl) return;
|
|
256
|
+
|
|
257
|
+
const pointer = { x: event.clientX, y: event.clientY };
|
|
258
|
+
const triggerRect = triggerEl.getBoundingClientRect();
|
|
259
|
+
const popoverRect = panelEl.getBoundingClientRect();
|
|
260
|
+
const safePolygon = getSafePolygon(triggerRect, popoverRect, resolvedAnchor);
|
|
261
|
+
|
|
262
|
+
if (isInsideRect(triggerRect, pointer.x, pointer.y)) return;
|
|
263
|
+
if (isInsideRect(popoverRect, pointer.x, pointer.y)) return;
|
|
264
|
+
if (pointInPolygon(pointer, safePolygon)) return;
|
|
265
|
+
|
|
266
|
+
closePanel();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
$effect(() => {
|
|
270
|
+
if (!open) return;
|
|
271
|
+
void tick().then(() => {
|
|
272
|
+
positionPanel();
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
</script>
|
|
276
|
+
|
|
277
|
+
<svelte:window
|
|
278
|
+
onclick={handleOutsideClick}
|
|
279
|
+
onscroll={closePanel}
|
|
280
|
+
onresize={closePanel}
|
|
281
|
+
onkeydown={handleEscape}
|
|
282
|
+
onmousemove={handlePointerMove}
|
|
283
|
+
/>
|
|
284
|
+
|
|
285
|
+
<div class="relative inline-block text-left" data-popover-root>
|
|
286
|
+
<div bind:this={triggerEl}>
|
|
287
|
+
<button
|
|
288
|
+
type="button"
|
|
289
|
+
class="text-left"
|
|
290
|
+
onclick={toggle}
|
|
291
|
+
aria-expanded={open}
|
|
292
|
+
aria-haspopup="dialog"
|
|
293
|
+
>
|
|
294
|
+
{@render trigger()}
|
|
295
|
+
</button>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
{#if open}
|
|
299
|
+
<div
|
|
300
|
+
use:portalContent
|
|
301
|
+
class="popover fixed z-50 focus:outline-none {className}"
|
|
302
|
+
data-popover
|
|
303
|
+
style="top: {panelCoords.top}px; left: {panelCoords.left}px; transform: {panelTransform};"
|
|
304
|
+
role="dialog"
|
|
305
|
+
aria-modal="false"
|
|
306
|
+
tabindex="-1"
|
|
307
|
+
bind:this={panelEl}
|
|
308
|
+
>
|
|
309
|
+
{@render children()}
|
|
310
|
+
</div>
|
|
311
|
+
{/if}
|
|
312
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
trigger: Snippet;
|
|
4
|
+
children: Snippet;
|
|
5
|
+
open?: boolean;
|
|
6
|
+
class?: string;
|
|
7
|
+
align?: 'left' | 'center' | 'right';
|
|
8
|
+
anchor?: 'top' | 'bottom' | 'leading' | 'trailing';
|
|
9
|
+
}
|
|
10
|
+
declare const Popover: import("svelte").Component<Props, {}, "open">;
|
|
11
|
+
type Popover = ReturnType<typeof Popover>;
|
|
12
|
+
export default Popover;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Popover.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Popover.svelte';
|
package/dist/index.d.ts
CHANGED
|
@@ -11,4 +11,6 @@ export { default as Spinner } from './components/Spinner.svelte';
|
|
|
11
11
|
export { default as TextShimmer } from './components/TextShimmer.svelte';
|
|
12
12
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
13
13
|
export { default as Dropdown } from './components/Dropdown';
|
|
14
|
+
export { default as Popover } from './components/Popover';
|
|
14
15
|
export { default as ChipInput } from './components/ChipInput.svelte';
|
|
16
|
+
export { default as AsyncButton } from './components/AsyncButton.svelte';
|
package/dist/index.js
CHANGED
|
@@ -11,4 +11,6 @@ export { default as Spinner } from './components/Spinner.svelte';
|
|
|
11
11
|
export { default as TextShimmer } from './components/TextShimmer.svelte';
|
|
12
12
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
13
13
|
export { default as Dropdown } from './components/Dropdown';
|
|
14
|
+
export { default as Popover } from './components/Popover';
|
|
14
15
|
export { default as ChipInput } from './components/ChipInput.svelte';
|
|
16
|
+
export { default as AsyncButton } from './components/AsyncButton.svelte';
|
package/dist/style/index.css
CHANGED
package/dist/style.css
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
--color-zinc-100: oklch(96.7% 0.001 286.375);
|
|
16
16
|
--color-zinc-200: oklch(92% 0.004 286.32);
|
|
17
17
|
--color-zinc-300: oklch(87.1% 0.006 286.286);
|
|
18
|
+
--color-zinc-400: oklch(70.5% 0.015 286.067);
|
|
18
19
|
--color-zinc-500: oklch(55.2% 0.016 285.938);
|
|
19
20
|
--color-zinc-700: oklch(37% 0.013 285.805);
|
|
20
21
|
--color-zinc-900: oklch(21% 0.006 285.885);
|
|
@@ -275,6 +276,9 @@
|
|
|
275
276
|
.w-60 {
|
|
276
277
|
width: calc(var(--spacing) * 60);
|
|
277
278
|
}
|
|
279
|
+
.w-64 {
|
|
280
|
+
width: calc(var(--spacing) * 64);
|
|
281
|
+
}
|
|
278
282
|
.w-fit {
|
|
279
283
|
width: fit-content;
|
|
280
284
|
}
|
|
@@ -369,6 +373,9 @@
|
|
|
369
373
|
.bg-zinc-50 {
|
|
370
374
|
background-color: var(--color-zinc-50);
|
|
371
375
|
}
|
|
376
|
+
.bg-zinc-900 {
|
|
377
|
+
background-color: var(--color-zinc-900);
|
|
378
|
+
}
|
|
372
379
|
.p-0 {
|
|
373
380
|
padding: calc(var(--spacing) * 0);
|
|
374
381
|
}
|
|
@@ -384,6 +391,9 @@
|
|
|
384
391
|
.px-2 {
|
|
385
392
|
padding-inline: calc(var(--spacing) * 2);
|
|
386
393
|
}
|
|
394
|
+
.px-3 {
|
|
395
|
+
padding-inline: calc(var(--spacing) * 3);
|
|
396
|
+
}
|
|
387
397
|
.px-4 {
|
|
388
398
|
padding-inline: calc(var(--spacing) * 4);
|
|
389
399
|
}
|
|
@@ -422,6 +432,9 @@
|
|
|
422
432
|
.text-red-700 {
|
|
423
433
|
color: var(--color-red-700);
|
|
424
434
|
}
|
|
435
|
+
.text-white {
|
|
436
|
+
color: var(--color-white);
|
|
437
|
+
}
|
|
425
438
|
.text-zinc-500 {
|
|
426
439
|
color: var(--color-zinc-500);
|
|
427
440
|
}
|
|
@@ -491,6 +504,11 @@
|
|
|
491
504
|
}
|
|
492
505
|
}
|
|
493
506
|
}
|
|
507
|
+
.focus\:border-zinc-400 {
|
|
508
|
+
&:focus {
|
|
509
|
+
border-color: var(--color-zinc-400);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
494
512
|
.focus\:outline-none {
|
|
495
513
|
&:focus {
|
|
496
514
|
--tw-outline-style: none;
|
|
@@ -517,6 +535,11 @@
|
|
|
517
535
|
opacity: 40%;
|
|
518
536
|
}
|
|
519
537
|
}
|
|
538
|
+
.disabled\:opacity-50 {
|
|
539
|
+
&:disabled {
|
|
540
|
+
opacity: 50%;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
520
543
|
}
|
|
521
544
|
@layer base {
|
|
522
545
|
html, body {
|
|
@@ -644,6 +667,16 @@
|
|
|
644
667
|
.dropdown-item:hover {
|
|
645
668
|
background-color: var(--color-zinc-100);
|
|
646
669
|
}
|
|
670
|
+
.popover {
|
|
671
|
+
border-radius: var(--radius-md);
|
|
672
|
+
border-style: var(--tw-border-style);
|
|
673
|
+
border-width: 1px;
|
|
674
|
+
border-color: var(--color-gray-200);
|
|
675
|
+
background-color: var(--color-white);
|
|
676
|
+
padding: calc(var(--spacing) * 2);
|
|
677
|
+
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
|
678
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
679
|
+
}
|
|
647
680
|
.sheet {
|
|
648
681
|
border-radius: var(--radius-md);
|
|
649
682
|
background-color: var(--color-white);
|