@canlooks/can-ui 0.0.77 → 0.0.79
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/dist/cjs/components/cascade/cascade.js +1 -1
- package/dist/cjs/components/dateTimePicker/timer.js +1 -1
- package/dist/cjs/components/inputBase/inputBase.js +2 -5
- package/dist/cjs/components/optionsBase/optionsBase.d.ts +4 -0
- package/dist/cjs/components/optionsBase/optionsBase.js +11 -10
- package/dist/cjs/components/popper/popperContext.d.ts +1 -1
- package/dist/cjs/components/popper/popperContext.js +3 -3
- package/dist/cjs/components/select/select.js +1 -1
- package/dist/cjs/components/textarea/textarea.d.ts +8 -2
- package/dist/cjs/components/textarea/textarea.js +3 -2
- package/dist/cjs/components/treeSelect/treeSelect.js +1 -1
- package/dist/cjs/extensions/reactiveForm/reactiveForm.d.ts +2 -2
- package/dist/cjs/utils/utils.d.ts +7 -2
- package/dist/cjs/utils/utils.js +17 -3
- package/dist/esm/components/cascade/cascade.js +2 -2
- package/dist/esm/components/dateTimePicker/timer.js +2 -2
- package/dist/esm/components/inputBase/inputBase.js +3 -6
- package/dist/esm/components/optionsBase/optionsBase.d.ts +4 -0
- package/dist/esm/components/optionsBase/optionsBase.js +13 -12
- package/dist/esm/components/popper/popperContext.d.ts +1 -1
- package/dist/esm/components/popper/popperContext.js +2 -2
- package/dist/esm/components/select/select.js +2 -2
- package/dist/esm/components/textarea/textarea.d.ts +8 -2
- package/dist/esm/components/textarea/textarea.js +3 -2
- package/dist/esm/components/treeSelect/treeSelect.js +2 -2
- package/dist/esm/extensions/reactiveForm/reactiveForm.d.ts +2 -2
- package/dist/esm/utils/utils.d.ts +7 -2
- package/dist/esm/utils/utils.js +16 -3
- package/package.json +1 -1
|
@@ -60,7 +60,7 @@ loading, options, labelKey = 'label', primaryKey = 'value', childrenKey = 'child
|
|
|
60
60
|
setInnerOptions(await loadOptions(searchValue, parent));
|
|
61
61
|
}, loading);
|
|
62
62
|
(0, react_1.useEffect)(() => {
|
|
63
|
-
if (loadOptions && !searchable && !innerOpen.current && (0, utils_1.
|
|
63
|
+
if (loadOptions && !searchable && !innerOpen.current && !(0, utils_1.isNoValue)(innerValue)) {
|
|
64
64
|
// 第一次渲染就有value,需要请求数据
|
|
65
65
|
innerLoadOptions(innerSearchValue.current);
|
|
66
66
|
}
|
|
@@ -40,7 +40,7 @@ function TimerItem({ type, value, onChange }) {
|
|
|
40
40
|
return false;
|
|
41
41
|
};
|
|
42
42
|
const scrollerRef = (0, react_1.useRef)(null);
|
|
43
|
-
const selectedItemRef = (0, popper_1.
|
|
43
|
+
const selectedItemRef = (0, popper_1.useScrollToTarget)(scrollerRef);
|
|
44
44
|
const renderItems = (count) => {
|
|
45
45
|
const ret = [];
|
|
46
46
|
for (let i = 0; i < count; i++) {
|
|
@@ -57,13 +57,10 @@ min, max, step, precision, placeholder, disabled, readOnly, autoFocus, defaultVa
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
const shouldRenderClearButton = () => {
|
|
60
|
-
if (!clearable || disabled || readOnly
|
|
60
|
+
if (!clearable || disabled || readOnly) {
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
return !!innerValue.current.length;
|
|
65
|
-
}
|
|
66
|
-
return true;
|
|
63
|
+
return (0, utils_1.isNoValue)(innerValue.current);
|
|
67
64
|
};
|
|
68
65
|
return ((0, jsx_runtime_1.jsxs)("div", { ...(0, utils_1.mergeComponentProps)(props, {
|
|
69
66
|
ref: innerRef,
|
|
@@ -4,6 +4,10 @@ import { LoadingProps } from '../loading';
|
|
|
4
4
|
import { OptionType } from '../selectionContext';
|
|
5
5
|
import { Id } from '../../types';
|
|
6
6
|
export interface MenuOptionType<V extends Id = Id> extends Omit<MenuItemProps, 'children'>, Omit<OptionType<V>, 'children'> {
|
|
7
|
+
/**
|
|
8
|
+
* 若指定为`true`,则弹框打开时会自动滚动到该选项,默认为`false`
|
|
9
|
+
*/
|
|
10
|
+
scrollHere?: boolean;
|
|
7
11
|
}
|
|
8
12
|
export type OptionsBaseSharedProps<O extends MenuOptionType<V>, V extends Id = Id> = {
|
|
9
13
|
showCheckbox?: boolean;
|
|
@@ -68,14 +68,14 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
68
68
|
* 渲染部分
|
|
69
69
|
*/
|
|
70
70
|
const scrollerRef = (0, react_1.useRef)(null);
|
|
71
|
-
const selectedItemRef = (0, popper_1.
|
|
71
|
+
const selectedItemRef = (0, popper_1.useScrollToTarget)(scrollerRef);
|
|
72
72
|
const syncOnToggleSelected = (0, utils_1.useSync)(onToggleSelected);
|
|
73
73
|
const renderedOptions = (0, react_1.useMemo)(() => {
|
|
74
74
|
if (!filteredOptions?.length) {
|
|
75
75
|
return (0, jsx_runtime_1.jsx)(placeholder_1.Placeholder, {});
|
|
76
76
|
}
|
|
77
77
|
const makeProps = (params) => ({
|
|
78
|
-
ref: params.selected ? selectedItemRef : void 0,
|
|
78
|
+
ref: params.scrollHere || params.selected ? selectedItemRef : void 0,
|
|
79
79
|
showCheckbox,
|
|
80
80
|
selected: params.selected,
|
|
81
81
|
focused: verticalIndex.current === params.index,
|
|
@@ -84,11 +84,11 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
84
84
|
: params.label,
|
|
85
85
|
onClick: e => {
|
|
86
86
|
e.stopPropagation();
|
|
87
|
-
params.opt
|
|
87
|
+
params.opt?.onClick?.(e);
|
|
88
88
|
syncOnToggleSelected.current?.(params.value, e);
|
|
89
89
|
},
|
|
90
90
|
onPointerEnter: e => {
|
|
91
|
-
params.opt
|
|
91
|
+
params.opt?.onPointerEnter?.(e);
|
|
92
92
|
setVerticalIndex(-1);
|
|
93
93
|
},
|
|
94
94
|
children: null
|
|
@@ -97,13 +97,13 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
97
97
|
return filteredOptions.map((opt, index) => {
|
|
98
98
|
const value = opt[primaryKey];
|
|
99
99
|
const label = opt[labelKey] ?? value;
|
|
100
|
-
return ((0, jsx_runtime_1.jsx)(menuItem_1.MenuItem, { value: value, ...opt,
|
|
101
|
-
opt,
|
|
100
|
+
return ((0, jsx_runtime_1.jsx)(menuItem_1.MenuItem, { value: value, ...(0, utils_1.mergeComponentProps)(opt, makeProps({
|
|
102
101
|
label,
|
|
103
102
|
value,
|
|
104
103
|
index,
|
|
105
|
-
selected: (0, utils_1.isSelected)(value, selectedValue)
|
|
106
|
-
|
|
104
|
+
selected: (0, utils_1.isSelected)(value, selectedValue),
|
|
105
|
+
scrollHere: opt.scrollHere
|
|
106
|
+
})) }, opt.key ?? value ?? index));
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
// children
|
|
@@ -111,13 +111,14 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
111
111
|
if (!(0, react_1.isValidElement)(c)) {
|
|
112
112
|
return c;
|
|
113
113
|
}
|
|
114
|
-
const { value, label } = c.props;
|
|
114
|
+
const { value, label, scrollHere } = c.props;
|
|
115
115
|
return (0, react_1.cloneElement)(c, makeProps({
|
|
116
116
|
opt: c.props,
|
|
117
117
|
label,
|
|
118
118
|
value: value,
|
|
119
119
|
index,
|
|
120
|
-
selected: (0, utils_1.isSelected)(value, selectedValue)
|
|
120
|
+
selected: (0, utils_1.isSelected)(value, selectedValue),
|
|
121
|
+
scrollHere
|
|
121
122
|
}));
|
|
122
123
|
});
|
|
123
124
|
}, [filteredOptions, selectedValue, verticalIndex.current, showCheckbox]);
|
|
@@ -17,4 +17,4 @@ export declare function usePopperContext(): {
|
|
|
17
17
|
* 当弹框打开时,滚动至已选项
|
|
18
18
|
* @returns {RefObject} ref
|
|
19
19
|
*/
|
|
20
|
-
export declare function
|
|
20
|
+
export declare function useScrollToTarget<T extends HTMLElement>(scrollerRef: RefObject<Element | null>): RefObject<T | null>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PopperContext = void 0;
|
|
4
4
|
exports.usePopperContext = usePopperContext;
|
|
5
|
-
exports.
|
|
5
|
+
exports.useScrollToTarget = useScrollToTarget;
|
|
6
6
|
const react_1 = require("react");
|
|
7
7
|
exports.PopperContext = (0, react_1.createContext)({
|
|
8
8
|
autoClose: false,
|
|
@@ -20,13 +20,13 @@ function usePopperContext() {
|
|
|
20
20
|
* 当弹框打开时,滚动至已选项
|
|
21
21
|
* @returns {RefObject} ref
|
|
22
22
|
*/
|
|
23
|
-
function
|
|
23
|
+
function useScrollToTarget(scrollerRef) {
|
|
24
24
|
const ref = (0, react_1.useRef)(null);
|
|
25
25
|
const { beforeOpenCallbacks } = usePopperContext();
|
|
26
26
|
(0, react_1.useEffect)(() => {
|
|
27
27
|
const beforeOpen = () => {
|
|
28
28
|
if (ref.current && scrollerRef.current && scrollerRef.current.scrollHeight > scrollerRef.current.clientHeight) {
|
|
29
|
-
scrollerRef.current.scrollTop = ref.current.offsetTop
|
|
29
|
+
scrollerRef.current.scrollTop = ref.current.offsetTop + ref.current.clientHeight / 2 - scrollerRef.current.clientHeight / 2;
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
beforeOpenCallbacks.add(beforeOpen);
|
|
@@ -115,7 +115,7 @@ showCheckbox = !!multiple, loading, options, labelKey = 'label', primaryKey = 'v
|
|
|
115
115
|
value: innerValue,
|
|
116
116
|
onClear,
|
|
117
117
|
onBlur
|
|
118
|
-
}), "data-focused": innerOpen.current, children: inputBaseProps => (0, jsx_runtime_1.jsxs)("div", { className: select_style_1.classes.contentWrap, children: [
|
|
118
|
+
}), "data-focused": innerOpen.current, children: inputBaseProps => (0, jsx_runtime_1.jsxs)("div", { className: select_style_1.classes.contentWrap, children: [(0, utils_1.isNoValue)(innerValue)
|
|
119
119
|
? (0, jsx_runtime_1.jsx)("div", { className: select_style_1.classes.placeholder, children: props.placeholder ?? '请选择' })
|
|
120
120
|
: (0, jsx_runtime_1.jsx)("div", { className: select_style_1.classes.backfill, children: renderBackfillFn() }), (0, jsx_runtime_1.jsx)("input", { size: 1, ...(0, utils_1.mergeComponentProps)(inputBaseProps, inputProps), "data-hidden": "true" }), (0, jsx_runtime_1.jsx)("div", { className: select_style_1.classes.arrow, "data-open": innerOpen.current, children: loading
|
|
121
121
|
? (0, jsx_runtime_1.jsx)(loadingIndicator_1.LoadingIndicator, {})
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { Ref, ComponentProps } from 'react';
|
|
1
|
+
import { Ref, ComponentProps, CSSProperties } from 'react';
|
|
2
2
|
import { InputBaseProps } from '../inputBase';
|
|
3
3
|
export interface TextareaProps extends Omit<InputBaseProps<'textarea'>, 'children' | 'prefix' | 'suffix'> {
|
|
4
4
|
textareaProps?: ComponentProps<'textarea'>;
|
|
5
5
|
textareaRef?: Ref<HTMLTextAreaElement>;
|
|
6
6
|
rows?: number;
|
|
7
7
|
fullWidth?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* @enum {@link fullWidth} 为`true`时,默认为`vertical`
|
|
10
|
+
* @enum {@link fullWidth} 为`false`时,默认为`both`
|
|
11
|
+
* @enum 若设为`none`,则不可调整尺寸
|
|
12
|
+
*/
|
|
13
|
+
resize?: CSSProperties['resize'];
|
|
8
14
|
}
|
|
9
|
-
export declare const Textarea: import("react").MemoExoticComponent<({ textareaProps, textareaRef, rows, fullWidth, ...props }: TextareaProps) => import("@emotion/react/jsx-runtime").JSX.Element>;
|
|
15
|
+
export declare const Textarea: import("react").MemoExoticComponent<({ textareaProps, textareaRef, rows, fullWidth, resize, ...props }: TextareaProps) => import("@emotion/react/jsx-runtime").JSX.Element>;
|
|
@@ -6,10 +6,11 @@ const react_1 = require("react");
|
|
|
6
6
|
const inputBase_1 = require("../inputBase");
|
|
7
7
|
const textarea_style_1 = require("./textarea.style");
|
|
8
8
|
const utils_1 = require("../../utils");
|
|
9
|
-
exports.Textarea = (0, react_1.memo)(({ textareaProps, textareaRef, rows, fullWidth = false, ...props }) => {
|
|
9
|
+
exports.Textarea = (0, react_1.memo)(({ textareaProps, textareaRef, rows, fullWidth = false, resize = fullWidth ? 'vertical' : 'both', ...props }) => {
|
|
10
10
|
return ((0, jsx_runtime_1.jsx)(inputBase_1.InputBase, { ...props, css: textarea_style_1.style, className: (0, utils_1.clsx)(textarea_style_1.classes.root, props.className), "data-full-width": fullWidth, children: inputBaseProps => (0, jsx_runtime_1.jsx)("textarea", { ...(0, utils_1.mergeComponentProps)(inputBaseProps, {
|
|
11
11
|
rows,
|
|
12
12
|
ref: textareaRef,
|
|
13
|
-
className: textarea_style_1.classes.textarea
|
|
13
|
+
className: textarea_style_1.classes.textarea,
|
|
14
|
+
style: { resize }
|
|
14
15
|
}) }) }));
|
|
15
16
|
});
|
|
@@ -74,7 +74,7 @@ placeholder = '请选择', autoFocus, clearable, onClear, ...props }) => {
|
|
|
74
74
|
return ((0, jsx_runtime_1.jsx)(popper_1.Popper, { css: popper_style_1.popperStyle, open: innerOpen.current, onOpenChange: openChangeHandler, placement: "bottom", variant: "collapse", trigger: ['click', 'enter'], disabled: props.disabled || props.readOnly, sizeAdaptable: sizeAdaptable, content: (0, jsx_runtime_1.jsx)(tree_1.Tree, { ...props, nodes: options, value: innerValue, onChange: setInnerValue }), ...popperProps, popperRef: popperRef, onPointerDown: e => {
|
|
75
75
|
popperProps?.onPointerDown?.(e);
|
|
76
76
|
e.preventDefault();
|
|
77
|
-
}, children: (0, jsx_runtime_1.jsx)(inputBase_1.InputBase, { clearable: !!props.multiple, css: treeSelect_style_1.style, className: (0, utils_1.clsx)(treeSelect_style_1.classes.root, props.className), "data-focused": open, value: innerValue, onClear: clearHandler, placeholder: placeholder, autoFocus: autoFocus, disabled: props.disabled, readOnly: props.readOnly, children: inputBaseProps => (0, jsx_runtime_1.jsxs)("div", { className: treeSelect_style_1.classes.contentWrap, children: [
|
|
77
|
+
}, children: (0, jsx_runtime_1.jsx)(inputBase_1.InputBase, { clearable: !!props.multiple, css: treeSelect_style_1.style, className: (0, utils_1.clsx)(treeSelect_style_1.classes.root, props.className), "data-focused": open, value: innerValue, onClear: clearHandler, placeholder: placeholder, autoFocus: autoFocus, disabled: props.disabled, readOnly: props.readOnly, children: inputBaseProps => (0, jsx_runtime_1.jsxs)("div", { className: treeSelect_style_1.classes.contentWrap, children: [(0, utils_1.isNoValue)(innerValue)
|
|
78
78
|
? (0, jsx_runtime_1.jsx)("div", { className: treeSelect_style_1.classes.placeholder, children: placeholder })
|
|
79
79
|
: (0, jsx_runtime_1.jsx)("div", { className: treeSelect_style_1.classes.backfill, children: renderBackfillFn() }), (0, jsx_runtime_1.jsx)("input", { size: 1, ...(0, utils_1.mergeComponentProps)(inputBaseProps, inputProps), "data-hidden": "true" }), (0, jsx_runtime_1.jsx)("div", { className: treeSelect_style_1.classes.arrow, "data-open": open, children: loading
|
|
80
80
|
? (0, jsx_runtime_1.jsx)(loadingIndicator_1.LoadingIndicator, {})
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
import { FormProps, FormRef } from '../../components/form';
|
|
23
23
|
import { ReactElement, Ref } from 'react';
|
|
24
24
|
import { ReactiveFormItem } from './reactiveFormItem';
|
|
25
|
-
export type
|
|
25
|
+
export type ReactiveFormRef = Pick<FormRef, 'submit' | 'getFormErrors' | 'getFieldError' | 'resetForm' | 'resetField' | 'isFormTouched' | 'isFieldTouched'>;
|
|
26
26
|
export interface ReactiveFormProps extends Omit<FormProps, 'ref' | 'initialValue' | 'onChange' | 'onFinish' | 'items'> {
|
|
27
|
-
ref?: Ref<
|
|
27
|
+
ref?: Ref<ReactiveFormRef>;
|
|
28
28
|
onChange?(): void;
|
|
29
29
|
onFinish?(): void;
|
|
30
30
|
}
|
|
@@ -80,13 +80,18 @@ export declare function isPromise<T>(it: any): it is Promise<T>;
|
|
|
80
80
|
* @param promise
|
|
81
81
|
*/
|
|
82
82
|
export declare function getPromiseState(promise: Promise<any>): Promise<'pending' | 'fulfilled' | 'rejected'>;
|
|
83
|
+
/**
|
|
84
|
+
* 判断表单控件是否为空值
|
|
85
|
+
* @param value
|
|
86
|
+
*/
|
|
87
|
+
export declare function isNoValue(value: any): boolean;
|
|
83
88
|
/**
|
|
84
89
|
* 将节点用某个分隔符拼接起来,通常用于渲染多选项
|
|
85
90
|
* @param arr
|
|
86
91
|
* @param callback
|
|
87
|
-
* @param
|
|
92
|
+
* @param separator
|
|
88
93
|
*/
|
|
89
|
-
export declare function joinNodes<T>(arr: T[], callback: (item: T, index: number) => ReactNode,
|
|
94
|
+
export declare function joinNodes<T>(arr: T[], callback: (item: T, index: number) => ReactNode, separator?: ReactNode): (string | number | bigint | boolean | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | import("react").ReactPortal | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined)[];
|
|
90
95
|
/**
|
|
91
96
|
* 修复数字输入框的值,包括最大值最小值限制,以及小数点精度
|
|
92
97
|
* @param value
|
package/dist/cjs/utils/utils.js
CHANGED
|
@@ -14,6 +14,7 @@ exports.cloneRef = cloneRef;
|
|
|
14
14
|
exports.isUnset = isUnset;
|
|
15
15
|
exports.isPromise = isPromise;
|
|
16
16
|
exports.getPromiseState = getPromiseState;
|
|
17
|
+
exports.isNoValue = isNoValue;
|
|
17
18
|
exports.joinNodes = joinNodes;
|
|
18
19
|
exports.fixInputNumber = fixInputNumber;
|
|
19
20
|
exports.isChildOf = isChildOf;
|
|
@@ -216,16 +217,29 @@ function getPromiseState(promise) {
|
|
|
216
217
|
return res === s ? 'pending' : 'fulfilled';
|
|
217
218
|
}).catch(() => 'rejected');
|
|
218
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* 判断表单控件是否为空值
|
|
222
|
+
* @param value
|
|
223
|
+
*/
|
|
224
|
+
function isNoValue(value) {
|
|
225
|
+
if (isUnset(value)) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
if (Array.isArray(value) || typeof value === 'string') {
|
|
229
|
+
return !value.length;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
219
233
|
/**
|
|
220
234
|
* 将节点用某个分隔符拼接起来,通常用于渲染多选项
|
|
221
235
|
* @param arr
|
|
222
236
|
* @param callback
|
|
223
|
-
* @param
|
|
237
|
+
* @param separator
|
|
224
238
|
*/
|
|
225
|
-
function joinNodes(arr, callback,
|
|
239
|
+
function joinNodes(arr, callback, separator = ' / ') {
|
|
226
240
|
return arr.flatMap((item, index) => {
|
|
227
241
|
return index > 0
|
|
228
|
-
? [
|
|
242
|
+
? [separator, callback(item, index)]
|
|
229
243
|
: callback(item, index);
|
|
230
244
|
});
|
|
231
245
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
2
|
import { createContext, memo, useContext, useDeferredValue, useEffect, useMemo, useState } from 'react';
|
|
3
|
-
import { useControlled, useLoading, useKeyboard, joinNodes, clsx,
|
|
3
|
+
import { useControlled, useLoading, useKeyboard, joinNodes, clsx, mergeComponentProps, isNoValue } from '../../utils';
|
|
4
4
|
import { Input } from '../input';
|
|
5
5
|
import { InputBase } from '../inputBase';
|
|
6
6
|
import { Popper } from '../popper';
|
|
@@ -56,7 +56,7 @@ loading, options, labelKey = 'label', primaryKey = 'value', childrenKey = 'child
|
|
|
56
56
|
setInnerOptions(await loadOptions(searchValue, parent));
|
|
57
57
|
}, loading);
|
|
58
58
|
useEffect(() => {
|
|
59
|
-
if (loadOptions && !searchable && !innerOpen.current &&
|
|
59
|
+
if (loadOptions && !searchable && !innerOpen.current && !isNoValue(innerValue)) {
|
|
60
60
|
// 第一次渲染就有value,需要请求数据
|
|
61
61
|
innerLoadOptions(innerSearchValue.current);
|
|
62
62
|
}
|
|
@@ -3,7 +3,7 @@ import { memo, useRef } from 'react';
|
|
|
3
3
|
import { classes, style } from './timer.style';
|
|
4
4
|
import { MenuItem } from '../menuItem';
|
|
5
5
|
import { useDateTimePickerContext } from './dateTimePicker';
|
|
6
|
-
import {
|
|
6
|
+
import { useScrollToTarget } from '../popper';
|
|
7
7
|
export const Timer = memo(({ showHours, showMinutes, showSeconds, value, onChange }) => {
|
|
8
8
|
return (_jsxs("div", { css: style, className: classes.root, children: [showHours && _jsx(TimerItem, { type: "hour", value: value, onChange: onChange }), showMinutes && _jsx(TimerItem, { type: "minute", value: value, onChange: onChange }), showSeconds && _jsx(TimerItem, { type: "second", value: value, onChange: onChange })] }));
|
|
9
9
|
});
|
|
@@ -37,7 +37,7 @@ function TimerItem({ type, value, onChange }) {
|
|
|
37
37
|
return false;
|
|
38
38
|
};
|
|
39
39
|
const scrollerRef = useRef(null);
|
|
40
|
-
const selectedItemRef =
|
|
40
|
+
const selectedItemRef = useScrollToTarget(scrollerRef);
|
|
41
41
|
const renderItems = (count) => {
|
|
42
42
|
const ret = [];
|
|
43
43
|
for (let i = 0; i < count; i++) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
2
|
import { useImperativeHandle, useRef } from 'react';
|
|
3
3
|
import { classes, useStyle } from './inputBase.style';
|
|
4
|
-
import { fixInputNumber,
|
|
4
|
+
import { fixInputNumber, isNoValue, mergeComponentProps, useControlled } from '../../utils';
|
|
5
5
|
import { useTheme } from '../theme';
|
|
6
6
|
import { Button } from '../button';
|
|
7
7
|
import { LoadingIndicator } from '../loadingIndicator';
|
|
@@ -54,13 +54,10 @@ min, max, step, precision, placeholder, disabled, readOnly, autoFocus, defaultVa
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
const shouldRenderClearButton = () => {
|
|
57
|
-
if (!clearable || disabled || readOnly
|
|
57
|
+
if (!clearable || disabled || readOnly) {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
return !!innerValue.current.length;
|
|
62
|
-
}
|
|
63
|
-
return true;
|
|
60
|
+
return isNoValue(innerValue.current);
|
|
64
61
|
};
|
|
65
62
|
return (_jsxs("div", { ...mergeComponentProps(props, {
|
|
66
63
|
ref: innerRef,
|
|
@@ -4,6 +4,10 @@ import { LoadingProps } from '../loading';
|
|
|
4
4
|
import { OptionType } from '../selectionContext';
|
|
5
5
|
import { Id } from '../../types';
|
|
6
6
|
export interface MenuOptionType<V extends Id = Id> extends Omit<MenuItemProps, 'children'>, Omit<OptionType<V>, 'children'> {
|
|
7
|
+
/**
|
|
8
|
+
* 若指定为`true`,则弹框打开时会自动滚动到该选项,默认为`false`
|
|
9
|
+
*/
|
|
10
|
+
scrollHere?: boolean;
|
|
7
11
|
}
|
|
8
12
|
export type OptionsBaseSharedProps<O extends MenuOptionType<V>, V extends Id = Id> = {
|
|
9
13
|
showCheckbox?: boolean;
|
|
@@ -3,10 +3,10 @@ import { useMemo, memo, Children, isValidElement, cloneElement, useRef } from 'r
|
|
|
3
3
|
import { Highlight } from '../highlight';
|
|
4
4
|
import { MenuItem } from '../menuItem';
|
|
5
5
|
import { Placeholder } from '../placeholder';
|
|
6
|
-
import { clsx, isSelected, useKeyboard, useSync } from '../../utils';
|
|
6
|
+
import { clsx, isSelected, mergeComponentProps, useKeyboard, useSync } from '../../utils';
|
|
7
7
|
import { classes, style } from './optionsBase.style';
|
|
8
8
|
import { Loading } from '../loading';
|
|
9
|
-
import { usePopperContext,
|
|
9
|
+
import { usePopperContext, useScrollToTarget } from '../popper';
|
|
10
10
|
export const OptionsBase = memo(({
|
|
11
11
|
// 共享属性
|
|
12
12
|
showCheckbox, loading, options, children, labelKey = 'label', primaryKey = 'value', searchTokenKey = 'searchToken', filterPredicate,
|
|
@@ -65,14 +65,14 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
65
65
|
* 渲染部分
|
|
66
66
|
*/
|
|
67
67
|
const scrollerRef = useRef(null);
|
|
68
|
-
const selectedItemRef =
|
|
68
|
+
const selectedItemRef = useScrollToTarget(scrollerRef);
|
|
69
69
|
const syncOnToggleSelected = useSync(onToggleSelected);
|
|
70
70
|
const renderedOptions = useMemo(() => {
|
|
71
71
|
if (!filteredOptions?.length) {
|
|
72
72
|
return _jsx(Placeholder, {});
|
|
73
73
|
}
|
|
74
74
|
const makeProps = (params) => ({
|
|
75
|
-
ref: params.selected ? selectedItemRef : void 0,
|
|
75
|
+
ref: params.scrollHere || params.selected ? selectedItemRef : void 0,
|
|
76
76
|
showCheckbox,
|
|
77
77
|
selected: params.selected,
|
|
78
78
|
focused: verticalIndex.current === params.index,
|
|
@@ -81,11 +81,11 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
81
81
|
: params.label,
|
|
82
82
|
onClick: e => {
|
|
83
83
|
e.stopPropagation();
|
|
84
|
-
params.opt
|
|
84
|
+
params.opt?.onClick?.(e);
|
|
85
85
|
syncOnToggleSelected.current?.(params.value, e);
|
|
86
86
|
},
|
|
87
87
|
onPointerEnter: e => {
|
|
88
|
-
params.opt
|
|
88
|
+
params.opt?.onPointerEnter?.(e);
|
|
89
89
|
setVerticalIndex(-1);
|
|
90
90
|
},
|
|
91
91
|
children: null
|
|
@@ -94,13 +94,13 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
94
94
|
return filteredOptions.map((opt, index) => {
|
|
95
95
|
const value = opt[primaryKey];
|
|
96
96
|
const label = opt[labelKey] ?? value;
|
|
97
|
-
return (_jsx(MenuItem, { value: value, ...opt,
|
|
98
|
-
opt,
|
|
97
|
+
return (_jsx(MenuItem, { value: value, ...mergeComponentProps(opt, makeProps({
|
|
99
98
|
label,
|
|
100
99
|
value,
|
|
101
100
|
index,
|
|
102
|
-
selected: isSelected(value, selectedValue)
|
|
103
|
-
|
|
101
|
+
selected: isSelected(value, selectedValue),
|
|
102
|
+
scrollHere: opt.scrollHere
|
|
103
|
+
})) }, opt.key ?? value ?? index));
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
// children
|
|
@@ -108,13 +108,14 @@ searchValue, selectedValue, onToggleSelected, ...props }) => {
|
|
|
108
108
|
if (!isValidElement(c)) {
|
|
109
109
|
return c;
|
|
110
110
|
}
|
|
111
|
-
const { value, label } = c.props;
|
|
111
|
+
const { value, label, scrollHere } = c.props;
|
|
112
112
|
return cloneElement(c, makeProps({
|
|
113
113
|
opt: c.props,
|
|
114
114
|
label,
|
|
115
115
|
value: value,
|
|
116
116
|
index,
|
|
117
|
-
selected: isSelected(value, selectedValue)
|
|
117
|
+
selected: isSelected(value, selectedValue),
|
|
118
|
+
scrollHere
|
|
118
119
|
}));
|
|
119
120
|
});
|
|
120
121
|
}, [filteredOptions, selectedValue, verticalIndex.current, showCheckbox]);
|
|
@@ -17,4 +17,4 @@ export declare function usePopperContext(): {
|
|
|
17
17
|
* 当弹框打开时,滚动至已选项
|
|
18
18
|
* @returns {RefObject} ref
|
|
19
19
|
*/
|
|
20
|
-
export declare function
|
|
20
|
+
export declare function useScrollToTarget<T extends HTMLElement>(scrollerRef: RefObject<Element | null>): RefObject<T | null>;
|
|
@@ -15,13 +15,13 @@ export function usePopperContext() {
|
|
|
15
15
|
* 当弹框打开时,滚动至已选项
|
|
16
16
|
* @returns {RefObject} ref
|
|
17
17
|
*/
|
|
18
|
-
export function
|
|
18
|
+
export function useScrollToTarget(scrollerRef) {
|
|
19
19
|
const ref = useRef(null);
|
|
20
20
|
const { beforeOpenCallbacks } = usePopperContext();
|
|
21
21
|
useEffect(() => {
|
|
22
22
|
const beforeOpen = () => {
|
|
23
23
|
if (ref.current && scrollerRef.current && scrollerRef.current.scrollHeight > scrollerRef.current.clientHeight) {
|
|
24
|
-
scrollerRef.current.scrollTop = ref.current.offsetTop
|
|
24
|
+
scrollerRef.current.scrollTop = ref.current.offsetTop + ref.current.clientHeight / 2 - scrollerRef.current.clientHeight / 2;
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
beforeOpenCallbacks.add(beforeOpen);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
|
|
2
2
|
import { Children, isValidElement, memo, useCallback, useDeferredValue, useMemo, useRef } from 'react';
|
|
3
3
|
import { InputBase } from '../inputBase';
|
|
4
|
-
import {
|
|
4
|
+
import { isNoValue, mergeComponentProps, useControlled } from '../../utils';
|
|
5
5
|
import { classes, style } from './select.style';
|
|
6
6
|
import { Popper } from '../popper';
|
|
7
7
|
import { MenuItem } from '../menuItem';
|
|
@@ -112,7 +112,7 @@ showCheckbox = !!multiple, loading, options, labelKey = 'label', primaryKey = 'v
|
|
|
112
112
|
value: innerValue,
|
|
113
113
|
onClear,
|
|
114
114
|
onBlur
|
|
115
|
-
}), "data-focused": innerOpen.current, children: inputBaseProps => _jsxs("div", { className: classes.contentWrap, children: [
|
|
115
|
+
}), "data-focused": innerOpen.current, children: inputBaseProps => _jsxs("div", { className: classes.contentWrap, children: [isNoValue(innerValue)
|
|
116
116
|
? _jsx("div", { className: classes.placeholder, children: props.placeholder ?? '请选择' })
|
|
117
117
|
: _jsx("div", { className: classes.backfill, children: renderBackfillFn() }), _jsx("input", { size: 1, ...mergeComponentProps(inputBaseProps, inputProps), "data-hidden": "true" }), _jsx("div", { className: classes.arrow, "data-open": innerOpen.current, children: loading
|
|
118
118
|
? _jsx(LoadingIndicator, {})
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { Ref, ComponentProps } from 'react';
|
|
1
|
+
import { Ref, ComponentProps, CSSProperties } from 'react';
|
|
2
2
|
import { InputBaseProps } from '../inputBase';
|
|
3
3
|
export interface TextareaProps extends Omit<InputBaseProps<'textarea'>, 'children' | 'prefix' | 'suffix'> {
|
|
4
4
|
textareaProps?: ComponentProps<'textarea'>;
|
|
5
5
|
textareaRef?: Ref<HTMLTextAreaElement>;
|
|
6
6
|
rows?: number;
|
|
7
7
|
fullWidth?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* @enum {@link fullWidth} 为`true`时,默认为`vertical`
|
|
10
|
+
* @enum {@link fullWidth} 为`false`时,默认为`both`
|
|
11
|
+
* @enum 若设为`none`,则不可调整尺寸
|
|
12
|
+
*/
|
|
13
|
+
resize?: CSSProperties['resize'];
|
|
8
14
|
}
|
|
9
|
-
export declare const Textarea: import("react").MemoExoticComponent<({ textareaProps, textareaRef, rows, fullWidth, ...props }: TextareaProps) => import("@emotion/react/jsx-runtime").JSX.Element>;
|
|
15
|
+
export declare const Textarea: import("react").MemoExoticComponent<({ textareaProps, textareaRef, rows, fullWidth, resize, ...props }: TextareaProps) => import("@emotion/react/jsx-runtime").JSX.Element>;
|
|
@@ -3,10 +3,11 @@ import { memo } from 'react';
|
|
|
3
3
|
import { InputBase } from '../inputBase';
|
|
4
4
|
import { classes, style } from './textarea.style';
|
|
5
5
|
import { clsx, mergeComponentProps } from '../../utils';
|
|
6
|
-
export const Textarea = memo(({ textareaProps, textareaRef, rows, fullWidth = false, ...props }) => {
|
|
6
|
+
export const Textarea = memo(({ textareaProps, textareaRef, rows, fullWidth = false, resize = fullWidth ? 'vertical' : 'both', ...props }) => {
|
|
7
7
|
return (_jsx(InputBase, { ...props, css: style, className: clsx(classes.root, props.className), "data-full-width": fullWidth, children: inputBaseProps => _jsx("textarea", { ...mergeComponentProps(inputBaseProps, {
|
|
8
8
|
rows,
|
|
9
9
|
ref: textareaRef,
|
|
10
|
-
className: classes.textarea
|
|
10
|
+
className: classes.textarea,
|
|
11
|
+
style: { resize }
|
|
11
12
|
}) }) }));
|
|
12
13
|
});
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
|
2
2
|
import { Children, isValidElement, memo, useMemo } from 'react';
|
|
3
3
|
import { Popper } from '../popper';
|
|
4
4
|
import { Tree, TreeNode } from '../tree';
|
|
5
|
-
import { useControlled, toArray, clsx, mergeComponentProps } from '../../utils';
|
|
5
|
+
import { useControlled, toArray, clsx, mergeComponentProps, isNoValue } from '../../utils';
|
|
6
6
|
import { InputBase } from '../inputBase';
|
|
7
7
|
import { LoadingIndicator } from '../loadingIndicator';
|
|
8
8
|
import { popperStyle } from '../popper/popper.style';
|
|
@@ -71,7 +71,7 @@ placeholder = '请选择', autoFocus, clearable, onClear, ...props }) => {
|
|
|
71
71
|
return (_jsx(Popper, { css: popperStyle, open: innerOpen.current, onOpenChange: openChangeHandler, placement: "bottom", variant: "collapse", trigger: ['click', 'enter'], disabled: props.disabled || props.readOnly, sizeAdaptable: sizeAdaptable, content: _jsx(Tree, { ...props, nodes: options, value: innerValue, onChange: setInnerValue }), ...popperProps, popperRef: popperRef, onPointerDown: e => {
|
|
72
72
|
popperProps?.onPointerDown?.(e);
|
|
73
73
|
e.preventDefault();
|
|
74
|
-
}, children: _jsx(InputBase, { clearable: !!props.multiple, css: style, className: clsx(classes.root, props.className), "data-focused": open, value: innerValue, onClear: clearHandler, placeholder: placeholder, autoFocus: autoFocus, disabled: props.disabled, readOnly: props.readOnly, children: inputBaseProps => _jsxs("div", { className: classes.contentWrap, children: [
|
|
74
|
+
}, children: _jsx(InputBase, { clearable: !!props.multiple, css: style, className: clsx(classes.root, props.className), "data-focused": open, value: innerValue, onClear: clearHandler, placeholder: placeholder, autoFocus: autoFocus, disabled: props.disabled, readOnly: props.readOnly, children: inputBaseProps => _jsxs("div", { className: classes.contentWrap, children: [isNoValue(innerValue)
|
|
75
75
|
? _jsx("div", { className: classes.placeholder, children: placeholder })
|
|
76
76
|
: _jsx("div", { className: classes.backfill, children: renderBackfillFn() }), _jsx("input", { size: 1, ...mergeComponentProps(inputBaseProps, inputProps), "data-hidden": "true" }), _jsx("div", { className: classes.arrow, "data-open": open, children: loading
|
|
77
77
|
? _jsx(LoadingIndicator, {})
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
import { FormProps, FormRef } from '../../components/form';
|
|
23
23
|
import { ReactElement, Ref } from 'react';
|
|
24
24
|
import { ReactiveFormItem } from './reactiveFormItem';
|
|
25
|
-
export type
|
|
25
|
+
export type ReactiveFormRef = Pick<FormRef, 'submit' | 'getFormErrors' | 'getFieldError' | 'resetForm' | 'resetField' | 'isFormTouched' | 'isFieldTouched'>;
|
|
26
26
|
export interface ReactiveFormProps extends Omit<FormProps, 'ref' | 'initialValue' | 'onChange' | 'onFinish' | 'items'> {
|
|
27
|
-
ref?: Ref<
|
|
27
|
+
ref?: Ref<ReactiveFormRef>;
|
|
28
28
|
onChange?(): void;
|
|
29
29
|
onFinish?(): void;
|
|
30
30
|
}
|
|
@@ -80,13 +80,18 @@ export declare function isPromise<T>(it: any): it is Promise<T>;
|
|
|
80
80
|
* @param promise
|
|
81
81
|
*/
|
|
82
82
|
export declare function getPromiseState(promise: Promise<any>): Promise<'pending' | 'fulfilled' | 'rejected'>;
|
|
83
|
+
/**
|
|
84
|
+
* 判断表单控件是否为空值
|
|
85
|
+
* @param value
|
|
86
|
+
*/
|
|
87
|
+
export declare function isNoValue(value: any): boolean;
|
|
83
88
|
/**
|
|
84
89
|
* 将节点用某个分隔符拼接起来,通常用于渲染多选项
|
|
85
90
|
* @param arr
|
|
86
91
|
* @param callback
|
|
87
|
-
* @param
|
|
92
|
+
* @param separator
|
|
88
93
|
*/
|
|
89
|
-
export declare function joinNodes<T>(arr: T[], callback: (item: T, index: number) => ReactNode,
|
|
94
|
+
export declare function joinNodes<T>(arr: T[], callback: (item: T, index: number) => ReactNode, separator?: ReactNode): (string | number | bigint | boolean | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | import("react").ReactPortal | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined)[];
|
|
90
95
|
/**
|
|
91
96
|
* 修复数字输入框的值,包括最大值最小值限制,以及小数点精度
|
|
92
97
|
* @param value
|
package/dist/esm/utils/utils.js
CHANGED
|
@@ -191,16 +191,29 @@ export function getPromiseState(promise) {
|
|
|
191
191
|
return res === s ? 'pending' : 'fulfilled';
|
|
192
192
|
}).catch(() => 'rejected');
|
|
193
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* 判断表单控件是否为空值
|
|
196
|
+
* @param value
|
|
197
|
+
*/
|
|
198
|
+
export function isNoValue(value) {
|
|
199
|
+
if (isUnset(value)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
if (Array.isArray(value) || typeof value === 'string') {
|
|
203
|
+
return !value.length;
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
194
207
|
/**
|
|
195
208
|
* 将节点用某个分隔符拼接起来,通常用于渲染多选项
|
|
196
209
|
* @param arr
|
|
197
210
|
* @param callback
|
|
198
|
-
* @param
|
|
211
|
+
* @param separator
|
|
199
212
|
*/
|
|
200
|
-
export function joinNodes(arr, callback,
|
|
213
|
+
export function joinNodes(arr, callback, separator = ' / ') {
|
|
201
214
|
return arr.flatMap((item, index) => {
|
|
202
215
|
return index > 0
|
|
203
|
-
? [
|
|
216
|
+
? [separator, callback(item, index)]
|
|
204
217
|
: callback(item, index);
|
|
205
218
|
});
|
|
206
219
|
}
|