@joewinke/jatui 0.1.22 → 0.1.24

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.
Files changed (124) hide show
  1. package/CLAUDE.md +244 -0
  2. package/package.json +23 -10
  3. package/src/lib/actions/collapse.ts +0 -134
  4. package/src/lib/actions/railNav.ts +0 -473
  5. package/src/lib/components/AnnotationLayer.svelte +0 -108
  6. package/src/lib/components/AnnotationPanel.svelte +0 -319
  7. package/src/lib/components/AudioWaveform.svelte +0 -698
  8. package/src/lib/components/AvailabilityModal.svelte +0 -177
  9. package/src/lib/components/AvatarUpload.svelte +0 -234
  10. package/src/lib/components/Badge.svelte +0 -38
  11. package/src/lib/components/BookingForm.svelte +0 -278
  12. package/src/lib/components/BurndownChart.svelte +0 -778
  13. package/src/lib/components/Button.svelte +0 -81
  14. package/src/lib/components/CalendarPicker.svelte +0 -284
  15. package/src/lib/components/Card.svelte +0 -67
  16. package/src/lib/components/CharacterCounter.svelte +0 -82
  17. package/src/lib/components/ChipInput.svelte +0 -609
  18. package/src/lib/components/Collapse.svelte +0 -82
  19. package/src/lib/components/ColorSelector.svelte +0 -167
  20. package/src/lib/components/CommentThread.svelte +0 -773
  21. package/src/lib/components/ConfirmDialog.svelte +0 -348
  22. package/src/lib/components/ConfirmModal.svelte +0 -142
  23. package/src/lib/components/ContextMenu.svelte +0 -188
  24. package/src/lib/components/CountdownTimer.svelte +0 -94
  25. package/src/lib/components/DateRangePicker.svelte +0 -194
  26. package/src/lib/components/Drawer.svelte +0 -143
  27. package/src/lib/components/EmojiPicker.svelte +0 -159
  28. package/src/lib/components/EntityPreviewCard.svelte +0 -104
  29. package/src/lib/components/FileDropzone.svelte +0 -493
  30. package/src/lib/components/FilePicker.svelte +0 -488
  31. package/src/lib/components/FileThumbnail.svelte +0 -80
  32. package/src/lib/components/FilterDropdown.svelte +0 -203
  33. package/src/lib/components/FloatingActionBar.svelte +0 -354
  34. package/src/lib/components/GPSTracker.svelte +0 -202
  35. package/src/lib/components/HunkDiffView.svelte +0 -348
  36. package/src/lib/components/ImageLightbox.svelte +0 -274
  37. package/src/lib/components/ImageUpload.svelte +0 -147
  38. package/src/lib/components/InlineEdit.svelte +0 -291
  39. package/src/lib/components/InputDialog.svelte +0 -327
  40. package/src/lib/components/KeyboardShortcutsOverlay.svelte +0 -296
  41. package/src/lib/components/LazyImage.svelte +0 -123
  42. package/src/lib/components/LinkShortener.svelte +0 -274
  43. package/src/lib/components/LoadingSpinner.svelte +0 -106
  44. package/src/lib/components/LocationMap.svelte +0 -186
  45. package/src/lib/components/MapView.svelte +0 -341
  46. package/src/lib/components/MarkupEditor.svelte +0 -485
  47. package/src/lib/components/MarkupOverlay.svelte +0 -55
  48. package/src/lib/components/MediaWorkbench.svelte +0 -871
  49. package/src/lib/components/MilestoneCard.svelte +0 -98
  50. package/src/lib/components/MilestoneTimeline.svelte +0 -91
  51. package/src/lib/components/Modal.svelte +0 -244
  52. package/src/lib/components/PDFViewer.svelte +0 -105
  53. package/src/lib/components/PdfThumbnail.svelte +0 -63
  54. package/src/lib/components/PhoneInput.svelte +0 -212
  55. package/src/lib/components/ResizableDivider.svelte +0 -307
  56. package/src/lib/components/ResizablePanel.svelte +0 -305
  57. package/src/lib/components/SearchDropdown.svelte +0 -421
  58. package/src/lib/components/SelectInput.svelte +0 -237
  59. package/src/lib/components/SidebarUserFooter.svelte +0 -34
  60. package/src/lib/components/SignaturePad.svelte +0 -175
  61. package/src/lib/components/SmartImageEditor.svelte +0 -720
  62. package/src/lib/components/SortDropdown.svelte +0 -156
  63. package/src/lib/components/Sparkline.svelte +0 -116
  64. package/src/lib/components/SpeechForm.svelte +0 -114
  65. package/src/lib/components/StatusBadge.svelte +0 -157
  66. package/src/lib/components/TaskTypeIcon.svelte +0 -54
  67. package/src/lib/components/TextArea.svelte +0 -162
  68. package/src/lib/components/TextInput.svelte +0 -131
  69. package/src/lib/components/ThemeSelector.svelte +0 -206
  70. package/src/lib/components/TimeSlotPicker.svelte +0 -162
  71. package/src/lib/components/UserAvatar.svelte +0 -72
  72. package/src/lib/components/VariablePicker.svelte +0 -170
  73. package/src/lib/components/VoicePlayer.svelte +0 -422
  74. package/src/lib/components/linked-columns/LinkedColumns.svelte +0 -526
  75. package/src/lib/components/markup.ts +0 -287
  76. package/src/lib/components/messaging/Avatar.svelte +0 -81
  77. package/src/lib/components/messaging/ChannelInfoModal.svelte +0 -163
  78. package/src/lib/components/messaging/ChannelList.svelte +0 -107
  79. package/src/lib/components/messaging/ChannelMemberAvatarStack.svelte +0 -69
  80. package/src/lib/components/messaging/ChannelMembersModal.svelte +0 -182
  81. package/src/lib/components/messaging/CreateChannelModal.svelte +0 -190
  82. package/src/lib/components/messaging/DirectMessageList.svelte +0 -145
  83. package/src/lib/components/messaging/EmojiSelector.svelte +0 -261
  84. package/src/lib/components/messaging/MentionAutocomplete.svelte +0 -193
  85. package/src/lib/components/messaging/MessageAttachment.svelte +0 -273
  86. package/src/lib/components/messaging/MessageAttachmentUpload.svelte +0 -243
  87. package/src/lib/components/messaging/MessageInput.svelte +0 -452
  88. package/src/lib/components/messaging/MessageItem.svelte +0 -341
  89. package/src/lib/components/messaging/MessageThread.svelte +0 -306
  90. package/src/lib/components/messaging/NotificationSettingsModal.svelte +0 -234
  91. package/src/lib/components/messaging/QuotedMessageDisplay.svelte +0 -123
  92. package/src/lib/components/messaging/StartDMModal.svelte +0 -100
  93. package/src/lib/components/messaging/ThreadPanel.svelte +0 -153
  94. package/src/lib/components/pipeline/DESIGN.md +0 -361
  95. package/src/lib/components/pipeline/Pipeline.svelte +0 -391
  96. package/src/lib/components/pipeline/PipelineCard.svelte +0 -94
  97. package/src/lib/components/pipeline/PipelineColumn.svelte +0 -163
  98. package/src/lib/components/replay/ChapterTimeline.svelte +0 -326
  99. package/src/lib/components/session-nav/transcriptModel.ts +0 -352
  100. package/src/lib/data/emojis.ts +0 -1208
  101. package/src/lib/index.ts +0 -405
  102. package/src/lib/stores/confirmDialog.svelte.ts +0 -48
  103. package/src/lib/stores/inputDialog.svelte.ts +0 -51
  104. package/src/lib/styles/rail.css +0 -63
  105. package/src/lib/types/annotation.ts +0 -38
  106. package/src/lib/types/booking.ts +0 -143
  107. package/src/lib/types/comments.ts +0 -97
  108. package/src/lib/types/entityPreview.ts +0 -45
  109. package/src/lib/types/filePicker.ts +0 -16
  110. package/src/lib/types/googleMaps.d.ts +0 -51
  111. package/src/lib/types/maps.ts +0 -43
  112. package/src/lib/types/messaging.ts +0 -459
  113. package/src/lib/types/milestone.ts +0 -22
  114. package/src/lib/types/pipeline.ts +0 -150
  115. package/src/lib/types/smartImageEditor.ts +0 -39
  116. package/src/lib/types/templateVars.ts +0 -36
  117. package/src/lib/utils/currency.ts +0 -20
  118. package/src/lib/utils/daisyuiColors.ts +0 -243
  119. package/src/lib/utils/dateFormatters.ts +0 -196
  120. package/src/lib/utils/googleMapsLoader.ts +0 -84
  121. package/src/lib/utils/mentionParser.ts +0 -188
  122. package/src/lib/utils/phone.ts +0 -80
  123. package/src/lib/utils/phoneFormat.ts +0 -74
  124. package/src/lib/utils/taskUtils.ts +0 -75
@@ -1,156 +0,0 @@
1
- <script lang="ts">
2
- /**
3
- * SortDropdown Component - Reusable sort dropdown with optional filter input
4
- *
5
- * A standardized dropdown for sorting lists with:
6
- * - Sort button showing current option icon, label, and direction indicator
7
- * - Dropdown menu with configurable sort options
8
- * - Optional filter input for search/filtering
9
- */
10
-
11
- type SortDirection = 'asc' | 'desc';
12
-
13
- export interface SortOption {
14
- value: string;
15
- label: string;
16
- icon: string;
17
- defaultDir?: SortDirection;
18
- }
19
-
20
- interface Props {
21
- /** Sort options array (required) */
22
- options: SortOption[];
23
- /** Current sort value */
24
- sortBy: string;
25
- /** Current sort direction */
26
- sortDir: SortDirection;
27
- /** Current filter input value */
28
- filterValue?: string;
29
- /** Whether to show the filter input */
30
- showFilter?: boolean;
31
- /** Placeholder text for filter input */
32
- filterPlaceholder?: string;
33
- /** Button size variant */
34
- size?: 'xs' | 'sm' | 'md';
35
- /** Show label on small screens */
36
- showLabelOnMobile?: boolean;
37
- /** Callback when sort changes */
38
- onSortChange?: (value: string, dir: SortDirection) => void;
39
- /** Callback when filter changes */
40
- onFilterChange?: (value: string) => void;
41
- }
42
-
43
- let {
44
- options,
45
- sortBy,
46
- sortDir,
47
- filterValue = '',
48
- showFilter = false,
49
- filterPlaceholder = 'Filter...',
50
- size = 'xs',
51
- showLabelOnMobile = false,
52
- onSortChange,
53
- onFilterChange
54
- }: Props = $props();
55
-
56
- // Find current sort option
57
- const currentOption = $derived(options.find(o => o.value === sortBy));
58
- const currentIcon = $derived(currentOption?.icon || '');
59
- const currentLabel = $derived(currentOption?.label || 'Sort');
60
-
61
- // Handle sort option click
62
- function handleSortClick(value: string) {
63
- if (sortBy === value) {
64
- onSortChange?.(value, sortDir === 'asc' ? 'desc' : 'asc');
65
- } else {
66
- const opt = options.find(o => o.value === value);
67
- onSortChange?.(value, opt?.defaultDir ?? 'asc');
68
- }
69
- }
70
-
71
- // Handle filter input change
72
- function handleFilterInput(e: Event) {
73
- const target = e.currentTarget as HTMLInputElement;
74
- onFilterChange?.(target.value);
75
- }
76
-
77
- // Size classes
78
- const buttonSizeClass = $derived({
79
- xs: 'btn-xs',
80
- sm: 'btn-sm',
81
- md: ''
82
- }[size]);
83
-
84
- const menuSizeClass = $derived({
85
- xs: 'menu-xs',
86
- sm: 'menu-sm',
87
- md: ''
88
- }[size]);
89
-
90
- const inputSizeClass = $derived({
91
- xs: 'input-xs',
92
- sm: 'input-sm',
93
- md: ''
94
- }[size]);
95
-
96
- const inputWidthClass = $derived({
97
- xs: 'w-20 focus:w-32',
98
- sm: 'w-24 focus:w-36',
99
- md: 'w-32 focus:w-44'
100
- }[size]);
101
-
102
- import { fly } from 'svelte/transition';
103
- import { cubicOut } from 'svelte/easing';
104
- </script>
105
-
106
- <div class="flex items-center gap-1">
107
- <!-- Sort dropdown -->
108
- <!-- svelte-ignore a11y_click_events_have_key_events -->
109
- <!-- svelte-ignore a11y_no_static_element_interactions -->
110
- <div class="dropdown dropdown-end flex-shrink-0" onclick={(e) => e.stopPropagation()}>
111
- <button
112
- tabindex="0"
113
- class="btn {buttonSizeClass} btn-ghost gap-1 text-[0.75rem] opacity-70 hover:opacity-100"
114
- title="Sort"
115
- >
116
- <span>{currentIcon}</span>
117
- <span class="{showLabelOnMobile ? '' : 'hidden sm:inline'}">{currentLabel}</span>
118
- <span class="text-[9px]">{sortDir === 'asc' ? '▲' : '▼'}</span>
119
- </button>
120
- <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
121
- <ul tabindex="0" class="dropdown-content menu {menuSizeClass} bg-base-200 rounded-box z-40 w-36 p-1 border border-base-300">
122
- {#each options as opt (opt.value)}
123
- <li>
124
- <button
125
- class="flex items-center gap-2 {sortBy === opt.value ? 'active' : ''}"
126
- onclick={() => handleSortClick(opt.value)}
127
- >
128
- <span>{opt.icon}</span>
129
- <span class="flex-1">{opt.label}</span>
130
- {#if sortBy === opt.value}
131
- <span class="text-[9px] opacity-70">{sortDir === 'asc' ? '▲' : '▼'}</span>
132
- {/if}
133
- </button>
134
- </li>
135
- {/each}
136
- </ul>
137
- </div>
138
-
139
- <!-- Filter input (optional) -->
140
- {#if showFilter}
141
- <!-- svelte-ignore a11y_click_events_have_key_events -->
142
- <!-- svelte-ignore a11y_no_static_element_interactions -->
143
- <div class="flex-shrink-0" onclick={(e) => e.stopPropagation()}
144
- in:fly={{ x: -8, duration: 180, easing: cubicOut }}
145
- out:fly={{ x: -4, duration: 120, easing: cubicOut }}
146
- >
147
- <input
148
- type="text"
149
- placeholder={filterPlaceholder}
150
- value={filterValue}
151
- oninput={handleFilterInput}
152
- class="input {inputSizeClass} input-bordered {inputWidthClass} transition-all duration-200 bg-base-200/50"
153
- />
154
- </div>
155
- {/if}
156
- </div>
@@ -1,116 +0,0 @@
1
- <script lang="ts">
2
- /**
3
- * Sparkline — Lightweight D3 bar chart for trend visualization.
4
- * Animated entrance with staggered bars, configurable color and opacity gradient.
5
- *
6
- * Requires `d3` as a peer dependency.
7
- */
8
- import * as d3 from 'd3';
9
-
10
- let {
11
- values,
12
- width = 120,
13
- height = 32,
14
- color = 'oklch(var(--p))',
15
- fillOpacity = 0.15,
16
- labels,
17
- }: {
18
- values: number[];
19
- width?: number | 'auto';
20
- height?: number;
21
- color?: string;
22
- fillOpacity?: number;
23
- /** Optional tooltip label per bar. If provided, shown as SVG title on hover. */
24
- labels?: string[];
25
- } = $props();
26
-
27
- let el: HTMLDivElement | undefined = $state();
28
- let animated = false;
29
-
30
- function getEffectiveWidth(): number {
31
- if (width === 'auto' && el) return el.clientWidth;
32
- if (typeof width === 'number') return width;
33
- return 120;
34
- }
35
-
36
- $effect(() => {
37
- if (!el || values.length < 2) return;
38
-
39
- const w = getEffectiveWidth();
40
- if (w <= 0) return;
41
-
42
- d3.select(el).select('svg').remove();
43
-
44
- const pad = 1;
45
- const innerW = w - pad * 2;
46
- const innerH = height - pad * 2;
47
-
48
- if (innerW <= 0 || innerH <= 0) return;
49
-
50
- const svg = d3
51
- .select(el)
52
- .append('svg')
53
- .attr('width', w)
54
- .attr('height', height);
55
-
56
- const g = svg.append('g').attr('transform', `translate(${pad},${pad})`);
57
-
58
- const x = d3
59
- .scaleBand<number>()
60
- .domain(values.map((_, i) => i))
61
- .range([0, innerW])
62
- .padding(0.15);
63
-
64
- const yMin = d3.min(values)! * 0.95;
65
- const yMax = d3.max(values)! * 1.05;
66
- const y = d3.scaleLinear().domain([yMin, yMax]).range([innerH, 0]);
67
-
68
- const n = Math.max(1, values.length - 1);
69
- const shouldAnimate = !animated;
70
- animated = true;
71
-
72
- const bars = g
73
- .selectAll('.bar')
74
- .data(values)
75
- .join('rect')
76
- .attr('x', (_, i) => x(i)!)
77
- .attr('width', x.bandwidth())
78
- .attr('rx', Math.min(1, x.bandwidth() / 4))
79
- .style('fill', color)
80
- .style('fill-opacity', (_, i) => fillOpacity + (1 - fillOpacity) * (i / n));
81
-
82
- bars.each(function (d, i) {
83
- d3.select(this).selectAll('title').remove();
84
- const label = labels && labels[i] !== undefined ? labels[i] : String(d);
85
- d3.select(this).append('title').text(label);
86
- });
87
-
88
- if (shouldAnimate) {
89
- bars
90
- .attr('y', innerH)
91
- .attr('height', 0)
92
- .transition()
93
- .duration(400)
94
- .delay((_, i) => i * 15)
95
- .ease(d3.easeCubicOut)
96
- .attr('y', (d) => y(d))
97
- .attr('height', (d) => innerH - y(d));
98
- } else {
99
- bars.attr('y', (d) => y(d)).attr('height', (d) => innerH - y(d));
100
- }
101
- });
102
- </script>
103
-
104
- {#if values.length >= 2}
105
- <div
106
- bind:this={el}
107
- style="{width === 'auto' ? 'width: 100%' : `width: ${width}px`}; height: {height}px"
108
- ></div>
109
- {:else}
110
- <div
111
- class="flex items-center justify-center text-base-content/30 text-xs"
112
- style="{width === 'auto' ? 'width: 100%' : `width: ${width}px`}; height: {height}px"
113
- >
114
- --
115
- </div>
116
- {/if}
@@ -1,114 +0,0 @@
1
- <script lang="ts">
2
- /**
3
- * SpeechForm — Text-to-speech generation form with voice selection, character limit, and audio preview.
4
- *
5
- * Voice options are configurable via the `voiceOptions` prop. Includes a textarea with character counter,
6
- * voice selector dropdown, optional audio preview player, and submit/cancel buttons.
7
- */
8
-
9
- let {
10
- text = $bindable(""),
11
- voice = $bindable("en-US-female-27"),
12
- previewUrl = null,
13
- isCreating = false,
14
- maxLength = 1000,
15
- voiceOptions = [
16
- { id: "en-US-female-27", label: "US Female (Clear)" },
17
- { id: "en-US-male-27", label: "US Male (Clear)" },
18
- { id: "en-GB-female-11", label: "UK Female" },
19
- { id: "en-GB-male-10", label: "UK Male" },
20
- { id: "en-US-female-24", label: "US Female (Warm)" },
21
- { id: "en-US-male-24", label: "US Male (Deep)" },
22
- ],
23
- infoText = "The system will convert your text to natural-sounding speech which you can download and use.",
24
- onSubmit,
25
- onCancel,
26
- } = $props<{
27
- text?: string
28
- voice?: string
29
- previewUrl?: string | null
30
- isCreating?: boolean
31
- maxLength?: number
32
- voiceOptions?: { id: string; label: string }[]
33
- infoText?: string
34
- onSubmit: () => void
35
- onCancel: () => void
36
- }>()
37
-
38
- let remaining = $derived(maxLength - (text?.length || 0))
39
-
40
- $effect(() => {
41
- return () => {
42
- if (previewUrl) {
43
- URL.revokeObjectURL(previewUrl)
44
- }
45
- }
46
- })
47
- </script>
48
-
49
- <div class="space-y-4">
50
- <div class="form-control">
51
- <label class="label" for="speech-text-input">
52
- <span class="label-text font-medium">Text to Convert</span>
53
- <span class="label-text-alt">{remaining} characters left</span>
54
- </label>
55
- <textarea
56
- id="speech-text-input"
57
- bind:value={text}
58
- placeholder="Enter the text you want to convert to speech..."
59
- class="textarea min-h-32"
60
- maxlength={maxLength}
61
- disabled={isCreating}
62
- ></textarea>
63
- </div>
64
-
65
- <div class="form-control">
66
- <label class="label" for="speech-voice-select">
67
- <span class="label-text font-medium">Voice</span>
68
- </label>
69
- <select id="speech-voice-select" bind:value={voice} class="select" disabled={isCreating}>
70
- {#each voiceOptions as option}
71
- <option value={option.id}>{option.label}</option>
72
- {/each}
73
- </select>
74
- </div>
75
-
76
- {#if previewUrl}
77
- <div class="form-control">
78
- <div class="label">
79
- <span class="label-text font-medium">Preview</span>
80
- </div>
81
- <audio src={previewUrl} controls class="w-full" aria-label="Generated speech preview">
82
- Your browser does not support the audio element.
83
- </audio>
84
- </div>
85
- {/if}
86
-
87
- {#if infoText}
88
- <div class="flex items-center gap-2 text-info bg-info/10 p-3 rounded">
89
- <!-- Info icon -->
90
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
91
- <p class="text-sm">{infoText}</p>
92
- </div>
93
- {/if}
94
- </div>
95
-
96
- <div class="flex justify-end gap-2 mt-6">
97
- <button class="btn btn-ghost" onclick={onCancel} disabled={isCreating}>
98
- Cancel
99
- </button>
100
- <button
101
- class="btn btn-primary gap-2"
102
- onclick={onSubmit}
103
- disabled={isCreating || !text}
104
- >
105
- {#if isCreating}
106
- <span class="loading loading-spinner loading-sm"></span>
107
- Converting...
108
- {:else}
109
- <!-- Mic icon -->
110
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/></svg>
111
- Generate Speech
112
- {/if}
113
- </button>
114
- </div>
@@ -1,157 +0,0 @@
1
- <script module lang="ts">
2
- export type StatusValue =
3
- | 'active'
4
- | 'inactive'
5
- | 'pending'
6
- | 'completed'
7
- | 'cancelled'
8
- | 'draft'
9
- | 'scheduled'
10
- | 'in-progress'
11
- | 'overdue'
12
- | 'paid'
13
- | 'unpaid'
14
- | 'partial'
15
- | 'assigned'
16
- | 'available';
17
- </script>
18
-
19
- <script lang="ts">
20
- /**
21
- * StatusBadge Component
22
- *
23
- * Predefined status badge with semantic colors, icons, and dot indicators.
24
- */
25
-
26
- interface Props {
27
- status: StatusValue;
28
- size?: 'xs' | 'sm' | 'md' | 'lg';
29
- showIcon?: boolean;
30
- showDot?: boolean;
31
- customText?: string;
32
- clickable?: boolean;
33
- outlined?: boolean;
34
- class?: string;
35
- ariaLabel?: string;
36
- onclick?: (event: { event: MouseEvent; status: string }) => void;
37
- }
38
-
39
- let {
40
- status,
41
- size = 'md',
42
- showIcon = false,
43
- showDot = false,
44
- customText = '',
45
- clickable = false,
46
- outlined = false,
47
- class: className = '',
48
- ariaLabel = '',
49
- onclick
50
- }: Props = $props();
51
-
52
- const statusConfig: Record<string, { text: string; variant: string; icon: string }> = {
53
- active: { text: 'Active', variant: 'success', icon: '✓' },
54
- inactive: { text: 'Inactive', variant: 'error', icon: '✗' },
55
- pending: { text: 'Pending', variant: 'warning', icon: '' },
56
- completed: { text: 'Completed', variant: 'success', icon: '✓' },
57
- cancelled: { text: 'Cancelled', variant: 'error', icon: '✗' },
58
- draft: { text: 'Draft', variant: 'default', icon: '' },
59
- scheduled: { text: 'Scheduled', variant: 'info', icon: '' },
60
- 'in-progress': { text: 'In Progress', variant: 'primary', icon: '' },
61
- overdue: { text: 'Overdue', variant: 'error', icon: '' },
62
- paid: { text: 'Paid', variant: 'success', icon: '✓' },
63
- unpaid: { text: 'Unpaid', variant: 'warning', icon: '' },
64
- partial: { text: 'Partial', variant: 'warning', icon: '' },
65
- assigned: { text: 'Assigned', variant: 'success', icon: '✓' },
66
- available: { text: 'Available', variant: 'info', icon: '' }
67
- };
68
-
69
- const config = $derived(statusConfig[status] || { text: status || 'Unknown', variant: 'default', icon: '?' });
70
- const displayText = $derived(customText || config.text);
71
-
72
- const variantMap: Record<string, string> = {
73
- default: 'badge-ghost',
74
- primary: 'badge-primary',
75
- secondary: 'badge-secondary',
76
- accent: 'badge-accent',
77
- success: 'badge-success',
78
- warning: 'badge-warning',
79
- error: 'badge-error',
80
- info: 'badge-info'
81
- };
82
-
83
- const outlinedVariantMap: Record<string, string> = {
84
- default: 'badge-outline',
85
- primary: 'badge-primary badge-outline',
86
- secondary: 'badge-secondary badge-outline',
87
- accent: 'badge-accent badge-outline',
88
- success: 'badge-success badge-outline',
89
- warning: 'badge-warning badge-outline',
90
- error: 'badge-error badge-outline',
91
- info: 'badge-info badge-outline'
92
- };
93
-
94
- const variantClass = $derived(outlined ? outlinedVariantMap[config.variant] : variantMap[config.variant]);
95
- const sizeClass = $derived({ xs: 'badge-xs', sm: 'badge-sm', md: 'badge-md', lg: 'badge-lg' }[size]);
96
-
97
- const dotColor = $derived(
98
- {
99
- default: 'bg-base-content',
100
- primary: 'bg-primary',
101
- secondary: 'bg-secondary',
102
- accent: 'bg-accent',
103
- success: 'bg-success',
104
- warning: 'bg-warning',
105
- error: 'bg-error',
106
- info: 'bg-info'
107
- }[config.variant]
108
- );
109
-
110
- const badgeClass = $derived(
111
- ['badge', variantClass, sizeClass, clickable ? 'cursor-pointer hover:opacity-80' : '', className]
112
- .filter(Boolean)
113
- .join(' ')
114
- );
115
-
116
- function handleClick(event: MouseEvent) {
117
- if (clickable && onclick) {
118
- onclick({ event, status });
119
- }
120
- }
121
-
122
- function handleKeyDown(event: KeyboardEvent) {
123
- if (clickable && onclick && (event.key === 'Enter' || event.key === ' ')) {
124
- event.preventDefault();
125
- onclick({ event: event as any, status });
126
- }
127
- }
128
- </script>
129
-
130
- {#if clickable}
131
- <span
132
- class={badgeClass}
133
- role="button"
134
- tabindex="0"
135
- aria-label={ariaLabel || `Status: ${displayText}`}
136
- onclick={handleClick}
137
- onkeydown={handleKeyDown}
138
- >
139
- {#if showDot}
140
- <span class="w-2 h-2 rounded-full {dotColor} mr-1.5 {status === 'active' ? 'animate-pulse' : ''}"></span>
141
- {/if}
142
- {#if showIcon}
143
- <span class="mr-1">{config.icon}</span>
144
- {/if}
145
- {displayText}
146
- </span>
147
- {:else}
148
- <span class={badgeClass} aria-label={ariaLabel || `Status: ${displayText}`}>
149
- {#if showDot}
150
- <span class="w-2 h-2 rounded-full {dotColor} mr-1.5 {status === 'active' ? 'animate-pulse' : ''}"></span>
151
- {/if}
152
- {#if showIcon}
153
- <span class="mr-1">{config.icon}</span>
154
- {/if}
155
- {displayText}
156
- </span>
157
- {/if}
@@ -1,54 +0,0 @@
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>