@x33025/sveltely 0.0.23 → 0.0.25
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/ChipInput.svelte +151 -0
- package/dist/components/ChipInput.svelte.d.ts +10 -0
- package/dist/components/Dropdown/Dropdown.svelte +102 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/style/index.css +16 -0
- package/dist/style.css +74 -15
- package/package.json +1 -1
- package/dist/components/TabView/Tab.svelte +0 -52
- package/dist/components/TabView/Tab.svelte.d.ts +0 -11
- package/dist/components/TabView/TabPanel.svelte +0 -32
- package/dist/components/TabView/TabPanel.svelte.d.ts +0 -9
- package/dist/components/TabView/TabView.svelte +0 -38
- package/dist/components/TabView/TabView.svelte.d.ts +0 -10
- package/dist/components/TabView/index.d.ts +0 -8
- package/dist/components/TabView/index.js +0 -7
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { tick } from 'svelte';
|
|
4
|
+
import { Plus } from '@lucide/svelte';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
tags: string[];
|
|
9
|
+
selection?: string[];
|
|
10
|
+
action?: Snippet;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
placeholder = 'Add a tag...',
|
|
15
|
+
tags = $bindable<string[]>(),
|
|
16
|
+
selection = $bindable<string[] | undefined>(),
|
|
17
|
+
action
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
|
|
20
|
+
let inputValue = $state('');
|
|
21
|
+
let showInput = $state(false);
|
|
22
|
+
let inputEl = $state<HTMLInputElement | null>(null);
|
|
23
|
+
const selectionEnabled = $derived(selection !== undefined);
|
|
24
|
+
|
|
25
|
+
const addTag = (rawValue: string) => {
|
|
26
|
+
const nextTag = rawValue.trim();
|
|
27
|
+
if (!nextTag || tags.includes(nextTag)) return;
|
|
28
|
+
tags = [...tags, nextTag];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const toggleSelected = (tag: string) => {
|
|
32
|
+
if (!selection) return;
|
|
33
|
+
|
|
34
|
+
if (selection.includes(tag)) {
|
|
35
|
+
selection = selection.filter((value) => value !== tag);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
selection = [...selection, tag];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const onKeydown = (event: KeyboardEvent) => {
|
|
43
|
+
if ((event.key === 'Enter' || event.key === ',') && inputValue.trim()) {
|
|
44
|
+
event.preventDefault();
|
|
45
|
+
addTag(inputValue);
|
|
46
|
+
inputValue = '';
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (event.key === 'Escape') {
|
|
51
|
+
inputValue = '';
|
|
52
|
+
showInput = false;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (event.key === 'Backspace' && !inputValue && tags.length > 0) {
|
|
57
|
+
tags = tags.slice(0, -1);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const openInput = async () => {
|
|
62
|
+
showInput = true;
|
|
63
|
+
await tick();
|
|
64
|
+
inputEl?.focus();
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const onBlur = () => {
|
|
68
|
+
if (!inputValue.trim()) {
|
|
69
|
+
showInput = false;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
$effect(() => {
|
|
74
|
+
if (!selection) return;
|
|
75
|
+
const nextSelected = selection.filter((tag) => tags.includes(tag));
|
|
76
|
+
if (nextSelected.length !== selection.length) {
|
|
77
|
+
selection = nextSelected;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<div class="w-full max-w-lg">
|
|
83
|
+
<div class="tag-row flex flex-wrap items-center">
|
|
84
|
+
{#each tags as tag (tag)}
|
|
85
|
+
{#if selectionEnabled}
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
class="tag-surface inline-flex items-center gap-2"
|
|
89
|
+
class:tag-selected={selection?.includes(tag)}
|
|
90
|
+
onclick={() => toggleSelected(tag)}
|
|
91
|
+
aria-pressed={selection?.includes(tag)}
|
|
92
|
+
>
|
|
93
|
+
{tag}
|
|
94
|
+
</button>
|
|
95
|
+
{:else}
|
|
96
|
+
<span class="tag-surface inline-flex items-center gap-2">{tag}</span>
|
|
97
|
+
{/if}
|
|
98
|
+
{/each}
|
|
99
|
+
|
|
100
|
+
{#if showInput}
|
|
101
|
+
<input
|
|
102
|
+
bind:this={inputEl}
|
|
103
|
+
bind:value={inputValue}
|
|
104
|
+
class="tag-surface tag-input-field min-w-36 outline-none"
|
|
105
|
+
{placeholder}
|
|
106
|
+
onkeydown={onKeydown}
|
|
107
|
+
onblur={onBlur}
|
|
108
|
+
/>
|
|
109
|
+
{:else}
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
class="tag-surface chip-input-action inline-flex items-center justify-center font-semibold"
|
|
113
|
+
aria-label="Add tag"
|
|
114
|
+
onclick={openInput}
|
|
115
|
+
>
|
|
116
|
+
<Plus style="width: var(--chip-input-font-size); height: var(--chip-input-font-size);" />
|
|
117
|
+
</button>
|
|
118
|
+
{/if}
|
|
119
|
+
|
|
120
|
+
{#if action}
|
|
121
|
+
{@render action()}
|
|
122
|
+
{/if}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<style>
|
|
127
|
+
.tag-row {
|
|
128
|
+
gap: var(--chip-input-gap);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.tag-surface {
|
|
132
|
+
background: var(--chip-input-background);
|
|
133
|
+
color: var(--chip-input-text);
|
|
134
|
+
font-size: var(--chip-input-font-size);
|
|
135
|
+
border-radius: var(--chip-input-border-radius);
|
|
136
|
+
padding: var(--chip-input-padding);
|
|
137
|
+
border: 1px solid var(--chip-input-border-color);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.tag-selected {
|
|
141
|
+
border-color: var(--chip-input-highlight);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.tag-surface:hover {
|
|
145
|
+
background: var(--chip-input-hover);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.tag-input-field:hover {
|
|
149
|
+
background: var(--chip-input-background);
|
|
150
|
+
}
|
|
151
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type Props = {
|
|
3
|
+
placeholder?: string;
|
|
4
|
+
tags: string[];
|
|
5
|
+
selection?: string[];
|
|
6
|
+
action?: Snippet;
|
|
7
|
+
};
|
|
8
|
+
declare const ChipInput: import("svelte").Component<Props, {}, "tags" | "selection">;
|
|
9
|
+
type ChipInput = ReturnType<typeof ChipInput>;
|
|
10
|
+
export default ChipInput;
|
|
@@ -22,8 +22,70 @@
|
|
|
22
22
|
|
|
23
23
|
let isOpen = $state(false);
|
|
24
24
|
let triggerEl = $state<HTMLElement | null>(null);
|
|
25
|
+
let menuEl = $state<HTMLElement | null>(null);
|
|
25
26
|
let menuCoords = $state({ top: 0, left: 0 });
|
|
26
27
|
let menuTransform = $state('none');
|
|
28
|
+
let resolvedAnchor = $state<'top' | 'bottom' | 'leading' | 'trailing'>('bottom');
|
|
29
|
+
|
|
30
|
+
type Point = { x: number; y: number };
|
|
31
|
+
|
|
32
|
+
const isInsideRect = (rect: DOMRect, x: number, y: number) =>
|
|
33
|
+
x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
34
|
+
|
|
35
|
+
const pointInPolygon = (point: Point, polygon: Point[]) => {
|
|
36
|
+
let inside = false;
|
|
37
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
38
|
+
const xi = polygon[i].x;
|
|
39
|
+
const yi = polygon[i].y;
|
|
40
|
+
const xj = polygon[j].x;
|
|
41
|
+
const yj = polygon[j].y;
|
|
42
|
+
const intersects =
|
|
43
|
+
yi > point.y !== yj > point.y &&
|
|
44
|
+
point.x < ((xj - xi) * (point.y - yi)) / (yj - yi + Number.EPSILON) + xi;
|
|
45
|
+
if (intersects) inside = !inside;
|
|
46
|
+
}
|
|
47
|
+
return inside;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const getSafePolygon = (
|
|
51
|
+
triggerRect: DOMRect,
|
|
52
|
+
menuRect: DOMRect,
|
|
53
|
+
currentAnchor: 'top' | 'bottom' | 'leading' | 'trailing'
|
|
54
|
+
): Point[] => {
|
|
55
|
+
if (currentAnchor === 'bottom') {
|
|
56
|
+
return [
|
|
57
|
+
{ x: triggerRect.left, y: triggerRect.top },
|
|
58
|
+
{ x: triggerRect.right, y: triggerRect.top },
|
|
59
|
+
{ x: menuRect.right, y: menuRect.top },
|
|
60
|
+
{ x: menuRect.left, y: menuRect.top }
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (currentAnchor === 'top') {
|
|
65
|
+
return [
|
|
66
|
+
{ x: triggerRect.left, y: triggerRect.bottom },
|
|
67
|
+
{ x: triggerRect.right, y: triggerRect.bottom },
|
|
68
|
+
{ x: menuRect.right, y: menuRect.bottom },
|
|
69
|
+
{ x: menuRect.left, y: menuRect.bottom }
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (currentAnchor === 'leading') {
|
|
74
|
+
return [
|
|
75
|
+
{ x: triggerRect.right, y: triggerRect.top },
|
|
76
|
+
{ x: triggerRect.right, y: triggerRect.bottom },
|
|
77
|
+
{ x: menuRect.right, y: menuRect.bottom },
|
|
78
|
+
{ x: menuRect.right, y: menuRect.top }
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [
|
|
83
|
+
{ x: triggerRect.left, y: triggerRect.top },
|
|
84
|
+
{ x: triggerRect.left, y: triggerRect.bottom },
|
|
85
|
+
{ x: menuRect.left, y: menuRect.bottom },
|
|
86
|
+
{ x: menuRect.left, y: menuRect.top }
|
|
87
|
+
];
|
|
88
|
+
};
|
|
27
89
|
|
|
28
90
|
function open() {
|
|
29
91
|
if (triggerEl) {
|
|
@@ -32,29 +94,29 @@
|
|
|
32
94
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
33
95
|
const spaceLeft = rect.left;
|
|
34
96
|
const spaceRight = window.innerWidth - rect.right;
|
|
35
|
-
let
|
|
97
|
+
let nextAnchor = anchor;
|
|
36
98
|
|
|
37
99
|
if (anchor === 'bottom' && spaceBelow < spaceAbove) {
|
|
38
|
-
|
|
100
|
+
nextAnchor = 'top';
|
|
39
101
|
} else if (anchor === 'top' && spaceAbove < spaceBelow) {
|
|
40
|
-
|
|
102
|
+
nextAnchor = 'bottom';
|
|
41
103
|
} else if (anchor === 'leading' && spaceLeft < spaceRight) {
|
|
42
|
-
|
|
104
|
+
nextAnchor = 'trailing';
|
|
43
105
|
} else if (anchor === 'trailing' && spaceRight < spaceLeft) {
|
|
44
|
-
|
|
106
|
+
nextAnchor = 'leading';
|
|
45
107
|
}
|
|
108
|
+
resolvedAnchor = nextAnchor;
|
|
46
109
|
|
|
47
|
-
if (
|
|
110
|
+
if (nextAnchor === 'top' || nextAnchor === 'bottom') {
|
|
48
111
|
const left = align === 'left' ? rect.left + window.scrollX : rect.right + window.scrollX;
|
|
49
112
|
const top =
|
|
50
|
-
|
|
113
|
+
nextAnchor === 'bottom' ? rect.bottom + window.scrollY : rect.top + window.scrollY;
|
|
51
114
|
menuCoords = { top, left };
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
else if (resolvedAnchor === 'top') menuTransform = 'translateY(-100%)';
|
|
115
|
+
if (nextAnchor === 'top' && align === 'right') menuTransform = 'translate(-100%, -100%)';
|
|
116
|
+
else if (nextAnchor === 'top') menuTransform = 'translateY(-100%)';
|
|
55
117
|
else if (align === 'right') menuTransform = 'translateX(-100%)';
|
|
56
118
|
else menuTransform = 'none';
|
|
57
|
-
} else if (
|
|
119
|
+
} else if (nextAnchor === 'leading') {
|
|
58
120
|
menuCoords = { top: rect.top + window.scrollY, left: rect.left + window.scrollX };
|
|
59
121
|
menuTransform = 'translateX(-100%)';
|
|
60
122
|
} else {
|
|
@@ -84,12 +146,39 @@
|
|
|
84
146
|
close();
|
|
85
147
|
}
|
|
86
148
|
|
|
149
|
+
function handleEscape(event: KeyboardEvent) {
|
|
150
|
+
if (!isOpen) return;
|
|
151
|
+
if (event.key === 'Escape') close();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function handlePointerMove(event: MouseEvent) {
|
|
155
|
+
if (!isOpen || !triggerEl || !menuEl) return;
|
|
156
|
+
|
|
157
|
+
const pointer = { x: event.clientX, y: event.clientY };
|
|
158
|
+
const triggerRect = triggerEl.getBoundingClientRect();
|
|
159
|
+
const menuRect = menuEl.getBoundingClientRect();
|
|
160
|
+
const nextSafePolygon = getSafePolygon(triggerRect, menuRect, resolvedAnchor);
|
|
161
|
+
|
|
162
|
+
if (isInsideRect(triggerRect, pointer.x, pointer.y)) return;
|
|
163
|
+
if (isInsideRect(menuRect, pointer.x, pointer.y)) return;
|
|
164
|
+
|
|
165
|
+
if (pointInPolygon(pointer, nextSafePolygon)) return;
|
|
166
|
+
|
|
167
|
+
close();
|
|
168
|
+
}
|
|
169
|
+
|
|
87
170
|
function handleSelect() {
|
|
88
171
|
if (closeOnSelect) close();
|
|
89
172
|
}
|
|
90
173
|
</script>
|
|
91
174
|
|
|
92
|
-
<svelte:window
|
|
175
|
+
<svelte:window
|
|
176
|
+
onclick={handleOutsideClick}
|
|
177
|
+
onscroll={close}
|
|
178
|
+
onresize={close}
|
|
179
|
+
onkeydown={handleEscape}
|
|
180
|
+
onmousemove={handlePointerMove}
|
|
181
|
+
/>
|
|
93
182
|
|
|
94
183
|
<div class="dropdown-container relative inline-block text-left {className}">
|
|
95
184
|
<div bind:this={triggerEl}>
|
|
@@ -112,6 +201,7 @@
|
|
|
112
201
|
role="menu"
|
|
113
202
|
aria-orientation="vertical"
|
|
114
203
|
tabindex="-1"
|
|
204
|
+
bind:this={menuEl}
|
|
115
205
|
>
|
|
116
206
|
<div
|
|
117
207
|
class="overflow-auto"
|
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +10,4 @@ export { default as Spinner } from './components/Spinner.svelte';
|
|
|
10
10
|
export { default as TextShimmer } from './components/TextShimmer.svelte';
|
|
11
11
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
12
12
|
export { default as Dropdown } from './components/Dropdown';
|
|
13
|
-
export { default as
|
|
13
|
+
export { default as ChipInput } from './components/ChipInput.svelte';
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,4 @@ export { default as Spinner } from './components/Spinner.svelte';
|
|
|
10
10
|
export { default as TextShimmer } from './components/TextShimmer.svelte';
|
|
11
11
|
export { default as Tooltip } from './components/Tooltip.svelte';
|
|
12
12
|
export { default as Dropdown } from './components/Dropdown';
|
|
13
|
-
export { default as
|
|
13
|
+
export { default as ChipInput } from './components/ChipInput.svelte';
|
package/dist/style/index.css
CHANGED
|
@@ -41,6 +41,12 @@
|
|
|
41
41
|
height: 1px;
|
|
42
42
|
width: 100%;
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
.chip-input-action {
|
|
46
|
+
width: calc(var(--chip-input-font-size) + 16px);
|
|
47
|
+
height: calc(var(--chip-input-font-size) + 16px);
|
|
48
|
+
padding: 0;
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
@layer theme {
|
|
@@ -84,5 +90,15 @@
|
|
|
84
90
|
0px,
|
|
85
91
|
calc(var(--segmented-picker-border-radius) - var(--segmented-picker-outer-padding))
|
|
86
92
|
);
|
|
93
|
+
|
|
94
|
+
--chip-input-gap: var(--spacing);
|
|
95
|
+
--chip-input-padding: 4px 8px;
|
|
96
|
+
--chip-input-background: var(--color-zinc-100);
|
|
97
|
+
--chip-input-hover: var(--color-zinc-200);
|
|
98
|
+
--chip-input-text: var(--color-black);
|
|
99
|
+
--chip-input-font-size: 12px;
|
|
100
|
+
--chip-input-border-radius: 9999px;
|
|
101
|
+
--chip-input-border-color: transparent;
|
|
102
|
+
--chip-input-highlight: var(--color-zinc-300);
|
|
87
103
|
}
|
|
88
104
|
}
|
package/dist/style.css
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
8
8
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
|
9
9
|
"Courier New", monospace;
|
|
10
|
+
--color-red-50: oklch(97.1% 0.013 17.38);
|
|
11
|
+
--color-red-700: oklch(50.5% 0.213 27.518);
|
|
10
12
|
--color-gray-100: oklch(96.7% 0.003 264.542);
|
|
11
13
|
--color-gray-200: oklch(92.8% 0.006 264.531);
|
|
12
|
-
--color-gray-500: oklch(55.1% 0.027 264.364);
|
|
13
14
|
--color-gray-700: oklch(37.3% 0.034 259.733);
|
|
14
15
|
--color-gray-900: oklch(21% 0.034 264.665);
|
|
15
16
|
--color-zinc-50: oklch(98.5% 0 0);
|
|
@@ -23,8 +24,7 @@
|
|
|
23
24
|
--color-black: #000;
|
|
24
25
|
--color-white: #fff;
|
|
25
26
|
--spacing: 0.25rem;
|
|
26
|
-
--
|
|
27
|
-
--text-xs--line-height: calc(1 / 0.75);
|
|
27
|
+
--container-lg: 32rem;
|
|
28
28
|
--text-sm: 0.875rem;
|
|
29
29
|
--text-sm--line-height: calc(1.25 / 0.875);
|
|
30
30
|
--font-weight-medium: 500;
|
|
@@ -224,8 +224,8 @@
|
|
|
224
224
|
.z-50 {
|
|
225
225
|
z-index: 50;
|
|
226
226
|
}
|
|
227
|
-
.
|
|
228
|
-
margin-
|
|
227
|
+
.mt-2 {
|
|
228
|
+
margin-top: calc(var(--spacing) * 2);
|
|
229
229
|
}
|
|
230
230
|
.block {
|
|
231
231
|
display: block;
|
|
@@ -239,6 +239,9 @@
|
|
|
239
239
|
.inline-block {
|
|
240
240
|
display: inline-block;
|
|
241
241
|
}
|
|
242
|
+
.inline-flex {
|
|
243
|
+
display: inline-flex;
|
|
244
|
+
}
|
|
242
245
|
.size-4 {
|
|
243
246
|
width: calc(var(--spacing) * 4);
|
|
244
247
|
height: calc(var(--spacing) * 4);
|
|
@@ -268,6 +271,12 @@
|
|
|
268
271
|
.w-full {
|
|
269
272
|
width: 100%;
|
|
270
273
|
}
|
|
274
|
+
.max-w-lg {
|
|
275
|
+
max-width: var(--container-lg);
|
|
276
|
+
}
|
|
277
|
+
.min-w-36 {
|
|
278
|
+
min-width: calc(var(--spacing) * 36);
|
|
279
|
+
}
|
|
271
280
|
.flex-1 {
|
|
272
281
|
flex: 1;
|
|
273
282
|
}
|
|
@@ -283,9 +292,15 @@
|
|
|
283
292
|
.flex-col {
|
|
284
293
|
flex-direction: column;
|
|
285
294
|
}
|
|
295
|
+
.flex-wrap {
|
|
296
|
+
flex-wrap: wrap;
|
|
297
|
+
}
|
|
286
298
|
.items-center {
|
|
287
299
|
align-items: center;
|
|
288
300
|
}
|
|
301
|
+
.justify-center {
|
|
302
|
+
justify-content: center;
|
|
303
|
+
}
|
|
289
304
|
.gap-2 {
|
|
290
305
|
gap: calc(var(--spacing) * 2);
|
|
291
306
|
}
|
|
@@ -308,6 +323,9 @@
|
|
|
308
323
|
.rounded {
|
|
309
324
|
border-radius: 0.25rem;
|
|
310
325
|
}
|
|
326
|
+
.rounded-\[var\(--chip-input-border-radius\)\] {
|
|
327
|
+
border-radius: var(--chip-input-border-radius);
|
|
328
|
+
}
|
|
311
329
|
.rounded-full {
|
|
312
330
|
border-radius: calc(infinity * 1px);
|
|
313
331
|
}
|
|
@@ -331,6 +349,9 @@
|
|
|
331
349
|
border-left-style: var(--tw-border-style);
|
|
332
350
|
border-left-width: 1px;
|
|
333
351
|
}
|
|
352
|
+
.border-\[var\(--chip-input-border-color\)\] {
|
|
353
|
+
border-color: var(--chip-input-border-color);
|
|
354
|
+
}
|
|
334
355
|
.border-gray-200 {
|
|
335
356
|
border-color: var(--color-gray-200);
|
|
336
357
|
}
|
|
@@ -343,8 +364,8 @@
|
|
|
343
364
|
.border-zinc-200 {
|
|
344
365
|
border-color: var(--color-zinc-200);
|
|
345
366
|
}
|
|
346
|
-
.bg-
|
|
347
|
-
background-color: var(--
|
|
367
|
+
.bg-\[var\(--chip-input-background\)\] {
|
|
368
|
+
background-color: var(--chip-input-background);
|
|
348
369
|
}
|
|
349
370
|
.bg-white {
|
|
350
371
|
background-color: var(--color-white);
|
|
@@ -383,10 +404,6 @@
|
|
|
383
404
|
font-size: var(--text-sm);
|
|
384
405
|
line-height: var(--tw-leading, var(--text-sm--line-height));
|
|
385
406
|
}
|
|
386
|
-
.text-xs {
|
|
387
|
-
font-size: var(--text-xs);
|
|
388
|
-
line-height: var(--tw-leading, var(--text-xs--line-height));
|
|
389
|
-
}
|
|
390
407
|
.font-medium {
|
|
391
408
|
--tw-font-weight: var(--font-weight-medium);
|
|
392
409
|
font-weight: var(--font-weight-medium);
|
|
@@ -399,14 +416,14 @@
|
|
|
399
416
|
--tw-tracking: var(--tracking-wide);
|
|
400
417
|
letter-spacing: var(--tracking-wide);
|
|
401
418
|
}
|
|
402
|
-
.text-
|
|
403
|
-
color: var(--
|
|
419
|
+
.text-\[var\(--chip-input-text\)\] {
|
|
420
|
+
color: var(--chip-input-text);
|
|
404
421
|
}
|
|
405
422
|
.text-gray-700 {
|
|
406
423
|
color: var(--color-gray-700);
|
|
407
424
|
}
|
|
408
|
-
.text-
|
|
409
|
-
color: var(--color-
|
|
425
|
+
.text-red-700 {
|
|
426
|
+
color: var(--color-red-700);
|
|
410
427
|
}
|
|
411
428
|
.text-zinc-500 {
|
|
412
429
|
color: var(--color-zinc-500);
|
|
@@ -484,6 +501,17 @@
|
|
|
484
501
|
--tw-ease: var(--ease-in-out);
|
|
485
502
|
transition-timing-function: var(--ease-in-out);
|
|
486
503
|
}
|
|
504
|
+
.outline-none {
|
|
505
|
+
--tw-outline-style: none;
|
|
506
|
+
outline-style: none;
|
|
507
|
+
}
|
|
508
|
+
.hover\:bg-\[var\(--chip-input-hover\)\] {
|
|
509
|
+
&:hover {
|
|
510
|
+
@media (hover: hover) {
|
|
511
|
+
background-color: var(--chip-input-hover);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
487
515
|
.hover\:bg-gray-100 {
|
|
488
516
|
&:hover {
|
|
489
517
|
@media (hover: hover) {
|
|
@@ -491,6 +519,13 @@
|
|
|
491
519
|
}
|
|
492
520
|
}
|
|
493
521
|
}
|
|
522
|
+
.hover\:bg-red-50 {
|
|
523
|
+
&:hover {
|
|
524
|
+
@media (hover: hover) {
|
|
525
|
+
background-color: var(--color-red-50);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
494
529
|
.hover\:bg-zinc-100 {
|
|
495
530
|
&:hover {
|
|
496
531
|
@media (hover: hover) {
|
|
@@ -504,6 +539,16 @@
|
|
|
504
539
|
outline-style: none;
|
|
505
540
|
}
|
|
506
541
|
}
|
|
542
|
+
.disabled\:cursor-not-allowed {
|
|
543
|
+
&:disabled {
|
|
544
|
+
cursor: not-allowed;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
.disabled\:opacity-40 {
|
|
548
|
+
&:disabled {
|
|
549
|
+
opacity: 40%;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
507
552
|
}
|
|
508
553
|
@layer base {
|
|
509
554
|
html, body {
|
|
@@ -551,6 +596,11 @@
|
|
|
551
596
|
height: 1px;
|
|
552
597
|
width: 100%;
|
|
553
598
|
}
|
|
599
|
+
.chip-input-action {
|
|
600
|
+
width: calc(var(--chip-input-font-size) + 16px);
|
|
601
|
+
height: calc(var(--chip-input-font-size) + 16px);
|
|
602
|
+
padding: 0;
|
|
603
|
+
}
|
|
554
604
|
}
|
|
555
605
|
@layer theme {
|
|
556
606
|
:root {
|
|
@@ -589,6 +639,15 @@
|
|
|
589
639
|
0px,
|
|
590
640
|
calc(var(--segmented-picker-border-radius) - var(--segmented-picker-outer-padding))
|
|
591
641
|
);
|
|
642
|
+
--chip-input-gap: var(--spacing);
|
|
643
|
+
--chip-input-padding: 4px 8px;
|
|
644
|
+
--chip-input-background: var(--color-zinc-100);
|
|
645
|
+
--chip-input-hover: var(--color-zinc-200);
|
|
646
|
+
--chip-input-text: var(--color-black);
|
|
647
|
+
--chip-input-font-size: 12px;
|
|
648
|
+
--chip-input-border-radius: 9999px;
|
|
649
|
+
--chip-input-border-color: transparent;
|
|
650
|
+
--chip-input-highlight: var(--color-zinc-300);
|
|
592
651
|
}
|
|
593
652
|
}
|
|
594
653
|
@property --tw-rotate-x {
|
package/package.json
CHANGED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getContext } from 'svelte';
|
|
3
|
-
import type { Snippet } from 'svelte';
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
value: string;
|
|
7
|
-
disabled?: boolean;
|
|
8
|
-
class?: string;
|
|
9
|
-
activeClass?: string;
|
|
10
|
-
children?: Snippet;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
let {
|
|
14
|
-
value,
|
|
15
|
-
disabled = false,
|
|
16
|
-
class: className = '',
|
|
17
|
-
activeClass = '',
|
|
18
|
-
children
|
|
19
|
-
}: Props = $props();
|
|
20
|
-
|
|
21
|
-
const context = getContext<{
|
|
22
|
-
active: string;
|
|
23
|
-
setActive: (value: string) => void;
|
|
24
|
-
registerTab?: (value: string) => void;
|
|
25
|
-
}>('tabView');
|
|
26
|
-
|
|
27
|
-
const isActive = $derived(context.active === value);
|
|
28
|
-
|
|
29
|
-
$effect(() => {
|
|
30
|
-
context.registerTab?.(value);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
function handleClick() {
|
|
34
|
-
if (disabled) return;
|
|
35
|
-
context.setActive(value);
|
|
36
|
-
}
|
|
37
|
-
</script>
|
|
38
|
-
|
|
39
|
-
<button
|
|
40
|
-
id={'tab-' + value}
|
|
41
|
-
type="button"
|
|
42
|
-
role="tab"
|
|
43
|
-
aria-selected={isActive}
|
|
44
|
-
aria-controls={'panel-' + value}
|
|
45
|
-
{disabled}
|
|
46
|
-
class="{className} {isActive ? activeClass : ''}"
|
|
47
|
-
onclick={handleClick}
|
|
48
|
-
>
|
|
49
|
-
{#if children}
|
|
50
|
-
{@render children()}
|
|
51
|
-
{/if}
|
|
52
|
-
</button>
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Snippet } from 'svelte';
|
|
2
|
-
interface Props {
|
|
3
|
-
value: string;
|
|
4
|
-
disabled?: boolean;
|
|
5
|
-
class?: string;
|
|
6
|
-
activeClass?: string;
|
|
7
|
-
children?: Snippet;
|
|
8
|
-
}
|
|
9
|
-
declare const Tab: import("svelte").Component<Props, {}, "">;
|
|
10
|
-
type Tab = ReturnType<typeof Tab>;
|
|
11
|
-
export default Tab;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getContext } from 'svelte';
|
|
3
|
-
import type { Snippet } from 'svelte';
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
value: string;
|
|
7
|
-
class?: string;
|
|
8
|
-
children?: Snippet;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let { value, class: className = '', children }: Props = $props();
|
|
12
|
-
|
|
13
|
-
const context = getContext<{
|
|
14
|
-
active: string;
|
|
15
|
-
setActive: (value: string) => void;
|
|
16
|
-
}>('tabView');
|
|
17
|
-
|
|
18
|
-
const isActive = $derived(context.active === value);
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
{#if isActive}
|
|
22
|
-
<div
|
|
23
|
-
id={"panel-" + value}
|
|
24
|
-
role="tabpanel"
|
|
25
|
-
aria-labelledby={"tab-" + value}
|
|
26
|
-
class={className}
|
|
27
|
-
>
|
|
28
|
-
{#if children}
|
|
29
|
-
{@render children()}
|
|
30
|
-
{/if}
|
|
31
|
-
</div>
|
|
32
|
-
{/if}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { setContext } from 'svelte';
|
|
3
|
-
import type { Snippet } from 'svelte';
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
active: string;
|
|
7
|
-
onChange?: (value: string) => void;
|
|
8
|
-
tabs?: Snippet;
|
|
9
|
-
panels?: Snippet;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
let { active = $bindable(), onChange, tabs, panels }: Props = $props();
|
|
13
|
-
|
|
14
|
-
function setActive(value: string) {
|
|
15
|
-
if (active === value) return;
|
|
16
|
-
active = value;
|
|
17
|
-
onChange?.(value);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
setContext('tabView', {
|
|
21
|
-
get active() {
|
|
22
|
-
return active;
|
|
23
|
-
},
|
|
24
|
-
setActive
|
|
25
|
-
});
|
|
26
|
-
</script>
|
|
27
|
-
|
|
28
|
-
{#if tabs}
|
|
29
|
-
<div role="tablist">
|
|
30
|
-
{@render tabs()}
|
|
31
|
-
</div>
|
|
32
|
-
{/if}
|
|
33
|
-
|
|
34
|
-
{#if panels}
|
|
35
|
-
<div>
|
|
36
|
-
{@render panels()}
|
|
37
|
-
</div>
|
|
38
|
-
{/if}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { Snippet } from 'svelte';
|
|
2
|
-
interface Props {
|
|
3
|
-
active: string;
|
|
4
|
-
onChange?: (value: string) => void;
|
|
5
|
-
tabs?: Snippet;
|
|
6
|
-
panels?: Snippet;
|
|
7
|
-
}
|
|
8
|
-
declare const TabView: import("svelte").Component<Props, {}, "active">;
|
|
9
|
-
type TabView = ReturnType<typeof TabView>;
|
|
10
|
-
export default TabView;
|