@takaro/lib-components 0.3.1 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takaro/lib-components",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "Takaro UI is a simple and customizable component library to build React apps faster within the Takaro eco system",
6
6
  "license": "AGPL-3.0-or-later",
@@ -11,6 +11,7 @@ import {
11
11
  useTypeahead,
12
12
  useClick,
13
13
  shift,
14
+ flip,
14
15
  } from '@floating-ui/react';
15
16
 
16
17
  export interface UseDropdownOptions {
@@ -42,7 +43,13 @@ export function useDropdown({
42
43
  open,
43
44
  onOpenChange: setOpen,
44
45
  whileElementsMounted: autoUpdate,
45
- middleware: [offset({ mainAxis: 10 }), shift()],
46
+ middleware: [
47
+ offset({ mainAxis: 10 }),
48
+ flip({
49
+ fallbackPlacements: ['top', 'bottom'],
50
+ }),
51
+ shift(),
52
+ ],
46
53
  });
47
54
 
48
55
  const interactions = useInteractions([
@@ -0,0 +1,139 @@
1
+ import React from 'react';
2
+ import { Meta, StoryFn } from '@storybook/react';
3
+ import { EChartsArea, EChartsAreaProps } from './EChartsArea';
4
+ import { styled } from '../../../styled';
5
+ import { Card } from '../../../components';
6
+ import { faker } from '@faker-js/faker';
7
+
8
+ export default {
9
+ title: 'Charts/ECharts/Area',
10
+ component: EChartsArea,
11
+ args: {
12
+ smooth: true,
13
+ gradient: true,
14
+ showGrid: true,
15
+ showLegend: true,
16
+ xAxisLabel: 'Date',
17
+ yAxisLabel: 'Revenue ($)',
18
+ title: 'Area Chart Example',
19
+ },
20
+ } as Meta<EChartsAreaProps>;
21
+
22
+ const Wrapper = styled.div`
23
+ height: 500px;
24
+ width: 100%;
25
+ `;
26
+
27
+ interface RevenueData {
28
+ date: string;
29
+ revenue: number;
30
+ }
31
+
32
+ // Generate sample revenue data
33
+ function generateRevenueData(days: number = 30): RevenueData[] {
34
+ const data: RevenueData[] = [];
35
+ const now = new Date();
36
+ const baseValue = 1000;
37
+
38
+ for (let i = days - 1; i >= 0; i--) {
39
+ const date = new Date(now);
40
+ date.setDate(date.getDate() - i);
41
+
42
+ // Add some realistic variation
43
+ const dayOfWeek = date.getDay();
44
+ const weekendBonus = dayOfWeek === 0 || dayOfWeek === 6 ? 300 : 0;
45
+ const randomVariation = faker.number.int({ min: -200, max: 400 });
46
+ const trendGrowth = i * 5; // Slight upward trend
47
+
48
+ data.push({
49
+ date: date.toLocaleDateString(),
50
+ revenue: baseValue + weekendBonus + randomVariation + trendGrowth,
51
+ });
52
+ }
53
+
54
+ return data;
55
+ }
56
+
57
+ export const Default: StoryFn<EChartsAreaProps<RevenueData>> = (args) => {
58
+ const data = generateRevenueData();
59
+
60
+ return (
61
+ <Wrapper>
62
+ <EChartsArea<RevenueData>
63
+ {...args}
64
+ data={data}
65
+ xAccessor={(d) => d.date}
66
+ yAccessor={(d) => d.revenue}
67
+ seriesName="Daily Revenue"
68
+ />
69
+ </Wrapper>
70
+ );
71
+ };
72
+
73
+ export const NoGradient: StoryFn<EChartsAreaProps<RevenueData>> = (args) => {
74
+ const data = generateRevenueData();
75
+
76
+ return (
77
+ <Wrapper>
78
+ <EChartsArea<RevenueData>
79
+ {...args}
80
+ data={data}
81
+ xAccessor={(d) => d.date}
82
+ yAccessor={(d) => d.revenue}
83
+ seriesName="Revenue"
84
+ gradient={false}
85
+ title="Revenue Trend"
86
+ subtitle="Solid fill area chart"
87
+ />
88
+ </Wrapper>
89
+ );
90
+ };
91
+
92
+ export const ShopAnalytics: StoryFn = () => {
93
+ const data = generateRevenueData(90);
94
+
95
+ return (
96
+ <Wrapper>
97
+ <Card variant="outline">
98
+ <Card.Title label="Revenue Over Time" />
99
+ <Card.Body>
100
+ <div style={{ height: '400px' }}>
101
+ <EChartsArea
102
+ data={data}
103
+ xAccessor={(d) => d.date}
104
+ yAccessor={(d) => d.revenue}
105
+ seriesName="Revenue"
106
+ smooth={true}
107
+ gradient={true}
108
+ showGrid={true}
109
+ yAxisLabel="Revenue ($)"
110
+ tooltipFormatter={(params: any) => {
111
+ const value = params[0].value;
112
+ return `${params[0].name}<br/>Revenue: $${value.toLocaleString()}`;
113
+ }}
114
+ />
115
+ </div>
116
+ </Card.Body>
117
+ </Card>
118
+ </Wrapper>
119
+ );
120
+ };
121
+
122
+ export const StackedArea: StoryFn<EChartsAreaProps<RevenueData>> = (args) => {
123
+ const data = generateRevenueData(20);
124
+
125
+ return (
126
+ <Wrapper>
127
+ <EChartsArea<RevenueData>
128
+ {...args}
129
+ data={data}
130
+ xAccessor={(d) => d.date}
131
+ yAccessor={(d) => d.revenue}
132
+ seriesName="Product Sales"
133
+ stack={true}
134
+ title="Stacked Revenue"
135
+ subtitle="Multiple product categories"
136
+ />
137
+ </Wrapper>
138
+ );
139
+ };
@@ -0,0 +1,139 @@
1
+ import { FC, useMemo } from 'react';
2
+ import { EChartsOption } from 'echarts';
3
+ import { ResponsiveECharts, EChartsBaseProps } from './EChartsBase';
4
+ import { useTheme } from '../../../hooks';
5
+
6
+ export interface EChartsAreaProps<T = any> extends Omit<EChartsBaseProps, 'option'> {
7
+ data: T[];
8
+ xAccessor: (d: T) => string | number | Date;
9
+ yAccessor: (d: T) => number;
10
+ seriesName?: string;
11
+ smooth?: boolean;
12
+ showGrid?: boolean;
13
+ showLegend?: boolean;
14
+ xAxisLabel?: string;
15
+ yAxisLabel?: string;
16
+ title?: string;
17
+ subtitle?: string;
18
+ gradient?: boolean;
19
+ tooltipFormatter?: (params: any) => string;
20
+ stack?: boolean;
21
+ }
22
+
23
+ export const EChartsArea: FC<EChartsAreaProps> = ({
24
+ data,
25
+ xAccessor,
26
+ yAccessor,
27
+ seriesName = 'Value',
28
+ smooth = true,
29
+ showGrid = true,
30
+ showLegend = false,
31
+ xAxisLabel,
32
+ yAxisLabel,
33
+ title,
34
+ subtitle,
35
+ gradient = true,
36
+ tooltipFormatter,
37
+ stack = false,
38
+ ...chartProps
39
+ }) => {
40
+ const theme = useTheme();
41
+
42
+ const option: EChartsOption = useMemo(() => {
43
+ const xData = data.map(xAccessor);
44
+ const yData = data.map(yAccessor);
45
+
46
+ return {
47
+ title: title
48
+ ? {
49
+ text: title,
50
+ subtext: subtitle,
51
+ left: 'center',
52
+ }
53
+ : undefined,
54
+ legend: showLegend
55
+ ? {
56
+ data: [seriesName],
57
+ top: 30,
58
+ }
59
+ : undefined,
60
+ grid: showGrid
61
+ ? {
62
+ left: '3%',
63
+ right: '4%',
64
+ bottom: '3%',
65
+ containLabel: true,
66
+ }
67
+ : undefined,
68
+ tooltip: {
69
+ trigger: 'axis',
70
+ formatter: tooltipFormatter,
71
+ },
72
+ xAxis: {
73
+ type: 'category',
74
+ data: xData,
75
+ name: xAxisLabel,
76
+ nameLocation: 'middle',
77
+ nameGap: 30,
78
+ boundaryGap: false,
79
+ },
80
+ yAxis: {
81
+ type: 'value',
82
+ name: yAxisLabel,
83
+ nameLocation: 'middle',
84
+ nameGap: 50,
85
+ },
86
+ series: [
87
+ {
88
+ name: seriesName,
89
+ type: 'line',
90
+ data: yData,
91
+ smooth: smooth,
92
+ stack: stack ? 'total' : undefined,
93
+ areaStyle: gradient
94
+ ? {
95
+ color: {
96
+ type: 'linear',
97
+ x: 0,
98
+ y: 0,
99
+ x2: 0,
100
+ y2: 1,
101
+ colorStops: [
102
+ {
103
+ offset: 0,
104
+ color: theme.colors.primary + '80', // 50% opacity
105
+ },
106
+ {
107
+ offset: 1,
108
+ color: theme.colors.primary + '10', // 6% opacity
109
+ },
110
+ ],
111
+ },
112
+ }
113
+ : {},
114
+ emphasis: {
115
+ focus: 'series',
116
+ },
117
+ },
118
+ ],
119
+ };
120
+ }, [
121
+ data,
122
+ xAccessor,
123
+ yAccessor,
124
+ seriesName,
125
+ smooth,
126
+ showGrid,
127
+ showLegend,
128
+ xAxisLabel,
129
+ yAxisLabel,
130
+ title,
131
+ subtitle,
132
+ gradient,
133
+ tooltipFormatter,
134
+ stack,
135
+ theme,
136
+ ]);
137
+
138
+ return <ResponsiveECharts option={option} {...chartProps} />;
139
+ };
@@ -0,0 +1,141 @@
1
+ import React from 'react';
2
+ import { Meta, StoryFn } from '@storybook/react';
3
+ import { EChartsBar, EChartsBarProps } from './EChartsBar';
4
+ import { styled } from '../../../styled';
5
+ import { Card } from '../../../components';
6
+ import { faker } from '@faker-js/faker';
7
+
8
+ export default {
9
+ title: 'Charts/ECharts/Bar',
10
+ component: EChartsBar,
11
+ args: {
12
+ horizontal: false,
13
+ showGrid: true,
14
+ showLegend: true,
15
+ xAxisLabel: 'Category',
16
+ yAxisLabel: 'Value',
17
+ title: 'Bar Chart Example',
18
+ colorBy: 'data',
19
+ },
20
+ } as Meta<EChartsBarProps>;
21
+
22
+ const Wrapper = styled.div`
23
+ height: 500px;
24
+ width: 100%;
25
+ `;
26
+
27
+ interface CategoryData {
28
+ name: string;
29
+ value: number;
30
+ }
31
+
32
+ // Generate sample category data
33
+ function generateCategoryData(count: number = 10): CategoryData[] {
34
+ const categories = [
35
+ 'Weapons',
36
+ 'Armor',
37
+ 'Tools',
38
+ 'Food',
39
+ 'Materials',
40
+ 'Potions',
41
+ 'Books',
42
+ 'Gems',
43
+ 'Artifacts',
44
+ 'Misc',
45
+ ];
46
+
47
+ return categories.slice(0, count).map((name) => ({
48
+ name,
49
+ value: faker.number.int({ min: 10, max: 200 }),
50
+ }));
51
+ }
52
+
53
+ export const Default: StoryFn<EChartsBarProps<CategoryData>> = (args) => {
54
+ const data = generateCategoryData(8);
55
+
56
+ return (
57
+ <Wrapper>
58
+ <EChartsBar<CategoryData>
59
+ {...args}
60
+ data={data}
61
+ xAccessor={(d) => d.name}
62
+ yAccessor={(d) => d.value}
63
+ seriesName="Sales"
64
+ />
65
+ </Wrapper>
66
+ );
67
+ };
68
+
69
+ export const HorizontalBar: StoryFn<EChartsBarProps<CategoryData>> = (args) => {
70
+ const data = generateCategoryData(6);
71
+
72
+ return (
73
+ <Wrapper>
74
+ <EChartsBar<CategoryData>
75
+ {...args}
76
+ data={data}
77
+ xAccessor={(d) => d.name}
78
+ yAccessor={(d) => d.value}
79
+ seriesName="Inventory"
80
+ horizontal={true}
81
+ title="Item Inventory"
82
+ subtitle="Current stock levels"
83
+ xAxisLabel="Quantity"
84
+ yAxisLabel=""
85
+ />
86
+ </Wrapper>
87
+ );
88
+ };
89
+
90
+ export const TopSellingItems: StoryFn = () => {
91
+ const products = [
92
+ { name: 'Diamond Sword', revenue: 15000 },
93
+ { name: 'Iron Pickaxe', revenue: 12000 },
94
+ { name: 'Golden Apple', revenue: 9500 },
95
+ { name: 'Enchanted Book', revenue: 8000 },
96
+ { name: 'Ender Pearl', revenue: 6500 },
97
+ ];
98
+
99
+ return (
100
+ <Wrapper>
101
+ <Card variant="outline">
102
+ <Card.Title label="Top Selling Items" />
103
+ <Card.Body>
104
+ <div style={{ height: '400px' }}>
105
+ <EChartsBar
106
+ data={products}
107
+ xAccessor={(d) => d.name}
108
+ yAccessor={(d) => d.revenue}
109
+ seriesName="Revenue"
110
+ colorBy="data"
111
+ showGrid={true}
112
+ yAxisLabel="Revenue ($)"
113
+ tooltipFormatter={(params: any) => {
114
+ return `${params[0].name}<br/>Revenue: $${params[0].value.toLocaleString()}`;
115
+ }}
116
+ />
117
+ </div>
118
+ </Card.Body>
119
+ </Card>
120
+ </Wrapper>
121
+ );
122
+ };
123
+
124
+ export const MonochromeBar: StoryFn<EChartsBarProps<CategoryData>> = (args) => {
125
+ const data = generateCategoryData(12);
126
+
127
+ return (
128
+ <Wrapper>
129
+ <EChartsBar<CategoryData>
130
+ {...args}
131
+ data={data}
132
+ xAccessor={(d) => d.name}
133
+ yAccessor={(d) => d.value}
134
+ seriesName="Activity"
135
+ colorBy="series"
136
+ title="Player Activity by Hour"
137
+ barWidth="60%"
138
+ />
139
+ </Wrapper>
140
+ );
141
+ };
@@ -0,0 +1,133 @@
1
+ import { FC, useMemo } from 'react';
2
+ import { EChartsOption } from 'echarts';
3
+ import { ResponsiveECharts, EChartsBaseProps } from './EChartsBase';
4
+
5
+ export interface EChartsBarProps<T = any> extends Omit<EChartsBaseProps, 'option'> {
6
+ data: T[];
7
+ xAccessor: (d: T) => string | number;
8
+ yAccessor: (d: T) => number;
9
+ seriesName?: string;
10
+ horizontal?: boolean;
11
+ showGrid?: boolean;
12
+ showLegend?: boolean;
13
+ xAxisLabel?: string;
14
+ yAxisLabel?: string;
15
+ title?: string;
16
+ subtitle?: string;
17
+ barWidth?: string | number;
18
+ barGap?: string;
19
+ tooltipFormatter?: (params: any) => string;
20
+ colorBy?: 'series' | 'data';
21
+ }
22
+
23
+ export const EChartsBar: FC<EChartsBarProps> = ({
24
+ data,
25
+ xAccessor,
26
+ yAccessor,
27
+ seriesName = 'Value',
28
+ horizontal = false,
29
+ showGrid = true,
30
+ showLegend = false,
31
+ xAxisLabel,
32
+ yAxisLabel,
33
+ title,
34
+ subtitle,
35
+ barWidth,
36
+ barGap = '30%',
37
+ tooltipFormatter,
38
+ colorBy = 'series',
39
+ ...chartProps
40
+ }) => {
41
+ const option: EChartsOption = useMemo(() => {
42
+ const categories = data.map(xAccessor);
43
+ const values = data.map(yAccessor);
44
+
45
+ const xAxisConfig = {
46
+ type: horizontal ? 'value' : 'category',
47
+ data: horizontal ? undefined : categories,
48
+ name: xAxisLabel,
49
+ nameLocation: 'middle',
50
+ nameGap: 30,
51
+ };
52
+
53
+ const yAxisConfig = {
54
+ type: horizontal ? 'category' : 'value',
55
+ data: horizontal ? categories : undefined,
56
+ name: yAxisLabel,
57
+ nameLocation: 'middle',
58
+ nameGap: 50,
59
+ };
60
+
61
+ return {
62
+ title: title
63
+ ? {
64
+ text: title,
65
+ subtext: subtitle,
66
+ left: 'center',
67
+ }
68
+ : undefined,
69
+ legend: showLegend
70
+ ? {
71
+ data: [seriesName],
72
+ top: 30,
73
+ }
74
+ : undefined,
75
+ grid: showGrid
76
+ ? {
77
+ left: '3%',
78
+ right: '4%',
79
+ bottom: '3%',
80
+ containLabel: true,
81
+ }
82
+ : undefined,
83
+ tooltip: {
84
+ trigger: 'axis',
85
+ axisPointer: {
86
+ type: 'shadow',
87
+ },
88
+ formatter: tooltipFormatter,
89
+ },
90
+ xAxis: xAxisConfig as any,
91
+ yAxis: yAxisConfig as any,
92
+ series: [
93
+ {
94
+ name: seriesName,
95
+ type: 'bar',
96
+ data: values,
97
+ barWidth: barWidth,
98
+ barGap: barGap,
99
+ itemStyle:
100
+ colorBy === 'data'
101
+ ? {
102
+ color: (params: any) => {
103
+ const colors = ['#664de5', '#3ccd6A', '#f57c00', '#ff4252', '#06b6d4'];
104
+ return colors[params.dataIndex % colors.length];
105
+ },
106
+ }
107
+ : undefined,
108
+ emphasis: {
109
+ focus: 'series',
110
+ },
111
+ },
112
+ ],
113
+ };
114
+ }, [
115
+ data,
116
+ xAccessor,
117
+ yAccessor,
118
+ seriesName,
119
+ horizontal,
120
+ showGrid,
121
+ showLegend,
122
+ xAxisLabel,
123
+ yAxisLabel,
124
+ title,
125
+ subtitle,
126
+ barWidth,
127
+ barGap,
128
+ tooltipFormatter,
129
+ colorBy,
130
+ ]);
131
+
132
+ return <ResponsiveECharts option={option} {...chartProps} />;
133
+ };