@fe-free/core 2.5.7 → 2.6.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.
- package/CHANGELOG.md +17 -0
- package/package.json +2 -2
- package/src/index.ts +4 -1
- package/src/page_layout/index.tsx +58 -2
- package/src/slider/index.tsx +124 -0
- package/src/slider/number_slider.stories.tsx +32 -0
- package/src/slider/percentage_slider.stories.tsx +23 -0
- package/src/slider/style.scss +16 -0
- package/src/tabs/index.tsx +25 -16
- package/src/tabs/style.scss +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @fe-free/core
|
|
2
2
|
|
|
3
|
+
## 2.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- feat: page layout tabs
|
|
8
|
+
- @fe-free/tool@2.6.1
|
|
9
|
+
|
|
10
|
+
## 2.6.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- feat: slider
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- @fe-free/tool@2.6.0
|
|
19
|
+
|
|
3
20
|
## 2.5.7
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"author": "",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"remark-gfm": "^4.0.1",
|
|
42
42
|
"vanilla-jsoneditor": "^0.23.1",
|
|
43
43
|
"zustand": "^4.5.4",
|
|
44
|
-
"@fe-free/tool": "2.
|
|
44
|
+
"@fe-free/tool": "2.6.1"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@ant-design/pro-components": "2.8.9",
|
package/src/index.ts
CHANGED
|
@@ -41,8 +41,11 @@ export { downloadInterceptor } from './global/interceptors';
|
|
|
41
41
|
export { InfiniteList } from './infinite_list';
|
|
42
42
|
export type { InfiniteListProps } from './infinite_list';
|
|
43
43
|
export { Markdown } from './markdown';
|
|
44
|
-
export { PageLayout } from './page_layout';
|
|
44
|
+
export { PageLayout, PageLayoutTabs } from './page_layout';
|
|
45
|
+
export type { PageLayoutProps, PageLayoutTabsProps } from './page_layout';
|
|
45
46
|
export { routeTool } from './route';
|
|
47
|
+
export { NumberSlider, PercentageSlider } from './slider';
|
|
48
|
+
export type { NumberSliderProps, PercentageSliderProps } from './slider';
|
|
46
49
|
export { Table } from './table';
|
|
47
50
|
export type { TableProps } from './table';
|
|
48
51
|
export { Tabs } from './tabs';
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
|
+
import { Fragment, useMemo } from 'react';
|
|
3
|
+
import { useSearchParams } from 'react-router-dom';
|
|
4
|
+
import type { TabsProps } from '../tabs';
|
|
5
|
+
import { Tabs } from '../tabs';
|
|
2
6
|
|
|
3
7
|
interface PageLayoutProps {
|
|
4
8
|
direction?: 'horizontal' | 'vertical';
|
|
@@ -24,7 +28,7 @@ function PageLayout({
|
|
|
24
28
|
return (
|
|
25
29
|
<div
|
|
26
30
|
className={cn(
|
|
27
|
-
'flex
|
|
31
|
+
'flex h-full w-full',
|
|
28
32
|
{
|
|
29
33
|
'flex-row': direction === 'horizontal',
|
|
30
34
|
'flex-col': direction === 'vertical',
|
|
@@ -39,4 +43,56 @@ function PageLayout({
|
|
|
39
43
|
);
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
interface PageLayoutTabsProps extends PageLayoutProps {
|
|
47
|
+
tabsProps: Omit<TabsProps, 'items' | 'withSearchParams'> & {
|
|
48
|
+
items: {
|
|
49
|
+
key: string;
|
|
50
|
+
label: React.ReactNode;
|
|
51
|
+
children: React.ReactNode;
|
|
52
|
+
}[];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function PageLayoutTabs(props: PageLayoutTabsProps) {
|
|
57
|
+
const { tabsProps, ...rest } = props;
|
|
58
|
+
|
|
59
|
+
const [searchParams] = useSearchParams();
|
|
60
|
+
const tab = searchParams.get(tabsProps.tabKey || 'tab') || undefined;
|
|
61
|
+
|
|
62
|
+
const { children, newItems } = useMemo(() => {
|
|
63
|
+
const items = props.tabsProps.items;
|
|
64
|
+
|
|
65
|
+
const item = items.find((item) => item.key === tab);
|
|
66
|
+
const children = item?.children || items[0]?.children;
|
|
67
|
+
|
|
68
|
+
const newItems = items.map((item) => ({
|
|
69
|
+
...item,
|
|
70
|
+
children: undefined,
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
return { children, newItems };
|
|
74
|
+
}, [props.tabsProps.items, tab]);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<PageLayout
|
|
78
|
+
direction="vertical"
|
|
79
|
+
start={
|
|
80
|
+
<Tabs
|
|
81
|
+
tabBarExtraContent={{
|
|
82
|
+
left: <div className="w-4" />,
|
|
83
|
+
right: <div className="w-4" />,
|
|
84
|
+
}}
|
|
85
|
+
{...tabsProps}
|
|
86
|
+
items={newItems}
|
|
87
|
+
withSearchParams
|
|
88
|
+
/>
|
|
89
|
+
}
|
|
90
|
+
{...rest}
|
|
91
|
+
>
|
|
92
|
+
<Fragment key={tab}>{children}</Fragment>
|
|
93
|
+
</PageLayout>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { PageLayout, PageLayoutTabs };
|
|
98
|
+
export type { PageLayoutProps, PageLayoutTabsProps };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { InputNumber, Slider } from 'antd';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { useCallback, useMemo } from 'react';
|
|
4
|
+
import './style.scss';
|
|
5
|
+
|
|
6
|
+
interface NumberSliderProps {
|
|
7
|
+
/** 默认 0 */
|
|
8
|
+
value?: number;
|
|
9
|
+
onChange: (value: number) => void;
|
|
10
|
+
/** 默认 0 */
|
|
11
|
+
min?: number;
|
|
12
|
+
/** 默认 100 */
|
|
13
|
+
max?: number;
|
|
14
|
+
/** 默认 1 */
|
|
15
|
+
step?: number;
|
|
16
|
+
/** 输入框的数值精度,默认 0 */
|
|
17
|
+
precision?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function NumberSlider(props: NumberSliderProps) {
|
|
21
|
+
const { value = 0, onChange, min = 0, max = 100, step = 1, precision = 0 } = props;
|
|
22
|
+
return (
|
|
23
|
+
<div className="flex gap-2">
|
|
24
|
+
<div className="flex-1">
|
|
25
|
+
<Slider
|
|
26
|
+
value={value}
|
|
27
|
+
onChange={onChange}
|
|
28
|
+
min={min}
|
|
29
|
+
max={max}
|
|
30
|
+
step={step}
|
|
31
|
+
marks={{
|
|
32
|
+
[min]: min,
|
|
33
|
+
[value]: value,
|
|
34
|
+
[max]: max,
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
<div>
|
|
39
|
+
<InputNumber
|
|
40
|
+
value={value}
|
|
41
|
+
onChange={(v) => onChange(v ?? 0)}
|
|
42
|
+
min={min}
|
|
43
|
+
max={max}
|
|
44
|
+
step={step}
|
|
45
|
+
precision={precision}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface PercentageSliderProps {
|
|
53
|
+
/** 百分占比数组 */
|
|
54
|
+
value: number[];
|
|
55
|
+
onChange: (value: number[]) => void;
|
|
56
|
+
/** 默认 0 */
|
|
57
|
+
min?: number;
|
|
58
|
+
/** 默认 100 */
|
|
59
|
+
max?: number;
|
|
60
|
+
/** 默认 1 */
|
|
61
|
+
step?: number;
|
|
62
|
+
className?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function PercentageSlider(props: PercentageSliderProps) {
|
|
66
|
+
const { value, onChange, min = 0, max = 100, step = 1, className } = props;
|
|
67
|
+
|
|
68
|
+
const sliderValue = useMemo(() => {
|
|
69
|
+
let sum = min;
|
|
70
|
+
return value
|
|
71
|
+
.map((v) => {
|
|
72
|
+
sum += v;
|
|
73
|
+
return sum;
|
|
74
|
+
})
|
|
75
|
+
.slice(0, -1);
|
|
76
|
+
}, [value, min]);
|
|
77
|
+
|
|
78
|
+
const handleChange = useCallback(
|
|
79
|
+
(newValue: number[]) => {
|
|
80
|
+
const result = [...newValue, max].map((v, i) => v - (newValue[i - 1] || min));
|
|
81
|
+
onChange(result);
|
|
82
|
+
},
|
|
83
|
+
[max, min, onChange],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const marks = useMemo(() => {
|
|
87
|
+
const result = {
|
|
88
|
+
[min]: min,
|
|
89
|
+
[max]: max,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
[...sliderValue, max].forEach((v, i) => {
|
|
93
|
+
const m = value[i] / 2 + (sliderValue[i - 1] || min);
|
|
94
|
+
result[m] = value[i];
|
|
95
|
+
// result[v] = value[i];
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return result;
|
|
99
|
+
}, [min, max, sliderValue, value]);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Slider
|
|
103
|
+
value={sliderValue}
|
|
104
|
+
onChange={handleChange}
|
|
105
|
+
min={min}
|
|
106
|
+
max={max}
|
|
107
|
+
step={step}
|
|
108
|
+
range
|
|
109
|
+
marks={marks}
|
|
110
|
+
styles={{
|
|
111
|
+
rail: {
|
|
112
|
+
backgroundColor: '#91caff',
|
|
113
|
+
},
|
|
114
|
+
track: {
|
|
115
|
+
backgroundColor: 'transparent',
|
|
116
|
+
},
|
|
117
|
+
}}
|
|
118
|
+
className={classNames('fec-slider', className)}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { NumberSlider, PercentageSlider };
|
|
124
|
+
export type { NumberSliderProps, PercentageSliderProps };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { NumberSlider } from '@fe-free/core';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof NumberSlider> = {
|
|
6
|
+
title: '@fe-free/core/NumberSlider',
|
|
7
|
+
component: NumberSlider,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof NumberSlider>;
|
|
14
|
+
|
|
15
|
+
function Render(args: Story['args']) {
|
|
16
|
+
const [value, setValue] = useState(0);
|
|
17
|
+
return <NumberSlider value={value} onChange={setValue} {...args} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Basic: Story = {
|
|
21
|
+
args: {},
|
|
22
|
+
render: Render,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const One: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
max: 1,
|
|
28
|
+
step: 0.1,
|
|
29
|
+
precision: 1,
|
|
30
|
+
},
|
|
31
|
+
render: Render,
|
|
32
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { PercentageSlider } from '@fe-free/core';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof PercentageSlider> = {
|
|
6
|
+
title: '@fe-free/core/PercentageSlider',
|
|
7
|
+
component: PercentageSlider,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof PercentageSlider>;
|
|
14
|
+
|
|
15
|
+
function Render(args: Story['args']) {
|
|
16
|
+
const [value, setValue] = useState([10, 30, 60]);
|
|
17
|
+
return <PercentageSlider value={value} onChange={setValue} {...args} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Basic: Story = {
|
|
21
|
+
args: {},
|
|
22
|
+
render: Render,
|
|
23
|
+
};
|
package/src/tabs/index.tsx
CHANGED
|
@@ -1,37 +1,46 @@
|
|
|
1
1
|
import type { TabsProps as AntdTabsProps } from 'antd';
|
|
2
2
|
import { Tabs as AntdTabs } from 'antd';
|
|
3
|
-
import
|
|
3
|
+
import classNames from 'classnames';
|
|
4
4
|
import { useSearchParams } from 'react-router-dom';
|
|
5
|
+
import { routeTool } from '../route';
|
|
6
|
+
import './style.scss';
|
|
5
7
|
|
|
6
8
|
interface TabsProps extends AntdTabsProps {
|
|
7
9
|
/** 自动时设置和同步 searchParams tab */
|
|
8
10
|
withSearchParams?: boolean;
|
|
11
|
+
/** 设置 searchParams 的类型,默认 set */
|
|
12
|
+
searchParamsType?: 'set' | 'change';
|
|
13
|
+
/** 默认 tab */
|
|
14
|
+
tabKey?: string;
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
function Tabs(props: TabsProps) {
|
|
12
|
-
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}, [tab]);
|
|
18
|
+
const {
|
|
19
|
+
withSearchParams,
|
|
20
|
+
searchParamsType = 'set',
|
|
21
|
+
tabKey = 'tab',
|
|
22
|
+
activeKey,
|
|
23
|
+
onChange,
|
|
24
|
+
className,
|
|
25
|
+
...rest
|
|
26
|
+
} = props;
|
|
27
|
+
const [searchParams] = useSearchParams();
|
|
28
|
+
const tab = searchParams.get(tabKey) || undefined;
|
|
24
29
|
|
|
25
30
|
return (
|
|
26
31
|
<AntdTabs
|
|
27
32
|
{...rest}
|
|
33
|
+
className={classNames('fec-tabs', className)}
|
|
28
34
|
activeKey={withSearchParams ? tab : activeKey}
|
|
29
35
|
onChange={(key) => {
|
|
30
36
|
onChange?.(key);
|
|
31
|
-
if (props.withSearchParams) {
|
|
32
|
-
searchParams.set('tab', key);
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
if (props.withSearchParams) {
|
|
39
|
+
if (searchParamsType === 'set') {
|
|
40
|
+
routeTool.setSearchParams({ [tabKey]: key });
|
|
41
|
+
} else {
|
|
42
|
+
routeTool.changeSearchParams({ [tabKey]: key });
|
|
43
|
+
}
|
|
35
44
|
}
|
|
36
45
|
}}
|
|
37
46
|
/>
|