@aspect-ops/exon-ui 0.2.1 → 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.
Files changed (43) hide show
  1. package/dist/components/AspectRatio/AspectRatio.svelte +1 -0
  2. package/dist/components/Card/FlipCard.svelte +155 -0
  3. package/dist/components/Card/FlipCard.svelte.d.ts +13 -0
  4. package/dist/components/Card/index.d.ts +1 -0
  5. package/dist/components/Card/index.js +1 -0
  6. package/dist/components/Container/Container.svelte +1 -0
  7. package/dist/components/DataTable/DataTable.svelte +460 -0
  8. package/dist/components/DataTable/DataTable.svelte.d.ts +49 -0
  9. package/dist/components/DataTable/index.d.ts +2 -0
  10. package/dist/components/DataTable/index.js +1 -0
  11. package/dist/components/DoughnutChart/DoughnutChart.svelte +20 -2
  12. package/dist/components/Icon/Icon.svelte +15 -18
  13. package/dist/components/Icon/Icon.svelte.d.ts +2 -1
  14. package/dist/components/Menu/MenuContent.svelte +1 -0
  15. package/dist/components/Menu/MenuSubContent.svelte +1 -0
  16. package/dist/components/Mermaid/Mermaid.svelte +121 -7
  17. package/dist/components/Mermaid/Mermaid.svelte.d.ts +10 -0
  18. package/dist/components/PageHeader/PageHeader.svelte +140 -0
  19. package/dist/components/PageHeader/PageHeader.svelte.d.ts +30 -0
  20. package/dist/components/PageHeader/index.d.ts +1 -0
  21. package/dist/components/PageHeader/index.js +1 -0
  22. package/dist/components/StatCircle/StatCircle.svelte +172 -0
  23. package/dist/components/StatCircle/StatCircle.svelte.d.ts +19 -0
  24. package/dist/components/StatCircle/index.d.ts +1 -0
  25. package/dist/components/StatCircle/index.js +1 -0
  26. package/dist/components/StatsCard/StatsCard.svelte +301 -0
  27. package/dist/components/StatsCard/StatsCard.svelte.d.ts +32 -0
  28. package/dist/components/StatsCard/index.d.ts +2 -0
  29. package/dist/components/StatsCard/index.js +1 -0
  30. package/dist/components/StatusBadge/StatusBadge.svelte +221 -0
  31. package/dist/components/StatusBadge/StatusBadge.svelte.d.ts +22 -0
  32. package/dist/components/StatusBadge/index.d.ts +2 -0
  33. package/dist/components/StatusBadge/index.js +1 -0
  34. package/dist/components/StatusBanner/StatusBanner.svelte +325 -0
  35. package/dist/components/StatusBanner/StatusBanner.svelte.d.ts +13 -0
  36. package/dist/components/StatusBanner/index.d.ts +1 -0
  37. package/dist/components/StatusBanner/index.js +1 -0
  38. package/dist/index.d.ts +8 -2
  39. package/dist/index.js +7 -1
  40. package/dist/types/data-display.d.ts +72 -0
  41. package/dist/types/feedback.d.ts +10 -0
  42. package/dist/types/index.d.ts +2 -2
  43. package/package.json +3 -2
@@ -212,6 +212,17 @@
212
212
  </text>
213
213
  {/if}
214
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}
215
226
  </svg>
216
227
 
217
228
  <!-- Center total -->
@@ -290,6 +301,11 @@
290
301
  pointer-events: none;
291
302
  }
292
303
 
304
+ /* Center Background */
305
+ .doughnut-chart__center-bg {
306
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
307
+ }
308
+
293
309
  /* Center */
294
310
  .doughnut-chart__center {
295
311
  position: absolute;
@@ -298,12 +314,13 @@
298
314
  transform: translate(-50%, -50%);
299
315
  text-align: center;
300
316
  pointer-events: none;
317
+ z-index: 10;
301
318
  }
302
319
 
303
320
  .doughnut-chart__total {
304
321
  display: block;
305
- font-size: var(--text-2xl, 1.5rem);
306
- font-weight: var(--font-bold, 700);
322
+ font-size: var(--text-lg, 1.125rem);
323
+ font-weight: var(--font-semibold, 600);
307
324
  color: var(--color-text, #1f2937);
308
325
  line-height: 1.2;
309
326
  }
@@ -314,6 +331,7 @@
314
331
  color: var(--color-text-muted, #6b7280);
315
332
  text-transform: uppercase;
316
333
  letter-spacing: 0.05em;
334
+ margin-top: 2px;
317
335
  }
318
336
 
319
337
  /* Legend */
@@ -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
- name: string;
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
- name,
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, string> = {
22
- xs: '0.75rem',
23
- sm: '1rem',
24
- md: '1.25rem',
25
- lg: '1.5rem',
26
- xl: '2rem'
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
- <svg
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>
@@ -1,6 +1,7 @@
1
1
  import type { IconSize } from '../../types/index.js';
2
+ import type { ComponentType } from 'svelte';
2
3
  interface Props {
3
- name: string;
4
+ icon: ComponentType;
4
5
  size?: IconSize;
5
6
  class?: string;
6
7
  'aria-label'?: string;
@@ -36,6 +36,7 @@
36
36
  z-index: 50;
37
37
  min-width: 8rem;
38
38
  max-width: 20rem;
39
+ overflow: hidden;
39
40
  }
40
41
 
41
42
  /* Animation states */
@@ -25,6 +25,7 @@
25
25
  z-index: 51;
26
26
  min-width: 8rem;
27
27
  max-width: 20rem;
28
+ overflow: hidden;
28
29
  }
29
30
 
30
31
  /* Animation states */
@@ -15,6 +15,17 @@
15
15
  clusterBkg?: string;
16
16
  titleColor?: string;
17
17
  edgeLabelBackground?: string;
18
+ textColor?: string;
19
+ // Sequence diagram specific
20
+ actorLineColor?: string;
21
+ signalColor?: string;
22
+ signalTextColor?: string;
23
+ labelTextColor?: string;
24
+ actorTextColor?: string;
25
+ messageTextColor?: string;
26
+ loopTextColor?: string;
27
+ activationBorderColor?: string;
28
+ sequenceNumberColor?: string;
18
29
  }
19
30
 
20
31
  interface Props {
@@ -28,17 +39,61 @@
28
39
  children?: Snippet;
29
40
  }
30
41
 
31
- // Default ExonPro theme
32
- const defaultTheme: ThemeVariables = {
42
+ // Light theme colors
43
+ const lightTheme: ThemeVariables = {
33
44
  primaryColor: '#4654A3',
34
- primaryTextColor: '#270949',
45
+ primaryTextColor: '#ffffff',
35
46
  primaryBorderColor: '#4654A3',
36
- lineColor: '#270949',
47
+ lineColor: '#4b5563',
37
48
  secondaryColor: '#FF3131',
38
- tertiaryColor: '#f3f4f6'
49
+ tertiaryColor: '#f3f4f6',
50
+ textColor: '#000000',
51
+ // Sequence diagram - dark colors for light backgrounds
52
+ actorLineColor: '#4b5563',
53
+ signalColor: '#374151',
54
+ signalTextColor: '#000000',
55
+ labelTextColor: '#000000',
56
+ actorTextColor: '#ffffff',
57
+ messageTextColor: '#000000',
58
+ loopTextColor: '#000000',
59
+ activationBorderColor: '#4b5563',
60
+ sequenceNumberColor: '#ffffff'
39
61
  };
40
62
 
41
- let { code, theme = defaultTheme, class: className = '', children }: Props = $props();
63
+ // Dark theme colors
64
+ const darkTheme: ThemeVariables = {
65
+ primaryColor: '#4654A3',
66
+ primaryTextColor: '#ffffff',
67
+ primaryBorderColor: '#4654A3',
68
+ lineColor: '#9ca3af',
69
+ secondaryColor: '#FF3131',
70
+ tertiaryColor: '#1f2937',
71
+ textColor: '#7dd3fc',
72
+ // Sequence diagram - sky blue colors for dark backgrounds
73
+ actorLineColor: '#38bdf8',
74
+ signalColor: '#38bdf8',
75
+ signalTextColor: '#7dd3fc',
76
+ labelTextColor: '#7dd3fc',
77
+ actorTextColor: '#ffffff',
78
+ messageTextColor: '#7dd3fc',
79
+ loopTextColor: '#7dd3fc',
80
+ activationBorderColor: '#38bdf8',
81
+ sequenceNumberColor: '#ffffff'
82
+ };
83
+
84
+ // Detect theme
85
+ function getThemeColors(): ThemeVariables {
86
+ if (typeof window === 'undefined') return lightTheme;
87
+
88
+ const isDark =
89
+ document.documentElement.classList.contains('dark') ||
90
+ document.body.classList.contains('dark') ||
91
+ window.matchMedia('(prefers-color-scheme: dark)').matches;
92
+
93
+ return isDark ? darkTheme : lightTheme;
94
+ }
95
+
96
+ let { code, theme, class: className = '', children }: Props = $props();
42
97
 
43
98
  let containerRef: HTMLDivElement | undefined = $state();
44
99
  let slotRef: HTMLDivElement | undefined = $state();
@@ -62,11 +117,15 @@
62
117
  const mermaidModule = await import('mermaid');
63
118
  const mermaid = mermaidModule.default;
64
119
 
120
+ // Detect theme and merge with custom theme
121
+ const detectedTheme = getThemeColors();
122
+ const mergedTheme = { ...detectedTheme, ...theme };
123
+
65
124
  // Initialize mermaid with theme
66
125
  mermaid.initialize({
67
126
  startOnLoad: false,
68
127
  theme: 'base',
69
- themeVariables: { ...defaultTheme, ...theme },
128
+ themeVariables: mergedTheme,
70
129
  securityLevel: 'loose',
71
130
  fontFamily: 'inherit'
72
131
  });
@@ -203,4 +262,59 @@
203
262
  max-width: 100%;
204
263
  height: auto;
205
264
  }
265
+
266
+ /* Force text colors in dark mode for all diagrams (flowchart + sequence) */
267
+ :global(.dark) .mermaid-diagram :global(svg text),
268
+ :global(.dark) .mermaid-diagram :global(svg .messageText),
269
+ :global(body.dark) .mermaid-diagram :global(svg text),
270
+ :global(body.dark) .mermaid-diagram :global(svg .messageText),
271
+ :global(html.dark) .mermaid-diagram :global(svg text),
272
+ :global(html.dark) .mermaid-diagram :global(svg .messageText) {
273
+ fill: #7dd3fc !important;
274
+ }
275
+
276
+ /* Force line colors in dark mode for all diagrams */
277
+ :global(.dark) .mermaid-diagram :global(svg .messageLine0),
278
+ :global(.dark) .mermaid-diagram :global(svg .messageLine1),
279
+ :global(.dark) .mermaid-diagram :global(svg line),
280
+ :global(.dark) .mermaid-diagram :global(svg path),
281
+ :global(.dark) .mermaid-diagram :global(svg .edgePath path),
282
+ :global(.dark) .mermaid-diagram :global(svg .flowchart-link),
283
+ :global(body.dark) .mermaid-diagram :global(svg .messageLine0),
284
+ :global(body.dark) .mermaid-diagram :global(svg .messageLine1),
285
+ :global(body.dark) .mermaid-diagram :global(svg line),
286
+ :global(body.dark) .mermaid-diagram :global(svg path),
287
+ :global(body.dark) .mermaid-diagram :global(svg .edgePath path),
288
+ :global(body.dark) .mermaid-diagram :global(svg .flowchart-link),
289
+ :global(html.dark) .mermaid-diagram :global(svg .messageLine0),
290
+ :global(html.dark) .mermaid-diagram :global(svg .messageLine1),
291
+ :global(html.dark) .mermaid-diagram :global(svg line),
292
+ :global(html.dark) .mermaid-diagram :global(svg path),
293
+ :global(html.dark) .mermaid-diagram :global(svg .edgePath path),
294
+ :global(html.dark) .mermaid-diagram :global(svg .flowchart-link) {
295
+ stroke: #38bdf8 !important;
296
+ }
297
+
298
+ /* Flowchart edge labels */
299
+ :global(.dark) .mermaid-diagram :global(svg .edgeLabel),
300
+ :global(body.dark) .mermaid-diagram :global(svg .edgeLabel),
301
+ :global(html.dark) .mermaid-diagram :global(svg .edgeLabel) {
302
+ color: #7dd3fc !important;
303
+ }
304
+
305
+ :global(.dark) .mermaid-diagram :global(svg .edgeLabel text),
306
+ :global(body.dark) .mermaid-diagram :global(svg .edgeLabel text),
307
+ :global(html.dark) .mermaid-diagram :global(svg .edgeLabel text) {
308
+ fill: #7dd3fc !important;
309
+ }
310
+
311
+ /* Keep actor text and node text white for readability */
312
+ :global(.dark) .mermaid-diagram :global(svg .actor text),
313
+ :global(.dark) .mermaid-diagram :global(svg .node text),
314
+ :global(body.dark) .mermaid-diagram :global(svg .actor text),
315
+ :global(body.dark) .mermaid-diagram :global(svg .node text),
316
+ :global(html.dark) .mermaid-diagram :global(svg .actor text),
317
+ :global(html.dark) .mermaid-diagram :global(svg .node text) {
318
+ fill: #ffffff !important;
319
+ }
206
320
  </style>
@@ -12,6 +12,16 @@ interface ThemeVariables {
12
12
  clusterBkg?: string;
13
13
  titleColor?: string;
14
14
  edgeLabelBackground?: string;
15
+ textColor?: string;
16
+ actorLineColor?: string;
17
+ signalColor?: string;
18
+ signalTextColor?: string;
19
+ labelTextColor?: string;
20
+ actorTextColor?: string;
21
+ messageTextColor?: string;
22
+ loopTextColor?: string;
23
+ activationBorderColor?: string;
24
+ sequenceNumberColor?: string;
15
25
  }
16
26
  interface Props {
17
27
  /** Mermaid diagram code. If not provided, uses slot content */
@@ -0,0 +1,140 @@
1
+ <script lang="ts">
2
+ import Avatar from '../Avatar/Avatar.svelte';
3
+
4
+ interface Props {
5
+ /**
6
+ * Greeting text to display (e.g., "Welcome back,")
7
+ * @default "Welcome back,"
8
+ */
9
+ greeting?: string;
10
+ /**
11
+ * User name to display prominently
12
+ */
13
+ userName: string;
14
+ /**
15
+ * Avatar image source URL
16
+ */
17
+ avatarSrc?: string;
18
+ /**
19
+ * Custom initials for avatar (overrides auto-generated from userName)
20
+ */
21
+ avatarInitials?: string;
22
+ /**
23
+ * Additional CSS classes
24
+ */
25
+ class?: string;
26
+ /**
27
+ * Action icons snippet (e.g., notification bell, sync icon)
28
+ */
29
+ actions?: import('svelte').Snippet;
30
+ }
31
+
32
+ let {
33
+ greeting = 'Welcome back,',
34
+ userName,
35
+ avatarSrc,
36
+ avatarInitials,
37
+ class: className = '',
38
+ actions
39
+ }: Props = $props();
40
+
41
+ // Use custom initials if provided, otherwise derive from userName
42
+ const displayName = $derived(avatarInitials || userName);
43
+ </script>
44
+
45
+ <header class="page-header {className}">
46
+ <div class="page-header__content">
47
+ <div class="page-header__greeting">
48
+ <span class="page-header__greeting-text">{greeting}</span>
49
+ <h1 class="page-header__name">{userName}</h1>
50
+ </div>
51
+
52
+ <div class="page-header__actions-wrapper">
53
+ {#if actions}
54
+ <div class="page-header__actions">
55
+ {@render actions()}
56
+ </div>
57
+ {/if}
58
+
59
+ <Avatar src={avatarSrc} name={displayName} size="md" class="page-header__avatar" />
60
+ </div>
61
+ </div>
62
+ </header>
63
+
64
+ <style>
65
+ .page-header {
66
+ font-family: inherit;
67
+ padding: var(--space-md, 1rem) var(--space-lg, 1.5rem);
68
+ }
69
+
70
+ .page-header__content {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: space-between;
74
+ gap: var(--space-md, 1rem);
75
+ flex-wrap: wrap;
76
+ }
77
+
78
+ .page-header__greeting {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: var(--space-xs, 0.25rem);
82
+ min-width: 0;
83
+ flex: 1;
84
+ }
85
+
86
+ .page-header__greeting-text {
87
+ font-size: 0.875rem;
88
+ line-height: 1.25rem;
89
+ color: var(--color-text-muted, #6b7280);
90
+ }
91
+
92
+ .page-header__name {
93
+ font-size: 1.5rem;
94
+ line-height: 2rem;
95
+ font-weight: 700;
96
+ color: var(--color-text, #1f2937);
97
+ margin: 0;
98
+ overflow: hidden;
99
+ text-overflow: ellipsis;
100
+ }
101
+
102
+ .page-header__actions-wrapper {
103
+ display: flex;
104
+ align-items: center;
105
+ gap: var(--space-sm, 0.5rem);
106
+ flex-shrink: 0;
107
+ }
108
+
109
+ .page-header__actions {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: var(--space-xs, 0.25rem);
113
+ }
114
+
115
+ .page-header__avatar {
116
+ flex-shrink: 0;
117
+ }
118
+
119
+ /* Dark mode support (F11) */
120
+ :global([data-theme='dark']) .page-header__greeting-text {
121
+ color: var(--color-text-muted, #9ca3af);
122
+ }
123
+
124
+ :global([data-theme='dark']) .page-header__name {
125
+ color: var(--color-text, #f9fafb);
126
+ }
127
+
128
+ /* Responsive - stack on very narrow screens */
129
+ @media (max-width: 360px) {
130
+ .page-header__content {
131
+ flex-direction: column;
132
+ align-items: flex-start;
133
+ gap: var(--space-sm, 0.5rem);
134
+ }
135
+
136
+ .page-header__actions-wrapper {
137
+ align-self: flex-end;
138
+ }
139
+ }
140
+ </style>
@@ -0,0 +1,30 @@
1
+ interface Props {
2
+ /**
3
+ * Greeting text to display (e.g., "Welcome back,")
4
+ * @default "Welcome back,"
5
+ */
6
+ greeting?: string;
7
+ /**
8
+ * User name to display prominently
9
+ */
10
+ userName: string;
11
+ /**
12
+ * Avatar image source URL
13
+ */
14
+ avatarSrc?: string;
15
+ /**
16
+ * Custom initials for avatar (overrides auto-generated from userName)
17
+ */
18
+ avatarInitials?: string;
19
+ /**
20
+ * Additional CSS classes
21
+ */
22
+ class?: string;
23
+ /**
24
+ * Action icons snippet (e.g., notification bell, sync icon)
25
+ */
26
+ actions?: import('svelte').Snippet;
27
+ }
28
+ declare const PageHeader: import("svelte").Component<Props, {}, "">;
29
+ type PageHeader = ReturnType<typeof PageHeader>;
30
+ export default PageHeader;
@@ -0,0 +1 @@
1
+ export { default as PageHeader } from './PageHeader.svelte';
@@ -0,0 +1 @@
1
+ export { default as PageHeader } from './PageHeader.svelte';
@@ -0,0 +1,172 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ /** The numeric value or string to display inside the circle */
4
+ value: number | string;
5
+ /** Label text displayed below the circle */
6
+ label: string;
7
+ /** Color variant for the value text */
8
+ color?: 'default' | 'primary' | 'warning' | 'error' | 'success';
9
+ /** Size of the circle */
10
+ size?: 'sm' | 'md' | 'lg';
11
+ /** Makes the circle clickable with hover effects */
12
+ clickable?: boolean;
13
+ /** Additional CSS classes */
14
+ class?: string;
15
+ /** Click handler */
16
+ onclick?: (event: MouseEvent) => void;
17
+ }
18
+
19
+ let {
20
+ value,
21
+ label,
22
+ color = 'default',
23
+ size = 'md',
24
+ clickable = false,
25
+ class: className = '',
26
+ onclick
27
+ }: Props = $props();
28
+ </script>
29
+
30
+ {#if clickable}
31
+ <button
32
+ class="stat-circle stat-circle--{color} stat-circle--{size} stat-circle--clickable {className}"
33
+ {onclick}
34
+ type="button"
35
+ >
36
+ <div class="stat-circle__circle">
37
+ <div class="stat-circle__value">{value}</div>
38
+ </div>
39
+ <div class="stat-circle__label">{label}</div>
40
+ </button>
41
+ {:else}
42
+ <div class="stat-circle stat-circle--{color} stat-circle--{size} {className}">
43
+ <div class="stat-circle__circle">
44
+ <div class="stat-circle__value">{value}</div>
45
+ </div>
46
+ <div class="stat-circle__label">{label}</div>
47
+ </div>
48
+ {/if}
49
+
50
+ <style>
51
+ .stat-circle {
52
+ display: inline-flex;
53
+ flex-direction: column;
54
+ align-items: center;
55
+ gap: var(--space-sm, 0.5rem);
56
+ font-family: inherit;
57
+ }
58
+
59
+ .stat-circle--clickable {
60
+ background: none;
61
+ border: none;
62
+ padding: 0;
63
+ cursor: pointer;
64
+ -webkit-tap-highlight-color: transparent;
65
+ }
66
+
67
+ .stat-circle--clickable:hover .stat-circle__circle {
68
+ transform: scale(1.05);
69
+ }
70
+
71
+ .stat-circle--clickable:active .stat-circle__circle {
72
+ transform: scale(0.98);
73
+ }
74
+
75
+ .stat-circle__circle {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ border-radius: var(--radius-full, 9999px);
80
+ background: var(--color-bg-subtle, #f9fafb);
81
+ border: 1px solid var(--color-border-subtle, #e5e7eb);
82
+ transition:
83
+ transform 0.2s ease,
84
+ box-shadow 0.2s ease;
85
+ }
86
+
87
+ .stat-circle--clickable:focus-visible .stat-circle__circle {
88
+ outline: 2px solid var(--color-focus, #3b82f6);
89
+ outline-offset: 2px;
90
+ }
91
+
92
+ .stat-circle__value {
93
+ font-weight: 700;
94
+ line-height: 1;
95
+ }
96
+
97
+ .stat-circle__label {
98
+ font-size: var(--text-sm, 0.875rem);
99
+ font-weight: 500;
100
+ color: var(--color-text-muted, #6b7280);
101
+ text-align: center;
102
+ }
103
+
104
+ /* Size variants */
105
+ .stat-circle--sm .stat-circle__circle {
106
+ width: 3rem;
107
+ height: 3rem;
108
+ min-width: 3rem;
109
+ min-height: 3rem;
110
+ }
111
+
112
+ .stat-circle--sm .stat-circle__value {
113
+ font-size: var(--text-lg, 1.125rem);
114
+ }
115
+
116
+ .stat-circle--md .stat-circle__circle {
117
+ width: 4rem;
118
+ height: 4rem;
119
+ min-width: 4rem;
120
+ min-height: 4rem;
121
+ }
122
+
123
+ .stat-circle--md .stat-circle__value {
124
+ font-size: var(--text-2xl, 1.5rem);
125
+ }
126
+
127
+ .stat-circle--lg .stat-circle__circle {
128
+ width: 5rem;
129
+ height: 5rem;
130
+ min-width: 5rem;
131
+ min-height: 5rem;
132
+ }
133
+
134
+ .stat-circle--lg .stat-circle__value {
135
+ font-size: var(--text-3xl, 1.875rem);
136
+ }
137
+
138
+ /* Color variants */
139
+ .stat-circle--default .stat-circle__value {
140
+ color: var(--color-text, #1f2937);
141
+ }
142
+
143
+ .stat-circle--primary .stat-circle__value {
144
+ color: var(--color-primary, #3b82f6);
145
+ }
146
+
147
+ .stat-circle--warning .stat-circle__value {
148
+ color: var(--color-warning, #f59e0b);
149
+ }
150
+
151
+ .stat-circle--error .stat-circle__value {
152
+ color: var(--color-error, #ef4444);
153
+ }
154
+
155
+ .stat-circle--success .stat-circle__value {
156
+ color: var(--color-success, #10b981);
157
+ }
158
+
159
+ /* Dark mode support */
160
+ :global([data-theme='dark']) .stat-circle__circle {
161
+ background: var(--color-bg-subtle-dark, #1f2937);
162
+ border-color: var(--color-border-subtle-dark, #374151);
163
+ }
164
+
165
+ :global([data-theme='dark']) .stat-circle__label {
166
+ color: var(--color-text-muted-dark, #9ca3af);
167
+ }
168
+
169
+ :global([data-theme='dark']) .stat-circle--default .stat-circle__value {
170
+ color: var(--color-text-dark, #f9fafb);
171
+ }
172
+ </style>
@@ -0,0 +1,19 @@
1
+ interface Props {
2
+ /** The numeric value or string to display inside the circle */
3
+ value: number | string;
4
+ /** Label text displayed below the circle */
5
+ label: string;
6
+ /** Color variant for the value text */
7
+ color?: 'default' | 'primary' | 'warning' | 'error' | 'success';
8
+ /** Size of the circle */
9
+ size?: 'sm' | 'md' | 'lg';
10
+ /** Makes the circle clickable with hover effects */
11
+ clickable?: boolean;
12
+ /** Additional CSS classes */
13
+ class?: string;
14
+ /** Click handler */
15
+ onclick?: (event: MouseEvent) => void;
16
+ }
17
+ declare const StatCircle: import("svelte").Component<Props, {}, "">;
18
+ type StatCircle = ReturnType<typeof StatCircle>;
19
+ export default StatCircle;
@@ -0,0 +1 @@
1
+ export { default as StatCircle } from './StatCircle.svelte';
@@ -0,0 +1 @@
1
+ export { default as StatCircle } from './StatCircle.svelte';