@fe-free/core 2.1.0 → 2.1.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.
- package/CHANGELOG.md +14 -0
- package/package.json +3 -2
- package/src/crud/crud.stories.tsx +14 -0
- package/src/crud/crud.tsx +83 -53
- package/src/crud/crud_of_simple.stories.tsx +83 -7
- package/src/crud/crud_of_simple.tsx +25 -11
- package/src/crud/style.scss +30 -0
- package/src/crud/types.tsx +8 -0
- package/src/index.ts +1 -0
- package/src/use_localforage_state.tsx +30 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"author": "",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"axios": "^1.6.5",
|
|
30
30
|
"classnames": "^2.5.1",
|
|
31
31
|
"github-markdown-css": "^5.8.1",
|
|
32
|
+
"localforage": "^1.10.0",
|
|
32
33
|
"lodash-es": "^4.17.21",
|
|
33
34
|
"react-ace": "^11.0.1",
|
|
34
35
|
"react-markdown": "^9.1.0",
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
"remark-gfm": "^4.0.1",
|
|
39
40
|
"vanilla-jsoneditor": "^0.23.1",
|
|
40
41
|
"zustand": "^4.5.4",
|
|
41
|
-
"@fe-free/tool": "2.1.
|
|
42
|
+
"@fe-free/tool": "2.1.2"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
45
|
"@ant-design/pro-components": "^2.8.7",
|
|
@@ -183,6 +183,14 @@ export const MoreCustom: Story = {
|
|
|
183
183
|
},
|
|
184
184
|
}}
|
|
185
185
|
createButton={<Button type="primary">自定义新建文本</Button>}
|
|
186
|
+
readProps={{
|
|
187
|
+
operateIsDisabled: (record) => {
|
|
188
|
+
if (record.id % 3) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
},
|
|
193
|
+
}}
|
|
186
194
|
requestDeleteByRecord={fakeDeleteByRecord}
|
|
187
195
|
deleteProps={{
|
|
188
196
|
nameIndex: 'name',
|
|
@@ -214,6 +222,12 @@ export const MoreCustom: Story = {
|
|
|
214
222
|
}
|
|
215
223
|
return true;
|
|
216
224
|
},
|
|
225
|
+
operateIsHidden: (record) => {
|
|
226
|
+
if (record.id % 4) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
},
|
|
217
231
|
}}
|
|
218
232
|
/>
|
|
219
233
|
);
|
package/src/crud/crud.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ActionType } from '@ant-design/pro-components';
|
|
2
2
|
import { Button, message } from 'antd';
|
|
3
|
+
import classNames from 'classnames';
|
|
3
4
|
import { isString } from 'lodash-es';
|
|
4
5
|
import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
|
|
5
6
|
import { Link } from 'react-router-dom';
|
|
@@ -112,67 +113,97 @@ function CRUDComponent<
|
|
|
112
113
|
const btns: React.ReactNode[] = [];
|
|
113
114
|
|
|
114
115
|
if (actions.includes('read')) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
116
|
+
const hidden = readProps?.operateIsHidden?.(record) || false;
|
|
117
|
+
if (!hidden) {
|
|
118
|
+
const disabled = readProps?.operateIsDisabled?.(record) || false;
|
|
119
|
+
if (disabled) {
|
|
120
|
+
btns.push(
|
|
121
|
+
<span key="read" className="text-desc cursor-not-allowed">
|
|
122
|
+
{readProps?.operateText || '查看'}
|
|
123
|
+
</span>,
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
btns.push(
|
|
127
|
+
<CRUDDetail
|
|
128
|
+
key="read"
|
|
129
|
+
id={record[idField]}
|
|
130
|
+
record={record}
|
|
131
|
+
onSuccess={handleReload}
|
|
132
|
+
trigger={<a>{readProps?.operateText || '查看'}</a>}
|
|
133
|
+
action="read"
|
|
134
|
+
{...detailProps}
|
|
135
|
+
/>,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
if (actions.includes('read_detail')) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
142
|
+
const hidden = readProps?.operateIsHidden?.(record) || false;
|
|
143
|
+
if (!hidden) {
|
|
144
|
+
const disabled = readProps?.operateIsDisabled?.(record) || false;
|
|
145
|
+
if (disabled) {
|
|
146
|
+
btns.push(
|
|
147
|
+
<span key="read" className="text-desc cursor-not-allowed">
|
|
148
|
+
{readProps?.operateText || '查看'}
|
|
149
|
+
</span>,
|
|
150
|
+
);
|
|
151
|
+
} else {
|
|
152
|
+
btns.push(
|
|
153
|
+
<Link
|
|
154
|
+
key="read_detail"
|
|
155
|
+
to={`./detail/${record[detailIdIndex || 'id']}`}
|
|
156
|
+
target={readProps?.target}
|
|
157
|
+
>
|
|
158
|
+
{readProps?.operateText || '查看'}
|
|
159
|
+
</Link>,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
138
163
|
}
|
|
139
164
|
|
|
140
165
|
if (actions.includes('update')) {
|
|
141
|
-
const
|
|
166
|
+
const hidden = updateProps?.operateIsHidden?.(record) || false;
|
|
167
|
+
if (!hidden) {
|
|
168
|
+
const disabled = updateProps?.operateIsDisabled?.(record) || false;
|
|
142
169
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
170
|
+
if (disabled) {
|
|
171
|
+
btns.push(
|
|
172
|
+
<span key="update" className="text-desc cursor-not-allowed">
|
|
173
|
+
{updateProps?.operateText || '编辑'}
|
|
174
|
+
</span>,
|
|
175
|
+
);
|
|
176
|
+
} else {
|
|
177
|
+
btns.push(
|
|
178
|
+
<CRUDDetail
|
|
179
|
+
key="update"
|
|
180
|
+
id={record[idField]}
|
|
181
|
+
record={record}
|
|
182
|
+
onSuccess={handleReload}
|
|
183
|
+
trigger={<a>{updateProps?.operateText || '编辑'}</a>}
|
|
184
|
+
action="update"
|
|
185
|
+
{...detailProps}
|
|
186
|
+
/>,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
161
189
|
}
|
|
162
190
|
}
|
|
163
191
|
|
|
164
192
|
if (actions.includes('delete') && deleteProps) {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
193
|
+
const hidden = deleteProps?.operateIsHidden?.(record) || false;
|
|
194
|
+
if (!hidden) {
|
|
195
|
+
const disabled = deleteProps?.operateIsDisabled?.(record) || false;
|
|
196
|
+
btns.push(
|
|
197
|
+
<OperateDelete
|
|
198
|
+
key="delete"
|
|
199
|
+
name={record[deleteProps.nameIndex]}
|
|
200
|
+
desc={deleteProps.desc}
|
|
201
|
+
operateText={deleteProps.operateText}
|
|
202
|
+
disabled={disabled}
|
|
203
|
+
onDelete={getHandleDelete(record)}
|
|
204
|
+
/>,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
176
207
|
}
|
|
177
208
|
|
|
178
209
|
return (
|
|
@@ -206,9 +237,8 @@ function CRUDComponent<
|
|
|
206
237
|
operateColumnProps,
|
|
207
238
|
actions,
|
|
208
239
|
deleteProps,
|
|
240
|
+
readProps,
|
|
209
241
|
handleReload,
|
|
210
|
-
readProps?.operateText,
|
|
211
|
-
readProps?.target,
|
|
212
242
|
detailProps,
|
|
213
243
|
detailIdIndex,
|
|
214
244
|
updateProps,
|
|
@@ -259,7 +289,7 @@ function CRUDComponent<
|
|
|
259
289
|
});
|
|
260
290
|
|
|
261
291
|
return (
|
|
262
|
-
<div className=
|
|
292
|
+
<div className={classNames('fec-crud')}>
|
|
263
293
|
<Table<DataSource>
|
|
264
294
|
rowKey="id"
|
|
265
295
|
{...tableProps}
|
|
@@ -17,10 +17,45 @@ export const Normal: Story = {
|
|
|
17
17
|
render: () => {
|
|
18
18
|
const columns = [
|
|
19
19
|
{
|
|
20
|
-
title: '
|
|
21
|
-
dataIndex: '
|
|
20
|
+
title: '名字(省略)',
|
|
21
|
+
dataIndex: 'name',
|
|
22
22
|
search: true,
|
|
23
|
+
ellipsis: true,
|
|
23
24
|
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<CRUDOfSimple
|
|
29
|
+
actions={['create', 'delete']}
|
|
30
|
+
tableProps={{
|
|
31
|
+
columns,
|
|
32
|
+
request: fakeRequest,
|
|
33
|
+
pagination: false,
|
|
34
|
+
}}
|
|
35
|
+
requestDeleteByRecord={fakeDeleteByRecord}
|
|
36
|
+
deleteProps={{
|
|
37
|
+
nameIndex: 'name',
|
|
38
|
+
}}
|
|
39
|
+
detailForm={() => (
|
|
40
|
+
<>
|
|
41
|
+
<ProFormText
|
|
42
|
+
name="name"
|
|
43
|
+
label="名字"
|
|
44
|
+
required
|
|
45
|
+
rules={[{ required: true }]}
|
|
46
|
+
extra="extra extra extra extra"
|
|
47
|
+
/>
|
|
48
|
+
</>
|
|
49
|
+
)}
|
|
50
|
+
requestCreateByValues={fakeCreate}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const WithSearch: Story = {
|
|
57
|
+
render: () => {
|
|
58
|
+
const columns = [
|
|
24
59
|
{
|
|
25
60
|
title: '名字(省略)',
|
|
26
61
|
dataIndex: 'name',
|
|
@@ -53,19 +88,59 @@ export const Normal: Story = {
|
|
|
53
88
|
</>
|
|
54
89
|
)}
|
|
55
90
|
requestCreateByValues={fakeCreate}
|
|
91
|
+
simpleSearchProps={{
|
|
92
|
+
name: 'name',
|
|
93
|
+
widthFull: true,
|
|
94
|
+
}}
|
|
56
95
|
/>
|
|
57
96
|
);
|
|
58
97
|
},
|
|
59
98
|
};
|
|
60
99
|
|
|
61
|
-
export const
|
|
100
|
+
export const HoverShow: Story = {
|
|
62
101
|
render: () => {
|
|
63
102
|
const columns = [
|
|
64
103
|
{
|
|
65
|
-
title: '
|
|
66
|
-
dataIndex: '
|
|
104
|
+
title: '名字(省略)',
|
|
105
|
+
dataIndex: 'name',
|
|
67
106
|
search: true,
|
|
107
|
+
ellipsis: true,
|
|
68
108
|
},
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<CRUDOfSimple
|
|
113
|
+
actions={['create', 'delete']}
|
|
114
|
+
tableProps={{
|
|
115
|
+
columns,
|
|
116
|
+
request: fakeRequest,
|
|
117
|
+
pagination: false,
|
|
118
|
+
}}
|
|
119
|
+
requestDeleteByRecord={fakeDeleteByRecord}
|
|
120
|
+
deleteProps={{
|
|
121
|
+
nameIndex: 'name',
|
|
122
|
+
}}
|
|
123
|
+
detailForm={() => (
|
|
124
|
+
<>
|
|
125
|
+
<ProFormText
|
|
126
|
+
name="name"
|
|
127
|
+
label="名字"
|
|
128
|
+
required
|
|
129
|
+
rules={[{ required: true }]}
|
|
130
|
+
extra="extra extra extra extra"
|
|
131
|
+
/>
|
|
132
|
+
</>
|
|
133
|
+
)}
|
|
134
|
+
requestCreateByValues={fakeCreate}
|
|
135
|
+
simpleOperateHoverShow
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const JustSearch: Story = {
|
|
142
|
+
render: () => {
|
|
143
|
+
const columns = [
|
|
69
144
|
{
|
|
70
145
|
title: '名字(省略)',
|
|
71
146
|
dataIndex: 'name',
|
|
@@ -76,7 +151,7 @@ export const WithSearch: Story = {
|
|
|
76
151
|
|
|
77
152
|
return (
|
|
78
153
|
<CRUDOfSimple
|
|
79
|
-
actions={['
|
|
154
|
+
actions={['delete']}
|
|
80
155
|
tableProps={{
|
|
81
156
|
columns,
|
|
82
157
|
request: fakeRequest,
|
|
@@ -99,7 +174,8 @@ export const WithSearch: Story = {
|
|
|
99
174
|
)}
|
|
100
175
|
requestCreateByValues={fakeCreate}
|
|
101
176
|
simpleSearchProps={{
|
|
102
|
-
name: '
|
|
177
|
+
name: 'name',
|
|
178
|
+
widthFull: true,
|
|
103
179
|
}}
|
|
104
180
|
/>
|
|
105
181
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useDebounce } from 'ahooks';
|
|
2
2
|
import { Input } from 'antd';
|
|
3
|
+
import classNames from 'classnames';
|
|
3
4
|
import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
5
|
import { CRUD } from './crud';
|
|
5
6
|
import type { CRUDMethods, CRUDProps } from './types';
|
|
@@ -8,12 +9,15 @@ interface CRUDOfSimpleProps<
|
|
|
8
9
|
DataSource extends Record<string, any> = any,
|
|
9
10
|
Key extends string | number = string,
|
|
10
11
|
> extends CRUDProps<DataSource, Key> {
|
|
12
|
+
simpleOperateHoverShow?: boolean;
|
|
11
13
|
// 传才开启搜索
|
|
12
14
|
simpleSearchProps?: {
|
|
13
15
|
/** 搜索项的名称,默认 keywords */
|
|
14
16
|
name: string;
|
|
15
17
|
/** 搜索项的 placeholder,默认 请输入 */
|
|
16
18
|
placeholder?: string;
|
|
19
|
+
/** 占满宽度 */
|
|
20
|
+
widthFull?: boolean;
|
|
17
21
|
};
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -39,12 +43,13 @@ function SearchRender(props: {
|
|
|
39
43
|
allowClear
|
|
40
44
|
value={props.value}
|
|
41
45
|
onChange={(e) => props.onChange(e.target.value)}
|
|
46
|
+
className="w-full"
|
|
42
47
|
/>
|
|
43
48
|
);
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef<CRUDMethods>) {
|
|
47
|
-
const { simpleSearchProps, tableProps, ...rest } = props;
|
|
52
|
+
const { simpleSearchProps, tableProps, simpleOperateHoverShow, ...rest } = props;
|
|
48
53
|
|
|
49
54
|
useTips(props);
|
|
50
55
|
const [searchValue, setSearchValue] = useState<string>('');
|
|
@@ -61,15 +66,14 @@ function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef
|
|
|
61
66
|
const toolBarRender = useCallback(
|
|
62
67
|
(...args) => {
|
|
63
68
|
return [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
</div>,
|
|
69
|
+
simpleSearchProps && (
|
|
70
|
+
<SearchRender
|
|
71
|
+
key="search-input"
|
|
72
|
+
placeholder={simpleSearchProps.placeholder}
|
|
73
|
+
value={searchValue}
|
|
74
|
+
onChange={(value) => setSearchValue(value)}
|
|
75
|
+
/>
|
|
76
|
+
),
|
|
73
77
|
// @ts-ignore
|
|
74
78
|
...(tableProps.toolBarRender ? tableProps.toolBarRender(...args) : []),
|
|
75
79
|
];
|
|
@@ -89,7 +93,12 @@ function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef
|
|
|
89
93
|
}, [debouncedSearchValue, simpleSearchProps, tableProps.params]);
|
|
90
94
|
|
|
91
95
|
return (
|
|
92
|
-
<div
|
|
96
|
+
<div
|
|
97
|
+
className={classNames('fec-crud-of-simple', {
|
|
98
|
+
'fec-crud-of-simple-hover-show': simpleOperateHoverShow,
|
|
99
|
+
'fec-crud-of-simple-search-width-full': simpleSearchProps?.widthFull,
|
|
100
|
+
})}
|
|
101
|
+
>
|
|
93
102
|
<CRUD
|
|
94
103
|
ref={ref}
|
|
95
104
|
{...rest}
|
|
@@ -104,6 +113,11 @@ function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef
|
|
|
104
113
|
// 简单的隐藏搜索栏
|
|
105
114
|
search: false,
|
|
106
115
|
}}
|
|
116
|
+
operateColumnProps={{
|
|
117
|
+
// hoverShow 情况下,默认 width 1
|
|
118
|
+
width: simpleOperateHoverShow ? 1 : undefined,
|
|
119
|
+
...props.operateColumnProps,
|
|
120
|
+
}}
|
|
107
121
|
/>
|
|
108
122
|
</div>
|
|
109
123
|
);
|
package/src/crud/style.scss
CHANGED
|
@@ -13,4 +13,34 @@
|
|
|
13
13
|
.ant-pro-table-list-toolbar {
|
|
14
14
|
border-bottom: 1px solid #f0f0f0;
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
&.fec-crud-of-simple-hover-show {
|
|
18
|
+
.ant-table-cell-fix-right {
|
|
19
|
+
position: absolute !important;
|
|
20
|
+
display: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.ant-table-row {
|
|
24
|
+
&:hover {
|
|
25
|
+
.ant-table-cell-fix-right {
|
|
26
|
+
display: block;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&.fec-crud-of-simple-search-width-full {
|
|
33
|
+
.ant-pro-table-list-toolbar-container {
|
|
34
|
+
justify-content: unset;
|
|
35
|
+
|
|
36
|
+
.ant-pro-table-list-toolbar-right {
|
|
37
|
+
justify-content: unset;
|
|
38
|
+
|
|
39
|
+
& > div {
|
|
40
|
+
flex: 1;
|
|
41
|
+
margin-right: 8px;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
16
46
|
}
|
package/src/crud/types.tsx
CHANGED
|
@@ -62,6 +62,10 @@ interface CRUDProps<DataSource = any, Key = string> {
|
|
|
62
62
|
readProps?: {
|
|
63
63
|
/** 文本 */
|
|
64
64
|
operateText?: string;
|
|
65
|
+
/** ”查看”是否禁用 */
|
|
66
|
+
operateIsDisabled?: (record: DataSource) => boolean;
|
|
67
|
+
/** ”查看”是否隐藏 */
|
|
68
|
+
operateIsHidden?: (record: DataSource) => boolean;
|
|
65
69
|
/** 打开方式, action 为 read_detail 有效 */
|
|
66
70
|
target?: '_blank';
|
|
67
71
|
/** 保存按钮文本 */
|
|
@@ -81,6 +85,8 @@ interface CRUDProps<DataSource = any, Key = string> {
|
|
|
81
85
|
operateText?: string;
|
|
82
86
|
/** ”编辑”是否禁用 */
|
|
83
87
|
operateIsDisabled?: (record: DataSource) => boolean;
|
|
88
|
+
/** ”编辑”是否隐藏 */
|
|
89
|
+
operateIsHidden?: (record: DataSource) => boolean;
|
|
84
90
|
/** 保存按钮文本 */
|
|
85
91
|
submitText?: string;
|
|
86
92
|
/** 重置按钮文本 */
|
|
@@ -99,6 +105,8 @@ interface CRUDProps<DataSource = any, Key = string> {
|
|
|
99
105
|
operateText?: string;
|
|
100
106
|
/** “删除”是否禁用 */
|
|
101
107
|
operateIsDisabled?: (record: DataSource) => boolean;
|
|
108
|
+
/** ”删除”是否隐藏 */
|
|
109
|
+
operateIsHidden?: (record: DataSource) => boolean;
|
|
102
110
|
/** 显示名称索引 */
|
|
103
111
|
nameIndex: keyof DataSource;
|
|
104
112
|
/** 删除确认描述 */
|
package/src/index.ts
CHANGED
|
@@ -29,4 +29,5 @@ export {
|
|
|
29
29
|
export { Markdown } from './markdown';
|
|
30
30
|
export { Table } from './table';
|
|
31
31
|
export type { TableProps } from './table';
|
|
32
|
+
export { useLocalforageState } from './use_localforage_state';
|
|
32
33
|
export { CustomValueTypeEnum, customValueTypeMap } from './value_type_map';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import localforage from 'localforage';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
const useLocalforageState = <T = any,>(
|
|
5
|
+
key: string,
|
|
6
|
+
options: {
|
|
7
|
+
defaultValue?: T;
|
|
8
|
+
},
|
|
9
|
+
): [T | undefined, (value: T) => void, boolean] => {
|
|
10
|
+
const [ready, setReady] = useState(false);
|
|
11
|
+
const [value, setValue] = useState<T | undefined>(options.defaultValue);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
localforage.getItem(key).then((v) => {
|
|
15
|
+
if (v !== undefined) {
|
|
16
|
+
setValue(v as T);
|
|
17
|
+
}
|
|
18
|
+
setReady(true);
|
|
19
|
+
});
|
|
20
|
+
}, [key]);
|
|
21
|
+
|
|
22
|
+
const setValueAndSave = (v: T) => {
|
|
23
|
+
setValue(v);
|
|
24
|
+
localforage.setItem(key, v);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return [value, setValueAndSave, ready];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { useLocalforageState };
|