flowbite-svelte 1.26.0 → 1.27.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.
- package/dist/clipboard-manager/ClipboardManager.svelte +491 -0
- package/dist/clipboard-manager/ClipboardManager.svelte.d.ts +4 -0
- package/dist/clipboard-manager/index.d.ts +2 -0
- package/dist/clipboard-manager/index.js +2 -0
- package/dist/clipboard-manager/theme.d.ts +169 -0
- package/dist/clipboard-manager/theme.js +73 -0
- package/dist/command-palette/CommandPalette.svelte +1 -1
- package/dist/command-palette/CommandPalette.svelte.d.ts +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/kanban/KanbanBoard.svelte +2 -1
- package/dist/kanban/KanbanCard.svelte +1 -1
- package/dist/kanban/KanbanCard.svelte.d.ts +1 -1
- package/dist/kanban/theme.js +7 -7
- package/dist/scroll-spy/ScrollSpy.svelte +1 -1
- package/dist/scroll-spy/ScrollSpy.svelte.d.ts +1 -1
- package/dist/split-pane/Divider.svelte +1 -1
- package/dist/split-pane/Divider.svelte.d.ts +1 -1
- package/dist/split-pane/Pane.svelte +1 -1
- package/dist/split-pane/Pane.svelte.d.ts +1 -1
- package/dist/split-pane/SplitPane.svelte +1 -1
- package/dist/split-pane/SplitPane.svelte.d.ts +1 -1
- package/dist/theme/themes.d.ts +6 -5
- package/dist/theme/themes.js +6 -6
- package/dist/types.d.ts +38 -3
- package/package.json +6 -1
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge, Modal } from "..";
|
|
3
|
+
import { formatDistanceToNow } from "date-fns";
|
|
4
|
+
import { clipboardManager } from "./theme";
|
|
5
|
+
import type { ClipboardItem, ClipboardManagerProps } from "../types";
|
|
6
|
+
import { getTheme } from "../theme/themeUtils";
|
|
7
|
+
import clsx from "clsx";
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
items: initialItems = [],
|
|
11
|
+
placeholder = "Type and save to clipboard",
|
|
12
|
+
saveLabel = "Save",
|
|
13
|
+
clearLabel = "Clear All",
|
|
14
|
+
limit = 20,
|
|
15
|
+
saveToStorage = true,
|
|
16
|
+
toastDuration = 2000,
|
|
17
|
+
filterSensitive = true,
|
|
18
|
+
maxLength = 10000,
|
|
19
|
+
enableSelectionMenu = false,
|
|
20
|
+
selectionTarget = "body",
|
|
21
|
+
showInput = true,
|
|
22
|
+
class: className = "",
|
|
23
|
+
classes,
|
|
24
|
+
storageKey,
|
|
25
|
+
children,
|
|
26
|
+
emptyState,
|
|
27
|
+
open = $bindable(), // If undefined, renders inline; if defined, renders as modal
|
|
28
|
+
badgeProps = { color: "blue", class: "text-xs" },
|
|
29
|
+
modalProps,
|
|
30
|
+
detectSensitiveData
|
|
31
|
+
}: ClipboardManagerProps = $props();
|
|
32
|
+
|
|
33
|
+
const theme = getTheme("clipboardManager");
|
|
34
|
+
|
|
35
|
+
const isModal = $derived(open !== undefined);
|
|
36
|
+
|
|
37
|
+
const styles = $derived(clipboardManager());
|
|
38
|
+
|
|
39
|
+
let items = $state<ClipboardItem[]>(initialItems);
|
|
40
|
+
let newText = $state("");
|
|
41
|
+
let searchQuery = $state("");
|
|
42
|
+
let toast = $state<{ message: string; type: "success" | "error" | "info" } | null>(null);
|
|
43
|
+
|
|
44
|
+
// Selection menu state
|
|
45
|
+
let selectionMenu = $state<{
|
|
46
|
+
show: boolean;
|
|
47
|
+
x: number;
|
|
48
|
+
y: number;
|
|
49
|
+
text: string;
|
|
50
|
+
}>({ show: false, x: 0, y: 0, text: "" });
|
|
51
|
+
|
|
52
|
+
const STORAGE_KEY = storageKey ?? "flowbite-clipboard-manager";
|
|
53
|
+
|
|
54
|
+
// Save to localStorage whenever items change (but skip the initial load)
|
|
55
|
+
let isFirstLoad = true;
|
|
56
|
+
|
|
57
|
+
$effect(() => {
|
|
58
|
+
if (saveToStorage && typeof window !== "undefined") {
|
|
59
|
+
const saved = localStorage.getItem(STORAGE_KEY);
|
|
60
|
+
if (saved) {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(saved);
|
|
63
|
+
if (initialItems.length === 0) {
|
|
64
|
+
items = parsed;
|
|
65
|
+
} else if (isFirstLoad) {
|
|
66
|
+
// First load: use initial items and save them
|
|
67
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.error("Failed to parse clipboard data:", e);
|
|
71
|
+
}
|
|
72
|
+
} else if (initialItems.length > 0) {
|
|
73
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
isFirstLoad = false;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
$effect(() => {
|
|
80
|
+
if (saveToStorage && typeof window !== "undefined") {
|
|
81
|
+
if (isFirstLoad) {
|
|
82
|
+
isFirstLoad = false;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// --- Selection Menu Logic ---
|
|
90
|
+
$effect(() => {
|
|
91
|
+
if (!enableSelectionMenu || typeof window === "undefined") return;
|
|
92
|
+
|
|
93
|
+
const targetElements = document.querySelectorAll(selectionTarget);
|
|
94
|
+
const elements = targetElements.length > 0 ? Array.from(targetElements) : [document.body];
|
|
95
|
+
|
|
96
|
+
const handleMouseUp = () => {
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
const selection = window.getSelection();
|
|
99
|
+
const selectedText = selection?.toString().trim();
|
|
100
|
+
|
|
101
|
+
if (selectedText && selectedText.length > 0) {
|
|
102
|
+
const range = selection!.getRangeAt(0);
|
|
103
|
+
const rect = range.getBoundingClientRect();
|
|
104
|
+
|
|
105
|
+
selectionMenu = {
|
|
106
|
+
show: true,
|
|
107
|
+
x: rect.left + rect.width / 2,
|
|
108
|
+
y: rect.top - 10,
|
|
109
|
+
text: selectedText
|
|
110
|
+
};
|
|
111
|
+
} else {
|
|
112
|
+
selectionMenu = { show: false, x: 0, y: 0, text: "" };
|
|
113
|
+
}
|
|
114
|
+
}, 10);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const handleMouseDown = (e: MouseEvent) => {
|
|
118
|
+
// Close menu if clicking outside
|
|
119
|
+
if (selectionMenu.show && !(e.target as HTMLElement).closest(".selection-menu")) {
|
|
120
|
+
selectionMenu = { show: false, x: 0, y: 0, text: "" };
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
elements.forEach((el) => el.addEventListener("mouseup", handleMouseUp));
|
|
125
|
+
document.addEventListener("mousedown", handleMouseDown);
|
|
126
|
+
|
|
127
|
+
return () => {
|
|
128
|
+
elements.forEach((el) => el.removeEventListener("mouseup", handleMouseUp));
|
|
129
|
+
document.removeEventListener("mousedown", handleMouseDown);
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// --- Sensitive data detection ---
|
|
134
|
+
const defaultContainsSensitiveData = (text: string): boolean => {
|
|
135
|
+
if (!filterSensitive) return false;
|
|
136
|
+
|
|
137
|
+
const ccPattern = /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/;
|
|
138
|
+
const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d@$!%*?&]{12,}$/;
|
|
139
|
+
const apiKeyPattern = /\b[A-Za-z0-9_-]{32,}\b/;
|
|
140
|
+
const credentialPattern = /(password|passwd|pwd|token|secret|api[_-]?key)[\s:=]/i;
|
|
141
|
+
|
|
142
|
+
return ccPattern.test(text) || passwordPattern.test(text) || apiKeyPattern.test(text) || credentialPattern.test(text);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Use user-provided function if available, otherwise fallback
|
|
146
|
+
const containsSensitiveData = (text: string) => {
|
|
147
|
+
if (typeof detectSensitiveData === "function") {
|
|
148
|
+
return detectSensitiveData(text);
|
|
149
|
+
}
|
|
150
|
+
return defaultContainsSensitiveData(text);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// --- Helpers ---
|
|
154
|
+
const showToast = (message: string, type: "success" | "error" | "info" = "success") => {
|
|
155
|
+
toast = { message, type };
|
|
156
|
+
setTimeout(() => (toast = null), toastDuration);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const sortItems = (itemsList: ClipboardItem[]): ClipboardItem[] => {
|
|
160
|
+
const pinned = itemsList.filter((i) => i.pinned).sort((a, b) => b.timestamp - a.timestamp);
|
|
161
|
+
const unpinned = itemsList.filter((i) => !i.pinned).sort((a, b) => b.timestamp - a.timestamp);
|
|
162
|
+
return [...pinned, ...unpinned];
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const filteredItems = $derived(searchQuery.trim() ? items.filter((i) => i.text.toLowerCase().includes(searchQuery.toLowerCase())) : items);
|
|
166
|
+
|
|
167
|
+
// --- Save from selection menu ---
|
|
168
|
+
const saveSelection = async () => {
|
|
169
|
+
const text = selectionMenu.text;
|
|
170
|
+
if (!text) return;
|
|
171
|
+
|
|
172
|
+
if (text.length > maxLength) {
|
|
173
|
+
showToast(`Text too long (max ${maxLength} characters)`, "error");
|
|
174
|
+
selectionMenu = { show: false, x: 0, y: 0, text: "" };
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (containsSensitiveData(text)) {
|
|
179
|
+
showToast("Sensitive data detected. Not saved for security.", "error");
|
|
180
|
+
selectionMenu = { show: false, x: 0, y: 0, text: "" };
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const duplicate = items.find((i) => i.text === text);
|
|
185
|
+
if (duplicate) {
|
|
186
|
+
showToast("Already saved", "info");
|
|
187
|
+
selectionMenu = { show: false, x: 0, y: 0, text: "" };
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const item: ClipboardItem = {
|
|
192
|
+
id: Date.now(),
|
|
193
|
+
text,
|
|
194
|
+
timestamp: Date.now()
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
items = sortItems([item, ...items]).slice(0, limit);
|
|
198
|
+
showToast("Saved to clipboard manager");
|
|
199
|
+
selectionMenu = { show: false, x: 0, y: 0, text: "" };
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// --- Clipboard actions ---
|
|
203
|
+
const addToClipboard = async () => {
|
|
204
|
+
const trimmed = newText.trim();
|
|
205
|
+
if (!trimmed) return;
|
|
206
|
+
|
|
207
|
+
if (trimmed.length > maxLength) {
|
|
208
|
+
showToast(`Text too long (max ${maxLength} characters)`, "error");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (containsSensitiveData(trimmed)) {
|
|
213
|
+
showToast("Sensitive data detected. Not saved for security.", "error");
|
|
214
|
+
newText = "";
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const duplicate = items.find((i) => i.text === trimmed);
|
|
219
|
+
if (duplicate) {
|
|
220
|
+
showToast("This text is already in your clipboard", "info");
|
|
221
|
+
newText = "";
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const item: ClipboardItem = {
|
|
226
|
+
id: Date.now(),
|
|
227
|
+
text: trimmed,
|
|
228
|
+
timestamp: Date.now()
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
items = sortItems([item, ...items]).slice(0, limit);
|
|
232
|
+
newText = "";
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
await navigator.clipboard.writeText(item.text);
|
|
236
|
+
showToast("Saved and copied to clipboard");
|
|
237
|
+
} catch (e) {
|
|
238
|
+
console.error("Clipboard write failed:", e);
|
|
239
|
+
showToast("Saved but clipboard access denied", "error");
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const copyItem = async (item: ClipboardItem) => {
|
|
244
|
+
try {
|
|
245
|
+
await navigator.clipboard.writeText(item.text);
|
|
246
|
+
showToast("Copied to clipboard");
|
|
247
|
+
} catch (e) {
|
|
248
|
+
showToast("Clipboard access denied", "error");
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const deleteItem = (id: number) => {
|
|
253
|
+
items = items.filter((i) => i.id !== id);
|
|
254
|
+
showToast("Deleted");
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const togglePin = (id: number) => {
|
|
258
|
+
items = items.map((i) => (i.id === id ? { ...i, pinned: !i.pinned } : i));
|
|
259
|
+
items = sortItems(items);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const clearAll = () => {
|
|
263
|
+
if (confirm("Clear all clipboard items?")) {
|
|
264
|
+
items = [];
|
|
265
|
+
showToast("All items cleared");
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const handleKeydown = (e: KeyboardEvent) => {
|
|
270
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
addToClipboard();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
</script>
|
|
276
|
+
|
|
277
|
+
{#snippet inputArea()}
|
|
278
|
+
<div class={styles.inputSection({ class: clsx(theme?.inputSection, classes?.inputSection) })}>
|
|
279
|
+
<div class={styles.inputWrapper({ class: clsx(theme?.inputWrapper, classes?.inputWrapper) })}>
|
|
280
|
+
<input type="text" bind:value={newText} onkeydown={handleKeydown} {placeholder} class={styles.input({ class: clsx(theme?.input, classes?.input) })} />
|
|
281
|
+
<button onclick={addToClipboard} disabled={!newText.trim()} class={styles.addToClipboard({ class: clsx(theme?.addToClipboard, classes?.addToClipboard) })}>
|
|
282
|
+
{saveLabel}
|
|
283
|
+
</button>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<!-- Search and Clear -->
|
|
287
|
+
{#if items.length > 0}
|
|
288
|
+
<div class={styles.searchWrapper({ class: clsx(theme?.searchWrapper, classes?.searchWrapper) })}>
|
|
289
|
+
<div class={styles.searchContainer({ class: clsx(theme?.searchContainer, classes?.searchContainer) })}>
|
|
290
|
+
<input type="text" bind:value={searchQuery} placeholder="Search clipboard..." class={styles.searchInput({ class: clsx(theme?.searchInput, classes?.searchInput) })} />
|
|
291
|
+
<svg class={styles.searchIcon({ class: clsx(theme?.searchIcon, classes?.searchIcon) })} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
292
|
+
<circle cx="11" cy="11" r="8" />
|
|
293
|
+
<path d="m21 21-4.35-4.35" />
|
|
294
|
+
</svg>
|
|
295
|
+
</div>
|
|
296
|
+
<button onclick={clearAll} class={styles.clearAll({ class: clsx(theme?.clearAll, classes?.clearAll) })}>
|
|
297
|
+
{clearLabel}
|
|
298
|
+
</button>
|
|
299
|
+
</div>
|
|
300
|
+
{/if}
|
|
301
|
+
</div>
|
|
302
|
+
{/snippet}
|
|
303
|
+
|
|
304
|
+
{#snippet searchClear()}
|
|
305
|
+
<!-- Show only search and clear when input is hidden -->
|
|
306
|
+
<div class={styles.inputSection({ class: clsx(theme?.inputSection, classes?.inputSection) })}>
|
|
307
|
+
<div class={styles.searchWrapper({ class: clsx(theme?.searchWrapper, classes?.searchWrapper) })}>
|
|
308
|
+
<div class={styles.searchContainer({ class: clsx(theme?.searchContainer, classes?.searchContainer) })}>
|
|
309
|
+
<input type="text" bind:value={searchQuery} placeholder="Search clipboard..." class={styles.searchInput({ class: clsx(theme?.searchInput, classes?.searchInput) })} />
|
|
310
|
+
<svg class={styles.searchIcon({ class: clsx(theme?.searchIcon, classes?.searchIcon) })} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
311
|
+
<circle cx="11" cy="11" r="8" />
|
|
312
|
+
<path d="m21 21-4.35-4.35" />
|
|
313
|
+
</svg>
|
|
314
|
+
</div>
|
|
315
|
+
<button onclick={clearAll} class={styles.clearAll({ class: clsx(theme?.clearAll, classes?.clearAll) })}>
|
|
316
|
+
{clearLabel}
|
|
317
|
+
</button>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
{/snippet}
|
|
321
|
+
|
|
322
|
+
{#snippet itemList()}
|
|
323
|
+
<!-- Items list -->
|
|
324
|
+
<div class={styles.itemsList({ class: clsx(theme?.itemsList, classes?.itemsList) })}>
|
|
325
|
+
{#if filteredItems.length === 0}
|
|
326
|
+
{#if emptyState}
|
|
327
|
+
{@render emptyState()}
|
|
328
|
+
{:else}
|
|
329
|
+
<div class={styles.emptyState({ class: clsx(theme?.emptyState, classes?.emptyState) })}>
|
|
330
|
+
{#if items.length === 0}
|
|
331
|
+
<svg class={styles.emptyIcon({ class: clsx(theme?.emptyIcon, classes?.emptyIcon) })} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
332
|
+
<path
|
|
333
|
+
stroke-linecap="round"
|
|
334
|
+
stroke-linejoin="round"
|
|
335
|
+
stroke-width="2"
|
|
336
|
+
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
337
|
+
/>
|
|
338
|
+
</svg>
|
|
339
|
+
<p class={styles.emptyText({ class: clsx(theme?.emptyText, classes?.emptyText) })}>No clipboard items yet.</p>
|
|
340
|
+
<p class={styles.emptySubtext({ class: clsx(theme?.emptySubtext, classes?.emptySubtext) })}>
|
|
341
|
+
{#if enableSelectionMenu}
|
|
342
|
+
Select any text and click "Save" to add it here
|
|
343
|
+
{:else}
|
|
344
|
+
Start typing above to save text
|
|
345
|
+
{/if}
|
|
346
|
+
</p>
|
|
347
|
+
{:else}
|
|
348
|
+
<p class={styles.emptyText({ class: clsx(theme?.emptyText, classes?.emptyText) })}>No items match "{searchQuery}"</p>
|
|
349
|
+
{/if}
|
|
350
|
+
</div>
|
|
351
|
+
{/if}
|
|
352
|
+
{:else}
|
|
353
|
+
{#each filteredItems as item (item.id)}
|
|
354
|
+
{#if children}
|
|
355
|
+
{@render children({ item, copyItem, deleteItem, togglePin })}
|
|
356
|
+
{:else}
|
|
357
|
+
<div class={styles.item({ class: clsx(theme?.item, classes?.item) })}>
|
|
358
|
+
<!-- Content -->
|
|
359
|
+
<div class={styles.itemContent({ class: clsx(theme?.itemContent, classes?.itemContent) })}>
|
|
360
|
+
<div class={styles.itemHeader({ class: clsx(theme?.itemHeader, classes?.itemHeader) })}>
|
|
361
|
+
{#if item.pinned}
|
|
362
|
+
<Badge {...badgeProps}>Pinned</Badge>
|
|
363
|
+
{/if}
|
|
364
|
+
<span class={styles.itemTimestamp({ class: clsx(theme?.itemTimestamp, classes?.itemTimestamp) })}>
|
|
365
|
+
{formatDistanceToNow(item.timestamp, { addSuffix: true })}
|
|
366
|
+
</span>
|
|
367
|
+
</div>
|
|
368
|
+
<p class={styles.itemText({ class: clsx(theme?.itemText, classes?.itemText) })}>
|
|
369
|
+
{item.text}
|
|
370
|
+
</p>
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
<!-- Actions -->
|
|
374
|
+
<div class={styles.itemActions({ class: clsx(theme?.itemActions, classes?.itemActions) })}>
|
|
375
|
+
<!-- Copy -->
|
|
376
|
+
<button onclick={() => copyItem(item)} class={styles.actionButton({ class: clsx(theme?.actionButton, classes?.actionButton) })} aria-label="Copy">
|
|
377
|
+
<svg
|
|
378
|
+
class={styles.actionIcon({ class: clsx(theme?.actionIcon, classes?.actionIcon) })}
|
|
379
|
+
fill="none"
|
|
380
|
+
stroke="currentColor"
|
|
381
|
+
stroke-width="2"
|
|
382
|
+
stroke-linecap="round"
|
|
383
|
+
stroke-linejoin="round"
|
|
384
|
+
viewBox="0 0 24 24"
|
|
385
|
+
>
|
|
386
|
+
<rect width="8" height="8" x="8" y="8" rx="2" ry="2" />
|
|
387
|
+
<path d="M4 12V4a2 2 0 0 1 2-2h8" />
|
|
388
|
+
</svg>
|
|
389
|
+
</button>
|
|
390
|
+
|
|
391
|
+
<!-- Pin / Unpin -->
|
|
392
|
+
<button
|
|
393
|
+
onclick={() => togglePin(item.id)}
|
|
394
|
+
class={styles.pinButton({ pinned: item.pinned, class: clsx(theme?.pinButton, classes?.pinButton) })}
|
|
395
|
+
aria-label={item.pinned ? "Unpin" : "Pin"}
|
|
396
|
+
>
|
|
397
|
+
<svg
|
|
398
|
+
class={styles.actionIcon({ class: clsx(theme?.actionIcon, classes?.actionIcon) })}
|
|
399
|
+
fill="none"
|
|
400
|
+
stroke="currentColor"
|
|
401
|
+
stroke-width="2"
|
|
402
|
+
stroke-linecap="round"
|
|
403
|
+
stroke-linejoin="round"
|
|
404
|
+
viewBox="0 0 24 24"
|
|
405
|
+
>
|
|
406
|
+
<path d="M12 17v5" />
|
|
407
|
+
<path d="M8 13h8l1-5h-10z" />
|
|
408
|
+
<path d="M10 3h4v5h-4z" />
|
|
409
|
+
</svg>
|
|
410
|
+
</button>
|
|
411
|
+
|
|
412
|
+
<!-- Delete -->
|
|
413
|
+
<button onclick={() => deleteItem(item.id)} class={styles.deleteButton({ class: clsx(theme?.deleteButton, classes?.deleteButton) })} aria-label="Delete">
|
|
414
|
+
<svg
|
|
415
|
+
class={styles.actionIcon({ class: clsx(theme?.actionIcon, classes?.actionIcon) })}
|
|
416
|
+
fill="none"
|
|
417
|
+
stroke="currentColor"
|
|
418
|
+
stroke-width="2"
|
|
419
|
+
stroke-linecap="round"
|
|
420
|
+
stroke-linejoin="round"
|
|
421
|
+
viewBox="0 0 24 24"
|
|
422
|
+
>
|
|
423
|
+
<path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14Z" />
|
|
424
|
+
<path d="M10 11v6M14 11v6" />
|
|
425
|
+
</svg>
|
|
426
|
+
</button>
|
|
427
|
+
</div>
|
|
428
|
+
</div>
|
|
429
|
+
{/if}
|
|
430
|
+
{/each}
|
|
431
|
+
{/if}
|
|
432
|
+
</div>
|
|
433
|
+
{/snippet}
|
|
434
|
+
|
|
435
|
+
{#if isModal}
|
|
436
|
+
<Modal title="Clipboard Manager" bind:open {...modalProps}>
|
|
437
|
+
<div class={styles.base({ class: clsx(theme?.base, className) })}>
|
|
438
|
+
{#if showInput}
|
|
439
|
+
{@render inputArea()}
|
|
440
|
+
{:else if items.length > 0}
|
|
441
|
+
{@render searchClear()}
|
|
442
|
+
{/if}
|
|
443
|
+
|
|
444
|
+
{@render itemList()}
|
|
445
|
+
</div>
|
|
446
|
+
</Modal>
|
|
447
|
+
{:else}
|
|
448
|
+
<div class={styles.base({ class: clsx(theme?.base, className) })}>
|
|
449
|
+
{#if showInput}
|
|
450
|
+
{@render inputArea()}
|
|
451
|
+
{:else if items.length > 0}
|
|
452
|
+
{@render searchClear()}
|
|
453
|
+
{/if}
|
|
454
|
+
|
|
455
|
+
{@render itemList()}
|
|
456
|
+
</div>
|
|
457
|
+
{/if}
|
|
458
|
+
|
|
459
|
+
<!-- Selection Bubble Menu -->
|
|
460
|
+
{#if selectionMenu.show}
|
|
461
|
+
<div class={styles.selectionMenu({ class: clsx(theme?.selectionMenu, classes?.selectionMenu) })} style="left: {selectionMenu.x}px; top: {selectionMenu.y}px;">
|
|
462
|
+
<div class={styles.selectionBubble({ class: clsx(theme?.selectionBubble, classes?.selectionBubble) })}>
|
|
463
|
+
<span class={styles.selectionText({ class: clsx(theme?.selectionText, classes?.selectionText) })}>
|
|
464
|
+
{selectionMenu.text.slice(0, 50)}{selectionMenu.text.length > 50 ? "..." : ""}
|
|
465
|
+
</span>
|
|
466
|
+
<button onclick={saveSelection} class={styles.selectionButton()}>Save to Clipboard</button>
|
|
467
|
+
</div>
|
|
468
|
+
<!-- Arrow -->
|
|
469
|
+
<div class={styles.selectionArrow({ class: clsx(theme?.selectionArrow, classes?.selectionArrow) })}></div>
|
|
470
|
+
</div>
|
|
471
|
+
{/if}
|
|
472
|
+
|
|
473
|
+
<!-- Toast notification -->
|
|
474
|
+
{#if toast}
|
|
475
|
+
<div class={styles.toastContainer({ class: clsx(theme?.toastContainer, classes?.toastContainer) })}>
|
|
476
|
+
<div class={styles.toast({ type: toast.type, class: clsx(theme?.toast, classes?.toast) })}>
|
|
477
|
+
<svg class={styles.toastIcon({ class: clsx(theme?.toastIcon, classes?.toastIcon) })} fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
478
|
+
{#if toast.type === "success"}
|
|
479
|
+
<path d="M5 13l4 4L19 7" />
|
|
480
|
+
{:else if toast.type === "error"}
|
|
481
|
+
<circle cx="12" cy="12" r="10" />
|
|
482
|
+
<path d="M15 9l-6 6M9 9l6 6" />
|
|
483
|
+
{:else}
|
|
484
|
+
<circle cx="12" cy="12" r="10" />
|
|
485
|
+
<path d="M12 16v-4M12 8h.01" />
|
|
486
|
+
{/if}
|
|
487
|
+
</svg>
|
|
488
|
+
<span class={styles.toastText({ class: clsx(theme?.toastText, classes?.toastText) })}>{toast.message}</span>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
{/if}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { type VariantProps } from "tailwind-variants";
|
|
2
|
+
import type { Classes } from "../theme/themeUtils";
|
|
3
|
+
export type ClipboardManagerVariants = VariantProps<typeof clipboardManager> & Classes<typeof clipboardManager>;
|
|
4
|
+
export declare const clipboardManager: import("tailwind-variants").TVReturnType<{
|
|
5
|
+
pinned: {
|
|
6
|
+
true: {
|
|
7
|
+
pinButton: string;
|
|
8
|
+
};
|
|
9
|
+
false: {
|
|
10
|
+
pinButton: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
type: {
|
|
14
|
+
success: {
|
|
15
|
+
toast: string;
|
|
16
|
+
};
|
|
17
|
+
error: {
|
|
18
|
+
toast: string;
|
|
19
|
+
};
|
|
20
|
+
info: {
|
|
21
|
+
toast: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}, {
|
|
25
|
+
base: string;
|
|
26
|
+
inputSection: string;
|
|
27
|
+
inputWrapper: string;
|
|
28
|
+
input: string;
|
|
29
|
+
searchWrapper: string;
|
|
30
|
+
searchContainer: string;
|
|
31
|
+
searchInput: string;
|
|
32
|
+
searchIcon: string;
|
|
33
|
+
itemsList: string;
|
|
34
|
+
emptyState: string;
|
|
35
|
+
emptyIcon: string;
|
|
36
|
+
emptyText: string;
|
|
37
|
+
emptySubtext: string;
|
|
38
|
+
item: string;
|
|
39
|
+
itemContent: string;
|
|
40
|
+
itemHeader: string;
|
|
41
|
+
itemTimestamp: string;
|
|
42
|
+
itemText: string;
|
|
43
|
+
itemActions: string;
|
|
44
|
+
actionButton: string;
|
|
45
|
+
actionIcon: string;
|
|
46
|
+
pinButton: string;
|
|
47
|
+
deleteButton: string;
|
|
48
|
+
toastContainer: string;
|
|
49
|
+
toast: string;
|
|
50
|
+
toastIcon: string;
|
|
51
|
+
toastText: string;
|
|
52
|
+
addToClipboard: string;
|
|
53
|
+
clearAll: string;
|
|
54
|
+
selectionMenu: string;
|
|
55
|
+
selectionBubble: string;
|
|
56
|
+
selectionText: string;
|
|
57
|
+
selectionButton: string;
|
|
58
|
+
selectionArrow: string;
|
|
59
|
+
}, undefined, {
|
|
60
|
+
pinned: {
|
|
61
|
+
true: {
|
|
62
|
+
pinButton: string;
|
|
63
|
+
};
|
|
64
|
+
false: {
|
|
65
|
+
pinButton: string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
type: {
|
|
69
|
+
success: {
|
|
70
|
+
toast: string;
|
|
71
|
+
};
|
|
72
|
+
error: {
|
|
73
|
+
toast: string;
|
|
74
|
+
};
|
|
75
|
+
info: {
|
|
76
|
+
toast: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
}, {
|
|
80
|
+
base: string;
|
|
81
|
+
inputSection: string;
|
|
82
|
+
inputWrapper: string;
|
|
83
|
+
input: string;
|
|
84
|
+
searchWrapper: string;
|
|
85
|
+
searchContainer: string;
|
|
86
|
+
searchInput: string;
|
|
87
|
+
searchIcon: string;
|
|
88
|
+
itemsList: string;
|
|
89
|
+
emptyState: string;
|
|
90
|
+
emptyIcon: string;
|
|
91
|
+
emptyText: string;
|
|
92
|
+
emptySubtext: string;
|
|
93
|
+
item: string;
|
|
94
|
+
itemContent: string;
|
|
95
|
+
itemHeader: string;
|
|
96
|
+
itemTimestamp: string;
|
|
97
|
+
itemText: string;
|
|
98
|
+
itemActions: string;
|
|
99
|
+
actionButton: string;
|
|
100
|
+
actionIcon: string;
|
|
101
|
+
pinButton: string;
|
|
102
|
+
deleteButton: string;
|
|
103
|
+
toastContainer: string;
|
|
104
|
+
toast: string;
|
|
105
|
+
toastIcon: string;
|
|
106
|
+
toastText: string;
|
|
107
|
+
addToClipboard: string;
|
|
108
|
+
clearAll: string;
|
|
109
|
+
selectionMenu: string;
|
|
110
|
+
selectionBubble: string;
|
|
111
|
+
selectionText: string;
|
|
112
|
+
selectionButton: string;
|
|
113
|
+
selectionArrow: string;
|
|
114
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
115
|
+
pinned: {
|
|
116
|
+
true: {
|
|
117
|
+
pinButton: string;
|
|
118
|
+
};
|
|
119
|
+
false: {
|
|
120
|
+
pinButton: string;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
type: {
|
|
124
|
+
success: {
|
|
125
|
+
toast: string;
|
|
126
|
+
};
|
|
127
|
+
error: {
|
|
128
|
+
toast: string;
|
|
129
|
+
};
|
|
130
|
+
info: {
|
|
131
|
+
toast: string;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
}, {
|
|
135
|
+
base: string;
|
|
136
|
+
inputSection: string;
|
|
137
|
+
inputWrapper: string;
|
|
138
|
+
input: string;
|
|
139
|
+
searchWrapper: string;
|
|
140
|
+
searchContainer: string;
|
|
141
|
+
searchInput: string;
|
|
142
|
+
searchIcon: string;
|
|
143
|
+
itemsList: string;
|
|
144
|
+
emptyState: string;
|
|
145
|
+
emptyIcon: string;
|
|
146
|
+
emptyText: string;
|
|
147
|
+
emptySubtext: string;
|
|
148
|
+
item: string;
|
|
149
|
+
itemContent: string;
|
|
150
|
+
itemHeader: string;
|
|
151
|
+
itemTimestamp: string;
|
|
152
|
+
itemText: string;
|
|
153
|
+
itemActions: string;
|
|
154
|
+
actionButton: string;
|
|
155
|
+
actionIcon: string;
|
|
156
|
+
pinButton: string;
|
|
157
|
+
deleteButton: string;
|
|
158
|
+
toastContainer: string;
|
|
159
|
+
toast: string;
|
|
160
|
+
toastIcon: string;
|
|
161
|
+
toastText: string;
|
|
162
|
+
addToClipboard: string;
|
|
163
|
+
clearAll: string;
|
|
164
|
+
selectionMenu: string;
|
|
165
|
+
selectionBubble: string;
|
|
166
|
+
selectionText: string;
|
|
167
|
+
selectionButton: string;
|
|
168
|
+
selectionArrow: string;
|
|
169
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { tv } from "tailwind-variants";
|
|
2
|
+
export const clipboardManager = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
base: "relative max-w-2xl mx-auto p-4 space-y-4",
|
|
5
|
+
// Input section
|
|
6
|
+
inputSection: "space-y-2",
|
|
7
|
+
inputWrapper: "flex gap-2",
|
|
8
|
+
input: "flex-1 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-500 dark:text-white",
|
|
9
|
+
// Search section
|
|
10
|
+
searchWrapper: "flex gap-2",
|
|
11
|
+
searchContainer: "relative flex-1",
|
|
12
|
+
searchInput: "w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 pl-9 pr-3 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-500 dark:text-white",
|
|
13
|
+
searchIcon: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400",
|
|
14
|
+
// Items list
|
|
15
|
+
itemsList: "space-y-2 max-h-[500px] overflow-y-auto",
|
|
16
|
+
// Empty state
|
|
17
|
+
emptyState: "text-center py-8",
|
|
18
|
+
emptyIcon: "w-12 h-12 mx-auto text-gray-300 dark:text-gray-600 mb-3",
|
|
19
|
+
emptyText: "text-sm text-gray-500 dark:text-gray-400",
|
|
20
|
+
emptySubtext: "text-xs text-gray-400 dark:text-gray-500 mt-1",
|
|
21
|
+
// Item
|
|
22
|
+
item: "group flex items-start gap-3 rounded-lg border border-gray-200 dark:border-gray-700 p-3 transition hover:bg-gray-50 dark:hover:bg-gray-800/50",
|
|
23
|
+
itemContent: "flex-1 min-w-0",
|
|
24
|
+
itemHeader: "flex items-center gap-2 mb-1",
|
|
25
|
+
itemTimestamp: "text-xs text-gray-500 dark:text-gray-400",
|
|
26
|
+
itemText: "text-sm text-gray-900 dark:text-gray-100 break-words line-clamp-2",
|
|
27
|
+
// Actions
|
|
28
|
+
itemActions: "flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
29
|
+
actionButton: "p-1.5 rounded hover:bg-gray-200 dark:hover:bg-gray-700 transition flex items-center justify-center",
|
|
30
|
+
actionIcon: "w-4 h-4 flex-shrink-0",
|
|
31
|
+
pinButton: "p-1.5 rounded transition",
|
|
32
|
+
deleteButton: "p-1.5 rounded text-red-500 hover:bg-red-100 dark:hover:bg-red-900/20 transition",
|
|
33
|
+
// Toast
|
|
34
|
+
toastContainer: "fixed top-4 left-1/2 -translate-x-1/2 z-50 animate-[slideIn_0.2s_ease-out]",
|
|
35
|
+
toast: "flex items-center gap-2 px-4 py-2 rounded-lg shadow-lg",
|
|
36
|
+
toastIcon: "w-5 h-5",
|
|
37
|
+
toastText: "text-sm font-medium",
|
|
38
|
+
// buttons
|
|
39
|
+
addToClipboard: "whitespace-nowrap rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50",
|
|
40
|
+
clearAll: "rounded bg-red-600 px-4 py-2 text-white hover:bg-red-700",
|
|
41
|
+
// Selection bubble menu
|
|
42
|
+
selectionMenu: "selection-menu fixed z-50 -translate-x-1/2 -translate-y-full",
|
|
43
|
+
selectionBubble: "mb-2 flex items-center gap-2 rounded-lg bg-gray-900 px-3 py-2 text-white shadow-xl",
|
|
44
|
+
selectionText: "max-w-[200px] truncate text-xs",
|
|
45
|
+
selectionButton: "rounded bg-primary-700 px-2 py-1 text-xs font-medium whitespace-nowrap transition hover:bg-primary-500",
|
|
46
|
+
selectionArrow: "absolute bottom-1 left-1/2 h-2 w-2 -translate-x-1/2 rotate-45 bg-gray-900"
|
|
47
|
+
},
|
|
48
|
+
variants: {
|
|
49
|
+
pinned: {
|
|
50
|
+
true: {
|
|
51
|
+
pinButton: "text-blue-600 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/20"
|
|
52
|
+
},
|
|
53
|
+
false: {
|
|
54
|
+
pinButton: "hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
type: {
|
|
58
|
+
success: {
|
|
59
|
+
toast: "bg-green-500 text-white"
|
|
60
|
+
},
|
|
61
|
+
error: {
|
|
62
|
+
toast: "bg-red-500 text-white"
|
|
63
|
+
},
|
|
64
|
+
info: {
|
|
65
|
+
toast: "bg-blue-500 text-white"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
defaultVariants: {
|
|
70
|
+
pinned: false,
|
|
71
|
+
type: "success"
|
|
72
|
+
}
|
|
73
|
+
});
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
@component
|
|
195
195
|
[Go to docs](https://flowbite-svelte.com/)
|
|
196
196
|
## Type
|
|
197
|
-
[CommandPaletteProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
197
|
+
[CommandPaletteProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2192)
|
|
198
198
|
## Props
|
|
199
199
|
@prop open = $bindable(false)
|
|
200
200
|
@prop items = []
|
|
@@ -2,7 +2,7 @@ import type { CommandPaletteProps } from "../types";
|
|
|
2
2
|
/**
|
|
3
3
|
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
4
|
* ## Type
|
|
5
|
-
* [CommandPaletteProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
5
|
+
* [CommandPaletteProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2192)
|
|
6
6
|
* ## Props
|
|
7
7
|
* @prop open = $bindable(false)
|
|
8
8
|
* @prop items = []
|
package/dist/index.d.ts
CHANGED
|
@@ -78,10 +78,11 @@ export * from "./typography/span";
|
|
|
78
78
|
export * from "./video";
|
|
79
79
|
export * from "./utils";
|
|
80
80
|
export * from "./types";
|
|
81
|
+
export * from "./clipboard-manager";
|
|
81
82
|
export * from "./command-palette";
|
|
82
|
-
export * from "./virtuallist";
|
|
83
83
|
export * from "./kanban";
|
|
84
|
+
export * from "./scroll-spy";
|
|
84
85
|
export * from "./split-pane";
|
|
85
86
|
export * from "./tour";
|
|
86
|
-
export * from "./
|
|
87
|
+
export * from "./virtuallist";
|
|
87
88
|
export * from "./virtual-masonry";
|
package/dist/index.js
CHANGED
|
@@ -84,10 +84,11 @@ export * from "./video";
|
|
|
84
84
|
export * from "./utils";
|
|
85
85
|
export * from "./types";
|
|
86
86
|
// extend
|
|
87
|
+
export * from "./clipboard-manager";
|
|
87
88
|
export * from "./command-palette";
|
|
88
|
-
export * from "./virtuallist";
|
|
89
89
|
export * from "./kanban";
|
|
90
|
+
export * from "./scroll-spy";
|
|
90
91
|
export * from "./split-pane";
|
|
91
92
|
export * from "./tour";
|
|
92
|
-
export * from "./
|
|
93
|
+
export * from "./virtuallist";
|
|
93
94
|
export * from "./virtual-masonry";
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
columns = $bindable([]),
|
|
10
10
|
onMove = (_card: KanbanCardType, _from: KanbanColumnType, _to: KanbanColumnType) => {},
|
|
11
11
|
onAddCard = (_col: KanbanColumnType) => {},
|
|
12
|
+
cardProps,
|
|
12
13
|
class: className,
|
|
13
14
|
classes,
|
|
14
15
|
...restProps
|
|
@@ -88,7 +89,7 @@
|
|
|
88
89
|
|
|
89
90
|
<div class={styles.cardList({ class: clsx(theme?.cardList, classes?.cardList) })} role="list" aria-label={`${col.title} cards`}>
|
|
90
91
|
{#each col.cards as card (card.id)}
|
|
91
|
-
<KanbanCard {card} {classes} isDragging={draggedCard?.id === card.id} onDragStart={() => handleDragStart(card, col.id)} onDragEnd={handleDragEnd} />
|
|
92
|
+
<KanbanCard {card} {classes} {...cardProps} isDragging={draggedCard?.id === card.id} onDragStart={() => handleDragStart(card, col.id)} onDragEnd={handleDragEnd} />
|
|
92
93
|
{/each}
|
|
93
94
|
</div>
|
|
94
95
|
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
@component
|
|
48
48
|
[Go to docs](https://flowbite-svelte.com/)
|
|
49
49
|
## Type
|
|
50
|
-
[KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
50
|
+
[KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2121)
|
|
51
51
|
## Props
|
|
52
52
|
@prop card
|
|
53
53
|
@prop isDragging = false
|
|
@@ -2,7 +2,7 @@ import type { KanbanCardProps } from "../types";
|
|
|
2
2
|
/**
|
|
3
3
|
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
4
|
* ## Type
|
|
5
|
-
* [KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
5
|
+
* [KanbanCardProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2121)
|
|
6
6
|
* ## Props
|
|
7
7
|
* @prop card
|
|
8
8
|
* @prop isDragging = false
|
package/dist/kanban/theme.js
CHANGED
|
@@ -3,14 +3,14 @@ export const kanbanBoard = tv({
|
|
|
3
3
|
slots: {
|
|
4
4
|
container: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3 md:gap-4 p-2 md:p-4",
|
|
5
5
|
column: "w-full rounded-xl shadow-sm p-3 md:p-4 flex flex-col bg-surface-elevated text-surface-foreground transition-colors",
|
|
6
|
-
columnTitle: "text-sm md:text-base font-semibold mb-2 md:mb-3",
|
|
6
|
+
columnTitle: "text-sm md:text-base font-semibold mb-2 md:mb-3 dark:text-white",
|
|
7
7
|
cardList: "flex flex-col gap-2 flex-1 min-h-[60px]",
|
|
8
8
|
card: "bg-surface text-surface-foreground rounded-lg p-2.5 md:p-3 shadow-sm cursor-grab active:cursor-grabbing transition-all hover:bg-surface-hover hover:shadow-md",
|
|
9
9
|
cardTitle: "font-medium text-sm md:text-base",
|
|
10
10
|
cardDescription: "text-xs md:text-sm text-muted mt-1",
|
|
11
11
|
cardTags: "flex flex-wrap gap-1 mt-2",
|
|
12
12
|
cardTag: "text-[10px] md:text-xs bg-primary/10 text-primary px-1.5 md:px-2 py-0.5 rounded-full",
|
|
13
|
-
addButton: "mt-2 md:mt-3 w-full bg-primary text-primary-foreground rounded-lg py-1.5 text-xs md:text-sm font-medium hover:bg-primary/90 transition-colors focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
|
13
|
+
addButton: "mt-2 md:mt-3 w-full bg-primary text-primary-foreground rounded-lg py-1.5 text-xs md:text-sm dark:text-primary-500 font-medium hover:bg-primary/90 transition-colors focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
|
14
14
|
},
|
|
15
15
|
variants: {
|
|
16
16
|
isDragOver: {
|
|
@@ -27,11 +27,11 @@ export const kanbanBoard = tv({
|
|
|
27
27
|
});
|
|
28
28
|
export const kanbanCard = tv({
|
|
29
29
|
slots: {
|
|
30
|
-
card: "bg-surface text-surface-foreground rounded-lg p-2.5 md:p-3 shadow-sm cursor-grab active:cursor-grabbing transition-all hover:bg-surface-hover hover:shadow-md",
|
|
31
|
-
cardTitle: "font-medium text-sm md:text-base",
|
|
32
|
-
cardDescription: "text-xs md:text-sm text-muted mt-1",
|
|
33
|
-
cardTags: "flex flex-wrap gap-1 mt-2",
|
|
34
|
-
cardTag: "text-[10px] md:text-xs bg-primary/10 text-primary px-1.5 md:px-2 py-0.5 rounded-full"
|
|
30
|
+
card: "bg-surface text-surface-foreground rounded-lg p-2.5 md:p-3 shadow-sm shadow-black/20 dark:shadow-white/10 cursor-grab active:cursor-grabbing transition-all hover:bg-surface-hover hover:shadow-md",
|
|
31
|
+
cardTitle: "font-medium text-sm md:text-base dark:text-white",
|
|
32
|
+
cardDescription: "text-xs md:text-sm text-muted mt-1 dark:text-white",
|
|
33
|
+
cardTags: "flex flex-wrap gap-1 mt-2 dark:text-white",
|
|
34
|
+
cardTag: "text-[10px] md:text-xs bg-primary/10 text-primary px-1.5 md:px-2 py-0.5 rounded-full dark:text-white"
|
|
35
35
|
},
|
|
36
36
|
variants: {
|
|
37
37
|
isDragging: {
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
@component
|
|
256
256
|
[Go to docs](https://flowbite-svelte.com/)
|
|
257
257
|
## Type
|
|
258
|
-
[ScrollSpyProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
258
|
+
[ScrollSpyProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2207)
|
|
259
259
|
## Props
|
|
260
260
|
@prop items
|
|
261
261
|
@prop position = "top"
|
|
@@ -2,7 +2,7 @@ import type { ScrollSpyProps } from "../types";
|
|
|
2
2
|
/**
|
|
3
3
|
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
4
|
* ## Type
|
|
5
|
-
* [ScrollSpyProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
5
|
+
* [ScrollSpyProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2207)
|
|
6
6
|
* ## Props
|
|
7
7
|
* @prop items
|
|
8
8
|
* @prop position = "top"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
@component
|
|
38
38
|
[Go to docs](https://flowbite-svelte.com/)
|
|
39
39
|
## Type
|
|
40
|
-
[DividerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
40
|
+
[DividerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2150)
|
|
41
41
|
## Props
|
|
42
42
|
@prop direction
|
|
43
43
|
@prop index
|
|
@@ -2,7 +2,7 @@ import type { DividerProps } from "../types";
|
|
|
2
2
|
/**
|
|
3
3
|
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
4
|
* ## Type
|
|
5
|
-
* [DividerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
5
|
+
* [DividerProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2150)
|
|
6
6
|
* ## Props
|
|
7
7
|
* @prop direction
|
|
8
8
|
* @prop index
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
@component
|
|
48
48
|
[Go to docs](https://flowbite-svelte.com/)
|
|
49
49
|
## Type
|
|
50
|
-
[PaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
50
|
+
[PaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2144)
|
|
51
51
|
## Props
|
|
52
52
|
@prop children
|
|
53
53
|
@prop class: className = ""
|
|
@@ -2,7 +2,7 @@ import type { PaneProps } from "../types";
|
|
|
2
2
|
/**
|
|
3
3
|
* [Go to docs](https://flowbite-svelte.com/)
|
|
4
4
|
* ## Type
|
|
5
|
-
* [PaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
5
|
+
* [PaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2144)
|
|
6
6
|
* ## Props
|
|
7
7
|
* @prop children
|
|
8
8
|
* @prop class: className = ""
|
|
@@ -410,7 +410,7 @@
|
|
|
410
410
|
@component
|
|
411
411
|
[Go to docs](https://flowbite-svelte.com/)
|
|
412
412
|
## Type
|
|
413
|
-
[SplitPaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
413
|
+
[SplitPaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2130)
|
|
414
414
|
## Props
|
|
415
415
|
@prop direction = "horizontal"
|
|
416
416
|
@prop minSize = 100
|
|
@@ -15,7 +15,7 @@ import type { SplitPaneProps } from "../types";
|
|
|
15
15
|
/**
|
|
16
16
|
* [Go to docs](https://flowbite-svelte.com/)
|
|
17
17
|
* ## Type
|
|
18
|
-
* [SplitPaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#
|
|
18
|
+
* [SplitPaneProps](https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/types.ts#L2130)
|
|
19
19
|
* ## Props
|
|
20
20
|
* @prop direction = "horizontal"
|
|
21
21
|
* @prop minSize = 100
|
package/dist/theme/themes.d.ts
CHANGED
|
@@ -31,7 +31,6 @@ export { sidebar, sidebarBrand, sidebarButton, sidebarCta, sidebarDropdownWrappe
|
|
|
31
31
|
export { cardPlaceholder, imagePlaceholder, listPlaceholder, skeleton, testimonialPlaceholder, textPlaceholder, videoPlaceholder, widgetPlaceholder } from "../skeleton";
|
|
32
32
|
export { speedDial, speedDialButton } from "../speed-dial";
|
|
33
33
|
export { spinner } from "../spinner";
|
|
34
|
-
export { stepIndicator } from "../step-indicator";
|
|
35
34
|
export { stepper, progressStepper, verticalStepper, detailedStepper, breadcrumbStepper, timelineStepper } from "../stepper";
|
|
36
35
|
export { table, tableBodyCell, tableBodyRow, tableHead, tableHeadCell, tableSearch } from "../table";
|
|
37
36
|
export { tabs, tabItem } from "../tabs";
|
|
@@ -39,7 +38,6 @@ export { timeline, activity, activityItem, group, groupItem, timelineItem } from
|
|
|
39
38
|
export { toast } from "../toast";
|
|
40
39
|
export { toolbar, toolbarButton, toolbarGroup } from "../toolbar";
|
|
41
40
|
export { tooltip } from "../tooltip";
|
|
42
|
-
export { buttonToggle, buttonToggleGroup } from "../forms/button-toggle";
|
|
43
41
|
export { checkbox, checkboxButton } from "../forms/checkbox";
|
|
44
42
|
export { dropzone } from "../forms/dropzone";
|
|
45
43
|
export { fileupload } from "../forms/fileupload";
|
|
@@ -52,7 +50,6 @@ export { radio, radioButton } from "../forms/radio";
|
|
|
52
50
|
export { range } from "../forms/range";
|
|
53
51
|
export { search } from "../forms/search";
|
|
54
52
|
export { select, multiSelect } from "../forms/select";
|
|
55
|
-
export { tags } from "../forms/tags";
|
|
56
53
|
export { textarea } from "../forms/textarea";
|
|
57
54
|
export { timepicker } from "../forms/timepicker";
|
|
58
55
|
export { toggle } from "../forms/toggle";
|
|
@@ -68,10 +65,14 @@ export { mark } from "../typography/mark";
|
|
|
68
65
|
export { paragraph } from "../typography/paragraph";
|
|
69
66
|
export { secondary } from "../typography/secondary";
|
|
70
67
|
export { span } from "../typography/span";
|
|
68
|
+
export { buttonToggle, buttonToggleGroup } from "../forms/button-toggle";
|
|
69
|
+
export { clipboardManager } from "../clipboard-manager/theme";
|
|
71
70
|
export { commandPalette } from "../command-palette/theme";
|
|
72
|
-
export { virtualList } from "../virtuallist";
|
|
73
71
|
export { kanbanBoard, kanbanCard } from "../kanban/theme";
|
|
72
|
+
export { scrollspy } from "../scroll-spy/theme";
|
|
74
73
|
export { splitpane, pane, divider, dividerHitArea } from "../split-pane/theme";
|
|
74
|
+
export { stepIndicator } from "../step-indicator";
|
|
75
|
+
export { tags } from "../forms/tags";
|
|
75
76
|
export { tour } from "../tour/theme";
|
|
76
|
-
export {
|
|
77
|
+
export { virtualList } from "../virtuallist";
|
|
77
78
|
export { virtualMasonry } from "../virtual-masonry/theme";
|
package/dist/theme/themes.js
CHANGED
|
@@ -32,7 +32,6 @@ export { sidebar, sidebarBrand, sidebarButton, sidebarCta, sidebarDropdownWrappe
|
|
|
32
32
|
export { cardPlaceholder, imagePlaceholder, listPlaceholder, skeleton, testimonialPlaceholder, textPlaceholder, videoPlaceholder, widgetPlaceholder } from "../skeleton";
|
|
33
33
|
export { speedDial, speedDialButton } from "../speed-dial";
|
|
34
34
|
export { spinner } from "../spinner";
|
|
35
|
-
export { stepIndicator } from "../step-indicator";
|
|
36
35
|
export { stepper, progressStepper, verticalStepper, detailedStepper, breadcrumbStepper, timelineStepper } from "../stepper";
|
|
37
36
|
export { table, tableBodyCell, tableBodyRow, tableHead, tableHeadCell, tableSearch } from "../table";
|
|
38
37
|
export { tabs, tabItem } from "../tabs";
|
|
@@ -43,8 +42,6 @@ export { tooltip } from "../tooltip";
|
|
|
43
42
|
// export {utils} from "../utils";
|
|
44
43
|
// export {} from "../video";
|
|
45
44
|
// forms
|
|
46
|
-
// export {addon} from "../forms/input-addon";
|
|
47
|
-
export { buttonToggle, buttonToggleGroup } from "../forms/button-toggle";
|
|
48
45
|
export { checkbox, checkboxButton } from "../forms/checkbox";
|
|
49
46
|
export { dropzone } from "../forms/dropzone";
|
|
50
47
|
export { fileupload } from "../forms/fileupload";
|
|
@@ -57,7 +54,6 @@ export { radio, radioButton } from "../forms/radio";
|
|
|
57
54
|
export { range } from "../forms/range";
|
|
58
55
|
export { search } from "../forms/search";
|
|
59
56
|
export { select, multiSelect } from "../forms/select";
|
|
60
|
-
export { tags } from "../forms/tags";
|
|
61
57
|
export { textarea } from "../forms/textarea";
|
|
62
58
|
export { timepicker } from "../forms/timepicker";
|
|
63
59
|
export { toggle } from "../forms/toggle";
|
|
@@ -75,10 +71,14 @@ export { paragraph } from "../typography/paragraph";
|
|
|
75
71
|
export { secondary } from "../typography/secondary";
|
|
76
72
|
export { span } from "../typography/span";
|
|
77
73
|
// extend
|
|
74
|
+
export { buttonToggle, buttonToggleGroup } from "../forms/button-toggle";
|
|
75
|
+
export { clipboardManager } from "../clipboard-manager/theme";
|
|
78
76
|
export { commandPalette } from "../command-palette/theme";
|
|
79
|
-
export { virtualList } from "../virtuallist";
|
|
80
77
|
export { kanbanBoard, kanbanCard } from "../kanban/theme";
|
|
78
|
+
export { scrollspy } from "../scroll-spy/theme";
|
|
81
79
|
export { splitpane, pane, divider, dividerHitArea } from "../split-pane/theme";
|
|
80
|
+
export { stepIndicator } from "../step-indicator";
|
|
81
|
+
export { tags } from "../forms/tags";
|
|
82
82
|
export { tour } from "../tour/theme";
|
|
83
|
-
export {
|
|
83
|
+
export { virtualList } from "../virtuallist";
|
|
84
84
|
export { virtualMasonry } from "../virtual-masonry/theme";
|
package/dist/types.d.ts
CHANGED
|
@@ -1785,6 +1785,7 @@ export interface KanbanBoardProps extends KanbanBoardVariants, HTMLAttributes<HT
|
|
|
1785
1785
|
columns?: KanbanColumnType[];
|
|
1786
1786
|
onMove?: (card: KanbanCardType, from: KanbanColumnType, to: KanbanColumnType) => void;
|
|
1787
1787
|
onAddCard?: (col: KanbanColumnType) => void;
|
|
1788
|
+
cardProps?: Partial<Omit<KanbanCardProps, "card" | "isDragging" | "onDragStart" | "onDragEnd">>;
|
|
1788
1789
|
class?: ClassValue | null;
|
|
1789
1790
|
}
|
|
1790
1791
|
export interface KanbanCardProps extends KanbanCardVariants, HTMLAttributes<HTMLElement> {
|
|
@@ -1805,11 +1806,11 @@ export interface SplitPaneProps {
|
|
|
1805
1806
|
initialSizes?: number[];
|
|
1806
1807
|
onResize?: (sizes: number[]) => void;
|
|
1807
1808
|
children: Snippet;
|
|
1808
|
-
class?:
|
|
1809
|
+
class?: ClassValue | null;
|
|
1809
1810
|
}
|
|
1810
1811
|
export interface PaneProps {
|
|
1811
1812
|
children?: Snippet;
|
|
1812
|
-
class?:
|
|
1813
|
+
class?: ClassValue | null;
|
|
1813
1814
|
style?: string;
|
|
1814
1815
|
}
|
|
1815
1816
|
export interface DividerProps {
|
|
@@ -1819,7 +1820,7 @@ export interface DividerProps {
|
|
|
1819
1820
|
onTouchStart: (e: TouchEvent, index: number) => void;
|
|
1820
1821
|
onKeyDown: (e: KeyboardEvent, index: number) => void;
|
|
1821
1822
|
isDragging: boolean;
|
|
1822
|
-
class?:
|
|
1823
|
+
class?: ClassValue | null;
|
|
1823
1824
|
currentSize: number;
|
|
1824
1825
|
}
|
|
1825
1826
|
export type TourStep = {
|
|
@@ -1898,3 +1899,37 @@ export interface VirtualMasonryProps<T = unknown> extends VirtualMasonryVariants
|
|
|
1898
1899
|
ariaLabel?: string;
|
|
1899
1900
|
class?: ClassValue | null;
|
|
1900
1901
|
}
|
|
1902
|
+
import type { ClipboardManagerVariants } from "./clipboard-manager/theme";
|
|
1903
|
+
export interface ClipboardItem {
|
|
1904
|
+
id: number;
|
|
1905
|
+
text: string;
|
|
1906
|
+
pinned?: boolean;
|
|
1907
|
+
timestamp: number;
|
|
1908
|
+
}
|
|
1909
|
+
export interface ClipboardManagerProps extends ClipboardManagerVariants {
|
|
1910
|
+
children?: Snippet<[{
|
|
1911
|
+
item: ClipboardItem;
|
|
1912
|
+
copyItem: (item: ClipboardItem) => Promise<void>;
|
|
1913
|
+
deleteItem: (id: number) => void;
|
|
1914
|
+
togglePin: (id: number) => void;
|
|
1915
|
+
}]>;
|
|
1916
|
+
items?: ClipboardItem[];
|
|
1917
|
+
placeholder?: string;
|
|
1918
|
+
saveLabel?: string;
|
|
1919
|
+
clearLabel?: string;
|
|
1920
|
+
limit?: number;
|
|
1921
|
+
saveToStorage?: boolean;
|
|
1922
|
+
class?: ClassValue | null;
|
|
1923
|
+
toastDuration?: number;
|
|
1924
|
+
filterSensitive?: boolean;
|
|
1925
|
+
maxLength?: number;
|
|
1926
|
+
enableSelectionMenu?: boolean;
|
|
1927
|
+
selectionTarget?: string;
|
|
1928
|
+
showInput?: boolean;
|
|
1929
|
+
emptyState?: Snippet;
|
|
1930
|
+
storageKey?: string;
|
|
1931
|
+
open?: boolean;
|
|
1932
|
+
badgeProps?: Omit<BadgeProps, "children">;
|
|
1933
|
+
modalProps?: ModalProps;
|
|
1934
|
+
detectSensitiveData?: (text: string) => boolean;
|
|
1935
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowbite-svelte",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"description": "Flowbite components for Svelte",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": {
|
|
@@ -249,6 +249,10 @@
|
|
|
249
249
|
"types": "./dist/clipboard/Clipboard.svelte.d.ts",
|
|
250
250
|
"svelte": "./dist/clipboard/Clipboard.svelte"
|
|
251
251
|
},
|
|
252
|
+
"./ClipboardManager.svelte": {
|
|
253
|
+
"types": "./dist/clipboard-manager/ClipboardManager.svelte.d.ts",
|
|
254
|
+
"svelte": "./dist/clipboard-manager/ClipboardManager.svelte"
|
|
255
|
+
},
|
|
252
256
|
"./CommandPalette.svelte": {
|
|
253
257
|
"types": "./dist/command-palette/CommandPalette.svelte.d.ts",
|
|
254
258
|
"svelte": "./dist/command-palette/CommandPalette.svelte"
|
|
@@ -878,6 +882,7 @@
|
|
|
878
882
|
"package": "svelte-kit sync && svelte-package && publint",
|
|
879
883
|
"test": "npm run test:e2e && npm run test:unit",
|
|
880
884
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
885
|
+
"check:ignore": "npx sv check --ignore 'src/routes/testdir,dist,build' --no-tsconfig",
|
|
881
886
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
882
887
|
"lint": "prettier --check . && eslint .",
|
|
883
888
|
"format": "prettier --write .",
|