@aspect-ops/exon-ui 0.2.0 → 0.2.2
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 +200 -0
- package/dist/components/Accordion/Accordion.svelte +2 -2
- package/dist/components/Accordion/AccordionItem.svelte +2 -2
- package/dist/components/AspectRatio/AspectRatio.svelte +1 -0
- package/dist/components/Card/FlipCard.svelte +155 -0
- package/dist/components/Card/FlipCard.svelte.d.ts +13 -0
- package/dist/components/Card/index.d.ts +1 -0
- package/dist/components/Card/index.js +1 -0
- package/dist/components/Chatbot/ChatMessage.svelte +143 -0
- package/dist/components/Chatbot/ChatMessage.svelte.d.ts +8 -0
- package/dist/components/Chatbot/Chatbot.svelte +640 -0
- package/dist/components/Chatbot/Chatbot.svelte.d.ts +22 -0
- package/dist/components/Chatbot/index.d.ts +3 -0
- package/dist/components/Chatbot/index.js +2 -0
- package/dist/components/Chatbot/types.d.ts +48 -0
- package/dist/components/Chatbot/types.js +2 -0
- package/dist/components/ContactForm/ContactForm.svelte +564 -0
- package/dist/components/ContactForm/ContactForm.svelte.d.ts +44 -0
- package/dist/components/ContactForm/index.d.ts +1 -0
- package/dist/components/ContactForm/index.js +1 -0
- package/dist/components/Container/Container.svelte +1 -0
- package/dist/components/DataTable/DataTable.svelte +460 -0
- package/dist/components/DataTable/DataTable.svelte.d.ts +49 -0
- package/dist/components/DataTable/index.d.ts +2 -0
- package/dist/components/DataTable/index.js +1 -0
- package/dist/components/DoughnutChart/DoughnutChart.svelte +390 -0
- package/dist/components/DoughnutChart/DoughnutChart.svelte.d.ts +25 -0
- package/dist/components/DoughnutChart/index.d.ts +1 -0
- package/dist/components/DoughnutChart/index.js +1 -0
- package/dist/components/FAB/FAB.svelte +5 -1
- package/dist/components/FAB/FABGroup.svelte +10 -2
- package/dist/components/FileUpload/FileUpload.svelte +12 -12
- package/dist/components/Icon/Icon.svelte +15 -18
- package/dist/components/Icon/Icon.svelte.d.ts +2 -1
- package/dist/components/Menu/MenuContent.svelte +1 -0
- package/dist/components/Menu/MenuSubContent.svelte +1 -0
- package/dist/components/Mermaid/Mermaid.svelte +320 -0
- package/dist/components/Mermaid/Mermaid.svelte.d.ts +38 -0
- package/dist/components/Mermaid/index.d.ts +1 -0
- package/dist/components/Mermaid/index.js +1 -0
- package/dist/components/Mermaid/mermaid.d.ts +21 -0
- package/dist/components/PageHeader/PageHeader.svelte +140 -0
- package/dist/components/PageHeader/PageHeader.svelte.d.ts +30 -0
- package/dist/components/PageHeader/index.d.ts +1 -0
- package/dist/components/PageHeader/index.js +1 -0
- package/dist/components/Popover/PopoverTrigger.svelte +1 -3
- package/dist/components/StatCircle/StatCircle.svelte +172 -0
- package/dist/components/StatCircle/StatCircle.svelte.d.ts +19 -0
- package/dist/components/StatCircle/index.d.ts +1 -0
- package/dist/components/StatCircle/index.js +1 -0
- package/dist/components/StatsCard/StatsCard.svelte +301 -0
- package/dist/components/StatsCard/StatsCard.svelte.d.ts +32 -0
- package/dist/components/StatsCard/index.d.ts +2 -0
- package/dist/components/StatsCard/index.js +1 -0
- package/dist/components/StatusBadge/StatusBadge.svelte +221 -0
- package/dist/components/StatusBadge/StatusBadge.svelte.d.ts +22 -0
- package/dist/components/StatusBadge/index.d.ts +2 -0
- package/dist/components/StatusBadge/index.js +1 -0
- package/dist/components/StatusBanner/StatusBanner.svelte +325 -0
- package/dist/components/StatusBanner/StatusBanner.svelte.d.ts +13 -0
- package/dist/components/StatusBanner/index.d.ts +1 -0
- package/dist/components/StatusBanner/index.js +1 -0
- package/dist/components/ViewCounter/ViewCounter.svelte +157 -0
- package/dist/components/ViewCounter/ViewCounter.svelte.d.ts +17 -0
- package/dist/components/ViewCounter/index.d.ts +1 -0
- package/dist/components/ViewCounter/index.js +1 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +16 -1
- package/dist/styles/tokens.css +2 -1
- package/dist/types/data-display.d.ts +72 -0
- package/dist/types/feedback.d.ts +10 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/input.d.ts +20 -0
- package/package.json +4 -2
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface DataItem {
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/** Chart title */
|
|
9
|
+
title?: string;
|
|
10
|
+
/** Data items for the chart */
|
|
11
|
+
data: DataItem[];
|
|
12
|
+
/** Chart size in pixels */
|
|
13
|
+
size?: number;
|
|
14
|
+
/** Doughnut thickness as ratio (0-1) */
|
|
15
|
+
thickness?: number;
|
|
16
|
+
/** Custom color palette */
|
|
17
|
+
colors?: string[];
|
|
18
|
+
/** Whether to show legend */
|
|
19
|
+
showLegend?: boolean;
|
|
20
|
+
/** Whether to show center total */
|
|
21
|
+
showTotal?: boolean;
|
|
22
|
+
/** Custom class */
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Default color palette
|
|
27
|
+
const defaultColors = [
|
|
28
|
+
'#8FBE6D',
|
|
29
|
+
'#F5D76E',
|
|
30
|
+
'#F7CA4F',
|
|
31
|
+
'#E8945A',
|
|
32
|
+
'#E25D5D',
|
|
33
|
+
'#6B5B95',
|
|
34
|
+
'#4A90A4',
|
|
35
|
+
'#5FBFBF'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
let {
|
|
39
|
+
title = '',
|
|
40
|
+
data = [],
|
|
41
|
+
size = 200,
|
|
42
|
+
thickness = 0.6,
|
|
43
|
+
colors = defaultColors,
|
|
44
|
+
showLegend = true,
|
|
45
|
+
showTotal = true,
|
|
46
|
+
class: className = ''
|
|
47
|
+
}: Props = $props();
|
|
48
|
+
|
|
49
|
+
// Calculate total
|
|
50
|
+
const total = $derived(data.reduce((sum, item) => sum + item.value, 0));
|
|
51
|
+
|
|
52
|
+
// Calculate arc data
|
|
53
|
+
interface ArcData extends DataItem {
|
|
54
|
+
percentage: number;
|
|
55
|
+
color: string;
|
|
56
|
+
startAngle: number;
|
|
57
|
+
endAngle: number;
|
|
58
|
+
path: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const arcs = $derived.by(() => {
|
|
62
|
+
if (total === 0) return [];
|
|
63
|
+
|
|
64
|
+
const result: ArcData[] = [];
|
|
65
|
+
let currentAngle = -90; // Start from top
|
|
66
|
+
|
|
67
|
+
data.forEach((item, index) => {
|
|
68
|
+
const percentage = (item.value / total) * 100;
|
|
69
|
+
const angle = (item.value / total) * 360;
|
|
70
|
+
const startAngle = currentAngle;
|
|
71
|
+
const endAngle = currentAngle + angle;
|
|
72
|
+
|
|
73
|
+
const path = describeArc(size / 2, size / 2, size / 2 - 10, startAngle, endAngle);
|
|
74
|
+
|
|
75
|
+
result.push({
|
|
76
|
+
...item,
|
|
77
|
+
percentage,
|
|
78
|
+
color: colors[index % colors.length],
|
|
79
|
+
startAngle,
|
|
80
|
+
endAngle,
|
|
81
|
+
path
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
currentAngle = endAngle;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Calculate inner radius for doughnut
|
|
91
|
+
const innerRadius = $derived((size / 2 - 10) * (1 - thickness));
|
|
92
|
+
const outerRadius = $derived(size / 2 - 10);
|
|
93
|
+
|
|
94
|
+
// Helper to convert polar to cartesian coordinates
|
|
95
|
+
function polarToCartesian(
|
|
96
|
+
centerX: number,
|
|
97
|
+
centerY: number,
|
|
98
|
+
radius: number,
|
|
99
|
+
angleInDegrees: number
|
|
100
|
+
): { x: number; y: number } {
|
|
101
|
+
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
|
|
102
|
+
return {
|
|
103
|
+
x: centerX + radius * Math.cos(angleInRadians),
|
|
104
|
+
y: centerY + radius * Math.sin(angleInRadians)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Helper to describe an arc path
|
|
109
|
+
function describeArc(
|
|
110
|
+
x: number,
|
|
111
|
+
y: number,
|
|
112
|
+
radius: number,
|
|
113
|
+
startAngle: number,
|
|
114
|
+
endAngle: number
|
|
115
|
+
): string {
|
|
116
|
+
// Adjust angles (add 90 to start from top)
|
|
117
|
+
const start = polarToCartesian(x, y, radius, endAngle + 90);
|
|
118
|
+
const end = polarToCartesian(x, y, radius, startAngle + 90);
|
|
119
|
+
const innerStart = polarToCartesian(x, y, innerRadius, endAngle + 90);
|
|
120
|
+
const innerEnd = polarToCartesian(x, y, innerRadius, startAngle + 90);
|
|
121
|
+
|
|
122
|
+
const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
|
|
123
|
+
|
|
124
|
+
// Create doughnut arc path
|
|
125
|
+
const d = [
|
|
126
|
+
'M',
|
|
127
|
+
start.x,
|
|
128
|
+
start.y,
|
|
129
|
+
'A',
|
|
130
|
+
radius,
|
|
131
|
+
radius,
|
|
132
|
+
0,
|
|
133
|
+
largeArcFlag,
|
|
134
|
+
0,
|
|
135
|
+
end.x,
|
|
136
|
+
end.y,
|
|
137
|
+
'L',
|
|
138
|
+
innerEnd.x,
|
|
139
|
+
innerEnd.y,
|
|
140
|
+
'A',
|
|
141
|
+
innerRadius,
|
|
142
|
+
innerRadius,
|
|
143
|
+
0,
|
|
144
|
+
largeArcFlag,
|
|
145
|
+
1,
|
|
146
|
+
innerStart.x,
|
|
147
|
+
innerStart.y,
|
|
148
|
+
'Z'
|
|
149
|
+
].join(' ');
|
|
150
|
+
|
|
151
|
+
return d;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Format number for display
|
|
155
|
+
function formatNumber(num: number): string {
|
|
156
|
+
if (num >= 1000000) {
|
|
157
|
+
return (num / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
|
|
158
|
+
}
|
|
159
|
+
if (num >= 1000) {
|
|
160
|
+
return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'K';
|
|
161
|
+
}
|
|
162
|
+
return num.toLocaleString();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Hovered arc index
|
|
166
|
+
let hoveredIndex = $state<number | null>(null);
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<div class="doughnut-chart {className}">
|
|
170
|
+
{#if title}
|
|
171
|
+
<h3 class="doughnut-chart__title">{title}</h3>
|
|
172
|
+
{/if}
|
|
173
|
+
|
|
174
|
+
<div class="doughnut-chart__content">
|
|
175
|
+
<!-- SVG Chart -->
|
|
176
|
+
<div class="doughnut-chart__svg-container">
|
|
177
|
+
<svg
|
|
178
|
+
width={size}
|
|
179
|
+
height={size}
|
|
180
|
+
viewBox="0 0 {size} {size}"
|
|
181
|
+
class="doughnut-chart__svg"
|
|
182
|
+
role="img"
|
|
183
|
+
aria-label={title || 'Doughnut chart'}
|
|
184
|
+
>
|
|
185
|
+
{#each arcs as arc, index}
|
|
186
|
+
<path
|
|
187
|
+
d={arc.path}
|
|
188
|
+
fill={arc.color}
|
|
189
|
+
class="doughnut-chart__arc"
|
|
190
|
+
class:doughnut-chart__arc--hovered={hoveredIndex === index}
|
|
191
|
+
onmouseenter={() => (hoveredIndex = index)}
|
|
192
|
+
onmouseleave={() => (hoveredIndex = null)}
|
|
193
|
+
role="presentation"
|
|
194
|
+
>
|
|
195
|
+
<title>{arc.label}: {formatNumber(arc.value)} ({arc.percentage.toFixed(1)}%)</title>
|
|
196
|
+
</path>
|
|
197
|
+
|
|
198
|
+
<!-- Percentage label on arc -->
|
|
199
|
+
{#if arc.percentage >= 5}
|
|
200
|
+
{@const midAngle = (arc.startAngle + arc.endAngle) / 2}
|
|
201
|
+
{@const labelRadius = (outerRadius + innerRadius) / 2}
|
|
202
|
+
{@const labelPos = polarToCartesian(size / 2, size / 2, labelRadius, midAngle + 90)}
|
|
203
|
+
<text
|
|
204
|
+
x={labelPos.x}
|
|
205
|
+
y={labelPos.y}
|
|
206
|
+
text-anchor="middle"
|
|
207
|
+
dominant-baseline="middle"
|
|
208
|
+
class="doughnut-chart__label"
|
|
209
|
+
fill="#ffffff"
|
|
210
|
+
>
|
|
211
|
+
{arc.percentage.toFixed(0)}%
|
|
212
|
+
</text>
|
|
213
|
+
{/if}
|
|
214
|
+
{/each}
|
|
215
|
+
|
|
216
|
+
<!-- Background circle for center content -->
|
|
217
|
+
{#if showTotal}
|
|
218
|
+
<circle
|
|
219
|
+
cx={size / 2}
|
|
220
|
+
cy={size / 2}
|
|
221
|
+
r={innerRadius * 0.95}
|
|
222
|
+
fill="var(--color-bg, #ffffff)"
|
|
223
|
+
class="doughnut-chart__center-bg"
|
|
224
|
+
/>
|
|
225
|
+
{/if}
|
|
226
|
+
</svg>
|
|
227
|
+
|
|
228
|
+
<!-- Center total -->
|
|
229
|
+
{#if showTotal}
|
|
230
|
+
<div class="doughnut-chart__center">
|
|
231
|
+
<span class="doughnut-chart__total">{formatNumber(total)}</span>
|
|
232
|
+
<span class="doughnut-chart__total-label">Total</span>
|
|
233
|
+
</div>
|
|
234
|
+
{/if}
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<!-- Legend -->
|
|
238
|
+
{#if showLegend}
|
|
239
|
+
<ul class="doughnut-chart__legend">
|
|
240
|
+
{#each arcs as arc, index}
|
|
241
|
+
<li
|
|
242
|
+
class="doughnut-chart__legend-item"
|
|
243
|
+
class:doughnut-chart__legend-item--hovered={hoveredIndex === index}
|
|
244
|
+
onmouseenter={() => (hoveredIndex = index)}
|
|
245
|
+
onmouseleave={() => (hoveredIndex = null)}
|
|
246
|
+
>
|
|
247
|
+
<span class="doughnut-chart__legend-color" style="background-color: {arc.color}"></span>
|
|
248
|
+
<span class="doughnut-chart__legend-label">{arc.label}</span>
|
|
249
|
+
<span class="doughnut-chart__legend-value">{formatNumber(arc.value)}</span>
|
|
250
|
+
</li>
|
|
251
|
+
{/each}
|
|
252
|
+
</ul>
|
|
253
|
+
{/if}
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<style>
|
|
258
|
+
.doughnut-chart {
|
|
259
|
+
font-family: inherit;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.doughnut-chart__title {
|
|
263
|
+
font-size: var(--text-lg, 1.125rem);
|
|
264
|
+
font-weight: var(--font-semibold, 600);
|
|
265
|
+
color: var(--color-text, #1f2937);
|
|
266
|
+
margin: 0 0 var(--space-md, 1rem);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.doughnut-chart__content {
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
gap: var(--space-xl, 2rem);
|
|
273
|
+
flex-wrap: wrap;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* SVG Container */
|
|
277
|
+
.doughnut-chart__svg-container {
|
|
278
|
+
position: relative;
|
|
279
|
+
flex-shrink: 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.doughnut-chart__svg {
|
|
283
|
+
display: block;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* Arc */
|
|
287
|
+
.doughnut-chart__arc {
|
|
288
|
+
cursor: pointer;
|
|
289
|
+
transition: opacity var(--transition-fast, 150ms ease);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.doughnut-chart__arc--hovered {
|
|
293
|
+
opacity: 0.85;
|
|
294
|
+
filter: brightness(1.1);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* Arc Label */
|
|
298
|
+
.doughnut-chart__label {
|
|
299
|
+
font-size: var(--text-xs, 0.75rem);
|
|
300
|
+
font-weight: var(--font-semibold, 600);
|
|
301
|
+
pointer-events: none;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* Center Background */
|
|
305
|
+
.doughnut-chart__center-bg {
|
|
306
|
+
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* Center */
|
|
310
|
+
.doughnut-chart__center {
|
|
311
|
+
position: absolute;
|
|
312
|
+
top: 50%;
|
|
313
|
+
left: 50%;
|
|
314
|
+
transform: translate(-50%, -50%);
|
|
315
|
+
text-align: center;
|
|
316
|
+
pointer-events: none;
|
|
317
|
+
z-index: 10;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.doughnut-chart__total {
|
|
321
|
+
display: block;
|
|
322
|
+
font-size: var(--text-lg, 1.125rem);
|
|
323
|
+
font-weight: var(--font-semibold, 600);
|
|
324
|
+
color: var(--color-text, #1f2937);
|
|
325
|
+
line-height: 1.2;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.doughnut-chart__total-label {
|
|
329
|
+
display: block;
|
|
330
|
+
font-size: var(--text-xs, 0.75rem);
|
|
331
|
+
color: var(--color-text-muted, #6b7280);
|
|
332
|
+
text-transform: uppercase;
|
|
333
|
+
letter-spacing: 0.05em;
|
|
334
|
+
margin-top: 2px;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/* Legend */
|
|
338
|
+
.doughnut-chart__legend {
|
|
339
|
+
list-style: none;
|
|
340
|
+
margin: 0;
|
|
341
|
+
padding: 0;
|
|
342
|
+
display: flex;
|
|
343
|
+
flex-direction: column;
|
|
344
|
+
gap: var(--space-sm, 0.5rem);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.doughnut-chart__legend-item {
|
|
348
|
+
display: flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
gap: var(--space-sm, 0.5rem);
|
|
351
|
+
padding: var(--space-xs, 0.25rem) 0;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
transition: opacity var(--transition-fast, 150ms ease);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.doughnut-chart__legend-item--hovered {
|
|
357
|
+
opacity: 0.85;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.doughnut-chart__legend-color {
|
|
361
|
+
width: 12px;
|
|
362
|
+
height: 12px;
|
|
363
|
+
border-radius: 2px;
|
|
364
|
+
flex-shrink: 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.doughnut-chart__legend-label {
|
|
368
|
+
flex: 1;
|
|
369
|
+
font-size: var(--text-sm, 0.875rem);
|
|
370
|
+
color: var(--color-text, #1f2937);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.doughnut-chart__legend-value {
|
|
374
|
+
font-size: var(--text-sm, 0.875rem);
|
|
375
|
+
font-weight: var(--font-medium, 500);
|
|
376
|
+
color: var(--color-text-muted, #6b7280);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/* Responsive */
|
|
380
|
+
@media (max-width: 480px) {
|
|
381
|
+
.doughnut-chart__content {
|
|
382
|
+
flex-direction: column;
|
|
383
|
+
align-items: center;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.doughnut-chart__legend {
|
|
387
|
+
width: 100%;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface DataItem {
|
|
2
|
+
label: string;
|
|
3
|
+
value: number;
|
|
4
|
+
}
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Chart title */
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Data items for the chart */
|
|
9
|
+
data: DataItem[];
|
|
10
|
+
/** Chart size in pixels */
|
|
11
|
+
size?: number;
|
|
12
|
+
/** Doughnut thickness as ratio (0-1) */
|
|
13
|
+
thickness?: number;
|
|
14
|
+
/** Custom color palette */
|
|
15
|
+
colors?: string[];
|
|
16
|
+
/** Whether to show legend */
|
|
17
|
+
showLegend?: boolean;
|
|
18
|
+
/** Whether to show center total */
|
|
19
|
+
showTotal?: boolean;
|
|
20
|
+
/** Custom class */
|
|
21
|
+
class?: string;
|
|
22
|
+
}
|
|
23
|
+
declare const DoughnutChart: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type DoughnutChart = ReturnType<typeof DoughnutChart>;
|
|
25
|
+
export default DoughnutChart;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as DoughnutChart } from './DoughnutChart.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as DoughnutChart } from './DoughnutChart.svelte';
|
|
@@ -176,8 +176,12 @@
|
|
|
176
176
|
|
|
177
177
|
/* Focus state */
|
|
178
178
|
.fab:focus-visible {
|
|
179
|
-
outline: 2px solid var(--color-
|
|
179
|
+
outline: 2px solid var(--color-text-inverse, #ffffff);
|
|
180
180
|
outline-offset: 2px;
|
|
181
|
+
box-shadow:
|
|
182
|
+
0 0 0 4px var(--color-primary-active, #1d4ed8),
|
|
183
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
184
|
+
0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
181
185
|
}
|
|
182
186
|
|
|
183
187
|
/* Disabled state */
|
|
@@ -240,8 +240,12 @@
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
.fab-group__trigger:focus-visible {
|
|
243
|
-
outline: 2px solid var(--color-
|
|
243
|
+
outline: 2px solid var(--color-text-inverse, #ffffff);
|
|
244
244
|
outline-offset: 2px;
|
|
245
|
+
box-shadow:
|
|
246
|
+
0 0 0 4px var(--color-primary-active, #1d4ed8),
|
|
247
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
248
|
+
0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
/* Rotation when open */
|
|
@@ -364,8 +368,12 @@
|
|
|
364
368
|
}
|
|
365
369
|
|
|
366
370
|
.fab-group__action:focus-visible {
|
|
367
|
-
outline: 2px solid var(--color-primary, #
|
|
371
|
+
outline: 2px solid var(--color-primary, #0090ff);
|
|
368
372
|
outline-offset: 2px;
|
|
373
|
+
box-shadow:
|
|
374
|
+
0 0 0 4px rgba(0, 144, 255, 0.2),
|
|
375
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
376
|
+
0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
369
377
|
}
|
|
370
378
|
|
|
371
379
|
/* Action icon */
|
|
@@ -328,13 +328,13 @@
|
|
|
328
328
|
text-align: center;
|
|
329
329
|
cursor: pointer;
|
|
330
330
|
transition: all 0.2s ease;
|
|
331
|
-
background-color: var(--color-
|
|
331
|
+
background-color: var(--color-bg, #ffffff);
|
|
332
332
|
min-height: var(--touch-target-min, 44px);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
.drop-zone:hover:not(.disabled) {
|
|
336
336
|
border-color: var(--color-primary, #3b82f6);
|
|
337
|
-
background-color: var(--color-
|
|
337
|
+
background-color: var(--color-bg-hover, #f9fafb);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
.drop-zone:focus-visible {
|
|
@@ -344,7 +344,7 @@
|
|
|
344
344
|
|
|
345
345
|
.drop-zone.dragging {
|
|
346
346
|
border-color: var(--color-primary, #3b82f6);
|
|
347
|
-
background-color: var(--color-primary-
|
|
347
|
+
background-color: var(--color-primary-bg, #eff6ff);
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
.drop-zone.has-files {
|
|
@@ -365,7 +365,7 @@
|
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
.upload-icon {
|
|
368
|
-
color: var(--color-text-
|
|
368
|
+
color: var(--color-text-muted, #6b7280);
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
.instructions {
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
.hint {
|
|
379
379
|
margin: 0;
|
|
380
380
|
font-size: var(--text-sm, 0.875rem);
|
|
381
|
-
color: var(--color-text-
|
|
381
|
+
color: var(--color-text-muted, #6b7280);
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
.file-list {
|
|
@@ -395,7 +395,7 @@
|
|
|
395
395
|
padding: var(--space-3, 0.75rem);
|
|
396
396
|
border: 1px solid var(--color-border, #e5e7eb);
|
|
397
397
|
border-radius: var(--radius-md, 8px);
|
|
398
|
-
background-color: var(--color-
|
|
398
|
+
background-color: var(--color-bg, #ffffff);
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
.file-preview {
|
|
@@ -406,7 +406,7 @@
|
|
|
406
406
|
align-items: center;
|
|
407
407
|
justify-content: center;
|
|
408
408
|
border-radius: var(--radius-sm, 4px);
|
|
409
|
-
background-color: var(--color-
|
|
409
|
+
background-color: var(--color-bg-muted, #f3f4f6);
|
|
410
410
|
overflow: hidden;
|
|
411
411
|
}
|
|
412
412
|
|
|
@@ -417,7 +417,7 @@
|
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
.file-icon {
|
|
420
|
-
color: var(--color-text-
|
|
420
|
+
color: var(--color-text-muted, #6b7280);
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
.file-info {
|
|
@@ -439,7 +439,7 @@
|
|
|
439
439
|
|
|
440
440
|
.file-size {
|
|
441
441
|
font-size: var(--text-xs, 0.75rem);
|
|
442
|
-
color: var(--color-text-
|
|
442
|
+
color: var(--color-text-muted, #6b7280);
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
.remove-btn {
|
|
@@ -451,15 +451,15 @@
|
|
|
451
451
|
justify-content: center;
|
|
452
452
|
border: none;
|
|
453
453
|
background: transparent;
|
|
454
|
-
color: var(--color-text-
|
|
454
|
+
color: var(--color-text-muted, #6b7280);
|
|
455
455
|
cursor: pointer;
|
|
456
456
|
border-radius: var(--radius-sm, 4px);
|
|
457
457
|
transition: all 0.2s ease;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
460
|
.remove-btn:hover:not(:disabled) {
|
|
461
|
-
background-color: var(--color-error-
|
|
462
|
-
color: var(--color-
|
|
461
|
+
background-color: var(--color-error-bg, #fef2f2);
|
|
462
|
+
color: var(--color-destructive, #ef4444);
|
|
463
463
|
}
|
|
464
464
|
|
|
465
465
|
.remove-btn:focus-visible {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { IconSize } from '../../types/index.js';
|
|
3
|
+
import type { ComponentType } from 'svelte';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
|
-
|
|
6
|
+
icon: ComponentType;
|
|
6
7
|
size?: IconSize;
|
|
7
8
|
class?: string;
|
|
8
9
|
'aria-label'?: string;
|
|
@@ -10,38 +11,34 @@
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
let {
|
|
13
|
-
|
|
14
|
+
icon: IconComponent,
|
|
14
15
|
size = 'md',
|
|
15
16
|
class: className = '',
|
|
16
17
|
'aria-label': ariaLabel,
|
|
17
18
|
'aria-hidden': ariaHidden = !ariaLabel
|
|
18
19
|
}: Props = $props();
|
|
19
20
|
|
|
20
|
-
// Size map aligned with typography scale
|
|
21
|
-
const sizeMap: Record<IconSize,
|
|
22
|
-
xs:
|
|
23
|
-
sm:
|
|
24
|
-
md:
|
|
25
|
-
lg:
|
|
26
|
-
xl:
|
|
21
|
+
// Size map aligned with typography scale (in pixels for Lucide)
|
|
22
|
+
const sizeMap: Record<IconSize, number> = {
|
|
23
|
+
xs: 12,
|
|
24
|
+
sm: 16,
|
|
25
|
+
md: 20,
|
|
26
|
+
lg: 24,
|
|
27
|
+
xl: 32
|
|
27
28
|
};
|
|
28
29
|
</script>
|
|
29
30
|
|
|
30
|
-
<
|
|
31
|
+
<IconComponent
|
|
32
|
+
size={sizeMap[size]}
|
|
31
33
|
class="icon icon--{size} {className}"
|
|
32
|
-
style="--icon-size: {sizeMap[size]}"
|
|
33
34
|
aria-label={ariaLabel}
|
|
34
35
|
aria-hidden={ariaHidden}
|
|
35
36
|
role={ariaLabel ? 'img' : undefined}
|
|
36
|
-
|
|
37
|
-
<use href="#{name}" />
|
|
38
|
-
</svg>
|
|
37
|
+
/>
|
|
39
38
|
|
|
40
39
|
<style>
|
|
41
|
-
.icon {
|
|
42
|
-
width: var(--icon-size);
|
|
43
|
-
height: var(--icon-size);
|
|
44
|
-
fill: currentColor;
|
|
40
|
+
:global(.icon) {
|
|
45
41
|
flex-shrink: 0;
|
|
42
|
+
color: currentColor;
|
|
46
43
|
}
|
|
47
44
|
</style>
|