@hi-ui/schema-fields 4.0.0-experimental.7 → 4.0.0-experimental.8
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/lib/base.d.ts +44 -0
- package/lib/bundle.d.ts +76 -0
- package/lib/bundle.js +66 -0
- package/lib/components/async-refill-placeholder/index.d.ts +7 -0
- package/lib/components/image-preview/index.d.ts +5 -0
- package/lib/components/image-preview/index.js +49 -0
- package/lib/components/span/index.d.ts +8 -0
- package/lib/components/upload-bridge/index.d.ts +5 -0
- package/lib/components/upload-bridge/index.js +214 -0
- package/lib/components/upload-bridge/type.d.ts +26 -0
- package/lib/components/upload-bridge/utils.d.ts +11 -0
- package/lib/components/upload-bridge/utils.js +66 -0
- package/lib/ctx.d.ts +26 -0
- package/lib/editable/ctx.d.ts +5 -0
- package/lib/editable/editable.d.ts +18 -0
- package/lib/editable/index.d.ts +14 -0
- package/lib/editable/readonly.d.ts +8 -0
- package/lib/editable/simple.d.ts +8 -0
- package/lib/editable/type.d.ts +9 -0
- package/lib/editable/use-readonly.d.ts +26 -0
- package/lib/extensible/selectable/index.d.ts +31 -0
- package/lib/extensible/selectable/index.js +101 -0
- package/lib/extensible/selectable/match-cascader.d.ts +19 -0
- package/lib/extensible/selectable/match-cascader.js +179 -0
- package/lib/extensible/selectable/type.d.ts +16 -0
- package/lib/fields/basic/cascader/index.d.ts +10 -0
- package/lib/fields/basic/cascader/index.js +83 -0
- package/lib/fields/basic/check-cascader/index.d.ts +11 -0
- package/lib/fields/basic/check-cascader/index.js +44 -0
- package/lib/fields/basic/check-select/index.d.ts +34 -0
- package/lib/fields/basic/check-select/index.js +75 -0
- package/lib/fields/basic/check-tree-select/index.d.ts +10 -0
- package/lib/fields/basic/check-tree-select/index.js +47 -0
- package/lib/fields/basic/checkbox/index.d.ts +19 -0
- package/lib/fields/basic/checkbox/index.js +70 -0
- package/lib/fields/basic/counter/index.d.ts +7 -0
- package/lib/fields/basic/counter/index.js +26 -0
- package/lib/fields/basic/radio/index.d.ts +7 -0
- package/lib/fields/basic/radio/index.js +30 -0
- package/lib/fields/basic/rating/index.d.ts +7 -0
- package/lib/fields/basic/rating/index.js +26 -0
- package/lib/fields/basic/select/index.d.ts +12 -0
- package/lib/fields/basic/select/index.js +83 -0
- package/lib/fields/basic/slider/index.d.ts +7 -0
- package/lib/fields/basic/slider/index.js +26 -0
- package/lib/fields/basic/switch/index.d.ts +7 -0
- package/lib/fields/basic/switch/index.js +33 -0
- package/lib/fields/basic/textarea/index.d.ts +7 -0
- package/lib/fields/basic/textarea/index.js +26 -0
- package/lib/fields/basic/time-picker/index.d.ts +8 -0
- package/lib/fields/basic/time-picker/index.js +31 -0
- package/lib/fields/basic/time-picker/index.scss.js +13 -0
- package/lib/fields/basic/tree-select/index.d.ts +11 -0
- package/lib/fields/basic/tree-select/index.js +47 -0
- package/lib/fields/basic/upload/custom.d.ts +3 -0
- package/lib/fields/basic/upload/custom.js +16 -0
- package/lib/fields/basic/upload/index.d.ts +8 -0
- package/lib/fields/basic/upload/index.js +34 -0
- package/lib/fields/enhance/number-range/index.d.ts +7 -0
- package/lib/fields/enhance/number-range/index.js +26 -0
- package/lib/fields/semantic/date/index.d.ts +15 -0
- package/lib/fields/semantic/date/index.js +67 -0
- package/lib/fields/semantic/image/index.d.ts +11 -0
- package/lib/fields/semantic/image/index.js +51 -0
- package/lib/fields/semantic/image/upload.d.ts +3 -0
- package/lib/fields/semantic/image/upload.js +20 -0
- package/lib/fields/semantic/link/index.d.ts +31 -0
- package/lib/fields/semantic/link/index.js +82 -0
- package/lib/fields/semantic/number/index.d.ts +21 -0
- package/lib/fields/semantic/number/index.js +91 -0
- package/lib/fields/semantic/tag/index.d.ts +25 -0
- package/lib/fields/semantic/tag/index.js +100 -0
- package/lib/fields/semantic/tag/index.scss.js +13 -0
- package/lib/fields/semantic/tag/preset.d.ts +54 -0
- package/lib/fields/semantic/text/index.d.ts +13 -0
- package/lib/index.d.ts +30 -0
- package/lib/utils/batch-dep-update.d.ts +13 -0
- package/lib/utils/form-binding.d.ts +11 -0
- package/lib/utils/label.d.ts +11 -0
- package/lib/utils.d.ts +15 -0
- package/package.json +1 -1
package/lib/base.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { InputProps } from '@hi-ui/input';
|
|
3
|
+
import { AbstractProField } from '@hi-ui/schema-core';
|
|
4
|
+
import type { FormItemProps, ProFieldProps, ProFieldRenderCtx, ProFieldRenderCellCtx, ProFieldRenderFooterCellCtx, ProFieldRenderFormItemCtx, ProFieldRenderEditableCtx, ProFieldRenderEditCellCtx, FieldRendererType } from '@hi-ui/schema-core';
|
|
5
|
+
import type { NormalFieldCtxType } from './utils';
|
|
6
|
+
export { ProFieldProps, ProFieldRenderCtx, ProFieldRenderCellCtx, ProFieldRenderFooterCellCtx, ProFieldRenderFormItemCtx, ProFieldRenderEditableCtx, ProFieldRenderEditCellCtx, FieldRendererType, };
|
|
7
|
+
/**
|
|
8
|
+
* 增强型的自定义渲染函数
|
|
9
|
+
* - 用于在字段内部渲染逻辑的基础之上进行额外增强
|
|
10
|
+
*/
|
|
11
|
+
export type EnhancedRenderersType = Partial<{
|
|
12
|
+
[K in keyof AbstractProField as K extends 'constructor' ? never : K]: AbstractProField[K] extends AnyFn ? (dom: React.ReactNode, innerCtx: {
|
|
13
|
+
render: ProField;
|
|
14
|
+
data: Parameters<AbstractProField[K]>[0];
|
|
15
|
+
renderCtx: Parameters<AbstractProField[K]>[1];
|
|
16
|
+
}) => React.ReactNode : never;
|
|
17
|
+
}>;
|
|
18
|
+
export declare class ProField extends AbstractProField {
|
|
19
|
+
dftDom: JSX.Element;
|
|
20
|
+
protected getFieldProps<FieldProps extends AnyObject>(dftProps: FieldProps, ctx: NormalFieldCtxType<FieldProps>): FieldProps;
|
|
21
|
+
protected getWrapperProps<WrapperProps extends AnyObject>(dftProps: WrapperProps, ctx: Pick<ProFieldRenderCtx<AnyObject, WrapperProps>, 'field'>): WrapperProps;
|
|
22
|
+
protected getFormItemProps(dftProps: FormItemProps, ctx: Pick<ProFieldRenderFormItemCtx, 'field'>): FormItemProps;
|
|
23
|
+
/**
|
|
24
|
+
* 渲染字符串的内部方法
|
|
25
|
+
* @desc 很多类型的字段最终渲染时都是文本,因此提取一个公共方法到此处
|
|
26
|
+
* @desc 封装了 EllipsisTooltip 的逻辑 */
|
|
27
|
+
protected renderString(text: string, ctx: ProFieldRenderCtx<ProFieldProps>): JSX.Element;
|
|
28
|
+
/** 只读模式的渲染逻辑 */
|
|
29
|
+
render(data: unknown, ctx: ProFieldRenderCtx<AnyType>): JSX.Element;
|
|
30
|
+
/**
|
|
31
|
+
* 表格单元格渲染逻辑
|
|
32
|
+
* @desc 默认直接使用 render 的逻辑 */
|
|
33
|
+
renderCell(data: unknown, ctx: ProFieldRenderCellCtx<AnyType>): JSX.Element;
|
|
34
|
+
/**
|
|
35
|
+
* 表格 Footer 单元格渲染逻辑
|
|
36
|
+
* @desc 没有默认逻辑,需由字段自行实现 */
|
|
37
|
+
renderFooterCell(ctx: ProFieldRenderFooterCellCtx<AnyType>): React.ReactNode;
|
|
38
|
+
renderFormItem(data: null, ctx: ProFieldRenderFormItemCtx<AnyType>): void;
|
|
39
|
+
protected getEditablePlaceholder(data: unknown, ctx: ProFieldRenderEditableCtx<AnyType>): unknown;
|
|
40
|
+
/** 编辑模式的渲染逻辑,在只读与可编辑之间切换 */
|
|
41
|
+
renderEditable(data: unknown, ctx: ProFieldRenderEditableCtx<AnyType>, extraProps?: InputProps): JSX.Element;
|
|
42
|
+
getDftPlaceholder(ctx: NormalFieldCtxType<AnyType>): string | undefined;
|
|
43
|
+
}
|
|
44
|
+
export type FieldMapType = Record<string, typeof ProField>;
|
package/lib/bundle.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { FieldMapType } from './base';
|
|
3
|
+
import { ProCascader, type ProCascaderProps } from './fields/basic/cascader';
|
|
4
|
+
import { ProCheckCascader, type ProCheckCascaderProps } from './fields/basic/check-cascader';
|
|
5
|
+
import { ProCheckSelect, type ProCheckSelectProps } from './fields/basic/check-select';
|
|
6
|
+
import { ProCheckTreeSelect, type ProCheckTreeSelectProps } from './fields/basic/check-tree-select';
|
|
7
|
+
import { ProCheckbox, type ProCheckboxProps } from './fields/basic/checkbox';
|
|
8
|
+
import { ProCounter, type ProCounterProps } from './fields/basic/counter';
|
|
9
|
+
import { ProDate, type ProDateProps } from './fields/semantic/date';
|
|
10
|
+
import { ProImage, type ProImageProps } from './fields/semantic/image';
|
|
11
|
+
import { ProLink, type ProLinkProps } from './fields/semantic/link';
|
|
12
|
+
import { ProNumber, type ProNumberProps } from './fields/semantic/number';
|
|
13
|
+
import { ProRadio, type ProRadioProps } from './fields/basic/radio';
|
|
14
|
+
import { ProRating, type ProRatingProps } from './fields/basic/rating';
|
|
15
|
+
import { ProSelect, type ProSelectProps } from './fields/basic/select';
|
|
16
|
+
import { ProSlider, type ProSliderProps } from './fields/basic/slider';
|
|
17
|
+
import { ProSwitch, type ProSwitchProps } from './fields/basic/switch';
|
|
18
|
+
import { ProTag, type ProTagProps } from './fields/semantic/tag';
|
|
19
|
+
import { ProText, type ProTextProps } from './fields/semantic/text';
|
|
20
|
+
import { ProTextArea, type ProTextAreaProps } from './fields/basic/textarea';
|
|
21
|
+
import { ProTimePicker, type ProTimePickerProps } from './fields/basic/time-picker';
|
|
22
|
+
import { ProTreeSelect, type ProTreeSelectProps } from './fields/basic/tree-select';
|
|
23
|
+
import { ProUpload, type ProUploadProps } from './fields/basic/upload';
|
|
24
|
+
import { ProNumberRange, type ProNumberRangeProps } from './fields/enhance/number-range';
|
|
25
|
+
export { ProCascader, ProCheckCascader, ProCheckSelect, ProCheckTreeSelect, ProCheckbox, ProCounter, ProDate, ProImage, ProLink, ProNumber, ProRadio, ProRating, ProSelect, ProSlider, ProSwitch, ProTag, ProText, ProTextArea, ProTimePicker, ProTreeSelect, ProUpload, ProNumberRange, };
|
|
26
|
+
export declare const ProFieldMap: {
|
|
27
|
+
cascader: typeof ProCascader;
|
|
28
|
+
'check-cascader': typeof ProCheckCascader;
|
|
29
|
+
'check-select': typeof ProCheckSelect;
|
|
30
|
+
'check-tree-select': typeof ProCheckTreeSelect;
|
|
31
|
+
checkbox: typeof ProCheckbox;
|
|
32
|
+
counter: typeof ProCounter;
|
|
33
|
+
date: typeof ProDate;
|
|
34
|
+
image: typeof ProImage;
|
|
35
|
+
link: typeof ProLink;
|
|
36
|
+
number: typeof ProNumber;
|
|
37
|
+
radio: typeof ProRadio;
|
|
38
|
+
rating: typeof ProRating;
|
|
39
|
+
select: typeof ProSelect;
|
|
40
|
+
slider: typeof ProSlider;
|
|
41
|
+
switch: typeof ProSwitch;
|
|
42
|
+
tag: typeof ProTag;
|
|
43
|
+
text: typeof ProText;
|
|
44
|
+
textarea: typeof ProTextArea;
|
|
45
|
+
'time-picker': typeof ProTimePicker;
|
|
46
|
+
'tree-select': typeof ProTreeSelect;
|
|
47
|
+
upload: typeof ProUpload;
|
|
48
|
+
'number-range': typeof ProNumberRange;
|
|
49
|
+
};
|
|
50
|
+
export type BuiltinFieldMapType = typeof ProFieldMap;
|
|
51
|
+
export type ProFieldMapType = FieldMapType & BuiltinFieldMapType;
|
|
52
|
+
export type ProFieldsProps = {
|
|
53
|
+
cascader: ProCascaderProps;
|
|
54
|
+
'check-cascader': ProCheckCascaderProps;
|
|
55
|
+
'check-select': ProCheckSelectProps;
|
|
56
|
+
'check-tree-select': ProCheckTreeSelectProps;
|
|
57
|
+
checkbox: ProCheckboxProps;
|
|
58
|
+
counter: ProCounterProps;
|
|
59
|
+
date: ProDateProps;
|
|
60
|
+
image: ProImageProps;
|
|
61
|
+
link: ProLinkProps;
|
|
62
|
+
number: ProNumberProps;
|
|
63
|
+
radio: ProRadioProps;
|
|
64
|
+
rating: ProRatingProps;
|
|
65
|
+
select: ProSelectProps;
|
|
66
|
+
slider: ProSliderProps;
|
|
67
|
+
switch: ProSwitchProps;
|
|
68
|
+
tag: ProTagProps;
|
|
69
|
+
text: ProTextProps;
|
|
70
|
+
textarea: ProTextAreaProps;
|
|
71
|
+
'time-picker': ProTimePickerProps;
|
|
72
|
+
'tree-select': ProTreeSelectProps;
|
|
73
|
+
upload: ProUploadProps;
|
|
74
|
+
'number-range': ProNumberRangeProps;
|
|
75
|
+
};
|
|
76
|
+
export declare function BuiltinFieldMapProvider(props: React.PropsWithChildren<unknown>): JSX.Element;
|
package/lib/bundle.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** @LICENSE
|
|
2
|
+
* @hi-ui/schema-fields
|
|
3
|
+
* https://github.com/XiaoMi/hiui/tree/master/packages/schema/schema-fields#readme
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) HiUI <mi-hiui@xiaomi.com>.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { FieldMapProvider } from './ctx.js';
|
|
12
|
+
import { ProCascader } from './fields/basic/cascader/index.js';
|
|
13
|
+
import { ProCheckCascader } from './fields/basic/check-cascader/index.js';
|
|
14
|
+
import { ProCheckSelect } from './fields/basic/check-select/index.js';
|
|
15
|
+
import { ProCheckTreeSelect } from './fields/basic/check-tree-select/index.js';
|
|
16
|
+
import { ProCheckbox } from './fields/basic/checkbox/index.js';
|
|
17
|
+
import { ProCounter } from './fields/basic/counter/index.js';
|
|
18
|
+
import { ProDate } from './fields/semantic/date/index.js';
|
|
19
|
+
import { ProImage } from './fields/semantic/image/index.js';
|
|
20
|
+
import { ProLink } from './fields/semantic/link/index.js';
|
|
21
|
+
import { ProNumber } from './fields/semantic/number/index.js';
|
|
22
|
+
import { ProRadio } from './fields/basic/radio/index.js';
|
|
23
|
+
import { ProRating } from './fields/basic/rating/index.js';
|
|
24
|
+
import { ProSelect } from './fields/basic/select/index.js';
|
|
25
|
+
import { ProSlider } from './fields/basic/slider/index.js';
|
|
26
|
+
import { ProSwitch } from './fields/basic/switch/index.js';
|
|
27
|
+
import { ProTag } from './fields/semantic/tag/index.js';
|
|
28
|
+
import { ProText } from './fields/semantic/text/index.js';
|
|
29
|
+
import { ProTextArea } from './fields/basic/textarea/index.js';
|
|
30
|
+
import { ProTimePicker } from './fields/basic/time-picker/index.js';
|
|
31
|
+
import { ProTreeSelect } from './fields/basic/tree-select/index.js';
|
|
32
|
+
import { ProUpload } from './fields/basic/upload/index.js';
|
|
33
|
+
import { ProNumberRange } from './fields/enhance/number-range/index.js';
|
|
34
|
+
var ProFieldMap = {
|
|
35
|
+
cascader: ProCascader,
|
|
36
|
+
'check-cascader': ProCheckCascader,
|
|
37
|
+
'check-select': ProCheckSelect,
|
|
38
|
+
'check-tree-select': ProCheckTreeSelect,
|
|
39
|
+
checkbox: ProCheckbox,
|
|
40
|
+
counter: ProCounter,
|
|
41
|
+
date: ProDate,
|
|
42
|
+
image: ProImage,
|
|
43
|
+
link: ProLink,
|
|
44
|
+
number: ProNumber,
|
|
45
|
+
radio: ProRadio,
|
|
46
|
+
rating: ProRating,
|
|
47
|
+
select: ProSelect,
|
|
48
|
+
slider: ProSlider,
|
|
49
|
+
"switch": ProSwitch,
|
|
50
|
+
tag: ProTag,
|
|
51
|
+
text: ProText,
|
|
52
|
+
textarea: ProTextArea,
|
|
53
|
+
'time-picker': ProTimePicker,
|
|
54
|
+
'tree-select': ProTreeSelect,
|
|
55
|
+
upload: ProUpload,
|
|
56
|
+
// 'edit-table': ProEditTable,
|
|
57
|
+
'number-range': ProNumberRange
|
|
58
|
+
// user: ProUser,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
function BuiltinFieldMapProvider(props) {
|
|
62
|
+
return /*#__PURE__*/React.createElement(FieldMapProvider, {
|
|
63
|
+
fields: ProFieldMap
|
|
64
|
+
}, props.children);
|
|
65
|
+
}
|
|
66
|
+
export { BuiltinFieldMapProvider, ProCascader, ProCheckCascader, ProCheckSelect, ProCheckTreeSelect, ProCheckbox, ProCounter, ProDate, ProFieldMap, ProImage, ProLink, ProNumber, ProNumberRange, ProRadio, ProRating, ProSelect, ProSlider, ProSwitch, ProTag, ProText, ProTextArea, ProTimePicker, ProTreeSelect, ProUpload };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** @LICENSE
|
|
2
|
+
* @hi-ui/schema-fields
|
|
3
|
+
* https://github.com/XiaoMi/hiui/tree/master/packages/schema/schema-fields#readme
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) HiUI <mi-hiui@xiaomi.com>.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import Preview from '@hi-ui/preview';
|
|
12
|
+
import Space from '@hi-ui/space';
|
|
13
|
+
function ImagePreview(props) {
|
|
14
|
+
var images = props.images;
|
|
15
|
+
var _React$useState = React.useState(false),
|
|
16
|
+
visible = _React$useState[0],
|
|
17
|
+
setVisible = _React$useState[1];
|
|
18
|
+
var _React$useState2 = React.useState(0),
|
|
19
|
+
current = _React$useState2[0],
|
|
20
|
+
setCurrent = _React$useState2[1];
|
|
21
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
22
|
+
className: "image-preview"
|
|
23
|
+
}, /*#__PURE__*/React.createElement(Preview, {
|
|
24
|
+
title: current + 1 + "/" + images.length,
|
|
25
|
+
src: images,
|
|
26
|
+
current: current,
|
|
27
|
+
onPreviewChange: setCurrent,
|
|
28
|
+
visible: visible,
|
|
29
|
+
onClose: function onClose() {
|
|
30
|
+
setVisible(false);
|
|
31
|
+
}
|
|
32
|
+
}), /*#__PURE__*/React.createElement(Space, null, images.map(function (url, index) {
|
|
33
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
34
|
+
key: index,
|
|
35
|
+
className: "image-preview__item-wrapper"
|
|
36
|
+
}, /*#__PURE__*/React.createElement("img", {
|
|
37
|
+
src: url,
|
|
38
|
+
style: {
|
|
39
|
+
width: '100%',
|
|
40
|
+
cursor: 'pointer'
|
|
41
|
+
},
|
|
42
|
+
onClick: function onClick() {
|
|
43
|
+
setCurrent(index);
|
|
44
|
+
setVisible(true);
|
|
45
|
+
}
|
|
46
|
+
}));
|
|
47
|
+
})));
|
|
48
|
+
}
|
|
49
|
+
export { ImagePreview };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type ReadonlyWrapperProps = {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
dataSet?: Record<string, Primitive | undefined>;
|
|
5
|
+
style?: React.CSSProperties;
|
|
6
|
+
};
|
|
7
|
+
export declare function ReadonlyWrapper(props: ReadonlyWrapperProps): JSX.Element;
|
|
8
|
+
export { ReadonlyWrapper as Span };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { UploadProps } from '@hi-ui/upload';
|
|
3
|
+
import type { UploadBridgeProps } from './type';
|
|
4
|
+
export { UploadBridgeProps };
|
|
5
|
+
export declare function UploadBridge(props: React.PropsWithChildren<UploadBridgeProps>): React.ReactElement<UploadProps, string | React.JSXElementConstructor<any>>;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/** @LICENSE
|
|
2
|
+
* @hi-ui/schema-fields
|
|
3
|
+
* https://github.com/XiaoMi/hiui/tree/master/packages/schema/schema-fields#readme
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) HiUI <mi-hiui@xiaomi.com>.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
import _regeneratorRuntime from '@babel/runtime/regenerator';
|
|
11
|
+
import { __rest, __awaiter } from 'tslib';
|
|
12
|
+
import React, { useMemo, useEffect } from 'react';
|
|
13
|
+
import { omit } from 'lodash-es';
|
|
14
|
+
import { useControllableValue } from 'ahooks';
|
|
15
|
+
import { message } from '@hi-ui/message';
|
|
16
|
+
import { useReadonlyRef } from '@hi-ui/schema-hooks';
|
|
17
|
+
import { mergeProps } from '@hi-ui/schema-utils';
|
|
18
|
+
import { toFileList, sliceToMaxCount, genFileIdentifier } from './utils.js';
|
|
19
|
+
var noopOnChange = function noopOnChange() {
|
|
20
|
+
// noopOnChange
|
|
21
|
+
};
|
|
22
|
+
function normalizeProps(props) {
|
|
23
|
+
var value = props.value;
|
|
24
|
+
// value 为字符传时转换为数组;undefined 保持 undefined
|
|
25
|
+
var nextValue = Array.isArray(value) ? value : typeof value === 'string' ? [value] : undefined;
|
|
26
|
+
// 存在 request 方法时,默认使用 json 模式
|
|
27
|
+
var nextMode = props.mode || (props.request ? 'json' : 'formData');
|
|
28
|
+
// maxCount 大于 1 时,默认使用 multiple 模式
|
|
29
|
+
var nextMultiple = props.multiple || (props.maxCount || 0) > 1;
|
|
30
|
+
// 没开启 multiple,也没传 maxCount,则默认限制为 1
|
|
31
|
+
var nextMaxCount = props.maxCount || (!props.multiple ? 1 : undefined);
|
|
32
|
+
return Object.assign(Object.assign({}, props), {
|
|
33
|
+
value: nextValue,
|
|
34
|
+
mode: nextMode,
|
|
35
|
+
multiple: nextMultiple,
|
|
36
|
+
maxCount: nextMaxCount
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function UploadBridge(props) {
|
|
40
|
+
var _this = this;
|
|
41
|
+
var children = props.children,
|
|
42
|
+
restProps = __rest(props, ["children"]);
|
|
43
|
+
var normalizedProps = normalizeProps(restProps);
|
|
44
|
+
var _useControllableValue = useControllableValue(normalizedProps),
|
|
45
|
+
_useControllableValue2 = _useControllableValue[0],
|
|
46
|
+
value = _useControllableValue2 === void 0 ? [] : _useControllableValue2,
|
|
47
|
+
_setValue = _useControllableValue[1];
|
|
48
|
+
var _useMemo = useMemo(function () {
|
|
49
|
+
return toFileList(value);
|
|
50
|
+
}, [value]),
|
|
51
|
+
fileList = _useMemo.fileList,
|
|
52
|
+
fileIdSet = _useMemo.fileIdSet;
|
|
53
|
+
var setValue = function setValue(value) {
|
|
54
|
+
// 只保留<maxCount>个文件
|
|
55
|
+
var sliced = sliceToMaxCount(value, normalizedProps.maxCount, 0);
|
|
56
|
+
var transformed = sliced.map(function (file) {
|
|
57
|
+
if (typeof file === 'string') return {
|
|
58
|
+
url: file,
|
|
59
|
+
fileId: file,
|
|
60
|
+
name: ''
|
|
61
|
+
};else return file;
|
|
62
|
+
});
|
|
63
|
+
_setValue(transformed);
|
|
64
|
+
};
|
|
65
|
+
// 此处的 Set 是给组件卸载的清理阶段有的
|
|
66
|
+
var fileUrlSetRef = useReadonlyRef(function () {
|
|
67
|
+
return new Set();
|
|
68
|
+
});
|
|
69
|
+
if (normalizedProps.mode === 'formData') {
|
|
70
|
+
fileList === null || fileList === void 0 ? void 0 : fileList.forEach(function (_ref) {
|
|
71
|
+
var url = _ref.url;
|
|
72
|
+
return fileUrlSetRef.current.add(url);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
useEffect(function () {
|
|
76
|
+
return function () {
|
|
77
|
+
// 组件卸载时清理所有创建的URL
|
|
78
|
+
// 此处忽略掉这条规则,因为就是需要最新的 fileUrlSetRef
|
|
79
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
80
|
+
fileUrlSetRef.current.forEach(function (url) {
|
|
81
|
+
if (url) {
|
|
82
|
+
// url 预期是 blob 协议的本地文件地址
|
|
83
|
+
// 若是外部传入的 http 协议的文件也没事
|
|
84
|
+
// revokeObjectURL 会直接忽略
|
|
85
|
+
URL.revokeObjectURL(url);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
}, [fileUrlSetRef]);
|
|
90
|
+
var customUpload = function customUpload(fileList) {
|
|
91
|
+
return __awaiter(_this, void 0, void 0, /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
92
|
+
var files, uniqueFiles, maxSize, maxSizeInBytes, oversizedFiles, sliced, urls, nextFiles, msg;
|
|
93
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
94
|
+
while (1) {
|
|
95
|
+
switch (_context.prev = _context.next) {
|
|
96
|
+
case 0:
|
|
97
|
+
if (fileList) {
|
|
98
|
+
_context.next = 2;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
return _context.abrupt("return");
|
|
102
|
+
case 2:
|
|
103
|
+
files = Array.from(fileList); // 1. 文件去重
|
|
104
|
+
// 直接使用已计算的fileList中的fileId
|
|
105
|
+
uniqueFiles = files.filter(function (file) {
|
|
106
|
+
return !(fileIdSet === null || fileIdSet === void 0 ? void 0 : fileIdSet.has(genFileIdentifier(file)));
|
|
107
|
+
});
|
|
108
|
+
if (!(uniqueFiles.length === 0)) {
|
|
109
|
+
_context.next = 7;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
message.open({
|
|
113
|
+
type: 'warning',
|
|
114
|
+
title: '所选文件已存在'
|
|
115
|
+
});
|
|
116
|
+
return _context.abrupt("return");
|
|
117
|
+
case 7:
|
|
118
|
+
// 2. 检查文件大小 (maxSize单位为KB,需要转换为bytes进行比较)
|
|
119
|
+
maxSize = normalizedProps.maxSize || Infinity;
|
|
120
|
+
maxSizeInBytes = maxSize * 1024;
|
|
121
|
+
oversizedFiles = uniqueFiles.filter(function (file) {
|
|
122
|
+
return file.size > maxSizeInBytes;
|
|
123
|
+
});
|
|
124
|
+
if (!(oversizedFiles.length > 0)) {
|
|
125
|
+
_context.next = 13;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
message.open({
|
|
129
|
+
type: 'warning',
|
|
130
|
+
title: "\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA" + maxSize + "KB\u9650\u5236"
|
|
131
|
+
});
|
|
132
|
+
return _context.abrupt("return");
|
|
133
|
+
case 13:
|
|
134
|
+
_context.prev = 13;
|
|
135
|
+
if (!(normalizedProps.mode === 'json')) {
|
|
136
|
+
_context.next = 27;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
if (normalizedProps.request) {
|
|
140
|
+
_context.next = 17;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
throw new Error('JSON 模式下必须提供 request 方法');
|
|
144
|
+
case 17:
|
|
145
|
+
// 只保留<maxCount - curLength>个文件
|
|
146
|
+
sliced = sliceToMaxCount(uniqueFiles, normalizedProps.maxCount, value.length); // 上传文件并获取URL
|
|
147
|
+
_context.next = 20;
|
|
148
|
+
return normalizedProps.request(sliced);
|
|
149
|
+
case 20:
|
|
150
|
+
urls = _context.sent;
|
|
151
|
+
if (!(!urls || urls.length === 0)) {
|
|
152
|
+
_context.next = 23;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
return _context.abrupt("return");
|
|
156
|
+
case 23:
|
|
157
|
+
nextFiles = urls.map(function (url, index) {
|
|
158
|
+
var fileId = genFileIdentifier(uniqueFiles[index]);
|
|
159
|
+
if (typeof url === 'string') return {
|
|
160
|
+
url: url,
|
|
161
|
+
fileId: fileId,
|
|
162
|
+
name: ''
|
|
163
|
+
};else return Object.assign(Object.assign({}, url), {
|
|
164
|
+
fileId: fileId
|
|
165
|
+
});
|
|
166
|
+
}); // 更新value
|
|
167
|
+
setValue([].concat(value || [], nextFiles));
|
|
168
|
+
_context.next = 28;
|
|
169
|
+
break;
|
|
170
|
+
case 27:
|
|
171
|
+
// formData模式直接返回File数组
|
|
172
|
+
setValue([].concat(value || [], uniqueFiles));
|
|
173
|
+
case 28:
|
|
174
|
+
_context.next = 35;
|
|
175
|
+
break;
|
|
176
|
+
case 30:
|
|
177
|
+
_context.prev = 30;
|
|
178
|
+
_context.t0 = _context["catch"](13);
|
|
179
|
+
console.log('UploadBridge customUpload request', _context.t0);
|
|
180
|
+
msg = '文件上传失败: ' + (_context.t0 instanceof Error ? _context.t0.message : '未知错误');
|
|
181
|
+
message.open({
|
|
182
|
+
type: 'error',
|
|
183
|
+
title: msg
|
|
184
|
+
});
|
|
185
|
+
case 35:
|
|
186
|
+
case "end":
|
|
187
|
+
return _context.stop();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}, _callee, null, [[13, 30]]);
|
|
191
|
+
}));
|
|
192
|
+
};
|
|
193
|
+
var handleRemove = function handleRemove(_1, _2, index) {
|
|
194
|
+
// 获取要删除的文件的URL
|
|
195
|
+
var fileToRemove = fileList === null || fileList === void 0 ? void 0 : fileList[index];
|
|
196
|
+
if (fileToRemove === null || fileToRemove === void 0 ? void 0 : fileToRemove.url) URL.revokeObjectURL(fileToRemove.url);
|
|
197
|
+
setValue(value.filter(function (_, idx) {
|
|
198
|
+
return idx !== index;
|
|
199
|
+
}));
|
|
200
|
+
return true;
|
|
201
|
+
};
|
|
202
|
+
var finalProps = mergeProps({
|
|
203
|
+
type: 'default'
|
|
204
|
+
}, omit(normalizedProps, [
|
|
205
|
+
// 移除无关的属性
|
|
206
|
+
'value', 'defaultValue', 'onChange', 'mode', 'request']));
|
|
207
|
+
return /*#__PURE__*/React.cloneElement(children, Object.assign(Object.assign({}, finalProps), {
|
|
208
|
+
customUpload: customUpload,
|
|
209
|
+
fileList: fileList,
|
|
210
|
+
onChange: noopOnChange,
|
|
211
|
+
onRemove: handleRemove
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
export { UploadBridge };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { StandardProps } from 'ahooks/es/useControllableValue';
|
|
2
|
+
import type { UploadProps } from '@hi-ui/upload';
|
|
3
|
+
export type BaseFileInfoType = {
|
|
4
|
+
/** 文件名称 */
|
|
5
|
+
name: string;
|
|
6
|
+
/** 包含协议、域名、路径等的完整文件地址 */
|
|
7
|
+
url: string;
|
|
8
|
+
};
|
|
9
|
+
export type UploadFileType = string | File | (BaseFileInfoType & {
|
|
10
|
+
/** 文件唯一标识 */
|
|
11
|
+
fileId?: string;
|
|
12
|
+
});
|
|
13
|
+
export type UploadBridgeProps = Partial<StandardProps<UploadFileType[]>> & Omit<UploadProps, 'customUpload' | 'onChange' | 'fileList' | 'onRemove' | 'maxSize' | 'uploadAction' | 'data' | 'name' | 'method' | 'headers' | 'withCredentials' | 'beforeUpload'> & {
|
|
14
|
+
/**
|
|
15
|
+
* 上传模式
|
|
16
|
+
* @default 'formData'
|
|
17
|
+
* @desc formData 模式时,响应给外部的是【文件】的列表
|
|
18
|
+
* @desc json 模式时,响应给外部的是【文件基本信息对象】的列表
|
|
19
|
+
* @desc json 模式时需传入 request 方法,用来上传文件
|
|
20
|
+
* @desc json 模式下,返回的信息顺序需与上传的顺序一致,否则可能导致自动去重逻辑失效
|
|
21
|
+
*/
|
|
22
|
+
mode?: 'formData' | 'json';
|
|
23
|
+
request?: (files: File[]) => Promise<(string | BaseFileInfoType)[]>;
|
|
24
|
+
/** 接收上传的文件体积上限(单位:KB) */
|
|
25
|
+
maxSize?: number;
|
|
26
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { UploadFileItem } from '@hi-ui/upload';
|
|
2
|
+
import type { UploadFileType } from './type';
|
|
3
|
+
export declare function toFileList(file?: UploadFileType | UploadFileType[]): {
|
|
4
|
+
fileIdSet?: undefined;
|
|
5
|
+
fileList?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
fileIdSet: Set<string>;
|
|
8
|
+
fileList: UploadFileItem[];
|
|
9
|
+
};
|
|
10
|
+
export declare function genFileIdentifier(file: UploadFileType): string;
|
|
11
|
+
export declare function sliceToMaxCount<T>(value: T[], maxCount: number | undefined, curCount?: number): T[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** @LICENSE
|
|
2
|
+
* @hi-ui/schema-fields
|
|
3
|
+
* https://github.com/XiaoMi/hiui/tree/master/packages/schema/schema-fields#readme
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) HiUI <mi-hiui@xiaomi.com>.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
import { extractFileInfo } from '@hi-ui/schema-utils';
|
|
11
|
+
function toFileList(file) {
|
|
12
|
+
if (!file) return {};
|
|
13
|
+
var fileIdSet = new Set();
|
|
14
|
+
var files = Array.isArray(file) ? file : [file];
|
|
15
|
+
var fileList = files.map(function (file) {
|
|
16
|
+
var normalizedFile = typeof file === 'string' ? {
|
|
17
|
+
symbol: file,
|
|
18
|
+
fileId: file,
|
|
19
|
+
url: file
|
|
20
|
+
} : file instanceof File ? {
|
|
21
|
+
symbol: file.name,
|
|
22
|
+
name: file.name,
|
|
23
|
+
fileId: file.name + "-" + file.size + "-" + file.lastModified,
|
|
24
|
+
url: URL.createObjectURL(file)
|
|
25
|
+
} : {
|
|
26
|
+
symbol: file.url,
|
|
27
|
+
name: file.name,
|
|
28
|
+
fileId: file.name + "-" + file.url,
|
|
29
|
+
url: file.url
|
|
30
|
+
};
|
|
31
|
+
var info = extractFileInfo(normalizedFile.symbol);
|
|
32
|
+
// 添加到 fileIdSet
|
|
33
|
+
fileIdSet.add(normalizedFile.fileId);
|
|
34
|
+
return {
|
|
35
|
+
name: normalizedFile.name || info.fullName,
|
|
36
|
+
fileId: normalizedFile.fileId,
|
|
37
|
+
fileType: info.ext,
|
|
38
|
+
uploadState: 'success',
|
|
39
|
+
url: normalizedFile.url
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
fileIdSet: fileIdSet,
|
|
44
|
+
fileList: fileList
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// 文件唯一标识生成函数
|
|
48
|
+
function genFileIdentifier(file) {
|
|
49
|
+
if (file instanceof File) {
|
|
50
|
+
return file.name + "-" + file.size + "-" + file.lastModified;
|
|
51
|
+
} else if (typeof file === 'string') {
|
|
52
|
+
return file;
|
|
53
|
+
} else {
|
|
54
|
+
return file.name + "-" + file.url;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function sliceToMaxCount(value, maxCount, curCount) {
|
|
58
|
+
if (curCount === void 0) {
|
|
59
|
+
curCount = 0;
|
|
60
|
+
}
|
|
61
|
+
// maxCount 存在时,计算需要保留的文件数量
|
|
62
|
+
var _maxCount = typeof maxCount === 'number' ? maxCount - curCount : undefined;
|
|
63
|
+
var sliceStart = Math.max(value.length - (_maxCount || 0), 0);
|
|
64
|
+
return value.slice(sliceStart);
|
|
65
|
+
}
|
|
66
|
+
export { genFileIdentifier, sliceToMaxCount, toFileList };
|
package/lib/ctx.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { FieldConfigType } from '@hi-ui/schema-core';
|
|
3
|
+
import type { ProField, EnhancedRenderersType } from './base';
|
|
4
|
+
import type { ProFieldMapType } from '.';
|
|
5
|
+
export declare function FieldMapProvider(props: React.PropsWithChildren<{
|
|
6
|
+
fields: Partial<ProFieldMapType>;
|
|
7
|
+
}>): JSX.Element;
|
|
8
|
+
export type UseFieldMapOpts = {
|
|
9
|
+
/** 自定义字段渲染器映射配置 */
|
|
10
|
+
fieldMap?: Partial<ProFieldMapType>;
|
|
11
|
+
};
|
|
12
|
+
export declare function useFieldMap(opts?: UseFieldMapOpts): ProFieldMapType;
|
|
13
|
+
export type MatchFieldClassOpts = UseFieldMapOpts & {
|
|
14
|
+
name?: string;
|
|
15
|
+
field: FieldConfigType;
|
|
16
|
+
fieldMap: ProFieldMapType;
|
|
17
|
+
};
|
|
18
|
+
export declare function matchFieldClass(opts: MatchFieldClassOpts): typeof ProField;
|
|
19
|
+
export declare function getFieldRenderFn<TKey extends keyof EnhancedRenderersType>(Class: typeof ProField, renderKey: TKey): ProField[TKey];
|
|
20
|
+
export declare function matchFieldRenderFn<TKey extends keyof EnhancedRenderersType>(opts: MatchFieldClassOpts & {
|
|
21
|
+
renderKey: TKey;
|
|
22
|
+
}): ProField[TKey];
|
|
23
|
+
export type UseMatchFieldClassOpts = Omit<MatchFieldClassOpts, 'fieldMap'> & {
|
|
24
|
+
fieldMap?: ProFieldMapType;
|
|
25
|
+
};
|
|
26
|
+
export declare function useMatchFieldClass(opts: UseMatchFieldClassOpts): typeof ProField;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { FieldControlType } from '@hi-ui/schema-core';
|
|
3
|
+
export type EditableControlCtxType = Partial<Pick<FieldControlType, 'editable' | 'readonly'>>;
|
|
4
|
+
export declare function EditableControlProvider(props: React.PropsWithChildren<EditableControlCtxType>): JSX.Element;
|
|
5
|
+
export declare function useEditableControlCtx(): Partial<Pick<FieldControlType, "readonly" | "editable">>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FieldConfigType } from '@hi-ui/schema-core';
|
|
2
|
+
import type { ProField, ProFieldRenderEditableCtx } from '../index';
|
|
3
|
+
export { isReadonly } from './use-readonly';
|
|
4
|
+
export type EditableCaseProps = {
|
|
5
|
+
/** 字段配置 */
|
|
6
|
+
field: FieldConfigType;
|
|
7
|
+
/** 字段渲染器 */
|
|
8
|
+
FieldClass: typeof ProField;
|
|
9
|
+
/** 渲染的上下文 */
|
|
10
|
+
ctx: Omit<ProFieldRenderEditableCtx, 'onActivate' | 'onDeactivate'>;
|
|
11
|
+
/** 当前值 */
|
|
12
|
+
value?: unknown;
|
|
13
|
+
/** 失焦时是否保持激活状态,默认为false */
|
|
14
|
+
keepActiveOnBlur?: boolean;
|
|
15
|
+
/** 是否默认激活 */
|
|
16
|
+
defaultActive?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export declare function EditableCase(props: EditableCaseProps): JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { UseFieldMapOpts } from '../ctx';
|
|
2
|
+
import { isSimpleCase, SimpleCase } from './simple';
|
|
3
|
+
import { ReadonlyCase, type ReadonlyCaseProps, type UsedBy } from './readonly';
|
|
4
|
+
import { EditableCase, type EditableCaseProps } from './editable';
|
|
5
|
+
export { isReadonly, useReadonly, runDynamicEditable } from './use-readonly';
|
|
6
|
+
export type EditableFieldProps<T extends UsedBy = 'other'> = UseFieldMapOpts & Omit<EditableCaseProps, 'FieldClass'> & Pick<ReadonlyCaseProps<T>, 'usedBy'> & {
|
|
7
|
+
/** 运行时的可编辑状态(可选),优先级最高 */
|
|
8
|
+
runtimeEditable?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function EditableField<T extends UsedBy = 'other'>(props: EditableFieldProps<T>): JSX.Element;
|
|
11
|
+
export { SimpleCase, ReadonlyCase, EditableCase, isSimpleCase, };
|
|
12
|
+
export type { ReadonlyCaseProps, EditableCaseProps, UsedBy, };
|
|
13
|
+
export * from './ctx';
|
|
14
|
+
export * from './type';
|