@makolabs/ripple 0.2.2-0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +43 -0
- package/dist/charts/Chart.svelte +3 -3
- package/dist/elements/dropdown/Dropdown.svelte +8 -3
- package/dist/index.d.ts +5 -20
- package/dist/index.js +0 -1
- package/package.json +121 -112
- package/dist/layout/card/StatsCard.svelte +0 -266
- package/dist/layout/card/StatsCard.svelte.d.ts +0 -4
- package/dist/layout/card/stats-card.d.ts +0 -191
- package/dist/layout/card/stats-card.js +0 -73
package/LICENSE
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Ripple UI License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 MakoLabs AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to use
|
|
7
|
+
the Software for personal, educational, and non-commercial purposes, subject
|
|
8
|
+
to the following conditions:
|
|
9
|
+
|
|
10
|
+
PERSONAL AND NON-COMMERCIAL USE:
|
|
11
|
+
- Personal projects and educational use are permitted free of charge
|
|
12
|
+
- Open source projects are permitted free of charge
|
|
13
|
+
- Non-profit organizations are permitted free of charge
|
|
14
|
+
|
|
15
|
+
COMMERCIAL USE RESTRICTIONS:
|
|
16
|
+
- Any commercial use of this Software requires a separate commercial license
|
|
17
|
+
- Commercial use includes, but is not limited to:
|
|
18
|
+
- Use in commercial products or services
|
|
19
|
+
- Use by for-profit organizations
|
|
20
|
+
- Use in products that generate revenue
|
|
21
|
+
- Integration into commercial applications
|
|
22
|
+
|
|
23
|
+
COMMERCIAL LICENSING:
|
|
24
|
+
For commercial use, please contact MakoLabs AI to obtain a commercial license:
|
|
25
|
+
- Email: licensing@makolabs.ai
|
|
26
|
+
- Website: https://makolabs.ai
|
|
27
|
+
|
|
28
|
+
CONDITIONS FOR ALL USE:
|
|
29
|
+
- The above copyright notice and this permission notice shall be included in
|
|
30
|
+
all copies or substantial portions of the Software
|
|
31
|
+
- Attribution to MakoLabs AI must be maintained in any derivative works
|
|
32
|
+
|
|
33
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
34
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
35
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
36
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
37
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
38
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
39
|
+
SOFTWARE.
|
|
40
|
+
|
|
41
|
+
VIOLATION OF LICENSE:
|
|
42
|
+
Any commercial use without a proper commercial license constitutes copyright
|
|
43
|
+
infringement and may result in legal action.
|
package/dist/charts/Chart.svelte
CHANGED
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
function getAreaStyle(seriesConfig: SeriesConfig<any>) {
|
|
128
128
|
if (!seriesConfig.color) return undefined;
|
|
129
129
|
if (!seriesConfig.showArea) return undefined;
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
const baseLinearAreaStyle = {
|
|
132
132
|
type: 'linear',
|
|
133
133
|
x: 0,
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
type: actualType,
|
|
266
266
|
yAxisIndex: seriesConfig.yAxisIndex || 0,
|
|
267
267
|
data: seriesData,
|
|
268
|
-
stack:
|
|
268
|
+
stack: seriesConfig.stack,
|
|
269
269
|
barWidth:
|
|
270
270
|
seriesConfig.barWidth ||
|
|
271
271
|
(barSeriesCount > 3 ? '15%' : barSeriesCount > 1 ? '25%' : '60%'),
|
|
@@ -274,7 +274,7 @@
|
|
|
274
274
|
color: getColor(seriesConfig.color),
|
|
275
275
|
itemStyle: {
|
|
276
276
|
color: getColor(seriesConfig.color),
|
|
277
|
-
borderRadius: [
|
|
277
|
+
borderRadius: [0, 0, 0, 0],
|
|
278
278
|
opacity: seriesConfig.opacity || 1
|
|
279
279
|
}
|
|
280
280
|
}),
|
|
@@ -114,12 +114,17 @@
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
function handleClickOutside(event: MouseEvent) {
|
|
117
|
+
// Only handle if this specific dropdown is open
|
|
118
|
+
if (!isOpen) return;
|
|
119
|
+
|
|
120
|
+
const target = event.target as Node;
|
|
121
|
+
|
|
122
|
+
// Check if click is outside both the dropdown and trigger
|
|
117
123
|
if (
|
|
118
|
-
isOpen &&
|
|
119
124
|
dropdownRef &&
|
|
120
|
-
!dropdownRef.contains(
|
|
125
|
+
!dropdownRef.contains(target) &&
|
|
121
126
|
triggerRef &&
|
|
122
|
-
!triggerRef.contains(
|
|
127
|
+
!triggerRef.contains(target)
|
|
123
128
|
) {
|
|
124
129
|
isOpen = false;
|
|
125
130
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { Color, Size, ChartColor };
|
|
|
17
17
|
import type { ClassValue } from 'tailwind-variants';
|
|
18
18
|
import type { Snippet } from 'svelte';
|
|
19
19
|
import type { Component } from 'svelte';
|
|
20
|
-
import type { HTMLButtonAttributes, HTMLAttributeAnchorTarget } from 'svelte/elements';
|
|
20
|
+
import type { HTMLButtonAttributes, HTMLAttributeAnchorTarget, HTMLInputAttributes } from 'svelte/elements';
|
|
21
21
|
import type { ECharts } from 'echarts/types/src/export/core.js';
|
|
22
22
|
import type { SuperForm } from 'sveltekit-superforms';
|
|
23
23
|
import type { FileAction, StorageAdapter } from './adapters/storage/types.js';
|
|
@@ -165,18 +165,6 @@ export type AlertProps = {
|
|
|
165
165
|
footer?: Snippet;
|
|
166
166
|
icon?: Component;
|
|
167
167
|
};
|
|
168
|
-
export type StatsCardProps = {
|
|
169
|
-
label?: string;
|
|
170
|
-
value?: string | number;
|
|
171
|
-
previousValue?: string | number;
|
|
172
|
-
previousValuePrefix?: string;
|
|
173
|
-
trend?: number;
|
|
174
|
-
color?: VariantColors;
|
|
175
|
-
chartData?: number[];
|
|
176
|
-
children?: Snippet;
|
|
177
|
-
class?: ClassValue;
|
|
178
|
-
formatLargeNumbers?: boolean;
|
|
179
|
-
};
|
|
180
168
|
export type MetricDetail = {
|
|
181
169
|
label: string;
|
|
182
170
|
value: string | number;
|
|
@@ -355,7 +343,6 @@ export { default as Badge } from './elements/badge/Badge.svelte';
|
|
|
355
343
|
export { default as Dropdown } from './elements/dropdown/Dropdown.svelte';
|
|
356
344
|
export { default as Select } from './elements/dropdown/Select.svelte';
|
|
357
345
|
export { default as Card } from './layout/card/Card.svelte';
|
|
358
|
-
export { default as StatsCard } from './layout/card/StatsCard.svelte';
|
|
359
346
|
export { default as MetricCard } from './layout/card/MetricCard.svelte';
|
|
360
347
|
export { default as Alert } from './elements/alert/Alert.svelte';
|
|
361
348
|
export type TabProps = {
|
|
@@ -616,7 +603,9 @@ export interface FormProps<T extends Record<string, unknown>> {
|
|
|
616
603
|
novalidate?: boolean;
|
|
617
604
|
children?: Snippet;
|
|
618
605
|
}
|
|
619
|
-
export type
|
|
606
|
+
export type InputProps = {
|
|
607
|
+
type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'date' | 'textarea';
|
|
608
|
+
value?: string | number;
|
|
620
609
|
name: string;
|
|
621
610
|
label?: string;
|
|
622
611
|
placeholder?: string;
|
|
@@ -627,11 +616,7 @@ export type BaseInputProps = {
|
|
|
627
616
|
color?: VariantColors;
|
|
628
617
|
id?: string;
|
|
629
618
|
errors?: string[];
|
|
630
|
-
};
|
|
631
|
-
export type InputProps = {
|
|
632
|
-
type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'date' | 'textarea';
|
|
633
|
-
value?: string | number;
|
|
634
|
-
} & BaseInputProps;
|
|
619
|
+
} & HTMLInputAttributes;
|
|
635
620
|
export type RadioOption = {
|
|
636
621
|
value: string;
|
|
637
622
|
label: string;
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,6 @@ export { default as Dropdown } from './elements/dropdown/Dropdown.svelte';
|
|
|
27
27
|
export { default as Select } from './elements/dropdown/Select.svelte';
|
|
28
28
|
// Elements - Card
|
|
29
29
|
export { default as Card } from './layout/card/Card.svelte';
|
|
30
|
-
export { default as StatsCard } from './layout/card/StatsCard.svelte';
|
|
31
30
|
export { default as MetricCard } from './layout/card/MetricCard.svelte';
|
|
32
31
|
// Elements - Alert
|
|
33
32
|
export { default as Alert } from './elements/alert/Alert.svelte';
|
package/package.json
CHANGED
|
@@ -1,114 +1,123 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
2
|
+
"name": "@makolabs/ripple",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Simple Svelte 5 powered component library ✨",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/makolabsai/ripple-ui.git"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "vite dev",
|
|
12
|
+
"build": "vite build && npm run prepack",
|
|
13
|
+
"preview": "vite preview",
|
|
14
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
15
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
16
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
17
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
18
|
+
"format": "prettier --write .",
|
|
19
|
+
"lint": "prettier --check . && eslint .",
|
|
20
|
+
"test:unit": "vitest",
|
|
21
|
+
"test": "npm run test:unit -- --run && npm run test:e2e",
|
|
22
|
+
"test:e2e": "playwright test",
|
|
23
|
+
"pub:minor": "git add . && git commit -m \"chore: prepare for publish minor\" && npm version minor && git push --follow-tags && npm publish",
|
|
24
|
+
"pub:patch": "git add . && git commit -m \"chore: prepare for publish patch\" && npm version patch && git push --follow-tags && npm publish",
|
|
25
|
+
"pub:prelish": "git add . && git commit -m \"chore: prepare for publish premajor\" && npm version premajor && git push --follow-tags && npm publish",
|
|
26
|
+
"pub:major": "git add . && git commit -m \"chore: prepare for publish major\" && npm version major && git push --follow-tags && npm publish",
|
|
27
|
+
"pub:preminor": "git add . && git commit -m \"chore: prepare for publish preminor\" && npm version preminor && git push --follow-tags && npm publish",
|
|
28
|
+
"pub:prepatch": "git add . && git commit -m \"chore: prepare for publish prepatch\" && npm version prepatch && git push --follow-tags && npm publish",
|
|
29
|
+
"pub:prerelease": "git add . && git commit -m \"chore: prepare for publish prerelease\" && npm version prerelease && git push --follow-tags && npm publish",
|
|
30
|
+
"pub:from-git": "git add . && git commit -m \"chore: prepare for publish from-git\" && npm version from-git && git push --follow-tags && npm publish",
|
|
31
|
+
"storybook": "storybook dev -p 6006",
|
|
32
|
+
"build-storybook": "storybook build"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"!dist/**/*.test.*",
|
|
37
|
+
"!dist/**/*.spec.*"
|
|
38
|
+
],
|
|
39
|
+
"sideEffects": [
|
|
40
|
+
"**/*.css"
|
|
41
|
+
],
|
|
42
|
+
"svelte": "./dist/index.js",
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"type": "module",
|
|
45
|
+
"exports": {
|
|
46
|
+
".": {
|
|
47
|
+
"types": "./dist/index.d.ts",
|
|
48
|
+
"svelte": "./dist/index.js"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"svelte": "^5.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@eslint/compat": "^1.2.5",
|
|
56
|
+
"@eslint/js": "^9.18.0",
|
|
57
|
+
"@playwright/test": "^1.49.1",
|
|
58
|
+
"@storybook/addon-a11y": "^9.0.18",
|
|
59
|
+
"@storybook/addon-docs": "^9.0.18",
|
|
60
|
+
"@storybook/addon-svelte-csf": "^5.0.7",
|
|
61
|
+
"@storybook/sveltekit": "^9.0.18",
|
|
62
|
+
"@sveltejs/adapter-cloudflare": "^5.0.1",
|
|
63
|
+
"@sveltejs/kit": "^2.16.0",
|
|
64
|
+
"@sveltejs/package": "^2.0.0",
|
|
65
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
66
|
+
"@tailwindcss/vite": "^4.0.14",
|
|
67
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
68
|
+
"@testing-library/svelte": "^5.2.4",
|
|
69
|
+
"@types/node": "^22.13.10",
|
|
70
|
+
"eslint": "^9.18.0",
|
|
71
|
+
"eslint-config-prettier": "^10.0.1",
|
|
72
|
+
"eslint-plugin-storybook": "^9.0.18",
|
|
73
|
+
"eslint-plugin-svelte": "^3.0.0",
|
|
74
|
+
"globals": "^16.0.0",
|
|
75
|
+
"jsdom": "^26.0.0",
|
|
76
|
+
"prettier": "^3.4.2",
|
|
77
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
78
|
+
"prettier-plugin-tailwindcss": "^0.6.11",
|
|
79
|
+
"publint": "^0.3.2",
|
|
80
|
+
"storybook": "^9.0.18",
|
|
81
|
+
"svelte": "^5.0.0",
|
|
82
|
+
"svelte-check": "^4.0.0",
|
|
83
|
+
"sveltekit-superforms": "^2.24.0",
|
|
84
|
+
"tailwindcss": "^4.0.0",
|
|
85
|
+
"typescript": "^5.0.0",
|
|
86
|
+
"typescript-eslint": "^8.20.0",
|
|
87
|
+
"vite": "^6.0.0",
|
|
88
|
+
"vitest": "^3.0.0",
|
|
89
|
+
"zod": "^3.24.2"
|
|
90
|
+
},
|
|
91
|
+
"keywords": [
|
|
92
|
+
"svelte",
|
|
93
|
+
"ui",
|
|
94
|
+
"svelte5",
|
|
95
|
+
"modal",
|
|
96
|
+
"navigation",
|
|
97
|
+
"ripple",
|
|
98
|
+
"button",
|
|
99
|
+
"sidebar",
|
|
100
|
+
"card",
|
|
101
|
+
"select",
|
|
102
|
+
"tooltip",
|
|
103
|
+
"accordion",
|
|
104
|
+
"tabs",
|
|
105
|
+
"alert",
|
|
106
|
+
"badge",
|
|
107
|
+
"breadcrumb",
|
|
108
|
+
"carousel",
|
|
109
|
+
"collapse",
|
|
110
|
+
"list-group",
|
|
111
|
+
"pagination",
|
|
112
|
+
"progress",
|
|
113
|
+
"spinner",
|
|
114
|
+
"toast"
|
|
115
|
+
],
|
|
116
|
+
"dependencies": {
|
|
117
|
+
"dayjs": "^1.11.13",
|
|
118
|
+
"echarts": "^5.6.0",
|
|
119
|
+
"svelte-sonner": "^0.3.28",
|
|
120
|
+
"tailwind-merge": "^3.0.2",
|
|
121
|
+
"tailwind-variants": "^1.0.0"
|
|
122
|
+
}
|
|
114
123
|
}
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { cn } from '../../helper/cls.js';
|
|
3
|
-
import { statsCard } from './stats-card.js';
|
|
4
|
-
import { onMount } from 'svelte';
|
|
5
|
-
import * as echarts from 'echarts/core';
|
|
6
|
-
|
|
7
|
-
// @ts-expect-error - ECharts types are not available
|
|
8
|
-
import { LineChart } from 'echarts/charts';
|
|
9
|
-
|
|
10
|
-
// @ts-expect-error - ECharts types are not available
|
|
11
|
-
import { GridComponent } from 'echarts/components';
|
|
12
|
-
// @ts-expect-error - ECharts types are not available
|
|
13
|
-
import { SVGRenderer } from 'echarts/renderers';
|
|
14
|
-
import { Color } from '../../variants.js';
|
|
15
|
-
import type { StatsCardProps } from '../../index.js';
|
|
16
|
-
|
|
17
|
-
// @ts-expect-error - ECharts types are not available
|
|
18
|
-
echarts.use([LineChart, GridComponent, SVGRenderer]);
|
|
19
|
-
|
|
20
|
-
let {
|
|
21
|
-
label,
|
|
22
|
-
value,
|
|
23
|
-
previousValue,
|
|
24
|
-
previousValuePrefix = 'from',
|
|
25
|
-
trend,
|
|
26
|
-
color = Color.PRIMARY,
|
|
27
|
-
chartData,
|
|
28
|
-
children,
|
|
29
|
-
class: className = '',
|
|
30
|
-
formatLargeNumbers = true
|
|
31
|
-
}: StatsCardProps = $props();
|
|
32
|
-
|
|
33
|
-
// Chart container reference
|
|
34
|
-
let chartContainer: HTMLDivElement | undefined = $state();
|
|
35
|
-
// @ts-expect-error - ECharts types are not available
|
|
36
|
-
let chart: echarts.ECharts | undefined = $state();
|
|
37
|
-
|
|
38
|
-
function getTrendDirection(trendX?: number): 'up' | 'down' | 'neutral' {
|
|
39
|
-
if (trendX === undefined || trendX === null) return 'neutral';
|
|
40
|
-
if (trendX > 0) {
|
|
41
|
-
return 'up';
|
|
42
|
-
} else if (trendX < 0) {
|
|
43
|
-
return 'down';
|
|
44
|
-
}
|
|
45
|
-
return 'neutral';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function formatValue(val?: string | number): string {
|
|
49
|
-
if (val === undefined || val === null) return '';
|
|
50
|
-
|
|
51
|
-
let numValue: number;
|
|
52
|
-
let currencySymbol: string | null = null;
|
|
53
|
-
|
|
54
|
-
// Handle string inputs
|
|
55
|
-
if (typeof val === 'string') {
|
|
56
|
-
// Check for currency symbols
|
|
57
|
-
const currencyMatch = val.match(/^(\$|€|£|¥)/);
|
|
58
|
-
if (currencyMatch) {
|
|
59
|
-
currencySymbol = currencyMatch[0];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Extract the numeric value
|
|
63
|
-
const cleanedStr = val.replace(/[^0-9.-]/g, '');
|
|
64
|
-
if (/^-?\d+(\.\d+)?$/.test(cleanedStr)) {
|
|
65
|
-
numValue = parseFloat(cleanedStr);
|
|
66
|
-
} else {
|
|
67
|
-
// Not a number we can format
|
|
68
|
-
return val;
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
// Already a number
|
|
72
|
-
numValue = val;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Format based on whether it's currency and size
|
|
76
|
-
if (currencySymbol) {
|
|
77
|
-
// Currency formatting with compact notation if needed
|
|
78
|
-
return new Intl.NumberFormat('en-US', {
|
|
79
|
-
style: 'currency',
|
|
80
|
-
currency: currencySymbolToCode(currencySymbol),
|
|
81
|
-
notation: formatLargeNumbers ? 'compact' : 'standard',
|
|
82
|
-
compactDisplay: 'short',
|
|
83
|
-
maximumFractionDigits: 1
|
|
84
|
-
}).format(numValue);
|
|
85
|
-
} else {
|
|
86
|
-
// Regular number formatting with compact notation if needed
|
|
87
|
-
return new Intl.NumberFormat('en-US', {
|
|
88
|
-
notation: formatLargeNumbers ? 'compact' : 'standard',
|
|
89
|
-
compactDisplay: 'short',
|
|
90
|
-
maximumFractionDigits: 1
|
|
91
|
-
}).format(numValue);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Helper function to convert currency symbols to ISO codes
|
|
96
|
-
function currencySymbolToCode(symbol: string): string {
|
|
97
|
-
const map: Record<string, string> = {
|
|
98
|
-
$: 'USD',
|
|
99
|
-
'€': 'EUR',
|
|
100
|
-
'£': 'GBP',
|
|
101
|
-
'¥': 'JPY'
|
|
102
|
-
};
|
|
103
|
-
return map[symbol] || 'USD';
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const chartColorMap = {
|
|
107
|
-
[Color.PRIMARY]: '#4F46E5',
|
|
108
|
-
[Color.SECONDARY]: '#6B7280',
|
|
109
|
-
[Color.INFO]: '#3B82F6',
|
|
110
|
-
[Color.SUCCESS]: '#10B981',
|
|
111
|
-
[Color.WARNING]: '#F59E0B',
|
|
112
|
-
[Color.DANGER]: '#EF4444',
|
|
113
|
-
[Color.DEFAULT]: '#6B7280'
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const trendDirection = $derived(getTrendDirection(trend));
|
|
117
|
-
const formattedValue = $derived(formatValue(value));
|
|
118
|
-
const formattedPreviousValue = $derived(
|
|
119
|
-
previousValue !== undefined ? formatValue(previousValue) : undefined
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const {
|
|
123
|
-
base,
|
|
124
|
-
label: labelSlot,
|
|
125
|
-
value: valueSlot,
|
|
126
|
-
trend: trendSlot,
|
|
127
|
-
previousValue: previousValueSlot
|
|
128
|
-
} = $derived(
|
|
129
|
-
statsCard({
|
|
130
|
-
color,
|
|
131
|
-
trendDirection
|
|
132
|
-
})
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const labelClasses = $derived(cn(labelSlot(), 'text-sm font-medium mb-1'));
|
|
136
|
-
const valueClasses = $derived(cn(valueSlot(), 'text-4xl font-bold'));
|
|
137
|
-
const trendClasses = $derived(cn(trendSlot(), 'text-sm font-medium ml-2'));
|
|
138
|
-
const previousValueClasses = $derived(cn(previousValueSlot(), 'text-sm text-default-500 mt-1'));
|
|
139
|
-
|
|
140
|
-
const trendFormatted = $derived.by(() => {
|
|
141
|
-
if (trend === undefined || trend === null) return '';
|
|
142
|
-
const absValue = Math.abs(trend);
|
|
143
|
-
const formattedValue = absValue.toFixed(1).replace(/\.0$/, '');
|
|
144
|
-
return trend >= 0 ? `+${formattedValue}%` : `-${formattedValue}%`;
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const chartColor = $derived(chartColorMap[color] || chartColorMap[Color.DEFAULT]);
|
|
148
|
-
|
|
149
|
-
onMount(() => {
|
|
150
|
-
if (!chartData || chartData.length < 2 || !chartContainer) return;
|
|
151
|
-
|
|
152
|
-
// @ts-expect-error - ECharts types are not available
|
|
153
|
-
chart = echarts.init(chartContainer);
|
|
154
|
-
|
|
155
|
-
// Set chart options
|
|
156
|
-
const options = {
|
|
157
|
-
grid: {
|
|
158
|
-
left: 0,
|
|
159
|
-
right: 0,
|
|
160
|
-
top: 0,
|
|
161
|
-
bottom: 0
|
|
162
|
-
},
|
|
163
|
-
xAxis: {
|
|
164
|
-
type: 'category',
|
|
165
|
-
show: false,
|
|
166
|
-
data: Array.from({ length: chartData.length }, (_, i) => i)
|
|
167
|
-
},
|
|
168
|
-
yAxis: {
|
|
169
|
-
type: 'value',
|
|
170
|
-
show: false
|
|
171
|
-
},
|
|
172
|
-
series: [
|
|
173
|
-
{
|
|
174
|
-
data: chartData,
|
|
175
|
-
type: 'line',
|
|
176
|
-
symbol: 'none',
|
|
177
|
-
lineStyle: {
|
|
178
|
-
color: chartColor,
|
|
179
|
-
width: 2
|
|
180
|
-
},
|
|
181
|
-
areaStyle: {
|
|
182
|
-
color: {
|
|
183
|
-
type: 'linear',
|
|
184
|
-
x: 0,
|
|
185
|
-
y: 0,
|
|
186
|
-
x2: 0,
|
|
187
|
-
y2: 1,
|
|
188
|
-
colorStops: [
|
|
189
|
-
{
|
|
190
|
-
offset: 0,
|
|
191
|
-
color: chartColor + '40' // 25% opacity
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
offset: 1,
|
|
195
|
-
color: chartColor + '00' // 0% opacity
|
|
196
|
-
}
|
|
197
|
-
]
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
smooth: true
|
|
201
|
-
}
|
|
202
|
-
],
|
|
203
|
-
animation: false,
|
|
204
|
-
animationDuration: 1000
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
chart.setOption(options);
|
|
208
|
-
|
|
209
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
210
|
-
chart && chart.resize();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
resizeObserver.observe(chartContainer);
|
|
214
|
-
|
|
215
|
-
return () => {
|
|
216
|
-
resizeObserver.disconnect();
|
|
217
|
-
chart.dispose();
|
|
218
|
-
};
|
|
219
|
-
});
|
|
220
|
-
</script>
|
|
221
|
-
|
|
222
|
-
<div
|
|
223
|
-
class={cn(
|
|
224
|
-
base(),
|
|
225
|
-
'@container flex flex-col rounded-lg border border-default-200 bg-white p-6 shadow-sm',
|
|
226
|
-
className
|
|
227
|
-
)}
|
|
228
|
-
>
|
|
229
|
-
<div class="flex items-center justify-between">
|
|
230
|
-
{#if label}
|
|
231
|
-
<dt class={labelClasses}>{label}</dt>
|
|
232
|
-
{/if}
|
|
233
|
-
{#if trend !== undefined && trend !== null}
|
|
234
|
-
<span class={trendClasses}>
|
|
235
|
-
{trendFormatted}
|
|
236
|
-
</span>
|
|
237
|
-
{/if}
|
|
238
|
-
</div>
|
|
239
|
-
|
|
240
|
-
<div class="mt-1 flex flex-row items-center overflow-clip">
|
|
241
|
-
<div class="w-full flex-grow">
|
|
242
|
-
{#if value !== undefined}
|
|
243
|
-
<dd class={valueClasses} title={value}>
|
|
244
|
-
{formattedValue}
|
|
245
|
-
</dd>
|
|
246
|
-
{/if}
|
|
247
|
-
|
|
248
|
-
{#if previousValue !== undefined}
|
|
249
|
-
<div class={previousValueClasses}>
|
|
250
|
-
{previousValuePrefix}
|
|
251
|
-
{formattedPreviousValue}
|
|
252
|
-
</div>
|
|
253
|
-
{/if}
|
|
254
|
-
</div>
|
|
255
|
-
|
|
256
|
-
{#if chartData && chartData.length > 1}
|
|
257
|
-
<div class="h-10 w-full min-w-32 @max-[212.862px]:hidden" bind:this={chartContainer}></div>
|
|
258
|
-
{/if}
|
|
259
|
-
</div>
|
|
260
|
-
|
|
261
|
-
{#if children}
|
|
262
|
-
<div class="mt-4">
|
|
263
|
-
{@render children()}
|
|
264
|
-
</div>
|
|
265
|
-
{/if}
|
|
266
|
-
</div>
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { Color } from '../../variants.js';
|
|
2
|
-
export declare const statsCard: import("tailwind-variants").TVReturnType<{
|
|
3
|
-
color: {
|
|
4
|
-
[Color.DEFAULT]: {
|
|
5
|
-
label: string;
|
|
6
|
-
value: string;
|
|
7
|
-
trend: string;
|
|
8
|
-
previousValue: string;
|
|
9
|
-
};
|
|
10
|
-
[Color.PRIMARY]: {
|
|
11
|
-
label: string;
|
|
12
|
-
value: string;
|
|
13
|
-
trend: string;
|
|
14
|
-
previousValue: string;
|
|
15
|
-
};
|
|
16
|
-
[Color.SECONDARY]: {
|
|
17
|
-
label: string;
|
|
18
|
-
value: string;
|
|
19
|
-
trend: string;
|
|
20
|
-
previousValue: string;
|
|
21
|
-
};
|
|
22
|
-
[Color.INFO]: {
|
|
23
|
-
label: string;
|
|
24
|
-
value: string;
|
|
25
|
-
trend: string;
|
|
26
|
-
previousValue: string;
|
|
27
|
-
};
|
|
28
|
-
[Color.SUCCESS]: {
|
|
29
|
-
label: string;
|
|
30
|
-
value: string;
|
|
31
|
-
trend: string;
|
|
32
|
-
previousValue: string;
|
|
33
|
-
};
|
|
34
|
-
[Color.WARNING]: {
|
|
35
|
-
label: string;
|
|
36
|
-
value: string;
|
|
37
|
-
trend: string;
|
|
38
|
-
previousValue: string;
|
|
39
|
-
};
|
|
40
|
-
[Color.DANGER]: {
|
|
41
|
-
label: string;
|
|
42
|
-
value: string;
|
|
43
|
-
trend: string;
|
|
44
|
-
previousValue: string;
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
trendDirection: {
|
|
48
|
-
up: {
|
|
49
|
-
trend: string;
|
|
50
|
-
};
|
|
51
|
-
down: {
|
|
52
|
-
trend: string;
|
|
53
|
-
};
|
|
54
|
-
neutral: {
|
|
55
|
-
trend: string;
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
}, {
|
|
59
|
-
base: string;
|
|
60
|
-
label: string;
|
|
61
|
-
value: string;
|
|
62
|
-
trend: string;
|
|
63
|
-
previousValue: string;
|
|
64
|
-
unit: string;
|
|
65
|
-
}, undefined, {
|
|
66
|
-
color: {
|
|
67
|
-
[Color.DEFAULT]: {
|
|
68
|
-
label: string;
|
|
69
|
-
value: string;
|
|
70
|
-
trend: string;
|
|
71
|
-
previousValue: string;
|
|
72
|
-
};
|
|
73
|
-
[Color.PRIMARY]: {
|
|
74
|
-
label: string;
|
|
75
|
-
value: string;
|
|
76
|
-
trend: string;
|
|
77
|
-
previousValue: string;
|
|
78
|
-
};
|
|
79
|
-
[Color.SECONDARY]: {
|
|
80
|
-
label: string;
|
|
81
|
-
value: string;
|
|
82
|
-
trend: string;
|
|
83
|
-
previousValue: string;
|
|
84
|
-
};
|
|
85
|
-
[Color.INFO]: {
|
|
86
|
-
label: string;
|
|
87
|
-
value: string;
|
|
88
|
-
trend: string;
|
|
89
|
-
previousValue: string;
|
|
90
|
-
};
|
|
91
|
-
[Color.SUCCESS]: {
|
|
92
|
-
label: string;
|
|
93
|
-
value: string;
|
|
94
|
-
trend: string;
|
|
95
|
-
previousValue: string;
|
|
96
|
-
};
|
|
97
|
-
[Color.WARNING]: {
|
|
98
|
-
label: string;
|
|
99
|
-
value: string;
|
|
100
|
-
trend: string;
|
|
101
|
-
previousValue: string;
|
|
102
|
-
};
|
|
103
|
-
[Color.DANGER]: {
|
|
104
|
-
label: string;
|
|
105
|
-
value: string;
|
|
106
|
-
trend: string;
|
|
107
|
-
previousValue: string;
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
trendDirection: {
|
|
111
|
-
up: {
|
|
112
|
-
trend: string;
|
|
113
|
-
};
|
|
114
|
-
down: {
|
|
115
|
-
trend: string;
|
|
116
|
-
};
|
|
117
|
-
neutral: {
|
|
118
|
-
trend: string;
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
}, {
|
|
122
|
-
base: string;
|
|
123
|
-
label: string;
|
|
124
|
-
value: string;
|
|
125
|
-
trend: string;
|
|
126
|
-
previousValue: string;
|
|
127
|
-
unit: string;
|
|
128
|
-
}, import("tailwind-variants").TVReturnType<{
|
|
129
|
-
color: {
|
|
130
|
-
[Color.DEFAULT]: {
|
|
131
|
-
label: string;
|
|
132
|
-
value: string;
|
|
133
|
-
trend: string;
|
|
134
|
-
previousValue: string;
|
|
135
|
-
};
|
|
136
|
-
[Color.PRIMARY]: {
|
|
137
|
-
label: string;
|
|
138
|
-
value: string;
|
|
139
|
-
trend: string;
|
|
140
|
-
previousValue: string;
|
|
141
|
-
};
|
|
142
|
-
[Color.SECONDARY]: {
|
|
143
|
-
label: string;
|
|
144
|
-
value: string;
|
|
145
|
-
trend: string;
|
|
146
|
-
previousValue: string;
|
|
147
|
-
};
|
|
148
|
-
[Color.INFO]: {
|
|
149
|
-
label: string;
|
|
150
|
-
value: string;
|
|
151
|
-
trend: string;
|
|
152
|
-
previousValue: string;
|
|
153
|
-
};
|
|
154
|
-
[Color.SUCCESS]: {
|
|
155
|
-
label: string;
|
|
156
|
-
value: string;
|
|
157
|
-
trend: string;
|
|
158
|
-
previousValue: string;
|
|
159
|
-
};
|
|
160
|
-
[Color.WARNING]: {
|
|
161
|
-
label: string;
|
|
162
|
-
value: string;
|
|
163
|
-
trend: string;
|
|
164
|
-
previousValue: string;
|
|
165
|
-
};
|
|
166
|
-
[Color.DANGER]: {
|
|
167
|
-
label: string;
|
|
168
|
-
value: string;
|
|
169
|
-
trend: string;
|
|
170
|
-
previousValue: string;
|
|
171
|
-
};
|
|
172
|
-
};
|
|
173
|
-
trendDirection: {
|
|
174
|
-
up: {
|
|
175
|
-
trend: string;
|
|
176
|
-
};
|
|
177
|
-
down: {
|
|
178
|
-
trend: string;
|
|
179
|
-
};
|
|
180
|
-
neutral: {
|
|
181
|
-
trend: string;
|
|
182
|
-
};
|
|
183
|
-
};
|
|
184
|
-
}, {
|
|
185
|
-
base: string;
|
|
186
|
-
label: string;
|
|
187
|
-
value: string;
|
|
188
|
-
trend: string;
|
|
189
|
-
previousValue: string;
|
|
190
|
-
unit: string;
|
|
191
|
-
}, undefined, unknown, unknown, undefined>>;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { tv } from 'tailwind-variants';
|
|
2
|
-
import { Color } from '../../variants.js';
|
|
3
|
-
export const statsCard = tv({
|
|
4
|
-
slots: {
|
|
5
|
-
base: '',
|
|
6
|
-
label: '',
|
|
7
|
-
value: '',
|
|
8
|
-
trend: '',
|
|
9
|
-
previousValue: '',
|
|
10
|
-
unit: '',
|
|
11
|
-
},
|
|
12
|
-
variants: {
|
|
13
|
-
color: {
|
|
14
|
-
[Color.DEFAULT]: {
|
|
15
|
-
label: 'text-default-500',
|
|
16
|
-
value: 'text-default-900',
|
|
17
|
-
trend: 'text-default-600',
|
|
18
|
-
previousValue: 'text-default-500'
|
|
19
|
-
},
|
|
20
|
-
[Color.PRIMARY]: {
|
|
21
|
-
label: 'text-primary-500',
|
|
22
|
-
value: 'text-primary-600',
|
|
23
|
-
trend: 'text-primary-600',
|
|
24
|
-
previousValue: 'text-primary-400'
|
|
25
|
-
},
|
|
26
|
-
[Color.SECONDARY]: {
|
|
27
|
-
label: 'text-secondary-500',
|
|
28
|
-
value: 'text-secondary-600',
|
|
29
|
-
trend: 'text-secondary-600',
|
|
30
|
-
previousValue: 'text-secondary-400'
|
|
31
|
-
},
|
|
32
|
-
[Color.INFO]: {
|
|
33
|
-
label: 'text-info-500',
|
|
34
|
-
value: 'text-info-600',
|
|
35
|
-
trend: 'text-info-600',
|
|
36
|
-
previousValue: 'text-info-400'
|
|
37
|
-
},
|
|
38
|
-
[Color.SUCCESS]: {
|
|
39
|
-
label: 'text-success-500',
|
|
40
|
-
value: 'text-success-600',
|
|
41
|
-
trend: 'text-success-600',
|
|
42
|
-
previousValue: 'text-success-400'
|
|
43
|
-
},
|
|
44
|
-
[Color.WARNING]: {
|
|
45
|
-
label: 'text-warning-500',
|
|
46
|
-
value: 'text-warning-600',
|
|
47
|
-
trend: 'text-warning-600',
|
|
48
|
-
previousValue: 'text-warning-400'
|
|
49
|
-
},
|
|
50
|
-
[Color.DANGER]: {
|
|
51
|
-
label: 'text-danger-500',
|
|
52
|
-
value: 'text-danger-600',
|
|
53
|
-
trend: 'text-danger-600',
|
|
54
|
-
previousValue: 'text-danger-400'
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
trendDirection: {
|
|
58
|
-
up: {
|
|
59
|
-
trend: 'text-success-600'
|
|
60
|
-
},
|
|
61
|
-
down: {
|
|
62
|
-
trend: 'text-danger-600'
|
|
63
|
-
},
|
|
64
|
-
neutral: {
|
|
65
|
-
trend: 'text-default-600'
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
defaultVariants: {
|
|
70
|
-
color: Color.DEFAULT,
|
|
71
|
-
trendDirection: 'neutral'
|
|
72
|
-
}
|
|
73
|
-
});
|