@joewinke/jatui 0.1.24 → 0.1.26
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/actions/collapse.d.ts +80 -0
- package/dist/actions/collapse.js +134 -0
- package/dist/actions/railNav.d.ts +167 -0
- package/dist/actions/railNav.js +351 -0
- package/dist/components/AnnotationLayer.svelte +108 -0
- package/dist/components/AnnotationLayer.svelte.d.ts +12 -0
- package/dist/components/AnnotationPanel.svelte +319 -0
- package/dist/components/AnnotationPanel.svelte.d.ts +13 -0
- package/dist/components/AudioWaveform.svelte +701 -0
- package/dist/components/AudioWaveform.svelte.d.ts +19 -0
- package/dist/components/AvailabilityModal.svelte +177 -0
- package/dist/components/AvailabilityModal.svelte.d.ts +14 -0
- package/dist/components/AvatarUpload.svelte +234 -0
- package/dist/components/AvatarUpload.svelte.d.ts +23 -0
- package/dist/components/Badge.svelte +38 -0
- package/dist/components/Badge.svelte.d.ts +14 -0
- package/dist/components/BookingForm.svelte +278 -0
- package/dist/components/BookingForm.svelte.d.ts +37 -0
- package/dist/components/BurndownChart.svelte +778 -0
- package/dist/components/BurndownChart.svelte.d.ts +17 -0
- package/dist/components/Button.svelte +81 -0
- package/dist/components/Button.svelte.d.ts +21 -0
- package/dist/components/CalendarPicker.svelte +284 -0
- package/dist/components/CalendarPicker.svelte.d.ts +22 -0
- package/dist/components/Card.svelte +67 -0
- package/dist/components/Card.svelte.d.ts +17 -0
- package/dist/components/CharacterCounter.svelte +82 -0
- package/dist/components/CharacterCounter.svelte.d.ts +10 -0
- package/dist/components/ChipInput.svelte +609 -0
- package/dist/components/ChipInput.svelte.d.ts +55 -0
- package/dist/components/Collapse.svelte +91 -0
- package/dist/components/Collapse.svelte.d.ts +18 -0
- package/dist/components/ColorSelector.svelte +167 -0
- package/dist/components/ColorSelector.svelte.d.ts +9 -0
- package/dist/components/CommentThread.svelte +773 -0
- package/dist/components/CommentThread.svelte.d.ts +32 -0
- package/dist/components/ConfirmDialog.svelte +348 -0
- package/dist/components/ConfirmDialog.svelte.d.ts +3 -0
- package/dist/components/ConfirmModal.svelte +142 -0
- package/dist/components/ConfirmModal.svelte.d.ts +15 -0
- package/dist/components/ContextMenu.svelte +188 -0
- package/dist/components/ContextMenu.svelte.d.ts +31 -0
- package/dist/components/CountdownTimer.svelte +94 -0
- package/dist/components/CountdownTimer.svelte.d.ts +10 -0
- package/dist/components/DateRangePicker.svelte +194 -0
- package/dist/components/DateRangePicker.svelte.d.ts +10 -0
- package/dist/components/Drawer.svelte +143 -0
- package/dist/components/Drawer.svelte.d.ts +33 -0
- package/dist/components/EmojiPicker.svelte +159 -0
- package/dist/components/EmojiPicker.svelte.d.ts +8 -0
- package/dist/components/EntityPreviewCard.svelte +104 -0
- package/dist/components/EntityPreviewCard.svelte.d.ts +9 -0
- package/dist/components/FileDropzone.svelte +493 -0
- package/dist/components/FileDropzone.svelte.d.ts +40 -0
- package/dist/components/FilePicker.svelte +488 -0
- package/dist/components/FilePicker.svelte.d.ts +20 -0
- package/dist/components/FileThumbnail.svelte +80 -0
- package/dist/components/FileThumbnail.svelte.d.ts +12 -0
- package/dist/components/FilterDropdown.svelte +203 -0
- package/dist/components/FilterDropdown.svelte.d.ts +48 -0
- package/dist/components/FloatingActionBar.svelte +354 -0
- package/dist/components/FloatingActionBar.svelte.d.ts +51 -0
- package/dist/components/GPSTracker.svelte +202 -0
- package/dist/components/GPSTracker.svelte.d.ts +34 -0
- package/dist/components/HunkDiffView.svelte +348 -0
- package/dist/components/HunkDiffView.svelte.d.ts +9 -0
- package/dist/components/ImageLightbox.svelte +274 -0
- package/dist/components/ImageLightbox.svelte.d.ts +25 -0
- package/dist/components/ImageUpload.svelte +147 -0
- package/dist/components/ImageUpload.svelte.d.ts +10 -0
- package/dist/components/InlineEdit.svelte +291 -0
- package/dist/components/InlineEdit.svelte.d.ts +45 -0
- package/dist/components/InputDialog.svelte +327 -0
- package/dist/components/InputDialog.svelte.d.ts +3 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte +296 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +42 -0
- package/dist/components/LazyImage.svelte +123 -0
- package/dist/components/LazyImage.svelte.d.ts +14 -0
- package/dist/components/LinkShortener.svelte +274 -0
- package/dist/components/LinkShortener.svelte.d.ts +27 -0
- package/dist/components/LoadingSpinner.svelte +106 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +21 -0
- package/dist/components/LocationMap.svelte +186 -0
- package/dist/components/LocationMap.svelte.d.ts +11 -0
- package/dist/components/MapView.svelte +341 -0
- package/dist/components/MapView.svelte.d.ts +20 -0
- package/dist/components/MarkupEditor.svelte +485 -0
- package/dist/components/MarkupEditor.svelte.d.ts +10 -0
- package/dist/components/MarkupOverlay.svelte +55 -0
- package/dist/components/MarkupOverlay.svelte.d.ts +8 -0
- package/dist/components/MediaWorkbench.svelte +871 -0
- package/dist/components/MediaWorkbench.svelte.d.ts +9 -0
- package/dist/components/MilestoneCard.svelte +98 -0
- package/dist/components/MilestoneCard.svelte.d.ts +16 -0
- package/dist/components/MilestoneTimeline.svelte +91 -0
- package/dist/components/MilestoneTimeline.svelte.d.ts +15 -0
- package/dist/components/Modal.svelte +244 -0
- package/dist/components/Modal.svelte.d.ts +35 -0
- package/dist/components/PDFViewer.svelte +105 -0
- package/dist/components/PDFViewer.svelte.d.ts +9 -0
- package/dist/components/PdfThumbnail.svelte +63 -0
- package/dist/components/PdfThumbnail.svelte.d.ts +9 -0
- package/dist/components/PhoneInput.svelte +212 -0
- package/dist/components/PhoneInput.svelte.d.ts +25 -0
- package/dist/components/ResizableDivider.svelte +307 -0
- package/dist/components/ResizableDivider.svelte.d.ts +26 -0
- package/dist/components/ResizablePanel.svelte +305 -0
- package/dist/components/ResizablePanel.svelte.d.ts +18 -0
- package/dist/components/SearchDropdown.svelte +421 -0
- package/dist/components/SearchDropdown.svelte.d.ts +24 -0
- package/dist/components/SelectInput.svelte +237 -0
- package/dist/components/SelectInput.svelte.d.ts +26 -0
- package/dist/components/SidebarUserFooter.svelte +34 -0
- package/dist/components/SidebarUserFooter.svelte.d.ts +16 -0
- package/dist/components/SignaturePad.svelte +175 -0
- package/dist/components/SignaturePad.svelte.d.ts +17 -0
- package/dist/components/SmartImageEditor.svelte +720 -0
- package/dist/components/SmartImageEditor.svelte.d.ts +24 -0
- package/dist/components/SortDropdown.svelte +156 -0
- package/dist/components/SortDropdown.svelte.d.ts +40 -0
- package/dist/components/Sparkline.svelte +116 -0
- package/dist/components/Sparkline.svelte.d.ts +12 -0
- package/dist/components/SpeechForm.svelte +114 -0
- package/dist/components/SpeechForm.svelte.d.ts +17 -0
- package/dist/components/StatusBadge.svelte +157 -0
- package/dist/components/StatusBadge.svelte.d.ts +24 -0
- package/dist/components/TaskTypeIcon.svelte +54 -0
- package/dist/components/TaskTypeIcon.svelte.d.ts +17 -0
- package/dist/components/TextArea.svelte +162 -0
- package/dist/components/TextArea.svelte.d.ts +30 -0
- package/dist/components/TextInput.svelte +131 -0
- package/dist/components/TextInput.svelte.d.ts +29 -0
- package/dist/components/ThemeSelector.svelte +206 -0
- package/dist/components/ThemeSelector.svelte.d.ts +14 -0
- package/dist/components/TimeSlotPicker.svelte +162 -0
- package/dist/components/TimeSlotPicker.svelte.d.ts +18 -0
- package/dist/components/UserAvatar.svelte +72 -0
- package/dist/components/UserAvatar.svelte.d.ts +11 -0
- package/dist/components/VariablePicker.svelte +170 -0
- package/dist/components/VariablePicker.svelte.d.ts +40 -0
- package/dist/components/VoicePlayer.svelte +422 -0
- package/dist/components/VoicePlayer.svelte.d.ts +18 -0
- package/dist/components/linked-columns/LinkedColumns.svelte +526 -0
- package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +130 -0
- package/dist/components/markup.d.ts +47 -0
- package/dist/components/markup.js +201 -0
- package/dist/components/messaging/Avatar.svelte +81 -0
- package/dist/components/messaging/Avatar.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelInfoModal.svelte +163 -0
- package/dist/components/messaging/ChannelInfoModal.svelte.d.ts +18 -0
- package/dist/components/messaging/ChannelList.svelte +107 -0
- package/dist/components/messaging/ChannelList.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelMemberAvatarStack.svelte +69 -0
- package/dist/components/messaging/ChannelMemberAvatarStack.svelte.d.ts +11 -0
- package/dist/components/messaging/ChannelMembersModal.svelte +182 -0
- package/dist/components/messaging/ChannelMembersModal.svelte.d.ts +16 -0
- package/dist/components/messaging/CreateChannelModal.svelte +190 -0
- package/dist/components/messaging/CreateChannelModal.svelte.d.ts +14 -0
- package/dist/components/messaging/DirectMessageList.svelte +145 -0
- package/dist/components/messaging/DirectMessageList.svelte.d.ts +12 -0
- package/dist/components/messaging/EmojiSelector.svelte +261 -0
- package/dist/components/messaging/EmojiSelector.svelte.d.ts +19 -0
- package/dist/components/messaging/MentionAutocomplete.svelte +193 -0
- package/dist/components/messaging/MentionAutocomplete.svelte.d.ts +17 -0
- package/dist/components/messaging/MessageAttachment.svelte +273 -0
- package/dist/components/messaging/MessageAttachment.svelte.d.ts +13 -0
- package/dist/components/messaging/MessageAttachmentUpload.svelte +243 -0
- package/dist/components/messaging/MessageAttachmentUpload.svelte.d.ts +19 -0
- package/dist/components/messaging/MessageInput.svelte +452 -0
- package/dist/components/messaging/MessageInput.svelte.d.ts +28 -0
- package/dist/components/messaging/MessageItem.svelte +341 -0
- package/dist/components/messaging/MessageItem.svelte.d.ts +41 -0
- package/dist/components/messaging/MessageThread.svelte +306 -0
- package/dist/components/messaging/MessageThread.svelte.d.ts +30 -0
- package/dist/components/messaging/NotificationSettingsModal.svelte +234 -0
- package/dist/components/messaging/NotificationSettingsModal.svelte.d.ts +13 -0
- package/dist/components/messaging/QuotedMessageDisplay.svelte +123 -0
- package/dist/components/messaging/QuotedMessageDisplay.svelte.d.ts +10 -0
- package/dist/components/messaging/StartDMModal.svelte +100 -0
- package/dist/components/messaging/StartDMModal.svelte.d.ts +14 -0
- package/dist/components/messaging/ThreadPanel.svelte +153 -0
- package/dist/components/messaging/ThreadPanel.svelte.d.ts +16 -0
- package/dist/components/pipeline/DESIGN.md +361 -0
- package/dist/components/pipeline/Pipeline.svelte +391 -0
- package/dist/components/pipeline/Pipeline.svelte.d.ts +28 -0
- package/dist/components/pipeline/PipelineCard.svelte +94 -0
- package/dist/components/pipeline/PipelineCard.svelte.d.ts +13 -0
- package/dist/components/pipeline/PipelineColumn.svelte +163 -0
- package/dist/components/pipeline/PipelineColumn.svelte.d.ts +30 -0
- package/dist/components/replay/ChapterTimeline.svelte +326 -0
- package/dist/components/replay/ChapterTimeline.svelte.d.ts +39 -0
- package/dist/components/session-nav/transcriptModel.d.ts +132 -0
- package/dist/components/session-nav/transcriptModel.js +285 -0
- package/dist/data/emojis.d.ts +18 -0
- package/dist/data/emojis.js +1196 -0
- package/dist/index.d.ts +142 -0
- package/dist/index.js +142 -0
- package/dist/stores/confirmDialog.svelte.d.ts +21 -0
- package/dist/stores/confirmDialog.svelte.js +31 -0
- package/dist/stores/inputDialog.svelte.d.ts +24 -0
- package/dist/stores/inputDialog.svelte.js +32 -0
- package/dist/styles/rail.css +63 -0
- package/dist/types/annotation.d.ts +32 -0
- package/dist/types/annotation.js +3 -0
- package/dist/types/booking.d.ts +115 -0
- package/dist/types/booking.js +24 -0
- package/dist/types/comments.d.ts +65 -0
- package/dist/types/comments.js +22 -0
- package/dist/types/entityPreview.d.ts +42 -0
- package/dist/types/entityPreview.js +10 -0
- package/dist/types/filePicker.d.ts +15 -0
- package/dist/types/filePicker.js +1 -0
- package/dist/types/googleMaps.d.ts +51 -0
- package/dist/types/maps.d.ts +43 -0
- package/dist/types/maps.js +4 -0
- package/dist/types/messaging.d.ts +282 -0
- package/dist/types/messaging.js +100 -0
- package/dist/types/milestone.d.ts +20 -0
- package/dist/types/milestone.js +7 -0
- package/dist/types/pipeline.d.ts +115 -0
- package/dist/types/pipeline.js +15 -0
- package/dist/types/smartImageEditor.d.ts +42 -0
- package/dist/types/smartImageEditor.js +1 -0
- package/dist/types/templateVars.d.ts +34 -0
- package/dist/types/templateVars.js +12 -0
- package/dist/utils/currency.d.ts +8 -0
- package/dist/utils/currency.js +17 -0
- package/dist/utils/daisyuiColors.d.ts +37 -0
- package/dist/utils/daisyuiColors.js +152 -0
- package/dist/utils/dateFormatters.d.ts +69 -0
- package/dist/utils/dateFormatters.js +199 -0
- package/dist/utils/googleMapsLoader.d.ts +19 -0
- package/dist/utils/googleMapsLoader.js +121 -0
- package/dist/utils/mentionParser.d.ts +67 -0
- package/dist/utils/mentionParser.js +132 -0
- package/dist/utils/phone.d.ts +13 -0
- package/dist/utils/phone.js +67 -0
- package/dist/utils/phoneFormat.d.ts +26 -0
- package/dist/utils/phoneFormat.js +69 -0
- package/dist/utils/taskUtils.d.ts +37 -0
- package/dist/utils/taskUtils.js +71 -0
- package/package.json +3 -2
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Stroke icon for a task issue_type value.
|
|
4
|
+
* Replaces the emoji returned by `typeIcon()`.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* <TaskTypeIcon type={task.issue_type} />
|
|
8
|
+
* <TaskTypeIcon type="bug" size={14} class="text-base-content/60" />
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
type: string | null | undefined
|
|
13
|
+
size?: number
|
|
14
|
+
class?: string
|
|
15
|
+
title?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let { type, size = 14, class: className = "", title }: Props = $props()
|
|
19
|
+
|
|
20
|
+
// Path data per type. Single-path lucide-style strokes so they inherit
|
|
21
|
+
// currentColor and size cleanly inside dense tables.
|
|
22
|
+
const paths: Record<string, string> = {
|
|
23
|
+
// Bug: body + antennae/legs
|
|
24
|
+
bug: "M12 4v2M8 6l1 2M16 6l-1 2M6 12H4M20 12h-2M6 16l-2 1M18 16l2 1M9 9h6v5a3 3 0 11-6 0V9z",
|
|
25
|
+
// Feature (spark): 4-point star
|
|
26
|
+
feature: "M12 3v4M12 17v4M3 12h4M17 12h4M5.5 5.5l2.5 2.5M16 16l2.5 2.5M5.5 18.5L8 16M16 8l2.5-2.5",
|
|
27
|
+
// Task: checklist
|
|
28
|
+
task: "M9 4h9a1 1 0 011 1v14a1 1 0 01-1 1H6a1 1 0 01-1-1V5a1 1 0 011-1h1M9 3h6v2H9zM8.5 11.5l1.5 1.5 3-3M8.5 16.5l1.5 1.5 3-3",
|
|
29
|
+
// Epic: mountain peaks
|
|
30
|
+
epic: "M3 19l5-8 4 6 3-4 6 6H3z",
|
|
31
|
+
// Default: document
|
|
32
|
+
document: "M13 3H6a1 1 0 00-1 1v16a1 1 0 001 1h12a1 1 0 001-1V9l-6-6zM13 3v6h6",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const d = $derived(paths[type ?? ""] ?? paths.document)
|
|
36
|
+
const label = $derived(title ?? (type ? `${type[0].toUpperCase()}${type.slice(1)}` : "Document"))
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<svg
|
|
40
|
+
viewBox="0 0 24 24"
|
|
41
|
+
fill="none"
|
|
42
|
+
stroke="currentColor"
|
|
43
|
+
stroke-width="2"
|
|
44
|
+
stroke-linecap="round"
|
|
45
|
+
stroke-linejoin="round"
|
|
46
|
+
width={size}
|
|
47
|
+
height={size}
|
|
48
|
+
class={className}
|
|
49
|
+
role="img"
|
|
50
|
+
aria-label={label}
|
|
51
|
+
>
|
|
52
|
+
<title>{label}</title>
|
|
53
|
+
<path d={d} />
|
|
54
|
+
</svg>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stroke icon for a task issue_type value.
|
|
3
|
+
* Replaces the emoji returned by `typeIcon()`.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* <TaskTypeIcon type={task.issue_type} />
|
|
7
|
+
* <TaskTypeIcon type="bug" size={14} class="text-base-content/60" />
|
|
8
|
+
*/
|
|
9
|
+
interface Props {
|
|
10
|
+
type: string | null | undefined;
|
|
11
|
+
size?: number;
|
|
12
|
+
class?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const TaskTypeIcon: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type TaskTypeIcon = ReturnType<typeof TaskTypeIcon>;
|
|
17
|
+
export default TaskTypeIcon;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* TextArea Component
|
|
4
|
+
*
|
|
5
|
+
* Multi-line text input with validation, auto-resize, and character count.
|
|
6
|
+
* Converted from Svelte 4 to Svelte 5 runes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
label?: string;
|
|
11
|
+
value?: string;
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
rows?: number;
|
|
14
|
+
maxlength?: number;
|
|
15
|
+
minlength?: number;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
readonly?: boolean;
|
|
19
|
+
autoResize?: boolean;
|
|
20
|
+
error?: string;
|
|
21
|
+
helpText?: string;
|
|
22
|
+
showCount?: boolean;
|
|
23
|
+
size?: 'sm' | 'md' | 'lg';
|
|
24
|
+
class?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
resize?: 'none' | 'vertical' | 'horizontal' | 'both';
|
|
28
|
+
oninput?: (value: string) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
label = '',
|
|
33
|
+
value = $bindable(''),
|
|
34
|
+
placeholder = '',
|
|
35
|
+
rows = 3,
|
|
36
|
+
maxlength,
|
|
37
|
+
minlength,
|
|
38
|
+
required = false,
|
|
39
|
+
disabled = false,
|
|
40
|
+
readonly = false,
|
|
41
|
+
autoResize = false,
|
|
42
|
+
error = '',
|
|
43
|
+
helpText = '',
|
|
44
|
+
showCount = false,
|
|
45
|
+
size = 'md',
|
|
46
|
+
class: className = '',
|
|
47
|
+
id = `textarea-${Math.random().toString(36).substr(2, 9)}`,
|
|
48
|
+
name = '',
|
|
49
|
+
resize = 'vertical',
|
|
50
|
+
oninput
|
|
51
|
+
}: Props = $props();
|
|
52
|
+
|
|
53
|
+
let textareaElement: HTMLTextAreaElement | undefined = $state();
|
|
54
|
+
|
|
55
|
+
const sizeClass = $derived({ sm: 'textarea-sm', md: '', lg: 'textarea-lg' }[size]);
|
|
56
|
+
const resizeClass = $derived(
|
|
57
|
+
{ none: 'resize-none', vertical: 'resize-y', horizontal: 'resize-x', both: 'resize' }[resize]
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const textareaClass = $derived(
|
|
61
|
+
[
|
|
62
|
+
'textarea textarea-bordered w-full transition-shadow duration-200',
|
|
63
|
+
sizeClass,
|
|
64
|
+
resizeClass,
|
|
65
|
+
error ? 'textarea-error' : '',
|
|
66
|
+
disabled ? 'textarea-disabled' : '',
|
|
67
|
+
className
|
|
68
|
+
]
|
|
69
|
+
.filter(Boolean)
|
|
70
|
+
.join(' ')
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const characterCount = $derived(value?.length || 0);
|
|
74
|
+
const isOverLimit = $derived(maxlength ? characterCount > maxlength : false);
|
|
75
|
+
|
|
76
|
+
function handleInput(event: Event) {
|
|
77
|
+
const target = event.target as HTMLTextAreaElement;
|
|
78
|
+
value = target.value;
|
|
79
|
+
if (autoResize) autoResizeTextarea(target);
|
|
80
|
+
oninput?.(value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function autoResizeTextarea(textarea: HTMLTextAreaElement) {
|
|
84
|
+
textarea.style.height = 'auto';
|
|
85
|
+
textarea.style.height = textarea.scrollHeight + 'px';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
$effect(() => {
|
|
89
|
+
if (autoResize && textareaElement && value !== undefined) {
|
|
90
|
+
autoResizeTextarea(textareaElement);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Error-state shake (transitions.dev). Replays `t-input-shake` when `error`
|
|
95
|
+
// becomes set. See TextInput for the full rationale.
|
|
96
|
+
let hadError = false;
|
|
97
|
+
$effect(() => {
|
|
98
|
+
const hasError = !!error;
|
|
99
|
+
if (hasError && !hadError && textareaElement) {
|
|
100
|
+
const reduce =
|
|
101
|
+
typeof window !== 'undefined' &&
|
|
102
|
+
window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
|
|
103
|
+
if (!reduce) {
|
|
104
|
+
textareaElement.classList.remove('t-input-shake');
|
|
105
|
+
void textareaElement.offsetWidth;
|
|
106
|
+
textareaElement.classList.add('t-input-shake');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
hadError = hasError;
|
|
110
|
+
});
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<div class="form-control w-full">
|
|
114
|
+
{#if label}
|
|
115
|
+
<label for={id} class="label">
|
|
116
|
+
<span class="text-[0.8125rem] font-medium text-base-content/85">
|
|
117
|
+
{label}
|
|
118
|
+
{#if required}
|
|
119
|
+
<span class="text-error ml-1">*</span>
|
|
120
|
+
{/if}
|
|
121
|
+
</span>
|
|
122
|
+
</label>
|
|
123
|
+
{/if}
|
|
124
|
+
|
|
125
|
+
<textarea
|
|
126
|
+
bind:this={textareaElement}
|
|
127
|
+
{id}
|
|
128
|
+
{name}
|
|
129
|
+
{placeholder}
|
|
130
|
+
{rows}
|
|
131
|
+
{maxlength}
|
|
132
|
+
{minlength}
|
|
133
|
+
{required}
|
|
134
|
+
{disabled}
|
|
135
|
+
{readonly}
|
|
136
|
+
bind:value
|
|
137
|
+
oninput={handleInput}
|
|
138
|
+
class={textareaClass}
|
|
139
|
+
aria-describedby={error ? `${id}-error` : helpText ? `${id}-help` : undefined}
|
|
140
|
+
aria-invalid={error ? 'true' : 'false'}
|
|
141
|
+
style={autoResize ? `min-height: calc(1.5em * ${rows} + 1rem);` : ''}
|
|
142
|
+
onanimationend={() => textareaElement?.classList.remove('t-input-shake')}
|
|
143
|
+
></textarea>
|
|
144
|
+
|
|
145
|
+
{#if showCount && maxlength}
|
|
146
|
+
<div class="label">
|
|
147
|
+
<span class="text-[0.8125rem] {isOverLimit ? 'text-error' : 'text-base-content/45'}">
|
|
148
|
+
{characterCount}/{maxlength}
|
|
149
|
+
</span>
|
|
150
|
+
</div>
|
|
151
|
+
{/if}
|
|
152
|
+
|
|
153
|
+
{#if error}
|
|
154
|
+
<div class="label">
|
|
155
|
+
<span id="{id}-error" class="text-[0.8125rem] text-error" role="alert">{error}</span>
|
|
156
|
+
</div>
|
|
157
|
+
{:else if helpText}
|
|
158
|
+
<div class="label">
|
|
159
|
+
<span id="{id}-help" class="text-[0.8125rem] text-base-content/45">{helpText}</span>
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
162
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextArea Component
|
|
3
|
+
*
|
|
4
|
+
* Multi-line text input with validation, auto-resize, and character count.
|
|
5
|
+
* Converted from Svelte 4 to Svelte 5 runes.
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
label?: string;
|
|
9
|
+
value?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
rows?: number;
|
|
12
|
+
maxlength?: number;
|
|
13
|
+
minlength?: number;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
readonly?: boolean;
|
|
17
|
+
autoResize?: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
helpText?: string;
|
|
20
|
+
showCount?: boolean;
|
|
21
|
+
size?: 'sm' | 'md' | 'lg';
|
|
22
|
+
class?: string;
|
|
23
|
+
id?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
resize?: 'none' | 'vertical' | 'horizontal' | 'both';
|
|
26
|
+
oninput?: (value: string) => void;
|
|
27
|
+
}
|
|
28
|
+
declare const TextArea: import("svelte").Component<Props, {}, "value">;
|
|
29
|
+
type TextArea = ReturnType<typeof TextArea>;
|
|
30
|
+
export default TextArea;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* TextInput Component
|
|
4
|
+
*
|
|
5
|
+
* Labeled text input with validation, error state, help text, and character count.
|
|
6
|
+
* Converted from Svelte 4 to Svelte 5 runes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
label: string;
|
|
11
|
+
value?: string;
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url';
|
|
14
|
+
required?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
readonly?: boolean;
|
|
17
|
+
error?: string;
|
|
18
|
+
helpText?: string;
|
|
19
|
+
size?: 'sm' | 'md' | 'lg';
|
|
20
|
+
maxlength?: number;
|
|
21
|
+
minlength?: number;
|
|
22
|
+
pattern?: string;
|
|
23
|
+
showCount?: boolean;
|
|
24
|
+
class?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
autocomplete?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let {
|
|
31
|
+
label,
|
|
32
|
+
value = $bindable(''),
|
|
33
|
+
placeholder = '',
|
|
34
|
+
type = 'text',
|
|
35
|
+
required = false,
|
|
36
|
+
disabled = false,
|
|
37
|
+
readonly = false,
|
|
38
|
+
error = '',
|
|
39
|
+
helpText = '',
|
|
40
|
+
size = 'md',
|
|
41
|
+
maxlength,
|
|
42
|
+
minlength,
|
|
43
|
+
pattern,
|
|
44
|
+
showCount = false,
|
|
45
|
+
class: className = '',
|
|
46
|
+
id = `text-input-${Math.random().toString(36).substr(2, 9)}`,
|
|
47
|
+
name = '',
|
|
48
|
+
autocomplete = ''
|
|
49
|
+
}: Props = $props();
|
|
50
|
+
|
|
51
|
+
const sizeClass = $derived({ sm: 'input-sm', md: '', lg: 'input-lg' }[size]);
|
|
52
|
+
|
|
53
|
+
const inputClass = $derived(
|
|
54
|
+
['input input-bordered w-full transition-shadow duration-200', sizeClass, error ? 'input-error' : '', disabled ? 'input-disabled' : '', className]
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
.join(' ')
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const characterCount = $derived(value?.length || 0);
|
|
60
|
+
const isOverLimit = $derived(maxlength ? characterCount > maxlength : false);
|
|
61
|
+
|
|
62
|
+
// Error-state shake (transitions.dev "Error state shake"). When `error`
|
|
63
|
+
// transitions from empty to set, replay the `t-input-shake` keyframe from the
|
|
64
|
+
// host app's transitions.css. Self-contained reflow-restart so a repeat error
|
|
65
|
+
// re-shakes. Colour stays on the `input-error` class; this owns only motion.
|
|
66
|
+
let inputEl: HTMLInputElement | undefined = $state();
|
|
67
|
+
let hadError = false;
|
|
68
|
+
$effect(() => {
|
|
69
|
+
const hasError = !!error;
|
|
70
|
+
if (hasError && !hadError && inputEl) {
|
|
71
|
+
const reduce =
|
|
72
|
+
typeof window !== 'undefined' &&
|
|
73
|
+
window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
|
|
74
|
+
if (!reduce) {
|
|
75
|
+
inputEl.classList.remove('t-input-shake');
|
|
76
|
+
void inputEl.offsetWidth; // force reflow to restart the animation
|
|
77
|
+
inputEl.classList.add('t-input-shake');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
hadError = hasError;
|
|
81
|
+
});
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<div class="form-control w-full">
|
|
85
|
+
<label for={id} class="label">
|
|
86
|
+
<span class="text-[0.8125rem] font-medium text-base-content/85">
|
|
87
|
+
{label}
|
|
88
|
+
{#if required}
|
|
89
|
+
<span class="text-error ml-1">*</span>
|
|
90
|
+
{/if}
|
|
91
|
+
</span>
|
|
92
|
+
</label>
|
|
93
|
+
|
|
94
|
+
<input
|
|
95
|
+
bind:this={inputEl}
|
|
96
|
+
{id}
|
|
97
|
+
{name}
|
|
98
|
+
{type}
|
|
99
|
+
{placeholder}
|
|
100
|
+
{required}
|
|
101
|
+
{disabled}
|
|
102
|
+
{readonly}
|
|
103
|
+
{maxlength}
|
|
104
|
+
{minlength}
|
|
105
|
+
{pattern}
|
|
106
|
+
autocomplete={autocomplete as AutoFill}
|
|
107
|
+
bind:value
|
|
108
|
+
class={inputClass}
|
|
109
|
+
aria-describedby={error ? `${id}-error` : helpText ? `${id}-help` : undefined}
|
|
110
|
+
aria-invalid={error ? 'true' : 'false'}
|
|
111
|
+
onanimationend={() => inputEl?.classList.remove('t-input-shake')}
|
|
112
|
+
/>
|
|
113
|
+
|
|
114
|
+
{#if showCount && maxlength}
|
|
115
|
+
<div class="label">
|
|
116
|
+
<span class="text-[0.8125rem] {isOverLimit ? 'text-error' : 'text-base-content/45'}">
|
|
117
|
+
{characterCount}/{maxlength}
|
|
118
|
+
</span>
|
|
119
|
+
</div>
|
|
120
|
+
{/if}
|
|
121
|
+
|
|
122
|
+
{#if error}
|
|
123
|
+
<div class="label">
|
|
124
|
+
<span id="{id}-error" class="text-[0.8125rem] text-error" role="alert">{error}</span>
|
|
125
|
+
</div>
|
|
126
|
+
{:else if helpText}
|
|
127
|
+
<div class="label">
|
|
128
|
+
<span id="{id}-help" class="text-[0.8125rem] text-base-content/45">{helpText}</span>
|
|
129
|
+
</div>
|
|
130
|
+
{/if}
|
|
131
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextInput Component
|
|
3
|
+
*
|
|
4
|
+
* Labeled text input with validation, error state, help text, and character count.
|
|
5
|
+
* Converted from Svelte 4 to Svelte 5 runes.
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
label: string;
|
|
9
|
+
value?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url';
|
|
12
|
+
required?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
readonly?: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
helpText?: string;
|
|
17
|
+
size?: 'sm' | 'md' | 'lg';
|
|
18
|
+
maxlength?: number;
|
|
19
|
+
minlength?: number;
|
|
20
|
+
pattern?: string;
|
|
21
|
+
showCount?: boolean;
|
|
22
|
+
class?: string;
|
|
23
|
+
id?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
autocomplete?: string;
|
|
26
|
+
}
|
|
27
|
+
declare const TextInput: import("svelte").Component<Props, {}, "value">;
|
|
28
|
+
type TextInput = ReturnType<typeof TextInput>;
|
|
29
|
+
export default TextInput;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* ThemeSelector — DaisyUI theme switcher with visual preview dots, localStorage persistence,
|
|
4
|
+
* and animated transitions on theme change.
|
|
5
|
+
*
|
|
6
|
+
* By default includes all 33 standard DaisyUI themes. Pass custom `themes` prop to override.
|
|
7
|
+
*/
|
|
8
|
+
import { onMount } from 'svelte';
|
|
9
|
+
|
|
10
|
+
interface ThemeOption {
|
|
11
|
+
name: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_THEMES: ThemeOption[] = [
|
|
16
|
+
{ name: 'light', label: 'Light' },
|
|
17
|
+
{ name: 'dark', label: 'Dark' },
|
|
18
|
+
{ name: 'cupcake', label: 'Cupcake' },
|
|
19
|
+
{ name: 'bumblebee', label: 'Bumblebee' },
|
|
20
|
+
{ name: 'emerald', label: 'Emerald' },
|
|
21
|
+
{ name: 'corporate', label: 'Corporate' },
|
|
22
|
+
{ name: 'synthwave', label: 'Synthwave' },
|
|
23
|
+
{ name: 'retro', label: 'Retro' },
|
|
24
|
+
{ name: 'cyberpunk', label: 'Cyberpunk' },
|
|
25
|
+
{ name: 'valentine', label: 'Valentine' },
|
|
26
|
+
{ name: 'halloween', label: 'Halloween' },
|
|
27
|
+
{ name: 'garden', label: 'Garden' },
|
|
28
|
+
{ name: 'forest', label: 'Forest' },
|
|
29
|
+
{ name: 'aqua', label: 'Aqua' },
|
|
30
|
+
{ name: 'lofi', label: 'Lofi' },
|
|
31
|
+
{ name: 'pastel', label: 'Pastel' },
|
|
32
|
+
{ name: 'fantasy', label: 'Fantasy' },
|
|
33
|
+
{ name: 'wireframe', label: 'Wireframe' },
|
|
34
|
+
{ name: 'black', label: 'Black' },
|
|
35
|
+
{ name: 'luxury', label: 'Luxury' },
|
|
36
|
+
{ name: 'dracula', label: 'Dracula' },
|
|
37
|
+
{ name: 'cmyk', label: 'CMYK' },
|
|
38
|
+
{ name: 'autumn', label: 'Autumn' },
|
|
39
|
+
{ name: 'business', label: 'Business' },
|
|
40
|
+
{ name: 'acid', label: 'Acid' },
|
|
41
|
+
{ name: 'lemonade', label: 'Lemonade' },
|
|
42
|
+
{ name: 'night', label: 'Night' },
|
|
43
|
+
{ name: 'coffee', label: 'Coffee' },
|
|
44
|
+
{ name: 'winter', label: 'Winter' },
|
|
45
|
+
{ name: 'dim', label: 'Dim' },
|
|
46
|
+
{ name: 'nord', label: 'Nord' },
|
|
47
|
+
{ name: 'sunset', label: 'Sunset' }
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
let {
|
|
51
|
+
dropUp = false,
|
|
52
|
+
themes = DEFAULT_THEMES,
|
|
53
|
+
defaultTheme = 'light',
|
|
54
|
+
storageKey = 'theme',
|
|
55
|
+
onThemeChange,
|
|
56
|
+
}: {
|
|
57
|
+
dropUp?: boolean;
|
|
58
|
+
themes?: ThemeOption[];
|
|
59
|
+
defaultTheme?: string;
|
|
60
|
+
storageKey?: string;
|
|
61
|
+
onThemeChange?: (theme: string) => void;
|
|
62
|
+
} = $props();
|
|
63
|
+
|
|
64
|
+
// svelte-ignore state_referenced_locally
|
|
65
|
+
let currentTheme = $state(defaultTheme);
|
|
66
|
+
let isAnimating = $state(false);
|
|
67
|
+
|
|
68
|
+
const currentThemeLabel = $derived(
|
|
69
|
+
themes.find((t) => t.name === currentTheme)?.label || currentTheme
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
onMount(() => {
|
|
73
|
+
const savedTheme = localStorage.getItem(storageKey);
|
|
74
|
+
if (savedTheme && themes.some((t) => t.name === savedTheme)) {
|
|
75
|
+
currentTheme = savedTheme;
|
|
76
|
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
77
|
+
} else {
|
|
78
|
+
currentTheme = defaultTheme;
|
|
79
|
+
document.documentElement.setAttribute('data-theme', defaultTheme);
|
|
80
|
+
localStorage.setItem(storageKey, defaultTheme);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
function handleThemeChange(themeName: string) {
|
|
85
|
+
if (themeName === currentTheme) return;
|
|
86
|
+
|
|
87
|
+
// Close dropdown first so the trigger is visible during animation
|
|
88
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
89
|
+
document.activeElement.blur();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
isAnimating = true;
|
|
93
|
+
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
currentTheme = themeName;
|
|
96
|
+
document.documentElement.setAttribute('data-theme', themeName);
|
|
97
|
+
localStorage.setItem(storageKey, themeName);
|
|
98
|
+
onThemeChange?.(themeName);
|
|
99
|
+
}, 150);
|
|
100
|
+
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
isAnimating = false;
|
|
103
|
+
}, 500);
|
|
104
|
+
}
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<div class="dropdown dropdown-end" class:dropdown-top={dropUp}>
|
|
108
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
109
|
+
<div
|
|
110
|
+
tabindex="0"
|
|
111
|
+
role="button"
|
|
112
|
+
class="flex items-center gap-2 cursor-pointer hover:bg-base-300 transition-colors px-2 py-1 rounded"
|
|
113
|
+
aria-label="Change Theme"
|
|
114
|
+
title="Change Theme"
|
|
115
|
+
>
|
|
116
|
+
<div
|
|
117
|
+
data-theme={currentTheme}
|
|
118
|
+
class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5 rounded-md p-1 transition-transform duration-300 ease-out"
|
|
119
|
+
class:theme-spin={isAnimating}
|
|
120
|
+
>
|
|
121
|
+
<div class="bg-base-content size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 0ms"></div>
|
|
122
|
+
<div class="bg-primary size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 50ms"></div>
|
|
123
|
+
<div class="bg-secondary size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 100ms"></div>
|
|
124
|
+
<div class="bg-accent size-1 rounded-full transition-all duration-300 theme-dot" class:theme-dot-animate={isAnimating} style="--dot-delay: 150ms"></div>
|
|
125
|
+
</div>
|
|
126
|
+
<span class="text-sm font-medium transition-opacity duration-200" class:opacity-50={isAnimating}>{currentThemeLabel}</span>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
130
|
+
<div
|
|
131
|
+
tabindex="0"
|
|
132
|
+
class="dropdown-content bg-base-200 text-base-content rounded-box
|
|
133
|
+
top-px max-h-[calc(50vh-6.5rem)] overflow-y-auto border border-base-300
|
|
134
|
+
mb-2 z-50"
|
|
135
|
+
>
|
|
136
|
+
<ul class="menu w-56">
|
|
137
|
+
<li class="menu-title text-xs">Theme</li>
|
|
138
|
+
|
|
139
|
+
{#each themes as theme}
|
|
140
|
+
<li>
|
|
141
|
+
<button
|
|
142
|
+
class="gap-3 px-2"
|
|
143
|
+
onclick={() => handleThemeChange(theme.name)}
|
|
144
|
+
class:active={currentTheme === theme.name}
|
|
145
|
+
>
|
|
146
|
+
<div
|
|
147
|
+
data-theme={theme.name}
|
|
148
|
+
class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5
|
|
149
|
+
rounded-md p-1"
|
|
150
|
+
>
|
|
151
|
+
<div class="bg-base-content size-1 rounded-full"></div>
|
|
152
|
+
<div class="bg-primary size-1 rounded-full"></div>
|
|
153
|
+
<div class="bg-secondary size-1 rounded-full"></div>
|
|
154
|
+
<div class="bg-accent size-1 rounded-full"></div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="w-32 truncate">{theme.label}</div>
|
|
158
|
+
|
|
159
|
+
{#if currentTheme === theme.name}
|
|
160
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="h-3 w-3 shrink-0">
|
|
161
|
+
<path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"></path>
|
|
162
|
+
</svg>
|
|
163
|
+
{:else}
|
|
164
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="invisible h-3 w-3 shrink-0">
|
|
165
|
+
<path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"></path>
|
|
166
|
+
</svg>
|
|
167
|
+
{/if}
|
|
168
|
+
</button>
|
|
169
|
+
</li>
|
|
170
|
+
{/each}
|
|
171
|
+
</ul>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<style>
|
|
176
|
+
/* NOTE: DaisyUI v5.0.0 has a stuck-animation bug on `.dropdown-content`
|
|
177
|
+
(`@keyframes dropdown { 0% { opacity: 0 } }` pins opacity to 0). If the
|
|
178
|
+
theme dropdown opens but stays invisible, the consuming project needs
|
|
179
|
+
the global fix in its `app.css`:
|
|
180
|
+
|
|
181
|
+
.dropdown-content { animation: none !important; }
|
|
182
|
+
|
|
183
|
+
Upstream daisyui >= 5.5.x patches this; once you upgrade you can drop
|
|
184
|
+
the override. */
|
|
185
|
+
|
|
186
|
+
.theme-spin {
|
|
187
|
+
animation: theme-spin 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@keyframes theme-spin {
|
|
191
|
+
0% { transform: rotate(0deg) scale(1); }
|
|
192
|
+
50% { transform: rotate(180deg) scale(1.2); }
|
|
193
|
+
100% { transform: rotate(360deg) scale(1); }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.theme-dot-animate {
|
|
197
|
+
animation: dot-pulse 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
|
198
|
+
animation-delay: var(--dot-delay);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@keyframes dot-pulse {
|
|
202
|
+
0% { transform: scale(1); opacity: 1; }
|
|
203
|
+
50% { transform: scale(0.5); opacity: 0.5; }
|
|
204
|
+
100% { transform: scale(1); opacity: 1; }
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface ThemeOption {
|
|
2
|
+
name: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
type $$ComponentProps = {
|
|
6
|
+
dropUp?: boolean;
|
|
7
|
+
themes?: ThemeOption[];
|
|
8
|
+
defaultTheme?: string;
|
|
9
|
+
storageKey?: string;
|
|
10
|
+
onThemeChange?: (theme: string) => void;
|
|
11
|
+
};
|
|
12
|
+
declare const ThemeSelector: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type ThemeSelector = ReturnType<typeof ThemeSelector>;
|
|
14
|
+
export default ThemeSelector;
|