@widgetic/design-system 0.5.21 → 0.5.23
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/README.md +2 -0
- package/dist/components/CopyableHeader.svelte +260 -448
- package/dist/components/CopyableHeader.svelte.d.ts +8 -5
- package/dist/components/EmptyState.svelte +100 -128
- package/dist/components/EmptyState.svelte.d.ts +16 -19
- package/dist/components/GenerateLoader.svelte +17 -10
- package/dist/components/PasswordInput.svelte +41 -44
- package/dist/components/PasswordInput.svelte.d.ts +2 -0
- package/dist/components/ThumbHashImage.svelte +117 -119
- package/dist/components/WScrollArea.svelte +47 -41
- package/dist/components/chat/ActivityRow.svelte +115 -0
- package/dist/components/chat/ActivityRow.svelte.d.ts +18 -0
- package/dist/components/chat/ChatHeader.svelte +114 -0
- package/dist/components/chat/ChatHeader.svelte.d.ts +26 -0
- package/dist/components/chat/Composer.svelte +172 -0
- package/dist/components/chat/Composer.svelte.d.ts +27 -0
- package/dist/components/chat/Message.svelte +158 -0
- package/dist/components/chat/Message.svelte.d.ts +28 -0
- package/dist/components/chat/OptionRow.svelte +59 -0
- package/dist/components/chat/OptionRow.svelte.d.ts +14 -0
- package/dist/components/chat/PlanCard.svelte +220 -0
- package/dist/components/chat/PlanCard.svelte.d.ts +34 -0
- package/dist/components/chat/QuestionCard.svelte +265 -0
- package/dist/components/chat/QuestionCard.svelte.d.ts +38 -0
- package/dist/components/chat/ScrollToBottom.svelte +51 -0
- package/dist/components/chat/ScrollToBottom.svelte.d.ts +11 -0
- package/dist/components/chat/SuggestionCard.svelte +79 -0
- package/dist/components/chat/SuggestionCard.svelte.d.ts +20 -0
- package/dist/components/chat/TaskRow.svelte +59 -0
- package/dist/components/chat/TaskRow.svelte.d.ts +11 -0
- package/dist/components/chat/composerLogic.d.ts +16 -0
- package/dist/components/chat/composerLogic.js +29 -0
- package/dist/components/chat/index.d.ts +16 -0
- package/dist/components/chat/index.js +16 -0
- package/dist/components/chat/questionCardLogic.d.ts +11 -0
- package/dist/components/chat/questionCardLogic.js +39 -0
- package/dist/components/index.d.ts +10 -1
- package/dist/components/index.js +10 -0
- package/dist/components/modals/ActionModal.svelte +186 -180
- package/dist/components/modals/BaseModal.svelte +404 -395
- package/dist/components/modals/BaseModal.svelte.d.ts +3 -0
- package/dist/components/modals/NewItemDialog.svelte +200 -243
- package/dist/components/modals/Portal.svelte +107 -105
- package/dist/components/modals/README.md +16 -2
- package/dist/components/modals/RenameDialog.svelte +234 -292
- package/dist/components/navigation/Breadcrumbs.svelte +415 -377
- package/dist/components/navigation/Breadcrumbs.svelte.d.ts +3 -2
- package/dist/components/navigation/types.d.ts +2 -0
- package/dist/components/pdf/PDFViewer.svelte +54 -1
- package/dist/components/search-input/SearchInput.svelte +51 -104
- package/dist/components/search-input/SearchInput.svelte.d.ts +9 -27
- package/dist/components/shad-editor/editor.css +1 -1
- package/dist/components/shad-editor/i18n/messages.d.ts +1 -1
- package/dist/components/shad-editor/i18n/messages.js +1 -1
- package/dist/components/shell/AppShell.svelte +52 -0
- package/dist/components/shell/AppShell.svelte.d.ts +13 -0
- package/dist/components/shell/Banner.svelte +90 -0
- package/dist/components/shell/Banner.svelte.d.ts +61 -0
- package/dist/components/shell/ErrorState.svelte +60 -0
- package/dist/components/shell/ErrorState.svelte.d.ts +13 -0
- package/dist/components/shell/Link.svelte +63 -0
- package/dist/components/shell/Link.svelte.d.ts +53 -0
- package/dist/components/shell/NavDrawer.svelte +117 -0
- package/dist/components/shell/NavDrawer.svelte.d.ts +10 -0
- package/dist/components/shell/NavRow.svelte +128 -0
- package/dist/components/shell/NavRow.svelte.d.ts +65 -0
- package/dist/components/shell/NavSectionLabel.svelte +27 -0
- package/dist/components/shell/NavSectionLabel.svelte.d.ts +9 -0
- package/dist/components/shell/NotificationRow.svelte +76 -0
- package/dist/components/shell/NotificationRow.svelte.d.ts +16 -0
- package/dist/components/shell/PageHeader.svelte +42 -0
- package/dist/components/shell/PageHeader.svelte.d.ts +12 -0
- package/dist/components/shell/ProfileRow.svelte +95 -0
- package/dist/components/shell/ProfileRow.svelte.d.ts +54 -0
- package/dist/components/shell/SettingsDialog.svelte +118 -0
- package/dist/components/shell/SettingsDialog.svelte.d.ts +19 -0
- package/dist/components/shell/SettingsNav.svelte +46 -0
- package/dist/components/shell/SettingsNav.svelte.d.ts +11 -0
- package/dist/components/shell/Sidebar.svelte +82 -0
- package/dist/components/shell/Sidebar.svelte.d.ts +25 -0
- package/dist/components/shell/Stat.svelte +63 -0
- package/dist/components/shell/Stat.svelte.d.ts +58 -0
- package/dist/components/shell/TopBar.svelte +37 -0
- package/dist/components/shell/TopBar.svelte.d.ts +11 -0
- package/dist/components/shell/WidgetRow.svelte +126 -0
- package/dist/components/shell/WidgetRow.svelte.d.ts +78 -0
- package/dist/components/shell/error-state-messages.d.ts +7 -0
- package/dist/components/shell/error-state-messages.js +9 -0
- package/dist/components/shell/index.d.ts +22 -0
- package/dist/components/shell/index.js +25 -0
- package/dist/components/shell/link-external.d.ts +2 -0
- package/dist/components/shell/link-external.js +7 -0
- package/dist/components/shell/messages.d.ts +7 -0
- package/dist/components/shell/messages.js +5 -0
- package/dist/components/shell/settings-nav-types.d.ts +12 -0
- package/dist/components/shell/settings-nav-types.js +3 -0
- package/dist/components/shell/shell-utils.d.ts +7 -0
- package/dist/components/shell/shell-utils.js +27 -0
- package/dist/components/shell/stat-tone.d.ts +3 -0
- package/dist/components/shell/stat-tone.js +11 -0
- package/dist/components/toast/ToasterModal.svelte +102 -168
- package/dist/components/toast/ToasterModal.svelte.d.ts +2 -12
- package/dist/components/ui/accordion/accordion-content.svelte +3 -2
- package/dist/components/ui/accordion/accordion-item.svelte +10 -1
- package/dist/components/ui/accordion/accordion-trigger.svelte +4 -3
- package/dist/components/ui/alert/alert-description.svelte +5 -1
- package/dist/components/ui/alert/alert-title.svelte +2 -1
- package/dist/components/ui/alert/index.d.ts +13 -11
- package/dist/components/ui/alert/index.js +4 -4
- package/dist/components/ui/avatar/avatar-utils.d.ts +2 -0
- package/dist/components/ui/avatar/avatar-utils.js +9 -0
- package/dist/components/ui/avatar/avatar.svelte +63 -0
- package/dist/components/ui/avatar/avatar.svelte.d.ts +80 -0
- package/dist/components/ui/avatar/index.d.ts +4 -0
- package/dist/components/ui/avatar/index.js +4 -0
- package/dist/components/ui/badge/badge.svelte +22 -10
- package/dist/components/ui/badge/badge.svelte.d.ts +14 -15
- package/dist/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +2 -2
- package/dist/components/ui/breadcrumb/breadcrumb-link.svelte +1 -1
- package/dist/components/ui/breadcrumb/breadcrumb-page.svelte +1 -1
- package/dist/components/ui/breadcrumb/breadcrumb-separator.svelte +2 -2
- package/dist/components/ui/button/button-padding.d.ts +9 -0
- package/dist/components/ui/button/button-padding.js +15 -0
- package/dist/components/ui/button/button.svelte +81 -31
- package/dist/components/ui/button/button.svelte.d.ts +48 -29
- package/dist/components/ui/calendar/calendar-cell.svelte +1 -1
- package/dist/components/ui/calendar/calendar-head-cell.svelte +1 -1
- package/dist/components/ui/calendar/calendar-next-button.svelte +2 -2
- package/dist/components/ui/calendar/calendar-prev-button.svelte +2 -2
- package/dist/components/ui/card/card-description.svelte +1 -1
- package/dist/components/ui/card/card-title.svelte +1 -1
- package/dist/components/ui/card/card.svelte +1 -1
- package/dist/components/ui/checkbox/checkbox.svelte +9 -5
- package/dist/components/ui/chip/chip.svelte +48 -0
- package/dist/components/ui/chip/chip.svelte.d.ts +13 -0
- package/dist/components/ui/chip/index.d.ts +2 -0
- package/dist/components/ui/chip/index.js +2 -0
- package/dist/components/ui/command/command-dialog.svelte +1 -1
- package/dist/components/ui/command/command-input.svelte +4 -4
- package/dist/components/ui/command/command-item.svelte +1 -1
- package/dist/components/ui/command/command.svelte +1 -1
- package/dist/components/ui/dialog/dialog-content.svelte +4 -4
- package/dist/components/ui/dialog/dialog-description.svelte +1 -1
- package/dist/components/ui/dialog/dialog-overlay.svelte +1 -1
- package/dist/components/ui/dialog/dialog-title.svelte +1 -1
- package/dist/components/ui/drawer/drawer-content.svelte +1 -1
- package/dist/components/ui/drawer/drawer-overlay.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +5 -5
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +20 -2
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +5 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +3 -3
- package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +2 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +3 -3
- package/dist/components/ui/field/field-ids.d.ts +7 -0
- package/dist/components/ui/field/field-ids.js +15 -0
- package/dist/components/ui/field/field.svelte +62 -0
- package/dist/components/ui/field/field.svelte.d.ts +21 -0
- package/dist/components/ui/field/index.d.ts +3 -0
- package/dist/components/ui/field/index.js +3 -0
- package/dist/components/ui/hover-card/hover-card-content.svelte +1 -1
- package/dist/components/ui/icon-button/icon-button.svelte +72 -0
- package/dist/components/ui/icon-button/icon-button.svelte.d.ts +90 -0
- package/dist/components/ui/icon-button/index.d.ts +2 -0
- package/dist/components/ui/icon-button/index.js +4 -0
- package/dist/components/ui/input/index.d.ts +1 -0
- package/dist/components/ui/input/input.svelte +11 -4
- package/dist/components/ui/input/input.svelte.d.ts +4 -1
- package/dist/components/ui/input-otp/input-otp-separator.svelte +2 -2
- package/dist/components/ui/input-otp/input-otp-slot.svelte +5 -4
- package/dist/components/ui/input-otp/input-otp-slot.svelte.d.ts +1 -1
- package/dist/components/ui/input-otp/input-otp.svelte +1 -1
- package/dist/components/ui/label/label.svelte +3 -2
- package/dist/components/ui/popover/popover-content.svelte +1 -1
- package/dist/components/ui/progress/progress.svelte +19 -5
- package/dist/components/ui/progress/progress.svelte.d.ts +4 -2
- package/dist/components/ui/radio-group/radio-group-item.svelte +4 -4
- package/dist/components/ui/range-calendar/range-calendar-head-cell.svelte +1 -1
- package/dist/components/ui/range-calendar/range-calendar-next-button.svelte +2 -2
- package/dist/components/ui/range-calendar/range-calendar-prev-button.svelte +2 -2
- package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte +4 -1
- package/dist/components/ui/scroll-area/scroll-area.svelte +1 -1
- package/dist/components/ui/select/select-content.svelte +1 -3
- package/dist/components/ui/select/select-item.svelte +5 -4
- package/dist/components/ui/select/select-scroll-down-button.svelte +2 -2
- package/dist/components/ui/select/select-scroll-up-button.svelte +2 -2
- package/dist/components/ui/select/select-trigger.svelte +4 -3
- package/dist/components/ui/skeleton/index.d.ts +2 -0
- package/dist/components/ui/skeleton/index.js +2 -0
- package/dist/components/ui/skeleton/skeleton.svelte +24 -0
- package/dist/components/ui/skeleton/skeleton.svelte.d.ts +8 -0
- package/dist/components/ui/slider/slider.svelte +5 -10
- package/dist/components/ui/sonner/sonner.svelte +19 -13
- package/dist/components/ui/stepper-button/index.d.ts +3 -0
- package/dist/components/ui/stepper-button/index.js +2 -0
- package/dist/components/ui/stepper-button/stepper-button.svelte +37 -0
- package/dist/components/ui/stepper-button/stepper-button.svelte.d.ts +9 -0
- package/dist/components/ui/switch/switch.svelte +8 -3
- package/dist/components/ui/table/table-cell.svelte +1 -1
- package/dist/components/ui/table/table-head.svelte +1 -1
- package/dist/components/ui/table/table-row.svelte +1 -1
- package/dist/components/ui/tabs/tabs-list.svelte +1 -1
- package/dist/components/ui/tabs/tabs-trigger.svelte +1 -1
- package/dist/components/ui/tag/index.d.ts +2 -0
- package/dist/components/ui/tag/index.js +2 -0
- package/dist/components/ui/tag/tag.svelte +42 -0
- package/dist/components/ui/tag/tag.svelte.d.ts +11 -0
- package/dist/components/ui/textarea/textarea.svelte +4 -3
- package/dist/components/ui/textarea/textarea.svelte.d.ts +2 -2
- package/dist/components/ui/toggle/toggle.svelte +9 -9
- package/dist/components/ui/toggle/toggle.svelte.d.ts +14 -21
- package/dist/components/ui/tooltip/tooltip-content.svelte +1 -1
- package/dist/fonts/Inter/Inter-Black.woff2 +0 -0
- package/dist/fonts/Inter/Inter-BlackItalic.woff2 +0 -0
- package/dist/fonts/Inter/Inter-Bold.woff2 +0 -0
- package/dist/fonts/Inter/Inter-BoldItalic.woff2 +0 -0
- package/dist/fonts/Inter/Inter-ExtraBold.woff2 +0 -0
- package/dist/fonts/Inter/Inter-ExtraBoldItalic.woff2 +0 -0
- package/dist/fonts/Inter/Inter-ExtraLight-BETA.woff2 +0 -0
- package/dist/fonts/Inter/Inter-ExtraLightItalic-BETA.woff2 +0 -0
- package/dist/fonts/Inter/Inter-Italic.woff2 +0 -0
- package/dist/fonts/Inter/Inter-Light-BETA.woff2 +0 -0
- package/dist/fonts/Inter/Inter-LightItalic-BETA.woff2 +0 -0
- package/dist/fonts/Inter/Inter-Medium.woff2 +0 -0
- package/dist/fonts/Inter/Inter-MediumItalic.woff2 +0 -0
- package/dist/fonts/Inter/Inter-Regular.woff2 +0 -0
- package/dist/fonts/Inter/Inter-SemiBold.woff2 +0 -0
- package/dist/fonts/Inter/Inter-SemiBoldItalic.woff2 +0 -0
- package/dist/fonts/Inter/Inter-Thin-BETA.woff2 +0 -0
- package/dist/fonts/Inter/Inter-ThinItalic-BETA.woff2 +0 -0
- package/dist/fonts/Inter/Inter-italic.var.woff2 +0 -0
- package/dist/fonts/Inter/Inter-upright.var.woff2 +0 -0
- package/dist/fonts/Inter/Inter.var.woff2 +0 -0
- package/dist/fonts/JetBrainsMono/JetBrainsMono-Regular.woff2 +0 -0
- package/dist/i18n/audit/designSystemWords.d.ts +1 -1
- package/dist/i18n/audit/designSystemWords.js +1 -1
- package/dist/styles/fonts.css +18 -14
- package/dist/styles/global.css +7 -19
- package/dist/styles/tokens.css +332 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +33 -2
- package/package.json +2 -1
- package/tailwind.config.ts +7 -47
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Figma: Composer (component set 65:31, node 65:31)
|
|
3
|
+
-->
|
|
4
|
+
<script lang="ts" module>
|
|
5
|
+
import type { Snippet } from 'svelte';
|
|
6
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
7
|
+
|
|
8
|
+
export type ComposerMessages = {
|
|
9
|
+
send: string;
|
|
10
|
+
stop: string;
|
|
11
|
+
attach: string;
|
|
12
|
+
characterLimit: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type ComposerProps = HTMLAttributes<HTMLDivElement> & {
|
|
16
|
+
ref?: HTMLDivElement | null;
|
|
17
|
+
class?: string;
|
|
18
|
+
value?: string;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
busy?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
maxLength?: number;
|
|
23
|
+
allowEmpty?: boolean;
|
|
24
|
+
messages?: Partial<ComposerMessages>;
|
|
25
|
+
onSend?: (text: string) => void;
|
|
26
|
+
onStop?: () => void;
|
|
27
|
+
leading?: Snippet;
|
|
28
|
+
trailing?: Snippet;
|
|
29
|
+
attachments?: Snippet;
|
|
30
|
+
};
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<script lang="ts">
|
|
34
|
+
import IconPlayerStop from '@tabler/icons-svelte/icons/player-stop';
|
|
35
|
+
import IconSend from '@tabler/icons-svelte/icons/send';
|
|
36
|
+
import { cn } from '../../utils.js';
|
|
37
|
+
import { IconButton } from '../ui/icon-button/index.js';
|
|
38
|
+
import { canSendText, clampTextareaHeight, shouldSendOnKeyDown } from './composerLogic.js';
|
|
39
|
+
|
|
40
|
+
const defaultMessages: ComposerMessages = {
|
|
41
|
+
send: 'Send message',
|
|
42
|
+
stop: 'Stop generating',
|
|
43
|
+
attach: 'Add attachment',
|
|
44
|
+
characterLimit: 'Message is too long'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
ref = $bindable(null),
|
|
49
|
+
class: className,
|
|
50
|
+
value = $bindable(''),
|
|
51
|
+
placeholder = 'Type your message…',
|
|
52
|
+
busy = $bindable(false),
|
|
53
|
+
disabled = false,
|
|
54
|
+
maxLength,
|
|
55
|
+
allowEmpty = false,
|
|
56
|
+
messages: messagesProp,
|
|
57
|
+
onSend,
|
|
58
|
+
onStop,
|
|
59
|
+
leading,
|
|
60
|
+
trailing,
|
|
61
|
+
attachments,
|
|
62
|
+
...restProps
|
|
63
|
+
}: ComposerProps = $props();
|
|
64
|
+
|
|
65
|
+
const messages = $derived({ ...defaultMessages, ...messagesProp });
|
|
66
|
+
|
|
67
|
+
let textareaRef = $state<HTMLTextAreaElement | null>(null);
|
|
68
|
+
let isComposing = $state(false);
|
|
69
|
+
|
|
70
|
+
const sendEnabled = $derived(
|
|
71
|
+
busy ? !disabled : canSendText(value, { disabled, allowEmpty, maxLength })
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const overLimit = $derived(maxLength != null && value.trim().length > maxLength);
|
|
75
|
+
|
|
76
|
+
function autosize() {
|
|
77
|
+
const el = textareaRef;
|
|
78
|
+
if (!el) return;
|
|
79
|
+
el.style.height = 'auto';
|
|
80
|
+
el.style.height = `${clampTextareaHeight(el.scrollHeight)}px`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handleSend() {
|
|
84
|
+
if (busy) {
|
|
85
|
+
if (!disabled) onStop?.();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!sendEnabled || overLimit) return;
|
|
89
|
+
const text = value.trim();
|
|
90
|
+
if (!allowEmpty && text.length === 0) return;
|
|
91
|
+
try {
|
|
92
|
+
onSend?.(text);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
99
|
+
if (event.isComposing || isComposing) {
|
|
100
|
+
if (event.key === 'Enter') event.preventDefault();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!shouldSendOnKeyDown(event.key, event.shiftKey, false)) return;
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
handleSend();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
$effect(() => {
|
|
109
|
+
value;
|
|
110
|
+
queueMicrotask(autosize);
|
|
111
|
+
});
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<div
|
|
115
|
+
bind:this={ref}
|
|
116
|
+
class={cn(
|
|
117
|
+
'max-w-content rounded-card border-input bg-background flex w-full flex-col gap-2 border p-3',
|
|
118
|
+
disabled && 'pointer-events-none opacity-50',
|
|
119
|
+
className
|
|
120
|
+
)}
|
|
121
|
+
{...restProps}
|
|
122
|
+
>
|
|
123
|
+
{#if attachments}
|
|
124
|
+
<div class="flex flex-wrap gap-1.5 overflow-hidden">{@render attachments()}</div>
|
|
125
|
+
{/if}
|
|
126
|
+
|
|
127
|
+
<div class="min-h-12 w-full px-1 pt-1">
|
|
128
|
+
<textarea
|
|
129
|
+
bind:this={textareaRef}
|
|
130
|
+
bind:value
|
|
131
|
+
class="text-body text-foreground placeholder:text-muted-foreground block w-full resize-none overflow-y-auto border-0 bg-transparent outline-hidden focus-visible:outline-hidden disabled:cursor-not-allowed"
|
|
132
|
+
style="min-height: 3rem; max-height: 7.5rem;"
|
|
133
|
+
{placeholder}
|
|
134
|
+
{disabled}
|
|
135
|
+
aria-invalid={overLimit ? true : undefined}
|
|
136
|
+
oninput={autosize}
|
|
137
|
+
onkeydown={handleKeyDown}
|
|
138
|
+
oncompositionstart={() => {
|
|
139
|
+
isComposing = true;
|
|
140
|
+
}}
|
|
141
|
+
oncompositionend={() => {
|
|
142
|
+
isComposing = false;
|
|
143
|
+
}}
|
|
144
|
+
></textarea>
|
|
145
|
+
{#if overLimit}
|
|
146
|
+
<p class="text-caption text-destructive mt-1" role="status">{messages.characterLimit}</p>
|
|
147
|
+
{/if}
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div class="flex items-center gap-2">
|
|
151
|
+
<div class="flex min-w-0 flex-1 items-center gap-1">
|
|
152
|
+
{#if leading}
|
|
153
|
+
{@render leading()}
|
|
154
|
+
{/if}
|
|
155
|
+
</div>
|
|
156
|
+
{#if trailing}
|
|
157
|
+
{@render trailing()}
|
|
158
|
+
{/if}
|
|
159
|
+
<IconButton
|
|
160
|
+
variant="primary"
|
|
161
|
+
label={busy ? messages.stop : messages.send}
|
|
162
|
+
disabled={!sendEnabled}
|
|
163
|
+
onclick={handleSend}
|
|
164
|
+
>
|
|
165
|
+
{#if busy}
|
|
166
|
+
<IconPlayerStop class="size-4 stroke-[1.333]" aria-hidden="true" />
|
|
167
|
+
{:else}
|
|
168
|
+
<IconSend class="size-4 stroke-[1.333]" aria-hidden="true" />
|
|
169
|
+
{/if}
|
|
170
|
+
</IconButton>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
export type ComposerMessages = {
|
|
4
|
+
send: string;
|
|
5
|
+
stop: string;
|
|
6
|
+
attach: string;
|
|
7
|
+
characterLimit: string;
|
|
8
|
+
};
|
|
9
|
+
export type ComposerProps = HTMLAttributes<HTMLDivElement> & {
|
|
10
|
+
ref?: HTMLDivElement | null;
|
|
11
|
+
class?: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
busy?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
maxLength?: number;
|
|
17
|
+
allowEmpty?: boolean;
|
|
18
|
+
messages?: Partial<ComposerMessages>;
|
|
19
|
+
onSend?: (text: string) => void;
|
|
20
|
+
onStop?: () => void;
|
|
21
|
+
leading?: Snippet;
|
|
22
|
+
trailing?: Snippet;
|
|
23
|
+
attachments?: Snippet;
|
|
24
|
+
};
|
|
25
|
+
declare const Composer: import("svelte").Component<ComposerProps, {}, "ref" | "value" | "busy">;
|
|
26
|
+
type Composer = ReturnType<typeof Composer>;
|
|
27
|
+
export default Composer;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Figma: Message (component set 67:4, node 67:4)
|
|
3
|
+
-->
|
|
4
|
+
<script lang="ts" module>
|
|
5
|
+
import type { Snippet } from 'svelte';
|
|
6
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
7
|
+
|
|
8
|
+
export type MessageRole = 'user' | 'assistant';
|
|
9
|
+
|
|
10
|
+
export type MessageMessages = {
|
|
11
|
+
copy: string;
|
|
12
|
+
regenerate: string;
|
|
13
|
+
restoreCheckpoint: string;
|
|
14
|
+
streaming: string;
|
|
15
|
+
error: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type MessageProps = HTMLAttributes<HTMLDivElement> & {
|
|
19
|
+
ref?: HTMLDivElement | null;
|
|
20
|
+
class?: string;
|
|
21
|
+
role?: MessageRole;
|
|
22
|
+
streaming?: boolean;
|
|
23
|
+
error?: boolean;
|
|
24
|
+
errorText?: string;
|
|
25
|
+
showActions?: boolean;
|
|
26
|
+
messages?: Partial<MessageMessages>;
|
|
27
|
+
onCopy?: () => void;
|
|
28
|
+
onRegenerate?: () => void;
|
|
29
|
+
onRestoreCheckpoint?: () => void;
|
|
30
|
+
actions?: Snippet;
|
|
31
|
+
children?: Snippet;
|
|
32
|
+
};
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import IconCopy from '@tabler/icons-svelte/icons/copy';
|
|
37
|
+
import IconRefresh from '@tabler/icons-svelte/icons/refresh';
|
|
38
|
+
import IconRestore from '@tabler/icons-svelte/icons/history';
|
|
39
|
+
import { cn } from '../../utils.js';
|
|
40
|
+
import { Button } from '../ui/button/index.js';
|
|
41
|
+
import { IconButton } from '../ui/icon-button/index.js';
|
|
42
|
+
|
|
43
|
+
const defaultMessages: MessageMessages = {
|
|
44
|
+
copy: 'Copy message',
|
|
45
|
+
regenerate: 'Regenerate response',
|
|
46
|
+
restoreCheckpoint: 'Restore checkpoint',
|
|
47
|
+
streaming: 'Assistant is typing',
|
|
48
|
+
error: 'Something went wrong'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let {
|
|
52
|
+
ref = $bindable(null),
|
|
53
|
+
class: className,
|
|
54
|
+
role = 'user',
|
|
55
|
+
streaming = false,
|
|
56
|
+
error = false,
|
|
57
|
+
errorText,
|
|
58
|
+
showActions = false,
|
|
59
|
+
messages: messagesProp,
|
|
60
|
+
onCopy,
|
|
61
|
+
onRegenerate,
|
|
62
|
+
onRestoreCheckpoint,
|
|
63
|
+
actions,
|
|
64
|
+
children,
|
|
65
|
+
...restProps
|
|
66
|
+
}: MessageProps = $props();
|
|
67
|
+
|
|
68
|
+
const messages = $derived({ ...defaultMessages, ...messagesProp });
|
|
69
|
+
const isUser = $derived(role === 'user');
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<div
|
|
73
|
+
bind:this={ref}
|
|
74
|
+
class={cn(
|
|
75
|
+
'group flex w-full max-w-[25.5rem] flex-col gap-1',
|
|
76
|
+
isUser ? 'ms-auto items-end' : 'items-start',
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
{...restProps}
|
|
80
|
+
>
|
|
81
|
+
{#if isUser}
|
|
82
|
+
<div
|
|
83
|
+
class={cn(
|
|
84
|
+
'rounded-card bg-muted text-body text-foreground max-w-full px-3 py-2 text-start',
|
|
85
|
+
error && 'border-destructive border'
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
{#if children}
|
|
89
|
+
{@render children()}
|
|
90
|
+
{/if}
|
|
91
|
+
</div>
|
|
92
|
+
<div
|
|
93
|
+
class={cn(
|
|
94
|
+
'duration-fast flex min-h-8 items-center gap-1 opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100 motion-reduce:transition-none',
|
|
95
|
+
showActions && 'opacity-100'
|
|
96
|
+
)}
|
|
97
|
+
>
|
|
98
|
+
{#if onRestoreCheckpoint}
|
|
99
|
+
<Button variant="ghost" size="sm" onclick={onRestoreCheckpoint}>
|
|
100
|
+
<IconRestore class="size-4 stroke-[1.333]" aria-hidden="true" />
|
|
101
|
+
{messages.restoreCheckpoint}
|
|
102
|
+
</Button>
|
|
103
|
+
{/if}
|
|
104
|
+
{#if onCopy}
|
|
105
|
+
<IconButton variant="ghost" label={messages.copy} onclick={onCopy}>
|
|
106
|
+
<IconCopy class="size-4 stroke-[1.333]" aria-hidden="true" />
|
|
107
|
+
</IconButton>
|
|
108
|
+
{/if}
|
|
109
|
+
{#if actions}
|
|
110
|
+
{@render actions()}
|
|
111
|
+
{/if}
|
|
112
|
+
</div>
|
|
113
|
+
{:else}
|
|
114
|
+
<div
|
|
115
|
+
class={cn('text-body text-foreground w-full', error && 'text-destructive')}
|
|
116
|
+
aria-live={streaming ? 'polite' : undefined}
|
|
117
|
+
aria-busy={streaming ? true : undefined}
|
|
118
|
+
>
|
|
119
|
+
{#if error}
|
|
120
|
+
<p class="text-body-medium">{errorText ?? messages.error}</p>
|
|
121
|
+
{:else if children}
|
|
122
|
+
{@render children()}
|
|
123
|
+
{/if}
|
|
124
|
+
{#if streaming}
|
|
125
|
+
<span class="sr-only">{messages.streaming}</span>
|
|
126
|
+
<span class="text-muted-foreground mt-2 inline-flex items-center gap-1" aria-hidden="true">
|
|
127
|
+
<span class="bg-muted-foreground size-1.5 rounded-full motion-safe:animate-pulse"></span>
|
|
128
|
+
<span
|
|
129
|
+
class="bg-muted-foreground size-1.5 rounded-full motion-safe:animate-pulse motion-safe:[animation-delay:120ms]"
|
|
130
|
+
></span>
|
|
131
|
+
<span
|
|
132
|
+
class="bg-muted-foreground size-1.5 rounded-full motion-safe:animate-pulse motion-safe:[animation-delay:240ms]"
|
|
133
|
+
></span>
|
|
134
|
+
</span>
|
|
135
|
+
{/if}
|
|
136
|
+
</div>
|
|
137
|
+
<div
|
|
138
|
+
class={cn(
|
|
139
|
+
'duration-fast flex min-h-8 items-center gap-1 opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100 motion-reduce:transition-none',
|
|
140
|
+
showActions && 'opacity-100'
|
|
141
|
+
)}
|
|
142
|
+
>
|
|
143
|
+
{#if onCopy}
|
|
144
|
+
<IconButton variant="ghost" label={messages.copy} onclick={onCopy}>
|
|
145
|
+
<IconCopy class="size-4 stroke-[1.333]" aria-hidden="true" />
|
|
146
|
+
</IconButton>
|
|
147
|
+
{/if}
|
|
148
|
+
{#if onRegenerate}
|
|
149
|
+
<IconButton variant="ghost" label={messages.regenerate} onclick={onRegenerate}>
|
|
150
|
+
<IconRefresh class="size-4 stroke-[1.333]" aria-hidden="true" />
|
|
151
|
+
</IconButton>
|
|
152
|
+
{/if}
|
|
153
|
+
{#if actions}
|
|
154
|
+
{@render actions()}
|
|
155
|
+
{/if}
|
|
156
|
+
</div>
|
|
157
|
+
{/if}
|
|
158
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
export type MessageRole = 'user' | 'assistant';
|
|
4
|
+
export type MessageMessages = {
|
|
5
|
+
copy: string;
|
|
6
|
+
regenerate: string;
|
|
7
|
+
restoreCheckpoint: string;
|
|
8
|
+
streaming: string;
|
|
9
|
+
error: string;
|
|
10
|
+
};
|
|
11
|
+
export type MessageProps = HTMLAttributes<HTMLDivElement> & {
|
|
12
|
+
ref?: HTMLDivElement | null;
|
|
13
|
+
class?: string;
|
|
14
|
+
role?: MessageRole;
|
|
15
|
+
streaming?: boolean;
|
|
16
|
+
error?: boolean;
|
|
17
|
+
errorText?: string;
|
|
18
|
+
showActions?: boolean;
|
|
19
|
+
messages?: Partial<MessageMessages>;
|
|
20
|
+
onCopy?: () => void;
|
|
21
|
+
onRegenerate?: () => void;
|
|
22
|
+
onRestoreCheckpoint?: () => void;
|
|
23
|
+
actions?: Snippet;
|
|
24
|
+
children?: Snippet;
|
|
25
|
+
};
|
|
26
|
+
declare const Message: import("svelte").Component<MessageProps, {}, "ref">;
|
|
27
|
+
type Message = ReturnType<typeof Message>;
|
|
28
|
+
export default Message;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Figma: Option Row (component set 68:4, node 68:4)
|
|
3
|
+
-->
|
|
4
|
+
<script lang="ts" module>
|
|
5
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
6
|
+
|
|
7
|
+
export type OptionRowSelectionMode = 'radio' | 'checkbox';
|
|
8
|
+
|
|
9
|
+
export type OptionRowProps = HTMLAttributes<HTMLButtonElement> & {
|
|
10
|
+
ref?: HTMLButtonElement | null;
|
|
11
|
+
class?: string;
|
|
12
|
+
label: string;
|
|
13
|
+
selected?: boolean;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
selectionMode?: OptionRowSelectionMode;
|
|
16
|
+
onSelect?: () => void;
|
|
17
|
+
};
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import IconCheck from '@tabler/icons-svelte/icons/check';
|
|
22
|
+
import { cn } from '../../utils.js';
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
ref = $bindable(null),
|
|
26
|
+
class: className,
|
|
27
|
+
label,
|
|
28
|
+
selected = false,
|
|
29
|
+
disabled = false,
|
|
30
|
+
selectionMode = 'radio',
|
|
31
|
+
onSelect,
|
|
32
|
+
...restProps
|
|
33
|
+
}: OptionRowProps = $props();
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<button
|
|
37
|
+
bind:this={ref}
|
|
38
|
+
type="button"
|
|
39
|
+
role={selectionMode === 'radio' ? 'radio' : 'checkbox'}
|
|
40
|
+
aria-checked={selected}
|
|
41
|
+
{disabled}
|
|
42
|
+
class={cn(
|
|
43
|
+
'border-border duration-fast hover:bg-accent focus-visible:ring-ring focus-visible:ring-offset-background flex h-9 w-full items-center gap-2 rounded-lg border px-3 py-2 text-start transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',
|
|
44
|
+
selected && 'border-primary bg-accent',
|
|
45
|
+
className
|
|
46
|
+
)}
|
|
47
|
+
onclick={() => onSelect?.()}
|
|
48
|
+
{...restProps}
|
|
49
|
+
>
|
|
50
|
+
<!-- Figma: the label comes first and fills the row; the check sits after it and exists only when selected -->
|
|
51
|
+
<span class={cn('text-body min-w-0 flex-1 truncate', selected && 'text-body-medium')}>
|
|
52
|
+
{label}
|
|
53
|
+
</span>
|
|
54
|
+
{#if selected}
|
|
55
|
+
<span class="text-foreground flex size-4 shrink-0 items-center justify-center" aria-hidden="true">
|
|
56
|
+
<IconCheck class="size-4 stroke-[1.333]" />
|
|
57
|
+
</span>
|
|
58
|
+
{/if}
|
|
59
|
+
</button>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
export type OptionRowSelectionMode = 'radio' | 'checkbox';
|
|
3
|
+
export type OptionRowProps = HTMLAttributes<HTMLButtonElement> & {
|
|
4
|
+
ref?: HTMLButtonElement | null;
|
|
5
|
+
class?: string;
|
|
6
|
+
label: string;
|
|
7
|
+
selected?: boolean;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
selectionMode?: OptionRowSelectionMode;
|
|
10
|
+
onSelect?: () => void;
|
|
11
|
+
};
|
|
12
|
+
declare const OptionRow: import("svelte").Component<OptionRowProps, {}, "ref">;
|
|
13
|
+
type OptionRow = ReturnType<typeof OptionRow>;
|
|
14
|
+
export default OptionRow;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Figma: Plan Card (component set 68:107, page Plan Card)
|
|
3
|
+
-->
|
|
4
|
+
<script lang="ts">
|
|
5
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
6
|
+
import type { Snippet } from 'svelte';
|
|
7
|
+
import IconChevronDown from '@tabler/icons-svelte/icons/chevron-down';
|
|
8
|
+
import IconCircle from '@tabler/icons-svelte/icons/circle';
|
|
9
|
+
import IconCircleCheck from '@tabler/icons-svelte/icons/circle-check';
|
|
10
|
+
import IconListCheck from '@tabler/icons-svelte/icons/list-check';
|
|
11
|
+
import IconLoader2 from '@tabler/icons-svelte/icons/loader-2';
|
|
12
|
+
import { Badge } from '../ui/badge';
|
|
13
|
+
import { Button } from '../ui/button';
|
|
14
|
+
import * as Collapsible from '../ui/collapsible';
|
|
15
|
+
import { cn } from '../../utils.js';
|
|
16
|
+
|
|
17
|
+
export type PlanTask = {
|
|
18
|
+
id: string;
|
|
19
|
+
label: string;
|
|
20
|
+
status: 'pending' | 'active' | 'done';
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type PlanCardMessages = {
|
|
24
|
+
progress: string;
|
|
25
|
+
showMoreTasks: string;
|
|
26
|
+
showFewerTasks: string;
|
|
27
|
+
primaryAction: string;
|
|
28
|
+
secondaryAction: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const defaultMessages: PlanCardMessages = {
|
|
32
|
+
progress: '{current} of {total}',
|
|
33
|
+
showMoreTasks: 'Show {count} more tasks',
|
|
34
|
+
showFewerTasks: 'Show fewer tasks',
|
|
35
|
+
primaryAction: 'Build',
|
|
36
|
+
secondaryAction: 'Edit plan'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type Props = HTMLAttributes<HTMLDivElement> & {
|
|
40
|
+
ref?: HTMLDivElement | null;
|
|
41
|
+
class?: string;
|
|
42
|
+
title: string;
|
|
43
|
+
tasks: PlanTask[];
|
|
44
|
+
/** Shown in the header badge; defaults from active task index and task count. */
|
|
45
|
+
progressCurrent?: number;
|
|
46
|
+
progressTotal?: number;
|
|
47
|
+
/** Tasks shown before the collapsible section. */
|
|
48
|
+
visibleTaskCount?: number;
|
|
49
|
+
messages?: Partial<PlanCardMessages>;
|
|
50
|
+
onPrimary?: () => void;
|
|
51
|
+
onSecondary?: () => void;
|
|
52
|
+
headerIcon?: Snippet;
|
|
53
|
+
primaryAction?: Snippet;
|
|
54
|
+
secondaryAction?: Snippet;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let {
|
|
58
|
+
ref = $bindable(null),
|
|
59
|
+
class: className,
|
|
60
|
+
title,
|
|
61
|
+
tasks,
|
|
62
|
+
progressCurrent,
|
|
63
|
+
progressTotal,
|
|
64
|
+
visibleTaskCount = 3,
|
|
65
|
+
messages: messagesProp,
|
|
66
|
+
onPrimary,
|
|
67
|
+
onSecondary,
|
|
68
|
+
headerIcon,
|
|
69
|
+
primaryAction,
|
|
70
|
+
secondaryAction,
|
|
71
|
+
...restProps
|
|
72
|
+
}: Props = $props();
|
|
73
|
+
|
|
74
|
+
const messages = $derived({ ...defaultMessages, ...messagesProp });
|
|
75
|
+
|
|
76
|
+
let expanded = $state(false);
|
|
77
|
+
|
|
78
|
+
const activeIndex = $derived(tasks.findIndex((t) => t.status === 'active'));
|
|
79
|
+
const resolvedProgressCurrent = $derived(
|
|
80
|
+
progressCurrent ?? (activeIndex >= 0 ? activeIndex + 1 : tasks.length > 0 ? 1 : 0)
|
|
81
|
+
);
|
|
82
|
+
const resolvedProgressTotal = $derived(progressTotal ?? tasks.length);
|
|
83
|
+
const progressLabel = $derived(
|
|
84
|
+
messages.progress
|
|
85
|
+
.replace('{current}', String(resolvedProgressCurrent))
|
|
86
|
+
.replace('{total}', String(resolvedProgressTotal))
|
|
87
|
+
.replace('{completed}', String(resolvedProgressCurrent))
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const showFooter = $derived(
|
|
91
|
+
Boolean(onPrimary || onSecondary || primaryAction || secondaryAction)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const visibleTasks = $derived(tasks.slice(0, visibleTaskCount));
|
|
95
|
+
const hiddenTasks = $derived(tasks.slice(visibleTaskCount));
|
|
96
|
+
const hiddenCount = $derived(hiddenTasks.length);
|
|
97
|
+
|
|
98
|
+
const showMoreLabel = $derived(
|
|
99
|
+
expanded
|
|
100
|
+
? messages.showFewerTasks
|
|
101
|
+
: messages.showMoreTasks.replace('{count}', String(hiddenCount))
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
function taskIcon(status: PlanTask['status']) {
|
|
105
|
+
if (status === 'done') return IconCircleCheck;
|
|
106
|
+
if (status === 'active') return IconLoader2;
|
|
107
|
+
return IconCircle;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handlePrimary() {
|
|
111
|
+
if (!onPrimary) return;
|
|
112
|
+
try {
|
|
113
|
+
onPrimary();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error(error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function handleSecondary() {
|
|
120
|
+
if (!onSecondary) return;
|
|
121
|
+
try {
|
|
122
|
+
onSecondary();
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
{#snippet taskRow(task: PlanTask)}
|
|
130
|
+
{@const Icon = taskIcon(task.status)}
|
|
131
|
+
<div class="flex min-h-7 items-center gap-2 px-0 py-1">
|
|
132
|
+
<Icon
|
|
133
|
+
class={cn(
|
|
134
|
+
'size-4 shrink-0 stroke-[1.333]',
|
|
135
|
+
task.status === 'active' && 'animate-spin motion-reduce:animate-none',
|
|
136
|
+
task.status === 'done' ? 'text-muted-foreground' : 'text-foreground'
|
|
137
|
+
)}
|
|
138
|
+
aria-hidden="true"
|
|
139
|
+
/>
|
|
140
|
+
<span
|
|
141
|
+
class={cn(
|
|
142
|
+
'text-body min-w-0 flex-1 truncate',
|
|
143
|
+
task.status === 'pending' && 'text-muted-foreground',
|
|
144
|
+
task.status === 'active' && 'text-body-medium text-foreground',
|
|
145
|
+
task.status === 'done' && 'text-muted-foreground line-through'
|
|
146
|
+
)}
|
|
147
|
+
>
|
|
148
|
+
{task.label}
|
|
149
|
+
</span>
|
|
150
|
+
</div>
|
|
151
|
+
{/snippet}
|
|
152
|
+
|
|
153
|
+
<div
|
|
154
|
+
bind:this={ref}
|
|
155
|
+
class={cn(
|
|
156
|
+
'rounded-card border-border bg-background flex w-full max-w-full flex-col gap-3 border p-4',
|
|
157
|
+
className
|
|
158
|
+
)}
|
|
159
|
+
{...restProps}
|
|
160
|
+
>
|
|
161
|
+
<header class="flex min-w-0 items-center gap-2">
|
|
162
|
+
<span class="text-foreground flex size-4 shrink-0 items-center justify-center">
|
|
163
|
+
{#if headerIcon}
|
|
164
|
+
{@render headerIcon()}
|
|
165
|
+
{:else}
|
|
166
|
+
<IconListCheck class="size-4 stroke-[1.333]" aria-hidden="true" />
|
|
167
|
+
{/if}
|
|
168
|
+
</span>
|
|
169
|
+
<h3 class="text-body-medium text-foreground min-w-0 flex-1 truncate">{title}</h3>
|
|
170
|
+
<Badge variant="default" class="shrink-0">{progressLabel}</Badge>
|
|
171
|
+
</header>
|
|
172
|
+
|
|
173
|
+
<div class="flex flex-col">
|
|
174
|
+
{#each visibleTasks as task (task.id)}
|
|
175
|
+
{@render taskRow(task)}
|
|
176
|
+
{/each}
|
|
177
|
+
|
|
178
|
+
{#if hiddenCount > 0}
|
|
179
|
+
<Collapsible.Root bind:open={expanded}>
|
|
180
|
+
<Collapsible.Trigger
|
|
181
|
+
class="text-label text-foreground duration-fast hover:bg-accent focus-visible:ring-ring focus-visible:ring-offset-background mt-1 inline-flex h-8 items-center gap-1 rounded-lg px-2 transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden"
|
|
182
|
+
>
|
|
183
|
+
{showMoreLabel}
|
|
184
|
+
<IconChevronDown
|
|
185
|
+
class={cn(
|
|
186
|
+
'duration-base size-4 stroke-[1.333] transition-transform motion-reduce:transition-none',
|
|
187
|
+
expanded && 'rotate-180'
|
|
188
|
+
)}
|
|
189
|
+
aria-hidden="true"
|
|
190
|
+
/>
|
|
191
|
+
</Collapsible.Trigger>
|
|
192
|
+
<Collapsible.Content class="flex flex-col overflow-hidden motion-reduce:transition-none">
|
|
193
|
+
{#each hiddenTasks as task (task.id)}
|
|
194
|
+
{@render taskRow(task)}
|
|
195
|
+
{/each}
|
|
196
|
+
</Collapsible.Content>
|
|
197
|
+
</Collapsible.Root>
|
|
198
|
+
{/if}
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
{#if showFooter}
|
|
202
|
+
<footer class="flex flex-wrap items-center gap-2 pt-1">
|
|
203
|
+
{#if secondaryAction}
|
|
204
|
+
{@render secondaryAction()}
|
|
205
|
+
{:else if onSecondary}
|
|
206
|
+
<Button variant="outline" size="sm" class="shrink-0" onclick={handleSecondary}>
|
|
207
|
+
{messages.secondaryAction}
|
|
208
|
+
</Button>
|
|
209
|
+
{/if}
|
|
210
|
+
<span class="min-w-0 flex-1" aria-hidden="true"></span>
|
|
211
|
+
{#if primaryAction}
|
|
212
|
+
{@render primaryAction()}
|
|
213
|
+
{:else if onPrimary}
|
|
214
|
+
<Button variant="default" size="sm" class="shrink-0" onclick={handlePrimary}>
|
|
215
|
+
{messages.primaryAction}
|
|
216
|
+
</Button>
|
|
217
|
+
{/if}
|
|
218
|
+
</footer>
|
|
219
|
+
{/if}
|
|
220
|
+
</div>
|