@joewinke/jatui 0.1.31 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +22 -0
- package/dist/actions/attention.d.ts +24 -0
- package/dist/actions/attention.js +94 -0
- package/dist/actions/collapse.js +29 -15
- package/dist/actions/floating.d.ts +53 -0
- package/dist/actions/floating.js +158 -0
- package/dist/actions/listNav.js +137 -108
- package/dist/actions/railNav.js +89 -72
- package/dist/actions/staggerEntrance.d.ts +28 -0
- package/dist/actions/staggerEntrance.js +71 -0
- package/dist/actions/tooltip.d.ts +28 -0
- package/dist/actions/tooltip.js +186 -0
- package/dist/assets/sounds/README.md +47 -0
- package/dist/assets/sounds/error.d.ts +1 -0
- package/dist/assets/sounds/error.js +4 -0
- package/dist/assets/sounds/notify.d.ts +1 -0
- package/dist/assets/sounds/notify.js +4 -0
- package/dist/assets/sounds/pop.d.ts +1 -0
- package/dist/assets/sounds/pop.js +4 -0
- package/dist/assets/sounds/send.d.ts +1 -0
- package/dist/assets/sounds/send.js +4 -0
- package/dist/assets/sounds/success.d.ts +1 -0
- package/dist/assets/sounds/success.js +4 -0
- package/dist/assets/sounds/toggle.d.ts +1 -0
- package/dist/assets/sounds/toggle.js +4 -0
- package/dist/components/AnimatedNumber.svelte +112 -0
- package/dist/components/AnimatedNumber.svelte.d.ts +8 -0
- package/dist/components/Badge.svelte +75 -4
- package/dist/components/Badge.svelte.d.ts +6 -1
- package/dist/components/Button.svelte +196 -21
- package/dist/components/Button.svelte.d.ts +10 -1
- package/dist/components/Carousel.svelte +265 -0
- package/dist/components/Carousel.svelte.d.ts +21 -0
- package/dist/components/ChipInput.svelte +63 -3
- package/dist/components/Collapse.svelte +70 -14
- package/dist/components/Collapse.svelte.d.ts +11 -0
- package/dist/components/ColorSelector.svelte +21 -24
- package/dist/components/CommandPalette.svelte +475 -0
- package/dist/components/CommandPalette.svelte.d.ts +3 -0
- package/dist/components/ContextMenu.svelte +9 -73
- package/dist/components/CopyButton.svelte +156 -0
- package/dist/components/CopyButton.svelte.d.ts +12 -0
- package/dist/components/HoldToConfirm.svelte +183 -0
- package/dist/components/HoldToConfirm.svelte.d.ts +11 -0
- package/dist/components/KeyboardShortcutsOverlay.svelte +54 -8
- package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +3 -0
- package/dist/components/PasswordMeter.svelte +87 -0
- package/dist/components/PasswordMeter.svelte.d.ts +8 -0
- package/dist/components/PinInput.svelte +186 -0
- package/dist/components/PinInput.svelte.d.ts +13 -0
- package/dist/components/QuantityStepper.svelte +185 -0
- package/dist/components/QuantityStepper.svelte.d.ts +12 -0
- package/dist/components/Skeleton.svelte +123 -0
- package/dist/components/Skeleton.svelte.d.ts +12 -0
- package/dist/components/SpeechForm.svelte +37 -0
- package/dist/components/StatusBadge.svelte +31 -3
- package/dist/components/StatusBadge.svelte.d.ts +0 -5
- package/dist/components/Tabs.svelte +270 -0
- package/dist/components/Tabs.svelte.d.ts +15 -0
- package/dist/components/TextInput.svelte +138 -38
- package/dist/components/TextInput.svelte.d.ts +9 -1
- package/dist/components/VoicePlayer.svelte +51 -0
- package/dist/components/basic/Alert.svelte +36 -0
- package/dist/components/basic/Alert.svelte.d.ts +9 -0
- package/dist/components/basic/Breadcrumb.svelte +27 -0
- package/dist/components/basic/Breadcrumb.svelte.d.ts +11 -0
- package/dist/components/basic/ButtonGroup.svelte +15 -0
- package/dist/components/basic/ButtonGroup.svelte.d.ts +8 -0
- package/dist/components/basic/Checkbox.svelte +85 -0
- package/dist/components/basic/Checkbox.svelte.d.ts +15 -0
- package/dist/components/basic/Empty.svelte +17 -0
- package/dist/components/basic/Empty.svelte.d.ts +8 -0
- package/dist/components/basic/Kbd.svelte +16 -0
- package/dist/components/basic/Kbd.svelte.d.ts +8 -0
- package/dist/components/basic/Pagination.svelte +104 -0
- package/dist/components/basic/Pagination.svelte.d.ts +12 -0
- package/dist/components/basic/RadioGroup.svelte +71 -0
- package/dist/components/basic/RadioGroup.svelte.d.ts +18 -0
- package/dist/components/basic/Separator.svelte +19 -0
- package/dist/components/basic/Separator.svelte.d.ts +8 -0
- package/dist/components/basic/Slider.svelte +72 -0
- package/dist/components/basic/Slider.svelte.d.ts +17 -0
- package/dist/components/basic/Switch.svelte +88 -0
- package/dist/components/basic/Switch.svelte.d.ts +14 -0
- package/dist/components/basic/Toggle.svelte +43 -0
- package/dist/components/basic/Toggle.svelte.d.ts +14 -0
- package/dist/components/graph/GraphCanvas.svelte +103 -0
- package/dist/components/graph/GraphCanvas.svelte.d.ts +22 -0
- package/dist/components/graph/JatuiNode.svelte +128 -0
- package/dist/components/graph/JatuiNode.svelte.d.ts +14 -0
- package/dist/components/graph/theme.d.ts +27 -0
- package/dist/components/graph/theme.js +136 -0
- package/dist/components/markup.js +62 -59
- package/dist/components/messaging/MessageItem.svelte +92 -0
- package/dist/components/messaging/MessageItem.svelte.d.ts +5 -0
- package/dist/components/messaging/TypingIndicator.svelte +63 -0
- package/dist/components/messaging/TypingIndicator.svelte.d.ts +7 -0
- package/dist/components/pipeline/PipelineColumn.svelte +44 -3
- package/dist/components/progress/ProgressBar.svelte +189 -0
- package/dist/components/progress/ProgressBar.svelte.d.ts +17 -0
- package/dist/components/progress/ProgressRing.svelte +138 -0
- package/dist/components/progress/ProgressRing.svelte.d.ts +12 -0
- package/dist/components/progress/Steps.svelte +195 -0
- package/dist/components/progress/Steps.svelte.d.ts +14 -0
- package/dist/components/session-nav/transcriptModel.js +66 -49
- package/dist/components/shell/NavigationMenu.svelte +422 -0
- package/dist/components/shell/NavigationMenu.svelte.d.ts +43 -0
- package/dist/components/shell/Sidebar.svelte +276 -0
- package/dist/components/shell/Sidebar.svelte.d.ts +32 -0
- package/dist/components/shell/SidebarGroup.svelte +180 -0
- package/dist/components/shell/SidebarGroup.svelte.d.ts +19 -0
- package/dist/components/shell/SidebarItem.svelte +170 -0
- package/dist/components/shell/SidebarItem.svelte.d.ts +24 -0
- package/dist/components/shell/shell.d.ts +30 -0
- package/dist/components/shell/shell.js +65 -0
- package/dist/components/table/DataTable.svelte +364 -0
- package/dist/components/table/DataTable.svelte.d.ts +4 -0
- package/dist/components/table/flexRender.d.ts +31 -0
- package/dist/components/table/flexRender.js +32 -0
- package/dist/components/table/types.d.ts +28 -0
- package/dist/components/table/types.js +1 -0
- package/dist/components/toast/ToastHost.svelte +21 -0
- package/dist/components/toast/ToastHost.svelte.d.ts +3 -0
- package/dist/components/toast/ToastItem.svelte +287 -0
- package/dist/components/toast/ToastItem.svelte.d.ts +9 -0
- package/dist/data/emojis.js +4 -4
- package/dist/dnd/autoScroll.js +12 -10
- package/dist/dnd/createDndBoard.svelte.js +110 -76
- package/dist/dnd/createDndBoard.test.js +161 -85
- package/dist/dnd/draggable.js +23 -15
- package/dist/dnd/dropTarget.js +29 -15
- package/dist/dnd/index.d.ts +0 -1
- package/dist/dnd/index.js +0 -1
- package/dist/dnd/types.d.ts +0 -9
- package/dist/dnd/types.js +14 -2
- package/dist/index.d.ts +60 -6
- package/dist/index.js +73 -6
- package/dist/showcase/AnimatedBeam.svelte +183 -0
- package/dist/showcase/AnimatedBeam.svelte.d.ts +26 -0
- package/dist/showcase/AuroraDrift.svelte +74 -0
- package/dist/showcase/AuroraDrift.svelte.d.ts +9 -0
- package/dist/showcase/BentoCard.svelte +50 -0
- package/dist/showcase/BentoCard.svelte.d.ts +14 -0
- package/dist/showcase/BentoGrid.svelte +43 -0
- package/dist/showcase/BentoGrid.svelte.d.ts +12 -0
- package/dist/showcase/BorderBeam.svelte +79 -0
- package/dist/showcase/BorderBeam.svelte.d.ts +12 -0
- package/dist/showcase/ClipWipe.svelte +82 -0
- package/dist/showcase/ClipWipe.svelte.d.ts +12 -0
- package/dist/showcase/Confetti.svelte +71 -0
- package/dist/showcase/Confetti.svelte.d.ts +10 -0
- package/dist/showcase/CursorSpotlight.svelte +85 -0
- package/dist/showcase/CursorSpotlight.svelte.d.ts +9 -0
- package/dist/showcase/DeviceMock.svelte +186 -0
- package/dist/showcase/DeviceMock.svelte.d.ts +11 -0
- package/dist/showcase/Dock.svelte +123 -0
- package/dist/showcase/Dock.svelte.d.ts +17 -0
- package/dist/showcase/FlipCard.svelte +103 -0
- package/dist/showcase/FlipCard.svelte.d.ts +10 -0
- package/dist/showcase/FocusReveal.svelte +83 -0
- package/dist/showcase/FocusReveal.svelte.d.ts +11 -0
- package/dist/showcase/Globe.svelte +202 -0
- package/dist/showcase/Globe.svelte.d.ts +16 -0
- package/dist/showcase/KenBurns.svelte +105 -0
- package/dist/showcase/KenBurns.svelte.d.ts +13 -0
- package/dist/showcase/Marquee.svelte +89 -0
- package/dist/showcase/Marquee.svelte.d.ts +9 -0
- package/dist/showcase/ParallaxTilt.svelte +98 -0
- package/dist/showcase/ParallaxTilt.svelte.d.ts +10 -0
- package/dist/showcase/ScrambleText.svelte +98 -0
- package/dist/showcase/ScrambleText.svelte.d.ts +11 -0
- package/dist/showcase/ShineSweep.svelte +81 -0
- package/dist/showcase/ShineSweep.svelte.d.ts +9 -0
- package/dist/showcase/Terminal.svelte +230 -0
- package/dist/showcase/Terminal.svelte.d.ts +18 -0
- package/dist/showcase/TextSplitReveal.svelte +146 -0
- package/dist/showcase/TextSplitReveal.svelte.d.ts +13 -0
- package/dist/showcase/TextWave.svelte +55 -0
- package/dist/showcase/TextWave.svelte.d.ts +8 -0
- package/dist/showcase/TypewriterText.svelte +130 -0
- package/dist/showcase/TypewriterText.svelte.d.ts +9 -0
- package/dist/showcase/backdrop/GrainGradient.svelte +126 -0
- package/dist/showcase/backdrop/GrainGradient.svelte.d.ts +23 -0
- package/dist/showcase/backdrop/MeshGradient.svelte +104 -0
- package/dist/showcase/backdrop/MeshGradient.svelte.d.ts +20 -0
- package/dist/showcase/backdrop/Metaballs.svelte +116 -0
- package/dist/showcase/backdrop/Metaballs.svelte.d.ts +18 -0
- package/dist/showcase/backdrop/NeuroNoise.svelte +115 -0
- package/dist/showcase/backdrop/NeuroNoise.svelte.d.ts +20 -0
- package/dist/showcase/backdrop/ShowcaseBackdrop.svelte +48 -0
- package/dist/showcase/backdrop/ShowcaseBackdrop.svelte.d.ts +12 -0
- package/dist/showcase/generative/GenerativeBackdrop.svelte +234 -0
- package/dist/showcase/generative/GenerativeBackdrop.svelte.d.ts +9 -0
- package/dist/showcase/generative/intent.d.ts +7 -0
- package/dist/showcase/generative/intent.js +67 -0
- package/dist/showcase/generative/manifest.d.ts +9 -0
- package/dist/showcase/generative/manifest.js +97 -0
- package/dist/showcase/generative/types.d.ts +48 -0
- package/dist/showcase/generative/types.js +8 -0
- package/dist/showcase/index.d.ts +36 -0
- package/dist/showcase/index.js +46 -0
- package/dist/showcase/scroll/SmoothScroll.svelte +49 -0
- package/dist/showcase/scroll/SmoothScroll.svelte.d.ts +12 -0
- package/dist/showcase/scroll/scrollProgress.d.ts +24 -0
- package/dist/showcase/scroll/scrollProgress.js +65 -0
- package/dist/showcase/scroll/scrollReveal.d.ts +30 -0
- package/dist/showcase/scroll/scrollReveal.js +70 -0
- package/dist/stores/commandPalette.svelte.d.ts +24 -0
- package/dist/stores/commandPalette.svelte.js +82 -0
- package/dist/stores/confirmDialog.svelte.js +5 -5
- package/dist/stores/inputDialog.svelte.js +5 -5
- package/dist/stores/toast.svelte.d.ts +28 -0
- package/dist/stores/toast.svelte.js +33 -0
- package/dist/styles/motion.css +46 -0
- package/dist/styles/tokens.css +31 -0
- package/dist/transitions/glide.d.ts +31 -0
- package/dist/transitions/glide.js +58 -0
- package/dist/types/booking.js +4 -4
- package/dist/types/comments.js +2 -2
- package/dist/types/messaging.js +3 -3
- package/dist/types/pipeline.js +2 -2
- package/dist/utils/currency.js +4 -2
- package/dist/utils/daisyuiColors.js +27 -25
- package/dist/utils/dateFormatters.d.ts +20 -0
- package/dist/utils/dateFormatters.js +65 -44
- package/dist/utils/googleMapsLoader.js +100 -46
- package/dist/utils/mentionParser.js +30 -30
- package/dist/utils/phone.js +27 -21
- package/dist/utils/phoneFormat.js +9 -9
- package/dist/utils/sound.d.ts +6 -0
- package/dist/utils/sound.js +113 -0
- package/dist/utils/splitText.d.ts +25 -0
- package/dist/utils/splitText.js +94 -0
- package/dist/utils/taskUtils.js +1 -1
- package/dist/utils/themeWipe.d.ts +21 -0
- package/dist/utils/themeWipe.js +94 -0
- package/dist/utils/viewTransition.d.ts +23 -0
- package/dist/utils/viewTransition.js +75 -0
- package/package.json +56 -4
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
import MessageAttachment from './MessageAttachment.svelte'
|
|
22
22
|
import EmojiSelector from './EmojiSelector.svelte'
|
|
23
23
|
|
|
24
|
+
import type { Snippet } from 'svelte'
|
|
25
|
+
|
|
24
26
|
interface Props {
|
|
25
27
|
message: Message
|
|
26
28
|
isOwn?: boolean
|
|
@@ -43,6 +45,10 @@
|
|
|
43
45
|
onopenthread?: (data: { messageId: string }) => void
|
|
44
46
|
onquotereply?: (data: { messageId: string }) => void
|
|
45
47
|
onreactionchange?: () => void
|
|
48
|
+
/** Enable swipe-to-reveal on coarse pointers (touch). Defaults to false. */
|
|
49
|
+
swipeActions?: boolean
|
|
50
|
+
/** Snippet rendered in the revealed action zone (right side, behind the item). */
|
|
51
|
+
swipeContent?: Snippet
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
let {
|
|
@@ -64,6 +70,8 @@
|
|
|
64
70
|
onopenthread,
|
|
65
71
|
onquotereply,
|
|
66
72
|
onreactionchange,
|
|
73
|
+
swipeActions = false,
|
|
74
|
+
swipeContent,
|
|
67
75
|
}: Props = $props()
|
|
68
76
|
|
|
69
77
|
let isEditing = $state(false)
|
|
@@ -72,6 +80,66 @@
|
|
|
72
80
|
let showEmojiSelector = $state(false)
|
|
73
81
|
let emojiSelectorPosition = $state({ x: 0, y: 0 })
|
|
74
82
|
|
|
83
|
+
// Swipe-to-reveal state (coarse pointer / touch only)
|
|
84
|
+
const SWIPE_THRESHOLD = 96
|
|
85
|
+
const RUBBER_BAND_LIMIT = 130
|
|
86
|
+
let swipeOpen = $state(false)
|
|
87
|
+
let swipeDragging = $state(false)
|
|
88
|
+
let swipeOffset = $state(0)
|
|
89
|
+
let swipeStartX = 0
|
|
90
|
+
let swipeStartOpen = false
|
|
91
|
+
|
|
92
|
+
function isCoarsePointer(e: PointerEvent) {
|
|
93
|
+
return e.pointerType === 'touch' || e.pointerType === 'pen'
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function swipeRubberBand(raw: number): number {
|
|
97
|
+
if (raw >= 0) return 0
|
|
98
|
+
const abs = Math.abs(raw)
|
|
99
|
+
if (abs <= SWIPE_THRESHOLD) return raw
|
|
100
|
+
const over = abs - SWIPE_THRESHOLD
|
|
101
|
+
const damped = SWIPE_THRESHOLD + over * (1 - over / (RUBBER_BAND_LIMIT * 2))
|
|
102
|
+
return -Math.min(damped, RUBBER_BAND_LIMIT)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function onSwipePointerDown(e: PointerEvent) {
|
|
106
|
+
if (!swipeActions || !swipeContent || !isCoarsePointer(e)) return
|
|
107
|
+
swipeStartX = e.clientX
|
|
108
|
+
swipeStartOpen = swipeOpen
|
|
109
|
+
swipeDragging = true
|
|
110
|
+
;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function onSwipePointerMove(e: PointerEvent) {
|
|
114
|
+
if (!swipeDragging) return
|
|
115
|
+
const baseOffset = swipeStartOpen ? -SWIPE_THRESHOLD : 0
|
|
116
|
+
const raw = baseOffset + (e.clientX - swipeStartX)
|
|
117
|
+
swipeOffset = swipeRubberBand(raw)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function onSwipePointerUp(e: PointerEvent) {
|
|
121
|
+
if (!swipeDragging) return
|
|
122
|
+
swipeDragging = false
|
|
123
|
+
const baseOffset = swipeStartOpen ? -SWIPE_THRESHOLD : 0
|
|
124
|
+
const delta = baseOffset + (e.clientX - swipeStartX)
|
|
125
|
+
// Velocity hint: fast leftward swipe counts even without crossing threshold
|
|
126
|
+
const velocity = e.clientX - swipeStartX
|
|
127
|
+
if (delta < -SWIPE_THRESHOLD / 2 || velocity < -30) {
|
|
128
|
+
swipeOpen = true
|
|
129
|
+
} else {
|
|
130
|
+
swipeOpen = false
|
|
131
|
+
}
|
|
132
|
+
swipeOffset = 0
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const swipeTranslateX = $derived(
|
|
136
|
+
swipeDragging
|
|
137
|
+
? swipeOffset
|
|
138
|
+
: swipeOpen
|
|
139
|
+
? -SWIPE_THRESHOLD
|
|
140
|
+
: 0
|
|
141
|
+
)
|
|
142
|
+
|
|
75
143
|
function formatTime(dateString: string): string {
|
|
76
144
|
const date = new Date(dateString)
|
|
77
145
|
const now = new Date()
|
|
@@ -182,6 +250,27 @@
|
|
|
182
250
|
}
|
|
183
251
|
</script>
|
|
184
252
|
|
|
253
|
+
<!-- Swipe wrapper: always rendered; overflow-hidden clips the action zone -->
|
|
254
|
+
<div
|
|
255
|
+
class="swipe-wrapper relative overflow-hidden"
|
|
256
|
+
class:rounded={swipeActions && !!swipeContent}
|
|
257
|
+
>
|
|
258
|
+
<!-- Action zone — only present when swipe is enabled -->
|
|
259
|
+
{#if swipeActions && swipeContent}
|
|
260
|
+
<div class="swipe-actions absolute inset-y-0 right-0 flex items-center pr-2 gap-1" aria-hidden={!swipeOpen}>
|
|
261
|
+
{@render swipeContent()}
|
|
262
|
+
</div>
|
|
263
|
+
{/if}
|
|
264
|
+
|
|
265
|
+
<!-- Draggable layer (swipe) or passthrough div -->
|
|
266
|
+
<div
|
|
267
|
+
style={swipeActions && swipeContent ? `transform: translateX(${swipeTranslateX}px); transition: ${swipeDragging ? 'none' : 'transform var(--motion-move) var(--motion-spring)'};` : undefined}
|
|
268
|
+
onpointerdown={swipeActions && swipeContent ? onSwipePointerDown : undefined}
|
|
269
|
+
onpointermove={swipeActions && swipeContent ? onSwipePointerMove : undefined}
|
|
270
|
+
onpointerup={swipeActions && swipeContent ? onSwipePointerUp : undefined}
|
|
271
|
+
onpointercancel={swipeActions && swipeContent ? onSwipePointerUp : undefined}
|
|
272
|
+
>
|
|
273
|
+
|
|
185
274
|
<div
|
|
186
275
|
class="group flex gap-3 px-2 py-1 rounded relative transition-colors w-full text-left cursor-pointer {message.isPinned ? 'bg-warning/5 hover:bg-warning/10' : 'hover:bg-base-200'}"
|
|
187
276
|
class:pt-2={!isConsecutive}
|
|
@@ -328,6 +417,9 @@
|
|
|
328
417
|
</div>
|
|
329
418
|
</div>
|
|
330
419
|
|
|
420
|
+
</div><!-- /draggable layer -->
|
|
421
|
+
</div><!-- /swipe-wrapper -->
|
|
422
|
+
|
|
331
423
|
<!-- Emoji Selector -->
|
|
332
424
|
{#if showEmojiSelector && currentUserId}
|
|
333
425
|
<EmojiSelector
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Message, ReactionSummary, StandardEmoji, CustomEmoji, ReactionCallbacks, AttachmentCallbacks } from '../../types/messaging';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
interface Props {
|
|
3
4
|
message: Message;
|
|
4
5
|
isOwn?: boolean;
|
|
@@ -35,6 +36,10 @@ interface Props {
|
|
|
35
36
|
messageId: string;
|
|
36
37
|
}) => void;
|
|
37
38
|
onreactionchange?: () => void;
|
|
39
|
+
/** Enable swipe-to-reveal on coarse pointers (touch). Defaults to false. */
|
|
40
|
+
swipeActions?: boolean;
|
|
41
|
+
/** Snippet rendered in the revealed action zone (right side, behind the item). */
|
|
42
|
+
swipeContent?: Snippet;
|
|
38
43
|
}
|
|
39
44
|
declare const MessageItem: import("svelte").Component<Props, {}, "">;
|
|
40
45
|
type MessageItem = ReturnType<typeof MessageItem>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
TypingIndicator — three-dot phase-offset bounce (kinetics: Typing Indicator).
|
|
3
|
+
Infinite loop is sanctioned for functional status indicators.
|
|
4
|
+
Reduced-motion: static dots + label remain visible.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
interface Props {
|
|
9
|
+
users?: string[]
|
|
10
|
+
class?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { users = [], class: cls = '' }: Props = $props()
|
|
14
|
+
|
|
15
|
+
const label = $derived(
|
|
16
|
+
users.length === 0
|
|
17
|
+
? 'typing…'
|
|
18
|
+
: users.length === 1
|
|
19
|
+
? `${users[0]} is typing…`
|
|
20
|
+
: users.length === 2
|
|
21
|
+
? `${users[0]} and ${users[1]} are typing…`
|
|
22
|
+
: `${users[0]} and ${users.length - 1} others are typing…`
|
|
23
|
+
)
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div class="typing-indicator inline-flex items-center gap-1.5 {cls}" aria-label={label} role="status">
|
|
27
|
+
<span class="dot" aria-hidden="true"></span>
|
|
28
|
+
<span class="dot" style="animation-delay: 0.16s" aria-hidden="true"></span>
|
|
29
|
+
<span class="dot" style="animation-delay: 0.32s" aria-hidden="true"></span>
|
|
30
|
+
{#if users.length > 0}
|
|
31
|
+
<span class="text-xs text-base-content/60 ml-0.5">{label}</span>
|
|
32
|
+
{/if}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<style>
|
|
36
|
+
.dot {
|
|
37
|
+
width: 4px;
|
|
38
|
+
height: 4px;
|
|
39
|
+
border-radius: 50%;
|
|
40
|
+
background-color: oklch(from var(--color-base-content, currentColor) l c h / 0.6);
|
|
41
|
+
animation: typing-bounce 1.2s ease-in-out infinite;
|
|
42
|
+
display: inline-block;
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@keyframes typing-bounce {
|
|
47
|
+
0%, 60%, 100% {
|
|
48
|
+
transform: translateY(0);
|
|
49
|
+
opacity: 0.4;
|
|
50
|
+
}
|
|
51
|
+
30% {
|
|
52
|
+
transform: translateY(-6px);
|
|
53
|
+
opacity: 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (prefers-reduced-motion: reduce) {
|
|
58
|
+
.dot {
|
|
59
|
+
animation: none;
|
|
60
|
+
opacity: 0.6;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
@@ -59,7 +59,25 @@
|
|
|
59
59
|
stage.droppableIn !== false
|
|
60
60
|
);
|
|
61
61
|
|
|
62
|
-
const flipDuration = $derived(drag?.flipDurationMs ??
|
|
62
|
+
const flipDuration = $derived(drag?.flipDurationMs ?? 300); // var(--motion-move) = 300ms
|
|
63
|
+
|
|
64
|
+
// Track which item was just dropped to play a spring settle pulse
|
|
65
|
+
let settledItemId = $state<string | number | null>(null);
|
|
66
|
+
let settleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
67
|
+
|
|
68
|
+
function handleFinalize(e: CustomEvent) {
|
|
69
|
+
onDndFinalize?.(e);
|
|
70
|
+
// Find the dropped item (first item if array changed, or use event detail)
|
|
71
|
+
const droppedItem = e.detail?.items?.[0];
|
|
72
|
+
if (droppedItem?.id !== undefined) {
|
|
73
|
+
if (settleTimer) clearTimeout(settleTimer);
|
|
74
|
+
settledItemId = droppedItem.id;
|
|
75
|
+
// Spring settle lasts ~400ms (single overshoot)
|
|
76
|
+
settleTimer = setTimeout(() => {
|
|
77
|
+
settledItemId = null;
|
|
78
|
+
}, 400);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
63
81
|
</script>
|
|
64
82
|
|
|
65
83
|
<div
|
|
@@ -119,11 +137,14 @@
|
|
|
119
137
|
dropTargetClasses: drag?.dropTargetClass ? [drag.dropTargetClass] : []
|
|
120
138
|
}}
|
|
121
139
|
onconsider={(e) => onDndConsider?.(e)}
|
|
122
|
-
onfinalize={
|
|
140
|
+
onfinalize={handleFinalize}
|
|
123
141
|
class="flex flex-col gap-2 min-h-8 flex-1"
|
|
124
142
|
>
|
|
125
143
|
{#each items as item (item.id)}
|
|
126
|
-
<div
|
|
144
|
+
<div
|
|
145
|
+
animate:flip={{ duration: flipDuration, easing: (t) => t }}
|
|
146
|
+
class={settledItemId === item.id ? 'pipeline-card-settle' : ''}
|
|
147
|
+
>
|
|
127
148
|
{#if card}
|
|
128
149
|
{@render card({ item, stage })}
|
|
129
150
|
{:else}
|
|
@@ -161,3 +182,23 @@
|
|
|
161
182
|
</div>
|
|
162
183
|
{/if}
|
|
163
184
|
</div>
|
|
185
|
+
|
|
186
|
+
<style>
|
|
187
|
+
/* Spring settle pulse — single overshoot scale on drop (gesture-release, sanctioned). */
|
|
188
|
+
.pipeline-card-settle > :global(*) {
|
|
189
|
+
animation: pipeline-settle 400ms var(--motion-spring) forwards;
|
|
190
|
+
transform-origin: center;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@keyframes pipeline-settle {
|
|
194
|
+
0% { transform: scale(1); }
|
|
195
|
+
40% { transform: scale(1.02); }
|
|
196
|
+
100% { transform: scale(1); }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@media (prefers-reduced-motion: reduce) {
|
|
200
|
+
.pipeline-card-settle > :global(*) {
|
|
201
|
+
animation: none;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
</style>
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Segment {
|
|
3
|
+
value: number;
|
|
4
|
+
color?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
value?: number;
|
|
9
|
+
indeterminate?: boolean;
|
|
10
|
+
segments?: Segment[];
|
|
11
|
+
color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
|
|
12
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
13
|
+
label?: string;
|
|
14
|
+
showLabel?: boolean;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
value = 0,
|
|
20
|
+
indeterminate = false,
|
|
21
|
+
segments,
|
|
22
|
+
color = 'primary',
|
|
23
|
+
size = 'md',
|
|
24
|
+
label,
|
|
25
|
+
showLabel = false,
|
|
26
|
+
class: className = ''
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const clampedValue = $derived(Math.min(100, Math.max(0, value)));
|
|
30
|
+
|
|
31
|
+
const heightMap: Record<NonNullable<Props['size']>, string> = {
|
|
32
|
+
xs: '4px',
|
|
33
|
+
sm: '6px',
|
|
34
|
+
md: '8px',
|
|
35
|
+
lg: '12px'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const colorMap: Record<NonNullable<Props['color']>, string> = {
|
|
39
|
+
primary: 'oklch(var(--p))',
|
|
40
|
+
secondary: 'oklch(var(--s))',
|
|
41
|
+
accent: 'oklch(var(--a))',
|
|
42
|
+
success: 'oklch(var(--su))',
|
|
43
|
+
warning: 'oklch(var(--wa))',
|
|
44
|
+
error: 'oklch(var(--er))',
|
|
45
|
+
info: 'oklch(var(--in))'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const trackHeight = $derived(heightMap[size]);
|
|
49
|
+
const fillColor = $derived(colorMap[color]);
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<div class="progress-wrap {className}" aria-label={label ?? 'Progress'}>
|
|
53
|
+
{#if label && showLabel}
|
|
54
|
+
<div class="progress-label">
|
|
55
|
+
<span>{label}</span>
|
|
56
|
+
{#if !indeterminate}
|
|
57
|
+
<span class="progress-pct">{clampedValue}%</span>
|
|
58
|
+
{/if}
|
|
59
|
+
</div>
|
|
60
|
+
{/if}
|
|
61
|
+
|
|
62
|
+
<div
|
|
63
|
+
class="progress-track"
|
|
64
|
+
style="height:{trackHeight}"
|
|
65
|
+
role="progressbar"
|
|
66
|
+
aria-valuenow={indeterminate ? undefined : clampedValue}
|
|
67
|
+
aria-valuemin={0}
|
|
68
|
+
aria-valuemax={100}
|
|
69
|
+
aria-label={label ?? 'Progress'}
|
|
70
|
+
>
|
|
71
|
+
{#if indeterminate}
|
|
72
|
+
<div class="progress-indeterminate" style="background:{fillColor}"></div>
|
|
73
|
+
{:else if segments && segments.length > 0}
|
|
74
|
+
<div class="progress-segments">
|
|
75
|
+
{#each segments as seg, i}
|
|
76
|
+
<div
|
|
77
|
+
class="progress-segment"
|
|
78
|
+
class:progress-segment--filled={i < value}
|
|
79
|
+
style="--seg-color:{seg.color ? seg.color : fillColor}"
|
|
80
|
+
></div>
|
|
81
|
+
{/each}
|
|
82
|
+
</div>
|
|
83
|
+
{:else}
|
|
84
|
+
<div
|
|
85
|
+
class="progress-fill"
|
|
86
|
+
style="width:{clampedValue}%;background:{fillColor}"
|
|
87
|
+
></div>
|
|
88
|
+
{/if}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<style>
|
|
93
|
+
.progress-wrap {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
gap: 0.375rem;
|
|
97
|
+
width: 100%;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.progress-label {
|
|
101
|
+
display: flex;
|
|
102
|
+
justify-content: space-between;
|
|
103
|
+
align-items: center;
|
|
104
|
+
font-size: 0.8125rem;
|
|
105
|
+
color: oklch(var(--bc) / 0.7);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.progress-pct {
|
|
109
|
+
font-variant-numeric: tabular-nums;
|
|
110
|
+
font-size: 0.75rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.progress-track {
|
|
114
|
+
position: relative;
|
|
115
|
+
width: 100%;
|
|
116
|
+
border-radius: 9999px;
|
|
117
|
+
background: oklch(var(--b3) / 1);
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Determinate fill — gentle glide spring on width change */
|
|
122
|
+
.progress-fill {
|
|
123
|
+
height: 100%;
|
|
124
|
+
border-radius: 9999px;
|
|
125
|
+
transition: width 0.9s var(--motion-glide);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Indeterminate sweep */
|
|
129
|
+
.progress-indeterminate {
|
|
130
|
+
position: absolute;
|
|
131
|
+
inset: 0;
|
|
132
|
+
width: 40%;
|
|
133
|
+
border-radius: 9999px;
|
|
134
|
+
animation: progress-slide 1.4s var(--motion-glide) infinite;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@keyframes progress-slide {
|
|
138
|
+
0% {
|
|
139
|
+
transform: translateX(-100%);
|
|
140
|
+
}
|
|
141
|
+
100% {
|
|
142
|
+
transform: translateX(320%);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Segment loader */
|
|
147
|
+
.progress-segments {
|
|
148
|
+
display: flex;
|
|
149
|
+
height: 100%;
|
|
150
|
+
gap: 3px;
|
|
151
|
+
padding: 0 2px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.progress-segment {
|
|
155
|
+
flex: 1;
|
|
156
|
+
height: 100%;
|
|
157
|
+
border-radius: 9999px;
|
|
158
|
+
background: oklch(var(--b1) / 1);
|
|
159
|
+
overflow: hidden;
|
|
160
|
+
position: relative;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.progress-segment::after {
|
|
164
|
+
content: '';
|
|
165
|
+
display: block;
|
|
166
|
+
height: 100%;
|
|
167
|
+
background: var(--seg-color);
|
|
168
|
+
transform: scaleX(0);
|
|
169
|
+
transform-origin: left;
|
|
170
|
+
transition: transform 0.35s var(--motion-glide);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.progress-segment--filled::after {
|
|
174
|
+
transform: scaleX(1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@media (prefers-reduced-motion: reduce) {
|
|
178
|
+
.progress-fill {
|
|
179
|
+
transition: none;
|
|
180
|
+
}
|
|
181
|
+
.progress-indeterminate {
|
|
182
|
+
animation: none;
|
|
183
|
+
transform: translateX(140%);
|
|
184
|
+
}
|
|
185
|
+
.progress-segment::after {
|
|
186
|
+
transition: none;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface Segment {
|
|
2
|
+
value: number;
|
|
3
|
+
color?: string;
|
|
4
|
+
}
|
|
5
|
+
interface Props {
|
|
6
|
+
value?: number;
|
|
7
|
+
indeterminate?: boolean;
|
|
8
|
+
segments?: Segment[];
|
|
9
|
+
color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
|
|
10
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
11
|
+
label?: string;
|
|
12
|
+
showLabel?: boolean;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const ProgressBar: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type ProgressBar = ReturnType<typeof ProgressBar>;
|
|
17
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
value?: number;
|
|
4
|
+
size?: number;
|
|
5
|
+
strokeWidth?: number;
|
|
6
|
+
color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
|
|
7
|
+
label?: string | null;
|
|
8
|
+
showValue?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
value = 0,
|
|
14
|
+
size = 80,
|
|
15
|
+
strokeWidth = 6,
|
|
16
|
+
color = 'primary',
|
|
17
|
+
label = null,
|
|
18
|
+
showValue = true,
|
|
19
|
+
class: className = ''
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
const clampedValue = $derived(Math.min(100, Math.max(0, value)));
|
|
23
|
+
|
|
24
|
+
const r = $derived((size - strokeWidth) / 2);
|
|
25
|
+
const circumference = $derived(2 * Math.PI * r);
|
|
26
|
+
const offset = $derived(circumference * (1 - clampedValue / 100));
|
|
27
|
+
const cx = $derived(size / 2);
|
|
28
|
+
const cy = $derived(size / 2);
|
|
29
|
+
|
|
30
|
+
const colorMap: Record<NonNullable<Props['color']>, string> = {
|
|
31
|
+
primary: 'oklch(var(--p))',
|
|
32
|
+
secondary: 'oklch(var(--s))',
|
|
33
|
+
accent: 'oklch(var(--a))',
|
|
34
|
+
success: 'oklch(var(--su))',
|
|
35
|
+
warning: 'oklch(var(--wa))',
|
|
36
|
+
error: 'oklch(var(--er))',
|
|
37
|
+
info: 'oklch(var(--in))'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const strokeColor = $derived(colorMap[color]);
|
|
41
|
+
const fontSize = $derived(Math.round(size * 0.2));
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<div
|
|
45
|
+
class="progress-ring {className}"
|
|
46
|
+
style="width:{size}px;height:{size}px"
|
|
47
|
+
role="progressbar"
|
|
48
|
+
aria-valuenow={clampedValue}
|
|
49
|
+
aria-valuemin={0}
|
|
50
|
+
aria-valuemax={100}
|
|
51
|
+
aria-label={label ?? 'Progress'}
|
|
52
|
+
>
|
|
53
|
+
<!-- Rotate -90deg so fill starts from 12 o'clock -->
|
|
54
|
+
<svg viewBox="0 0 {size} {size}" style="transform:rotate(-90deg)">
|
|
55
|
+
<!-- Track -->
|
|
56
|
+
<circle
|
|
57
|
+
{cx}
|
|
58
|
+
{cy}
|
|
59
|
+
r={r}
|
|
60
|
+
fill="none"
|
|
61
|
+
stroke="oklch(var(--b3) / 1)"
|
|
62
|
+
stroke-width={strokeWidth}
|
|
63
|
+
/>
|
|
64
|
+
<!-- Progress arc -->
|
|
65
|
+
<circle
|
|
66
|
+
{cx}
|
|
67
|
+
{cy}
|
|
68
|
+
r={r}
|
|
69
|
+
fill="none"
|
|
70
|
+
stroke={strokeColor}
|
|
71
|
+
stroke-width={strokeWidth}
|
|
72
|
+
stroke-linecap="round"
|
|
73
|
+
stroke-dasharray={circumference}
|
|
74
|
+
stroke-dashoffset={offset}
|
|
75
|
+
class="ring-prog"
|
|
76
|
+
/>
|
|
77
|
+
</svg>
|
|
78
|
+
|
|
79
|
+
{#if showValue || label}
|
|
80
|
+
<div class="ring-label" style="font-size:{fontSize}px">
|
|
81
|
+
{#if showValue}
|
|
82
|
+
<span class="ring-value">{clampedValue}%</span>
|
|
83
|
+
{/if}
|
|
84
|
+
{#if label}
|
|
85
|
+
<span class="ring-sublabel">{label}</span>
|
|
86
|
+
{/if}
|
|
87
|
+
</div>
|
|
88
|
+
{/if}
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<style>
|
|
92
|
+
.progress-ring {
|
|
93
|
+
position: relative;
|
|
94
|
+
display: inline-flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
justify-content: center;
|
|
97
|
+
flex-shrink: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.progress-ring svg {
|
|
101
|
+
position: absolute;
|
|
102
|
+
inset: 0;
|
|
103
|
+
width: 100%;
|
|
104
|
+
height: 100%;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ring-prog {
|
|
108
|
+
transition: stroke-dashoffset 0.9s var(--motion-glide);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.ring-label {
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
line-height: 1.1;
|
|
117
|
+
color: oklch(var(--bc) / 1);
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
z-index: 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.ring-value {
|
|
123
|
+
font-variant-numeric: tabular-nums;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.ring-sublabel {
|
|
128
|
+
font-size: 0.625em;
|
|
129
|
+
color: oklch(var(--bc) / 0.6);
|
|
130
|
+
margin-top: 0.125em;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@media (prefers-reduced-motion: reduce) {
|
|
134
|
+
.ring-prog {
|
|
135
|
+
transition: none;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
value?: number;
|
|
3
|
+
size?: number;
|
|
4
|
+
strokeWidth?: number;
|
|
5
|
+
color?: 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'error' | 'info';
|
|
6
|
+
label?: string | null;
|
|
7
|
+
showValue?: boolean;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const ProgressRing: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type ProgressRing = ReturnType<typeof ProgressRing>;
|
|
12
|
+
export default ProgressRing;
|