@joewinke/jatui 0.1.10 → 0.1.19
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 +123 -0
- package/package.json +3 -1
- package/src/lib/actions/railNav.ts +473 -0
- package/src/lib/components/AnnotationLayer.svelte +108 -0
- package/src/lib/components/AnnotationPanel.svelte +319 -0
- package/src/lib/components/AudioWaveform.svelte +9 -5
- package/src/lib/components/AvailabilityModal.svelte +7 -3
- package/src/lib/components/AvatarUpload.svelte +27 -4
- package/src/lib/components/BookingForm.svelte +11 -9
- package/src/lib/components/BurndownChart.svelte +778 -0
- package/src/lib/components/Button.svelte +10 -1
- package/src/lib/components/CalendarPicker.svelte +3 -3
- package/src/lib/components/Card.svelte +2 -2
- package/src/lib/components/ChipInput.svelte +21 -15
- package/src/lib/components/ColorSelector.svelte +17 -13
- package/src/lib/components/CommentThread.svelte +773 -0
- package/src/lib/components/ConfirmDialog.svelte +348 -0
- package/src/lib/components/ConfirmModal.svelte +78 -11
- package/src/lib/components/ContextMenu.svelte +188 -0
- package/src/lib/components/CountdownTimer.svelte +1 -1
- package/src/lib/components/DateRangePicker.svelte +6 -4
- package/src/lib/components/Drawer.svelte +36 -3
- package/src/lib/components/EntityPreviewCard.svelte +104 -0
- package/src/lib/components/FileDropzone.svelte +493 -0
- package/src/lib/components/FilePicker.svelte +83 -14
- package/src/lib/components/FileThumbnail.svelte +80 -0
- package/src/lib/components/FilterDropdown.svelte +11 -11
- package/src/lib/components/HunkDiffView.svelte +348 -0
- package/src/lib/components/ImageLightbox.svelte +274 -0
- package/src/lib/components/ImageUpload.svelte +58 -9
- package/src/lib/components/InlineEdit.svelte +15 -9
- package/src/lib/components/InputDialog.svelte +327 -0
- package/src/lib/components/LazyImage.svelte +1 -0
- package/src/lib/components/LinkShortener.svelte +1 -1
- package/src/lib/components/LoadingSpinner.svelte +6 -2
- package/src/lib/components/MarkupEditor.svelte +485 -0
- package/src/lib/components/MarkupOverlay.svelte +55 -0
- package/src/lib/components/MediaWorkbench.svelte +871 -0
- package/src/lib/components/MilestoneCard.svelte +1 -1
- package/src/lib/components/MilestoneTimeline.svelte +1 -1
- package/src/lib/components/Modal.svelte +39 -4
- package/src/lib/components/PDFViewer.svelte +105 -0
- package/src/lib/components/PdfThumbnail.svelte +3 -1
- package/src/lib/components/PhoneInput.svelte +183 -63
- package/src/lib/components/ResizablePanel.svelte +4 -4
- package/src/lib/components/SearchDropdown.svelte +26 -13
- package/src/lib/components/SelectInput.svelte +26 -4
- package/src/lib/components/SidebarUserFooter.svelte +1 -1
- package/src/lib/components/SignaturePad.svelte +8 -4
- package/src/lib/components/SmartImageEditor.svelte +720 -0
- package/src/lib/components/SortDropdown.svelte +9 -3
- package/src/lib/components/Sparkline.svelte +9 -0
- package/src/lib/components/StatusBadge.svelte +20 -18
- package/src/lib/components/TextArea.svelte +24 -5
- package/src/lib/components/TextInput.svelte +29 -6
- package/src/lib/components/ThemeSelector.svelte +15 -4
- package/src/lib/components/TimeSlotPicker.svelte +7 -7
- package/src/lib/components/UserAvatar.svelte +14 -1
- package/src/lib/components/VariablePicker.svelte +170 -0
- package/src/lib/components/VoicePlayer.svelte +4 -3
- package/src/lib/components/markup.ts +287 -0
- package/src/lib/components/messaging/ChannelInfoModal.svelte +9 -9
- package/src/lib/components/messaging/ChannelList.svelte +1 -1
- package/src/lib/components/messaging/ChannelMembersModal.svelte +1 -1
- package/src/lib/components/messaging/CreateChannelModal.svelte +1 -1
- package/src/lib/components/messaging/DirectMessageList.svelte +1 -1
- package/src/lib/components/messaging/EmojiSelector.svelte +2 -1
- package/src/lib/components/messaging/MentionAutocomplete.svelte +1 -1
- package/src/lib/components/messaging/MessageAttachment.svelte +3 -3
- package/src/lib/components/messaging/MessageAttachmentUpload.svelte +3 -3
- package/src/lib/components/messaging/MessageInput.svelte +1 -1
- package/src/lib/components/messaging/MessageItem.svelte +6 -3
- package/src/lib/components/messaging/NotificationSettingsModal.svelte +1 -1
- package/src/lib/components/messaging/QuotedMessageDisplay.svelte +6 -1
- package/src/lib/components/messaging/StartDMModal.svelte +1 -1
- package/src/lib/components/pipeline/Pipeline.svelte +4 -4
- package/src/lib/components/pipeline/PipelineCard.svelte +1 -1
- package/src/lib/components/pipeline/PipelineColumn.svelte +8 -3
- package/src/lib/index.ts +105 -1
- package/src/lib/stores/confirmDialog.svelte.ts +48 -0
- package/src/lib/stores/inputDialog.svelte.ts +51 -0
- package/src/lib/styles/rail.css +63 -0
- package/src/lib/types/annotation.ts +38 -0
- package/src/lib/types/comments.ts +97 -0
- package/src/lib/types/entityPreview.ts +45 -0
- package/src/lib/types/filePicker.ts +2 -0
- package/src/lib/types/smartImageEditor.ts +39 -0
- package/src/lib/types/templateVars.ts +36 -0
- package/src/lib/utils/dateFormatters.ts +12 -10
- package/src/lib/utils/phone.ts +80 -0
- package/src/lib/utils/taskUtils.ts +21 -7
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
}: Props = $props();
|
|
24
24
|
|
|
25
25
|
// Local state for custom date inputs
|
|
26
|
+
// svelte-ignore state_referenced_locally
|
|
26
27
|
let localFrom = $state(customFrom || '');
|
|
28
|
+
// svelte-ignore state_referenced_locally
|
|
27
29
|
let localTo = $state(customTo || '');
|
|
28
30
|
let showCustom = $state(false);
|
|
29
31
|
|
|
@@ -129,7 +131,7 @@
|
|
|
129
131
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
130
132
|
<div
|
|
131
133
|
tabindex="0"
|
|
132
|
-
class="dropdown-content rounded-box z-40 w-72 p-3
|
|
134
|
+
class="dropdown-content rounded-box z-40 w-72 p-3 mt-1 bg-base-200 border border-base-300"
|
|
133
135
|
>
|
|
134
136
|
<!-- Quick Presets -->
|
|
135
137
|
<div class="flex flex-wrap gap-1.5 mb-3">
|
|
@@ -147,7 +149,7 @@
|
|
|
147
149
|
<!-- Divider -->
|
|
148
150
|
<div class="my-2 flex items-center gap-2">
|
|
149
151
|
<div class="flex-1 h-px bg-base-300"></div>
|
|
150
|
-
<span class="text-
|
|
152
|
+
<span class="text-[0.75rem] text-base-content/45">or custom</span>
|
|
151
153
|
<div class="flex-1 h-px bg-base-300"></div>
|
|
152
154
|
</div>
|
|
153
155
|
|
|
@@ -156,7 +158,7 @@
|
|
|
156
158
|
<div class="flex gap-2">
|
|
157
159
|
<div class="form-control flex-1">
|
|
158
160
|
<label class="label py-0.5" for="date-range-from">
|
|
159
|
-
<span class="
|
|
161
|
+
<span class="text-[0.8125rem] text-base-content/45">From</span>
|
|
160
162
|
</label>
|
|
161
163
|
<input
|
|
162
164
|
id="date-range-from"
|
|
@@ -168,7 +170,7 @@
|
|
|
168
170
|
</div>
|
|
169
171
|
<div class="form-control flex-1">
|
|
170
172
|
<label class="label py-0.5" for="date-range-to">
|
|
171
|
-
<span class="
|
|
173
|
+
<span class="text-[0.8125rem] text-base-content/45">To</span>
|
|
172
174
|
</label>
|
|
173
175
|
<input
|
|
174
176
|
id="date-range-to"
|
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full';
|
|
15
15
|
showCloseButton?: boolean;
|
|
16
16
|
contentClass?: string;
|
|
17
|
+
/**
|
|
18
|
+
* When true (default), keydown events fired inside the drawer panel are
|
|
19
|
+
* stopped from propagating to window — preventing page-level list/keyboard
|
|
20
|
+
* navigation handlers (listNav-style) from receiving arrow keys, j/k, vim
|
|
21
|
+
* chords, digit counts, etc. while the drawer is open.
|
|
22
|
+
*
|
|
23
|
+
* Free-text fields (INPUT, TEXTAREA, contentEditable) are intentionally
|
|
24
|
+
* excluded so normal typing still works. Set to false only when the
|
|
25
|
+
* consumer needs custom window-level key handling even while the drawer
|
|
26
|
+
* is open.
|
|
27
|
+
*/
|
|
28
|
+
trapKeys?: boolean;
|
|
17
29
|
onclose?: () => void;
|
|
18
30
|
class?: string;
|
|
19
31
|
headerActions?: Snippet;
|
|
@@ -27,6 +39,7 @@
|
|
|
27
39
|
size = 'md',
|
|
28
40
|
showCloseButton = true,
|
|
29
41
|
contentClass = '',
|
|
42
|
+
trapKeys = true,
|
|
30
43
|
onclose,
|
|
31
44
|
class: className = '',
|
|
32
45
|
headerActions,
|
|
@@ -54,14 +67,33 @@
|
|
|
54
67
|
handleClose();
|
|
55
68
|
}
|
|
56
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Stop keydown events from bubbling to window while the drawer is open.
|
|
73
|
+
* This prevents page-level keyboard navigation (listNav-style handlers on
|
|
74
|
+
* window) from reacting to arrow keys, j/k, vim chords, digit counts, etc.
|
|
75
|
+
* typed inside the drawer.
|
|
76
|
+
*
|
|
77
|
+
* Free-text fields are excluded: page-level handlers already ignore them,
|
|
78
|
+
* and we must not interfere with normal typing or textarea shortcuts.
|
|
79
|
+
*/
|
|
80
|
+
function handlePanelKeydown(e: KeyboardEvent) {
|
|
81
|
+
if (!trapKeys) return;
|
|
82
|
+
const el = document.activeElement as HTMLElement | null;
|
|
83
|
+
const tag = el?.tagName;
|
|
84
|
+
const isTextField =
|
|
85
|
+
!!el && (tag === 'INPUT' || tag === 'TEXTAREA' || el.isContentEditable === true);
|
|
86
|
+
if (!isTextField) e.stopPropagation();
|
|
87
|
+
}
|
|
57
88
|
</script>
|
|
58
89
|
|
|
59
90
|
<svelte:window onkeydown={handleKeydown} />
|
|
60
91
|
|
|
61
92
|
<div
|
|
62
|
-
class="fixed inset-0
|
|
93
|
+
class="fixed inset-0 transition-opacity duration-300 {open
|
|
63
94
|
? 'z-[100] opacity-100'
|
|
64
95
|
: 'opacity-0 pointer-events-none -z-10'}"
|
|
96
|
+
style="background: color-mix(in oklch, var(--color-primary) 55%, transparent)"
|
|
65
97
|
onclick={open ? handleClose : undefined}
|
|
66
98
|
role={open ? 'presentation' : undefined}
|
|
67
99
|
></div>
|
|
@@ -69,16 +101,17 @@
|
|
|
69
101
|
<div
|
|
70
102
|
class="fixed top-0 {position === 'right'
|
|
71
103
|
? 'right-0'
|
|
72
|
-
: 'left-0'} h-full bg-base-
|
|
104
|
+
: 'left-0'} h-full bg-base-200 {position === 'right' ? 'border-l' : 'border-r'} border-base-300 {sizeClasses[size]} flex flex-col transition-all duration-300 {open
|
|
73
105
|
? 'z-[101] translate-x-0'
|
|
74
106
|
: '-z-10 ' + (position === 'right' ? 'translate-x-full' : '-translate-x-full')} {className}"
|
|
75
107
|
role="dialog"
|
|
76
108
|
aria-modal={open}
|
|
77
109
|
aria-label={title}
|
|
78
110
|
aria-hidden={!open}
|
|
111
|
+
onkeydown={handlePanelKeydown}
|
|
79
112
|
>
|
|
80
113
|
{#if title || showCloseButton || headerActions}
|
|
81
|
-
<div class="flex items-center justify-between p-6 border-b border-base-
|
|
114
|
+
<div class="flex items-center justify-between p-6 border-b border-base-content/10 flex-shrink-0">
|
|
82
115
|
{#if title}
|
|
83
116
|
<h2 class="text-xl font-semibold">{title}</h2>
|
|
84
117
|
{/if}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { EntityPreviewData, EntityPreviewBadge } from "../types/entityPreview"
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
data,
|
|
6
|
+
loading = false,
|
|
7
|
+
error = false,
|
|
8
|
+
}: {
|
|
9
|
+
data: EntityPreviewData | null
|
|
10
|
+
loading?: boolean
|
|
11
|
+
error?: boolean
|
|
12
|
+
} = $props()
|
|
13
|
+
|
|
14
|
+
// First letter monogram fallback when there's no image.
|
|
15
|
+
const monogram = $derived((data?.title ?? "?").trim().charAt(0).toUpperCase() || "?")
|
|
16
|
+
|
|
17
|
+
function toneClass(tone: EntityPreviewBadge["tone"]): string {
|
|
18
|
+
switch (tone) {
|
|
19
|
+
case "primary":
|
|
20
|
+
return "badge-primary"
|
|
21
|
+
case "info":
|
|
22
|
+
return "badge-info"
|
|
23
|
+
case "success":
|
|
24
|
+
return "badge-success"
|
|
25
|
+
case "warning":
|
|
26
|
+
return "badge-warning"
|
|
27
|
+
case "error":
|
|
28
|
+
return "badge-error"
|
|
29
|
+
default:
|
|
30
|
+
return "badge-ghost"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<div
|
|
36
|
+
class="bg-base-100 border-base-content/10 rounded-box w-72 overflow-hidden border text-left"
|
|
37
|
+
>
|
|
38
|
+
{#if loading}
|
|
39
|
+
<div class="space-y-2 p-4">
|
|
40
|
+
<div class="bg-base-300 h-3 w-20 animate-pulse rounded"></div>
|
|
41
|
+
<div class="bg-base-300 h-4 w-40 animate-pulse rounded"></div>
|
|
42
|
+
<div class="bg-base-300 h-3 w-full animate-pulse rounded"></div>
|
|
43
|
+
</div>
|
|
44
|
+
{:else if error || !data}
|
|
45
|
+
<div class="text-base-content/50 p-4 text-sm">Preview unavailable.</div>
|
|
46
|
+
{:else}
|
|
47
|
+
{#if data.imageUrl}
|
|
48
|
+
<div class="bg-base-200 aspect-[16/9] w-full overflow-hidden">
|
|
49
|
+
<img src={data.imageUrl} alt={data.title} class="size-full object-cover" />
|
|
50
|
+
</div>
|
|
51
|
+
{/if}
|
|
52
|
+
|
|
53
|
+
<div class="space-y-2 p-4">
|
|
54
|
+
<div class="flex items-start gap-3">
|
|
55
|
+
{#if !data.imageUrl}
|
|
56
|
+
<div
|
|
57
|
+
class="bg-primary/10 text-primary rounded-box flex size-9 shrink-0 items-center justify-center text-sm font-bold"
|
|
58
|
+
aria-hidden="true"
|
|
59
|
+
>
|
|
60
|
+
{monogram}
|
|
61
|
+
</div>
|
|
62
|
+
{/if}
|
|
63
|
+
<div class="min-w-0 flex-1">
|
|
64
|
+
<div class="text-base-content/45 text-[10px] font-medium uppercase tracking-wide">
|
|
65
|
+
{data.typeLabel}
|
|
66
|
+
</div>
|
|
67
|
+
<div class="truncate text-sm font-semibold leading-tight">
|
|
68
|
+
{#if data.href}
|
|
69
|
+
<a href={data.href} class="hover:underline">{data.title}</a>
|
|
70
|
+
{:else}
|
|
71
|
+
{data.title}
|
|
72
|
+
{/if}
|
|
73
|
+
</div>
|
|
74
|
+
{#if data.subtitle}
|
|
75
|
+
<div class="text-base-content/55 truncate text-xs">{data.subtitle}</div>
|
|
76
|
+
{/if}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{#if data.summary}
|
|
81
|
+
<p class="text-base-content/70 line-clamp-2 text-xs">{data.summary}</p>
|
|
82
|
+
{/if}
|
|
83
|
+
|
|
84
|
+
{#if data.badges && data.badges.length > 0}
|
|
85
|
+
<div class="flex flex-wrap gap-1">
|
|
86
|
+
{#each data.badges as badge}
|
|
87
|
+
<span class="badge {toneClass(badge.tone)} badge-sm capitalize">{badge.label}</span>
|
|
88
|
+
{/each}
|
|
89
|
+
</div>
|
|
90
|
+
{/if}
|
|
91
|
+
|
|
92
|
+
{#if data.stats && data.stats.length > 0}
|
|
93
|
+
<div class="border-base-content/10 grid grid-cols-2 gap-x-4 gap-y-1 border-t pt-2">
|
|
94
|
+
{#each data.stats.slice(0, 4) as stat}
|
|
95
|
+
<div>
|
|
96
|
+
<div class="text-base-content/45 text-[10px] uppercase tracking-wide">{stat.label}</div>
|
|
97
|
+
<div class="text-xs font-semibold tabular-nums">{stat.value}</div>
|
|
98
|
+
</div>
|
|
99
|
+
{/each}
|
|
100
|
+
</div>
|
|
101
|
+
{/if}
|
|
102
|
+
</div>
|
|
103
|
+
{/if}
|
|
104
|
+
</div>
|