@makolabs/ripple 1.12.0 → 1.13.1

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 (49) hide show
  1. package/dist/elements/collapsible/Collapsible.svelte +79 -0
  2. package/dist/elements/collapsible/Collapsible.svelte.d.ts +4 -0
  3. package/dist/elements/collapsible/CollapsibleTestWrapper.svelte +23 -0
  4. package/dist/elements/collapsible/CollapsibleTestWrapper.svelte.d.ts +8 -0
  5. package/dist/elements/collapsible/collapsible-types.d.ts +16 -0
  6. package/dist/elements/collapsible/collapsible-types.js +1 -0
  7. package/dist/elements/combobox/Combobox.svelte +274 -0
  8. package/dist/elements/combobox/Combobox.svelte.d.ts +25 -0
  9. package/dist/elements/combobox/ComboboxTestWrapper.svelte +38 -0
  10. package/dist/elements/combobox/ComboboxTestWrapper.svelte.d.ts +4 -0
  11. package/dist/elements/combobox/combobox-types.d.ts +39 -0
  12. package/dist/elements/combobox/combobox-types.js +1 -0
  13. package/dist/elements/empty-state/EmptyState.svelte +39 -0
  14. package/dist/elements/empty-state/EmptyState.svelte.d.ts +4 -0
  15. package/dist/elements/empty-state/EmptyStateTestWrapper.svelte +25 -0
  16. package/dist/elements/empty-state/EmptyStateTestWrapper.svelte.d.ts +8 -0
  17. package/dist/elements/empty-state/empty-state-types.d.ts +12 -0
  18. package/dist/elements/empty-state/empty-state-types.js +1 -0
  19. package/dist/elements/empty-state/empty-state.d.ts +100 -0
  20. package/dist/elements/empty-state/empty-state.js +42 -0
  21. package/dist/elements/pagination/Pagination.svelte +1 -1
  22. package/dist/elements/spinner/Spinner.svelte +38 -0
  23. package/dist/elements/spinner/Spinner.svelte.d.ts +4 -0
  24. package/dist/elements/spinner/spinner-types.d.ts +9 -0
  25. package/dist/elements/spinner/spinner-types.js +1 -0
  26. package/dist/elements/spinner/spinner.d.ts +163 -0
  27. package/dist/elements/spinner/spinner.js +32 -0
  28. package/dist/elements/tooltip/Tooltip.svelte +82 -0
  29. package/dist/elements/tooltip/Tooltip.svelte.d.ts +4 -0
  30. package/dist/elements/tooltip/TooltipTestWrapper.svelte +14 -0
  31. package/dist/elements/tooltip/TooltipTestWrapper.svelte.d.ts +7 -0
  32. package/dist/elements/tooltip/tooltip-types.d.ts +13 -0
  33. package/dist/elements/tooltip/tooltip-types.js +1 -0
  34. package/dist/index.d.ts +13 -0
  35. package/dist/index.js +13 -0
  36. package/dist/layout/card/MetricCard.svelte +61 -5
  37. package/dist/layout/card/MetricCardActionWrapper.svelte +29 -0
  38. package/dist/layout/card/MetricCardActionWrapper.svelte.d.ts +8 -0
  39. package/dist/layout/card/card-types.d.ts +19 -0
  40. package/dist/layout/card/metric-card.d.ts +26 -26
  41. package/dist/layout/card/metric-card.js +16 -2
  42. package/dist/layout/navbar/navbar.js +1 -1
  43. package/dist/modal/Modal.svelte +1 -1
  44. package/dist/modal/ModalFooter.svelte +35 -0
  45. package/dist/modal/ModalFooter.svelte.d.ts +11 -0
  46. package/dist/modal/ModalFooterTestWrapper.svelte +17 -0
  47. package/dist/modal/ModalFooterTestWrapper.svelte.d.ts +8 -0
  48. package/dist/modal/modal.js +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,100 @@
1
+ export declare const emptyState: import("tailwind-variants").TVReturnType<{
2
+ size: {
3
+ sm: {
4
+ wrapper: string;
5
+ iconWrapper: string;
6
+ title: string;
7
+ description: string;
8
+ };
9
+ base: {
10
+ wrapper: string;
11
+ iconWrapper: string;
12
+ title: string;
13
+ description: string;
14
+ };
15
+ lg: {
16
+ wrapper: string;
17
+ iconWrapper: string;
18
+ title: string;
19
+ description: string;
20
+ };
21
+ xl: {
22
+ wrapper: string;
23
+ iconWrapper: string;
24
+ title: string;
25
+ description: string;
26
+ };
27
+ };
28
+ }, {
29
+ wrapper: string;
30
+ iconWrapper: string;
31
+ title: string;
32
+ description: string;
33
+ actionWrapper: string;
34
+ }, undefined, {
35
+ size: {
36
+ sm: {
37
+ wrapper: string;
38
+ iconWrapper: string;
39
+ title: string;
40
+ description: string;
41
+ };
42
+ base: {
43
+ wrapper: string;
44
+ iconWrapper: string;
45
+ title: string;
46
+ description: string;
47
+ };
48
+ lg: {
49
+ wrapper: string;
50
+ iconWrapper: string;
51
+ title: string;
52
+ description: string;
53
+ };
54
+ xl: {
55
+ wrapper: string;
56
+ iconWrapper: string;
57
+ title: string;
58
+ description: string;
59
+ };
60
+ };
61
+ }, {
62
+ wrapper: string;
63
+ iconWrapper: string;
64
+ title: string;
65
+ description: string;
66
+ actionWrapper: string;
67
+ }, import("tailwind-variants").TVReturnType<{
68
+ size: {
69
+ sm: {
70
+ wrapper: string;
71
+ iconWrapper: string;
72
+ title: string;
73
+ description: string;
74
+ };
75
+ base: {
76
+ wrapper: string;
77
+ iconWrapper: string;
78
+ title: string;
79
+ description: string;
80
+ };
81
+ lg: {
82
+ wrapper: string;
83
+ iconWrapper: string;
84
+ title: string;
85
+ description: string;
86
+ };
87
+ xl: {
88
+ wrapper: string;
89
+ iconWrapper: string;
90
+ title: string;
91
+ description: string;
92
+ };
93
+ };
94
+ }, {
95
+ wrapper: string;
96
+ iconWrapper: string;
97
+ title: string;
98
+ description: string;
99
+ actionWrapper: string;
100
+ }, undefined, unknown, unknown, undefined>>;
@@ -0,0 +1,42 @@
1
+ import { tv } from 'tailwind-variants';
2
+ import { Size } from '../../variants.js';
3
+ export const emptyState = tv({
4
+ slots: {
5
+ wrapper: 'flex flex-col items-center justify-center text-center',
6
+ iconWrapper: 'text-default-400',
7
+ title: 'font-semibold text-default-800',
8
+ description: 'text-default-500 max-w-md',
9
+ actionWrapper: 'mt-2'
10
+ },
11
+ variants: {
12
+ size: {
13
+ [Size.SM]: {
14
+ wrapper: 'gap-2 p-6',
15
+ iconWrapper: 'h-8 w-8',
16
+ title: 'text-sm',
17
+ description: 'text-xs'
18
+ },
19
+ [Size.BASE]: {
20
+ wrapper: 'gap-3 p-8',
21
+ iconWrapper: 'h-10 w-10',
22
+ title: 'text-base',
23
+ description: 'text-sm'
24
+ },
25
+ [Size.LG]: {
26
+ wrapper: 'gap-4 p-12',
27
+ iconWrapper: 'h-14 w-14',
28
+ title: 'text-lg',
29
+ description: 'text-base'
30
+ },
31
+ [Size.XL]: {
32
+ wrapper: 'gap-5 p-16',
33
+ iconWrapper: 'h-16 w-16',
34
+ title: 'text-xl',
35
+ description: 'text-base'
36
+ }
37
+ }
38
+ },
39
+ defaultVariants: {
40
+ size: Size.BASE
41
+ }
42
+ });
@@ -252,7 +252,7 @@
252
252
  const defaultButtonClass =
253
253
  'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium';
254
254
  const defaultActiveButtonClass = 'bg-primary-100 text-primary-700';
255
- const defaultInactiveButtonClass = 'text-default-700 hover:bg-default-100';
255
+ const defaultInactiveButtonClass = 'text-default-700 hover:bg-default-100 cursor-pointer';
256
256
  const defaultDisabledButtonClass = 'text-default-300 cursor-not-allowed';
257
257
  const defaultPageButtonClass =
258
258
  'relative inline-flex items-center rounded-md px-3 py-1 text-sm font-medium';
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../helper/cls.js';
3
+ import { buildTestId } from '../../helper/testid.js';
4
+ import { spinner } from './spinner.js';
5
+ import { Color, Size } from '../../variants.js';
6
+ import type { SpinnerProps } from '../../index.js';
7
+
8
+ let {
9
+ size = Size.BASE,
10
+ color = Color.DEFAULT,
11
+ label,
12
+ class: className = '',
13
+ testId
14
+ }: SpinnerProps = $props();
15
+
16
+ const slots = $derived(spinner({ size, color }));
17
+ </script>
18
+
19
+ <span
20
+ class={cn(slots.wrapper(), className)}
21
+ role="status"
22
+ aria-live="polite"
23
+ data-testid={buildTestId('spinner', undefined, testId)}
24
+ >
25
+ <span
26
+ class={cn(
27
+ 'inline-block animate-spin rounded-full border-2 border-current border-t-transparent align-[-0.125em]',
28
+ slots.svg()
29
+ )}
30
+ data-spinner-ring=""
31
+ aria-hidden={label ? 'true' : undefined}
32
+ ></span>
33
+ {#if label}
34
+ <span class={slots.label()}>{label}</span>
35
+ {:else}
36
+ <span class="sr-only">Loading</span>
37
+ {/if}
38
+ </span>
@@ -0,0 +1,4 @@
1
+ import type { SpinnerProps } from '../../index.js';
2
+ declare const Spinner: import("svelte").Component<SpinnerProps, {}, "">;
3
+ type Spinner = ReturnType<typeof Spinner>;
4
+ export default Spinner;
@@ -0,0 +1,9 @@
1
+ import type { ClassValue } from 'tailwind-variants';
2
+ import type { VariantColors, VariantSizes } from '../../index.js';
3
+ export type SpinnerProps = {
4
+ size?: VariantSizes;
5
+ color?: VariantColors;
6
+ label?: string;
7
+ class?: ClassValue;
8
+ testId?: string;
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,163 @@
1
+ export declare const spinner: import("tailwind-variants").TVReturnType<{
2
+ size: {
3
+ xs: {
4
+ svg: string;
5
+ label: string;
6
+ };
7
+ sm: {
8
+ svg: string;
9
+ label: string;
10
+ };
11
+ base: {
12
+ svg: string;
13
+ label: string;
14
+ };
15
+ lg: {
16
+ svg: string;
17
+ label: string;
18
+ };
19
+ xl: {
20
+ svg: string;
21
+ label: string;
22
+ };
23
+ "2xl": {
24
+ svg: string;
25
+ label: string;
26
+ };
27
+ };
28
+ color: {
29
+ default: {
30
+ svg: string;
31
+ };
32
+ primary: {
33
+ svg: string;
34
+ };
35
+ secondary: {
36
+ svg: string;
37
+ };
38
+ info: {
39
+ svg: string;
40
+ };
41
+ success: {
42
+ svg: string;
43
+ };
44
+ warning: {
45
+ svg: string;
46
+ };
47
+ danger: {
48
+ svg: string;
49
+ };
50
+ };
51
+ }, {
52
+ wrapper: string;
53
+ svg: string;
54
+ label: string;
55
+ }, undefined, {
56
+ size: {
57
+ xs: {
58
+ svg: string;
59
+ label: string;
60
+ };
61
+ sm: {
62
+ svg: string;
63
+ label: string;
64
+ };
65
+ base: {
66
+ svg: string;
67
+ label: string;
68
+ };
69
+ lg: {
70
+ svg: string;
71
+ label: string;
72
+ };
73
+ xl: {
74
+ svg: string;
75
+ label: string;
76
+ };
77
+ "2xl": {
78
+ svg: string;
79
+ label: string;
80
+ };
81
+ };
82
+ color: {
83
+ default: {
84
+ svg: string;
85
+ };
86
+ primary: {
87
+ svg: string;
88
+ };
89
+ secondary: {
90
+ svg: string;
91
+ };
92
+ info: {
93
+ svg: string;
94
+ };
95
+ success: {
96
+ svg: string;
97
+ };
98
+ warning: {
99
+ svg: string;
100
+ };
101
+ danger: {
102
+ svg: string;
103
+ };
104
+ };
105
+ }, {
106
+ wrapper: string;
107
+ svg: string;
108
+ label: string;
109
+ }, import("tailwind-variants").TVReturnType<{
110
+ size: {
111
+ xs: {
112
+ svg: string;
113
+ label: string;
114
+ };
115
+ sm: {
116
+ svg: string;
117
+ label: string;
118
+ };
119
+ base: {
120
+ svg: string;
121
+ label: string;
122
+ };
123
+ lg: {
124
+ svg: string;
125
+ label: string;
126
+ };
127
+ xl: {
128
+ svg: string;
129
+ label: string;
130
+ };
131
+ "2xl": {
132
+ svg: string;
133
+ label: string;
134
+ };
135
+ };
136
+ color: {
137
+ default: {
138
+ svg: string;
139
+ };
140
+ primary: {
141
+ svg: string;
142
+ };
143
+ secondary: {
144
+ svg: string;
145
+ };
146
+ info: {
147
+ svg: string;
148
+ };
149
+ success: {
150
+ svg: string;
151
+ };
152
+ warning: {
153
+ svg: string;
154
+ };
155
+ danger: {
156
+ svg: string;
157
+ };
158
+ };
159
+ }, {
160
+ wrapper: string;
161
+ svg: string;
162
+ label: string;
163
+ }, undefined, unknown, unknown, undefined>>;
@@ -0,0 +1,32 @@
1
+ import { tv } from 'tailwind-variants';
2
+ import { Color, Size } from '../../variants.js';
3
+ export const spinner = tv({
4
+ slots: {
5
+ wrapper: 'inline-flex items-center gap-2',
6
+ svg: 'animate-spin',
7
+ label: 'text-default-600'
8
+ },
9
+ variants: {
10
+ size: {
11
+ [Size.XS]: { svg: 'h-3 w-3', label: 'text-xs' },
12
+ [Size.SM]: { svg: 'h-4 w-4', label: 'text-xs' },
13
+ [Size.BASE]: { svg: 'h-5 w-5', label: 'text-sm' },
14
+ [Size.LG]: { svg: 'h-6 w-6', label: 'text-base' },
15
+ [Size.XL]: { svg: 'h-8 w-8', label: 'text-lg' },
16
+ [Size.XXL]: { svg: 'h-12 w-12', label: 'text-xl' }
17
+ },
18
+ color: {
19
+ [Color.DEFAULT]: { svg: 'text-default-500' },
20
+ [Color.PRIMARY]: { svg: 'text-primary-600' },
21
+ [Color.SECONDARY]: { svg: 'text-secondary-600' },
22
+ [Color.INFO]: { svg: 'text-info-600' },
23
+ [Color.SUCCESS]: { svg: 'text-success-600' },
24
+ [Color.WARNING]: { svg: 'text-warning-600' },
25
+ [Color.DANGER]: { svg: 'text-danger-600' }
26
+ }
27
+ },
28
+ defaultVariants: {
29
+ size: Size.BASE,
30
+ color: Color.DEFAULT
31
+ }
32
+ });
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../helper/cls.js';
3
+ import { buildTestId } from '../../helper/testid.js';
4
+ import type { TooltipProps } from '../../index.js';
5
+
6
+ let {
7
+ content,
8
+ placement = 'top',
9
+ delay = 100,
10
+ disabled = false,
11
+ class: className = '',
12
+ tooltipClass = '',
13
+ children,
14
+ testId
15
+ }: TooltipProps = $props();
16
+
17
+ let visible = $state(false);
18
+ let showTimer: ReturnType<typeof setTimeout> | undefined;
19
+
20
+ function show() {
21
+ if (disabled) return;
22
+ clearTimeout(showTimer);
23
+ if (delay > 0) {
24
+ showTimer = setTimeout(() => {
25
+ visible = true;
26
+ }, delay);
27
+ } else {
28
+ visible = true;
29
+ }
30
+ }
31
+
32
+ function hide() {
33
+ clearTimeout(showTimer);
34
+ visible = false;
35
+ }
36
+
37
+ const placementClass = $derived(
38
+ {
39
+ top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
40
+ bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',
41
+ left: 'right-full top-1/2 -translate-y-1/2 mr-2',
42
+ right: 'left-full top-1/2 -translate-y-1/2 ml-2'
43
+ }[placement]
44
+ );
45
+
46
+ const arrowClass = $derived(
47
+ {
48
+ top: 'top-full left-1/2 -translate-x-1/2 border-t-default-800 border-l-transparent border-r-transparent border-b-transparent',
49
+ bottom:
50
+ 'bottom-full left-1/2 -translate-x-1/2 border-b-default-800 border-l-transparent border-r-transparent border-t-transparent',
51
+ left: 'left-full top-1/2 -translate-y-1/2 border-l-default-800 border-t-transparent border-b-transparent border-r-transparent',
52
+ right:
53
+ 'right-full top-1/2 -translate-y-1/2 border-r-default-800 border-t-transparent border-b-transparent border-l-transparent'
54
+ }[placement]
55
+ );
56
+ </script>
57
+
58
+ <span
59
+ class={cn('relative inline-flex', className)}
60
+ data-testid={buildTestId('tooltip', undefined, testId)}
61
+ onmouseenter={show}
62
+ onmouseleave={hide}
63
+ onfocusin={show}
64
+ onfocusout={hide}
65
+ role="group"
66
+ >
67
+ {@render children()}
68
+ {#if visible && !disabled}
69
+ <span
70
+ role="tooltip"
71
+ data-tooltip-visible="true"
72
+ class={cn(
73
+ 'bg-default-800 pointer-events-none absolute z-50 rounded px-2 py-1 text-xs whitespace-nowrap text-white shadow-lg',
74
+ placementClass,
75
+ tooltipClass
76
+ )}
77
+ >
78
+ {content}
79
+ <span class={cn('absolute h-0 w-0 border-4', arrowClass)} aria-hidden="true"></span>
80
+ </span>
81
+ {/if}
82
+ </span>
@@ -0,0 +1,4 @@
1
+ import type { TooltipProps } from '../../index.js';
2
+ declare const Tooltip: import("svelte").Component<TooltipProps, {}, "">;
3
+ type Tooltip = ReturnType<typeof Tooltip>;
4
+ export default Tooltip;
@@ -0,0 +1,14 @@
1
+ <script lang="ts">
2
+ import Tooltip from './Tooltip.svelte';
3
+ import type { TooltipProps } from '../../index.js';
4
+
5
+ type Props = Omit<TooltipProps, 'children'> & {
6
+ triggerText?: string;
7
+ };
8
+
9
+ let { triggerText = 'trigger', ...rest }: Props = $props();
10
+ </script>
11
+
12
+ <Tooltip {...rest}>
13
+ <button type="button">{triggerText}</button>
14
+ </Tooltip>
@@ -0,0 +1,7 @@
1
+ import type { TooltipProps } from '../../index.js';
2
+ type Props = Omit<TooltipProps, 'children'> & {
3
+ triggerText?: string;
4
+ };
5
+ declare const TooltipTestWrapper: import("svelte").Component<Props, {}, "">;
6
+ type TooltipTestWrapper = ReturnType<typeof TooltipTestWrapper>;
7
+ export default TooltipTestWrapper;
@@ -0,0 +1,13 @@
1
+ import type { ClassValue } from 'tailwind-variants';
2
+ import type { Snippet } from 'svelte';
3
+ export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
4
+ export type TooltipProps = {
5
+ content: string;
6
+ placement?: TooltipPlacement;
7
+ delay?: number;
8
+ disabled?: boolean;
9
+ class?: ClassValue;
10
+ tooltipClass?: ClassValue;
11
+ children: Snippet;
12
+ testId?: string;
13
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -37,6 +37,11 @@ export { Market } from './forms/market/market.js';
37
37
  export type { MarketSelectorProps, MarketSelectorVariant } from './forms/market/market-selector-types.js';
38
38
  export { countryCodeToFlagEmoji } from './forms/market/flag-emoji.js';
39
39
  export type { ProgressSegment, ProgressProps } from './elements/progress/progress-types.js';
40
+ export type { SpinnerProps } from './elements/spinner/spinner-types.js';
41
+ export type { EmptyStateProps, EmptyStateSize } from './elements/empty-state/empty-state-types.js';
42
+ export type { CollapsibleProps } from './elements/collapsible/collapsible-types.js';
43
+ export type { TooltipProps, TooltipPlacement } from './elements/tooltip/tooltip-types.js';
44
+ export type { ComboboxItem, ComboboxProps } from './elements/combobox/combobox-types.js';
40
45
  export type { AccordionProps } from './elements/accordion/accordion-types.js';
41
46
  export type { TimelineItem } from './elements/timeline/timeline-types.js';
42
47
  export type { FilterTab, FilterGroup, CompactFiltersProps } from './filters/filter-types.js';
@@ -48,6 +53,7 @@ export { tv, cn } from './helper/cls.js';
48
53
  export { isRouteActive } from './helper/nav.svelte.js';
49
54
  export { default as Button } from './button/Button.svelte';
50
55
  export { default as Modal } from './modal/Modal.svelte';
56
+ export { default as ModalFooter } from './modal/ModalFooter.svelte';
51
57
  export { default as Pipeline } from './pipeline/Pipeline.svelte';
52
58
  export { pipelineVariants } from './pipeline/pipeline.js';
53
59
  export type { PipelineStage, PipelineStageColor, PipelineStageEvent, PipelineStageClickEvent, PipelineStagePointerEvent, PipelineProps } from './pipeline/pipeline-types.js';
@@ -74,6 +80,13 @@ export { default as NavItem } from './layout/sidebar/NavItem.svelte';
74
80
  export { default as NavGroup } from './layout/sidebar/NavGroup.svelte';
75
81
  export { default as ActivityList } from './layout/activity-list/ActivityList.svelte';
76
82
  export { default as Progress } from './elements/progress/Progress.svelte';
83
+ export { default as Spinner } from './elements/spinner/Spinner.svelte';
84
+ export { spinner as spinnerVariants } from './elements/spinner/spinner.js';
85
+ export { default as EmptyState } from './elements/empty-state/EmptyState.svelte';
86
+ export { emptyState as emptyStateVariants } from './elements/empty-state/empty-state.js';
87
+ export { default as Collapsible } from './elements/collapsible/Collapsible.svelte';
88
+ export { default as Tooltip } from './elements/tooltip/Tooltip.svelte';
89
+ export { default as Combobox } from './elements/combobox/Combobox.svelte';
77
90
  export { default as Accordion } from './elements/accordion/Accordion.svelte';
78
91
  export { default as Timeline } from './elements/timeline/Timeline.svelte';
79
92
  export { default as Chart } from './charts/Chart.svelte';
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export { isRouteActive } from './helper/nav.svelte.js';
24
24
  export { default as Button } from './button/Button.svelte';
25
25
  // Modal
26
26
  export { default as Modal } from './modal/Modal.svelte';
27
+ export { default as ModalFooter } from './modal/ModalFooter.svelte';
27
28
  // Pipeline
28
29
  export { default as Pipeline } from './pipeline/Pipeline.svelte';
29
30
  export { pipelineVariants } from './pipeline/pipeline.js';
@@ -60,6 +61,18 @@ export { default as NavGroup } from './layout/sidebar/NavGroup.svelte';
60
61
  export { default as ActivityList } from './layout/activity-list/ActivityList.svelte';
61
62
  // Elements - Progress
62
63
  export { default as Progress } from './elements/progress/Progress.svelte';
64
+ // Elements - Spinner
65
+ export { default as Spinner } from './elements/spinner/Spinner.svelte';
66
+ export { spinner as spinnerVariants } from './elements/spinner/spinner.js';
67
+ // Elements - EmptyState
68
+ export { default as EmptyState } from './elements/empty-state/EmptyState.svelte';
69
+ export { emptyState as emptyStateVariants } from './elements/empty-state/empty-state.js';
70
+ // Elements - Collapsible
71
+ export { default as Collapsible } from './elements/collapsible/Collapsible.svelte';
72
+ // Elements - Tooltip
73
+ export { default as Tooltip } from './elements/tooltip/Tooltip.svelte';
74
+ // Elements - Combobox
75
+ export { default as Combobox } from './elements/combobox/Combobox.svelte';
63
76
  // Elements - Accordion
64
77
  export { default as Accordion } from './elements/accordion/Accordion.svelte';
65
78
  // Elements - Timeline
@@ -11,21 +11,67 @@
11
11
  details = [],
12
12
  percent,
13
13
  segments,
14
- class: className = ''
14
+ class: className = '',
15
+ onclick,
16
+ action,
17
+ actionHover
15
18
  }: MetricCardProps = $props();
16
19
 
20
+ const interactive = $derived(!!onclick);
21
+ const hasHoverSwap = $derived(interactive && !!actionHover);
22
+
17
23
  const {
18
24
  base,
19
25
  title: titleSlot,
20
26
  value: valueSlot,
21
27
  detail: detailSlot,
22
- progress: progressSlot
23
- } = $derived(metricCard());
28
+ progress: progressSlot,
29
+ action: actionSlot
30
+ } = $derived(metricCard({ interactive }));
24
31
 
25
32
  const baseClass = $derived(cn(base(), 'flex flex-col h-full', className));
33
+
34
+ function handleClick(event: MouseEvent) {
35
+ onclick?.(event);
36
+ }
26
37
  </script>
27
38
 
28
- <div class={baseClass}>
39
+ {#snippet defaultArrow()}
40
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="h-4 w-4">
41
+ <path
42
+ fill-rule="evenodd"
43
+ d="M5.22 14.78a.75.75 0 001.06 0l7.22-7.22v4.69a.75.75 0 001.5 0v-6.5a.75.75 0 00-.75-.75h-6.5a.75.75 0 000 1.5h4.69L5.22 13.72a.75.75 0 000 1.06z"
44
+ clip-rule="evenodd"
45
+ />
46
+ </svg>
47
+ {/snippet}
48
+
49
+ {#snippet body()}
50
+ {#if interactive}
51
+ <!-- Base action icon (always visible, fades out on hover when a hover variant is provided). -->
52
+ <span
53
+ class={cn(actionSlot(), hasHoverSwap && 'group-hover:opacity-0')}
54
+ data-metric-card-action=""
55
+ aria-hidden="true"
56
+ >
57
+ {#if action}
58
+ {@render action()}
59
+ {:else}
60
+ {@render defaultArrow()}
61
+ {/if}
62
+ </span>
63
+ {#if hasHoverSwap}
64
+ <!-- Hover action icon (opacity-0 by default, fades in on hover). -->
65
+ <span
66
+ class={cn(actionSlot(), 'opacity-0 group-hover:opacity-100')}
67
+ data-metric-card-action-hover=""
68
+ aria-hidden="true"
69
+ >
70
+ {@render actionHover!()}
71
+ </span>
72
+ {/if}
73
+ {/if}
74
+
29
75
  {#if title}
30
76
  <div class={titleSlot()}>{title}</div>
31
77
  {/if}
@@ -61,4 +107,14 @@
61
107
  <Progress value={percent} size={Size.SM} color={Color.SUCCESS} showLabel={false} />
62
108
  </div>
63
109
  {/if}
64
- </div>
110
+ {/snippet}
111
+
112
+ {#if interactive}
113
+ <button type="button" class={baseClass} data-metric-card="" onclick={handleClick}>
114
+ {@render body()}
115
+ </button>
116
+ {:else}
117
+ <div class={baseClass} data-metric-card="">
118
+ {@render body()}
119
+ </div>
120
+ {/if}