@ioca/react 1.5.28 → 1.5.30
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/css/index.css +1 -1
- package/lib/es/components/checkbox/checkbox.js +1 -1
- package/lib/es/components/form/useForm.js +3 -4
- package/lib/es/components/input/input.js +2 -2
- package/lib/es/components/input/number.js +3 -5
- package/lib/es/components/input/range.js +2 -2
- package/lib/es/components/input/textarea.js +1 -1
- package/lib/es/components/modal/hookModal.js +11 -1
- package/lib/es/components/modal/modal.js +24 -16
- package/lib/es/components/pill/create.js +15 -0
- package/lib/es/components/pill/index.js +5 -0
- package/lib/es/components/pill/item.js +23 -0
- package/lib/es/components/pill/pill.js +192 -0
- package/lib/es/components/select/options.js +6 -8
- package/lib/es/components/select/select.js +4 -8
- package/lib/es/components/tag/tag.js +2 -4
- package/lib/es/index.js +1 -1
- package/lib/index.js +269 -62
- package/lib/types/components/form/useForm.d.ts +1 -1
- package/lib/types/components/modal/type.d.ts +1 -1
- package/lib/types/components/pill/index.d.ts +5 -0
- package/lib/types/components/pill/pill.d.ts +6 -0
- package/lib/types/components/pill/type.d.ts +28 -0
- package/lib/types/components/tag/type.d.ts +0 -1
- package/lib/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/lib/es/components/card/card.js +0 -12
- package/lib/es/components/card/index.js +0 -5
- package/lib/types/components/card/card.d.ts +0 -6
- package/lib/types/components/card/index.d.ts +0 -5
- package/lib/types/components/card/type.d.ts +0 -13
package/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { debounce, uid, crush, throttle } from 'radash';
|
|
4
4
|
import { useState, useRef, useEffect, useCallback, useMemo, Children, cloneElement, createElement, isValidElement, memo, Fragment as Fragment$1, useTransition, forwardRef, useLayoutEffect, useContext, createContext, useImperativeHandle } from 'react';
|
|
5
|
-
import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, UndoRound, RedoRound, FormatBoldRound, FormatItalicRound, FormatUnderlinedRound, StrikethroughSRound, ClearAllRound, PlayArrowRound, PauseRound, StopRound, VolumeDownRound, VolumeOffRound, FullscreenRound, FullscreenExitRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, KeyboardDoubleArrowUpRound, SyncAltRound, VisibilityRound, VisibilityOffRound, MoreHorizRound, SearchRound, CheckRound, UnfoldMoreRound, CalendarMonthTwotone, AccessTimeRound, KeyboardArrowDownRound, MoveToInboxTwotone, OutboxTwotone, FilePresentOutlined, DriveFolderUploadOutlined } from '@ricons/material';
|
|
5
|
+
import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, UndoRound, RedoRound, FormatBoldRound, FormatItalicRound, FormatUnderlinedRound, StrikethroughSRound, ClearAllRound, PlayArrowRound, PauseRound, StopRound, VolumeDownRound, VolumeOffRound, FullscreenRound, FullscreenExitRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, KeyboardDoubleArrowUpRound, SyncAltRound, VisibilityRound, VisibilityOffRound, MoreHorizRound, SearchRound, CheckRound, UnfoldMoreRound, CalendarMonthTwotone, AccessTimeRound, AddRound, KeyboardArrowDownRound, MoveToInboxTwotone, OutboxTwotone, FilePresentOutlined, DriveFolderUploadOutlined } from '@ricons/material';
|
|
6
6
|
import { createRoot } from 'react-dom/client';
|
|
7
7
|
import { getScrollbarSize, List as List$2 } from 'react-window';
|
|
8
8
|
import { createPortal, flushSync } from 'react-dom';
|
|
@@ -575,14 +575,6 @@ const Badge = (props) => {
|
|
|
575
575
|
}), style: { fontSize: dotSize }, children: content })] }));
|
|
576
576
|
};
|
|
577
577
|
|
|
578
|
-
const Card = (props) => {
|
|
579
|
-
const { hideShadow, border, className, children, header, footer, ...restProps } = props;
|
|
580
|
-
return (jsxs("div", { className: classNames("i-card", className, {
|
|
581
|
-
shadow: !hideShadow,
|
|
582
|
-
"i-card-bordered": border,
|
|
583
|
-
}), ...restProps, children: [header && jsx("div", { className: 'i-card-header', children: header }), children && jsx("div", { className: 'i-card-content', children: children }), footer && jsx("div", { className: 'i-card-footer', children: footer })] }));
|
|
584
|
-
};
|
|
585
|
-
|
|
586
578
|
function getPosition($source, $popup, options = {}) {
|
|
587
579
|
const { refWindow, gap = 0, offset = 0, position = "top", align } = options;
|
|
588
580
|
if (!$source || !$popup)
|
|
@@ -917,7 +909,7 @@ function Checkbox(props) {
|
|
|
917
909
|
"i-input-inline": labelInline,
|
|
918
910
|
}, className), ...restProps, children: [label && (jsxs("span", { className: "i-input-label-text", children: [required && jsx("span", { className: "error", children: "*" }), label, message && jsx("p", { className: "i-checkbox-message", children: message })] })), jsx("div", { className: classNames("i-checkbox-options", {
|
|
919
911
|
"i-options-block": !optionInline,
|
|
920
|
-
|
|
912
|
+
[`i-checkbox-options-${type}`]: true,
|
|
921
913
|
}), children: formattedOptions.map((option) => {
|
|
922
914
|
return (jsx(CheckboxItem, { name: name, value: selectedValues.includes(option.value), optionValue: option.value, type: type, disabled: disabled || option.disabled, onChange: (checked, e) => handleChange(checked, option, e), children: renderItem ?? option.label }, option.value));
|
|
923
915
|
}) })] }));
|
|
@@ -3084,8 +3076,7 @@ class IFormInstance {
|
|
|
3084
3076
|
for (let i = 1; i < parts.length; i++) {
|
|
3085
3077
|
const ancestor = parts.slice(0, i).join(".");
|
|
3086
3078
|
if (ancestor in this.data) {
|
|
3087
|
-
console.warn(`[ioca-form] Field "${field}" conflicts with "${ancestor}". ` +
|
|
3088
|
-
"Nested representation in form.get() may be inconsistent.");
|
|
3079
|
+
console.warn(`[ioca-form] Field "${field}" conflicts with "${ancestor}". ` + "Nested representation in form.get() may be inconsistent.");
|
|
3089
3080
|
}
|
|
3090
3081
|
}
|
|
3091
3082
|
setDeep(this.data, field, value);
|
|
@@ -3137,7 +3128,7 @@ class IFormInstance {
|
|
|
3137
3128
|
if (!field && o === undefined)
|
|
3138
3129
|
return;
|
|
3139
3130
|
const rule = {
|
|
3140
|
-
validator: (v) => Array.isArray(v) ? v.length > 0 : ![undefined, null, ""].includes(v),
|
|
3131
|
+
validator: (v) => (Array.isArray(v) ? v.length > 0 : ![undefined, null, ""].includes(v)),
|
|
3141
3132
|
};
|
|
3142
3133
|
if (typeof o === "function")
|
|
3143
3134
|
rule.validator = o;
|
|
@@ -3162,7 +3153,7 @@ class IFormInstance {
|
|
|
3162
3153
|
});
|
|
3163
3154
|
}
|
|
3164
3155
|
});
|
|
3165
|
-
return
|
|
3156
|
+
return isAllValid ? data : false;
|
|
3166
3157
|
}
|
|
3167
3158
|
}
|
|
3168
3159
|
function useForm(form) {
|
|
@@ -3429,25 +3420,33 @@ function Modal(props) {
|
|
|
3429
3420
|
if (!toggable.current)
|
|
3430
3421
|
return;
|
|
3431
3422
|
toggable.current = false;
|
|
3432
|
-
|
|
3433
|
-
|
|
3423
|
+
const canClose = typeof closable === 'function' ? closable() : closable;
|
|
3424
|
+
const exec = (result) => {
|
|
3425
|
+
if (!result) {
|
|
3426
|
+
setBounced(true);
|
|
3427
|
+
const timer = setTimeout(() => {
|
|
3428
|
+
setBounced(false);
|
|
3429
|
+
toggable.current = true;
|
|
3430
|
+
}, 400);
|
|
3431
|
+
return () => clearTimeout(timer);
|
|
3432
|
+
}
|
|
3433
|
+
setActive(false);
|
|
3434
|
+
updateVisible(mid, false);
|
|
3434
3435
|
const timer = setTimeout(() => {
|
|
3435
|
-
|
|
3436
|
+
if (!keepDOM)
|
|
3437
|
+
setShow(false);
|
|
3436
3438
|
toggable.current = true;
|
|
3437
|
-
|
|
3439
|
+
onVisibleChange?.(false);
|
|
3440
|
+
onClose?.();
|
|
3441
|
+
}, 240);
|
|
3438
3442
|
return () => clearTimeout(timer);
|
|
3443
|
+
};
|
|
3444
|
+
if (canClose instanceof Promise) {
|
|
3445
|
+
canClose.then(exec);
|
|
3446
|
+
return;
|
|
3439
3447
|
}
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
const timer = setTimeout(() => {
|
|
3443
|
-
if (!keepDOM)
|
|
3444
|
-
setShow(false);
|
|
3445
|
-
toggable.current = true;
|
|
3446
|
-
onVisibleChange?.(false);
|
|
3447
|
-
onClose?.();
|
|
3448
|
-
}, 240);
|
|
3449
|
-
return () => clearTimeout(timer);
|
|
3450
|
-
}, [closable, keepDOM, onClose, onVisibleChange]);
|
|
3448
|
+
return exec(canClose);
|
|
3449
|
+
}, [closable, keepDOM, mid, onClose, onVisibleChange]);
|
|
3451
3450
|
const handleBackdropClick = () => {
|
|
3452
3451
|
backdropClosable && handleHide();
|
|
3453
3452
|
};
|
|
@@ -3507,7 +3506,7 @@ function Modal(props) {
|
|
|
3507
3506
|
e.stopPropagation();
|
|
3508
3507
|
handleClick();
|
|
3509
3508
|
onClick?.(e);
|
|
3510
|
-
}, role: "dialog", "aria-modal": top, "data-mid": mid, ...restProps, children: jsxs(ModalContext.Provider, { value: true, children: [customized && children, !customized &&
|
|
3509
|
+
}, role: "dialog", "aria-modal": top, "data-mid": mid, ...restProps, children: jsxs(ModalContext.Provider, { value: true, children: [customized && children, !customized && jsx(Content$2, { title: title, hideCloseButton: hideCloseButton, footer: footer, okButtonProps: okButtonProps, cancelButtonProps: cancelButtonProps, children: children, footerLeft: footerLeft, onOk: onOk, onClose: handleHide })] }) }) }), getContainer());
|
|
3511
3510
|
}
|
|
3512
3511
|
Modal.useModal = useModal;
|
|
3513
3512
|
|
|
@@ -3521,7 +3520,17 @@ const HookModal = (props) => {
|
|
|
3521
3520
|
},
|
|
3522
3521
|
close: () => {
|
|
3523
3522
|
state.visible = false;
|
|
3524
|
-
|
|
3523
|
+
const canClose = typeof mergedProps.closable === 'function'
|
|
3524
|
+
? mergedProps.closable()
|
|
3525
|
+
: (mergedProps.closable ?? true);
|
|
3526
|
+
if (canClose instanceof Promise) {
|
|
3527
|
+
canClose.then((result) => {
|
|
3528
|
+
if (!result)
|
|
3529
|
+
state.visible = true;
|
|
3530
|
+
});
|
|
3531
|
+
return;
|
|
3532
|
+
}
|
|
3533
|
+
if (canClose)
|
|
3525
3534
|
return;
|
|
3526
3535
|
Promise.resolve().then(() => {
|
|
3527
3536
|
state.visible = true;
|
|
@@ -4167,7 +4176,7 @@ function InputContainer(props) {
|
|
|
4167
4176
|
}
|
|
4168
4177
|
|
|
4169
4178
|
const Number$1 = (props) => {
|
|
4170
|
-
const { ref, label, name, value = "", labelInline, step = 1, min = -Infinity, max = Infinity, thousand, precision, type, className, width, status = "normal", append, border, underline, prepend, disabled, message, tip, hideControl, showMax, style, onChange, onEnter, onInput, onBlur, ...restProps } = props;
|
|
4179
|
+
const { ref, label, name, value = "", labelInline, step = 1, min = -Infinity, max = Infinity, thousand, precision, type, className, width, status = "normal", append, border = true, underline, prepend, disabled, message, tip, hideControl, showMax, style, onChange, onEnter, onInput, onBlur, ...restProps } = props;
|
|
4171
4180
|
const [inputValue, setInputValue] = useState(value === undefined || value === null ? "" : String(value));
|
|
4172
4181
|
const formatOut = (num) => {
|
|
4173
4182
|
const v = clamp(num, min, max);
|
|
@@ -4180,9 +4189,7 @@ const Number$1 = (props) => {
|
|
|
4180
4189
|
const body = negative ? s.slice(1) : s;
|
|
4181
4190
|
const [integer, decimal] = body.split(".");
|
|
4182
4191
|
const withThousand = integer.replace(/\B(?=(\d{3})+(?!\d))/g, thousand);
|
|
4183
|
-
return decimal
|
|
4184
|
-
? `${negative ? "-" : ""}${withThousand}.${decimal}`
|
|
4185
|
-
: `${negative ? "-" : ""}${withThousand}`;
|
|
4192
|
+
return decimal ? `${negative ? "-" : ""}${withThousand}.${decimal}` : `${negative ? "-" : ""}${withThousand}`;
|
|
4186
4193
|
};
|
|
4187
4194
|
const sanitizeNumberInput = (raw) => {
|
|
4188
4195
|
const hasMinus = raw.startsWith("-");
|
|
@@ -4263,11 +4270,11 @@ const Number$1 = (props) => {
|
|
|
4263
4270
|
[`i-input-${status}`]: status !== "normal",
|
|
4264
4271
|
"i-input-borderless": !border,
|
|
4265
4272
|
"i-input-underline": underline,
|
|
4266
|
-
}), children: [prepend && jsx("div", { className:
|
|
4273
|
+
}), children: [prepend && jsx("div", { className: "i-input-prepend", children: prepend }), !hideControl && !disabled && jsx(Helpericon, { active: true, icon: jsx(MinusRound, {}), onClick: () => handleOperate(-step) }), jsx("input", { ...inputProps }), !hideControl && !disabled && jsx(Helpericon, { active: true, icon: jsx(PlusRound, {}), onClick: () => handleOperate(step) }), showMax && max && !disabled && jsx(Helpericon, { active: true, icon: jsx(KeyboardDoubleArrowUpRound, {}), onClick: handleMax }), append && jsx("div", { className: "i-input-append", children: append })] }) }));
|
|
4267
4274
|
};
|
|
4268
4275
|
|
|
4269
4276
|
const Range = (props) => {
|
|
4270
|
-
const { label, name, value, labelInline, min = -Infinity, max = Infinity, type, className, status = "normal", message, tip, append, prepend, step = 1, width, thousand, precision, hideControl, placeholder, border, underline, autoSwitch, onChange, onBlur, style, ...restProps } = props;
|
|
4277
|
+
const { label, name, value, labelInline, min = -Infinity, max = Infinity, type, className, status = "normal", message, tip, append, prepend, step = 1, width, thousand, precision, hideControl, placeholder, border = true, underline, autoSwitch, onChange, onBlur, style, ...restProps } = props;
|
|
4271
4278
|
const [rangeValue, setRangeValue] = useState(value);
|
|
4272
4279
|
const getRangeNumber = (v) => clamp(v, min, max);
|
|
4273
4280
|
const getFormatNumber = (v) => formatNumber(v, { precision, thousand });
|
|
@@ -4327,11 +4334,11 @@ const Range = (props) => {
|
|
|
4327
4334
|
[`i-input-${status}`]: status !== "normal",
|
|
4328
4335
|
"i-input-borderless": !border,
|
|
4329
4336
|
"i-input-underline": underline,
|
|
4330
|
-
}), children: [prepend && jsx("div", { className:
|
|
4337
|
+
}), children: [prepend && jsx("div", { className: "i-input-prepend", children: prepend }), !hideControl && jsx(Helpericon, { active: true, icon: jsx(MinusRound, {}), onClick: (e) => handleOperate(e, -step, 0) }), jsx("input", { value: rangeValue?.[0] || "", placeholder: placeholder?.[0], ...inputProps, onBlur: handleBlur, onChange: (e) => handleChange(e, 0) }), !hideControl && jsx(Helpericon, { active: true, icon: jsx(PlusRound, {}), onClick: (e) => handleOperate(e, step, 0) }), jsx(Helpericon, { active: true, icon: jsx(SyncAltRound, {}), style: { margin: 0 }, onClick: handleSwitch }), !hideControl && jsx(Helpericon, { active: true, icon: jsx(MinusRound, {}), onClick: (e) => handleOperate(e, -step, 1) }), jsx("input", { value: rangeValue?.[1] || "", placeholder: placeholder?.[1], ...inputProps, onBlur: handleBlur, onChange: (e) => handleChange(e, 1) }), !hideControl && jsx(Helpericon, { active: true, icon: jsx(PlusRound, {}), onClick: (e) => handleOperate(e, step, 1) }), append && jsx("div", { className: "i-input-append", children: append })] }) }));
|
|
4331
4338
|
};
|
|
4332
4339
|
|
|
4333
4340
|
const Textarea = (props) => {
|
|
4334
|
-
const { ref, label, name, value = "", labelInline, className, status = "normal", message, tip, autoSize, border, width, style, resize, onChange, onEnter, ...restProps } = props;
|
|
4341
|
+
const { ref, label, name, value = "", labelInline, className, status = "normal", message, tip, autoSize, border = true, width, style, resize, onChange, onEnter, ...restProps } = props;
|
|
4335
4342
|
const [textareaValue, setTextareaValue] = useState(value);
|
|
4336
4343
|
const refTextarea = useRef(null);
|
|
4337
4344
|
const syncTextareaHeight = () => {
|
|
@@ -4380,7 +4387,7 @@ const Textarea = (props) => {
|
|
|
4380
4387
|
};
|
|
4381
4388
|
|
|
4382
4389
|
const Input = ((props) => {
|
|
4383
|
-
const { ref, type = "text", label, name, value = "", prepend, append, labelInline, className, status = "normal", message, tip, clear, width, hideVisible, border, underline, required, maxLength, onChange, onEnter, onClear, style, ...restProps } = props;
|
|
4390
|
+
const { ref, type = "text", label, name, value = "", prepend, append, labelInline, className, status = "normal", message, tip, clear, width, hideVisible, border = true, underline, required, maxLength, onChange, onEnter, onClear, style, ...restProps } = props;
|
|
4384
4391
|
const [inputValue, setInputValue] = useState(value);
|
|
4385
4392
|
const [inputType, setInputType] = useState(type);
|
|
4386
4393
|
const [visible, setVisible] = useState(false);
|
|
@@ -4438,7 +4445,7 @@ const Input = ((props) => {
|
|
|
4438
4445
|
[`i-input-${status}`]: status !== "normal",
|
|
4439
4446
|
"i-input-borderless": !border,
|
|
4440
4447
|
"i-input-underline": underline,
|
|
4441
|
-
}), children: [prepend && jsx("div", { className:
|
|
4448
|
+
}), children: [prepend && jsx("div", { className: "i-input-prepend", children: prepend }), jsx("input", { ...inputProps }), maxLength && inputValue?.length > 0 && (jsxs("span", { className: "color-8 pr-4 font-sm", children: [inputValue.length, " / ", maxLength] })), jsx(Helpericon, { active: !!clearable || showHelper, icon: HelperIcon, className: classNames({ "i-helpericon-clear": isClearBtn }), onClick: handleHelperClick }), append && jsx("div", { className: "i-input-append", children: append })] }) }));
|
|
4442
4449
|
});
|
|
4443
4450
|
Input.Textarea = Textarea;
|
|
4444
4451
|
Input.Number = Number$1;
|
|
@@ -4721,31 +4728,27 @@ const Pagination = (props) => {
|
|
|
4721
4728
|
};
|
|
4722
4729
|
|
|
4723
4730
|
const Tag = (props) => {
|
|
4724
|
-
const { dot, dotClass, outline, round, size = "normal",
|
|
4731
|
+
const { dot, dotClass, outline, round, size = "normal", className, children, onClose, onClick, ...restProps } = props;
|
|
4725
4732
|
return (jsxs("span", { className: classNames("i-tag", {
|
|
4726
4733
|
"i-tag-outline": outline,
|
|
4727
4734
|
"i-tag-clickable": onClick,
|
|
4728
4735
|
[`i-tag-${size}`]: size !== "normal",
|
|
4729
4736
|
round,
|
|
4730
|
-
}, className), onClick: onClick, ...restProps, children: [dot && jsx("span", { className: classNames("i-tag-dot", dotClass) }), children, onClose && (jsx(Helpericon, { active: true, className:
|
|
4731
|
-
"i-tag-hover-close": hoverShowClose,
|
|
4732
|
-
}), onClick: onClose }))] }));
|
|
4737
|
+
}, className), onClick: onClick, ...restProps, children: [dot && jsx("span", { className: classNames("i-tag-dot", dotClass) }), children, onClose && (jsx(Helpericon, { active: true, className: "i-tag-close i-tag-hover-close", onClick: onClose }))] }));
|
|
4733
4738
|
};
|
|
4734
4739
|
|
|
4735
4740
|
const Options = (props) => {
|
|
4736
|
-
const { value: val, options, filter, filterPlaceholder, multiple, empty = jsx(Empty, {}), onSelect, onFilter
|
|
4741
|
+
const { value: val, options, filter, filterPlaceholder, multiple, empty = jsx(Empty, {}), onSelect, onFilter } = props;
|
|
4737
4742
|
return (jsxs("div", { className: classNames("i-select-options", {
|
|
4738
4743
|
"i-select-options-multiple": multiple,
|
|
4739
|
-
}), children: [filter && multiple && (jsxs("div", { className:
|
|
4744
|
+
}), children: [filter && multiple && (jsxs("div", { className: "i-select-filter", children: [jsx(Icon, { icon: jsx(SearchRound, {}), className: "color-8 ml-8 my-auto", size: "1.2em" }), jsx("input", { type: "text", className: "i-input", placeholder: filterPlaceholder, onChange: onFilter })] })), options.length === 0 && empty, options.map((option, i) => {
|
|
4740
4745
|
const { label, value, disabled } = option;
|
|
4741
|
-
const isActive = multiple
|
|
4742
|
-
|
|
4743
|
-
: val === value;
|
|
4744
|
-
return (jsxs(List$1.Item, { active: isActive, type: 'option', onClick: () => onSelect?.(value, option), disabled: disabled, children: [multiple && (jsx(Icon, { icon: jsx(CheckRound, {}), className: 'i-select-option-check', size: '1em' })), label] }, value || i));
|
|
4746
|
+
const isActive = multiple ? val?.includes(value) : val === value;
|
|
4747
|
+
return (jsxs(List$1.Item, { active: isActive, type: "option", onClick: () => onSelect?.(value, option), disabled: disabled, children: [multiple && jsx(Icon, { icon: jsx(CheckRound, {}), className: "i-select-option-check", size: "1em" }), label] }, value || i));
|
|
4745
4748
|
})] }));
|
|
4746
4749
|
};
|
|
4747
4750
|
const activeOptions = (options = [], value = [], max = 3) => {
|
|
4748
|
-
const total = options.flatMap((opt) => value.includes(opt.value) ? [opt] : []);
|
|
4751
|
+
const total = options.flatMap((opt) => (value.includes(opt.value) ? [opt] : []));
|
|
4749
4752
|
if (max >= total.length)
|
|
4750
4753
|
return total;
|
|
4751
4754
|
const rest = total.length - max;
|
|
@@ -4760,7 +4763,7 @@ const displayValue = (config) => {
|
|
|
4760
4763
|
if (typeof opt === "number")
|
|
4761
4764
|
return jsxs(Tag, { children: ["+", opt] }, i);
|
|
4762
4765
|
const { label, value } = opt;
|
|
4763
|
-
return (jsx(Tag, {
|
|
4766
|
+
return (jsx(Tag, { onClose: (e) => {
|
|
4764
4767
|
e?.stopPropagation();
|
|
4765
4768
|
onSelect?.(value, opt);
|
|
4766
4769
|
}, children: label }, value));
|
|
@@ -4770,7 +4773,7 @@ const displayValue = (config) => {
|
|
|
4770
4773
|
};
|
|
4771
4774
|
|
|
4772
4775
|
const Select = (props) => {
|
|
4773
|
-
const { ref, type = "text", name, label, value = "", placeholder, required, options = [], multiple, prepend, append, labelInline, style, className, message, status = "normal", hideClear, hideArrow, maxDisplay, border, filter, tip, filterPlaceholder = "...", popupProps, onSelect, onChange, ...restProps } = props;
|
|
4776
|
+
const { ref, type = "text", name, label, value = "", placeholder, required, options = [], multiple, prepend, append, labelInline, style, className, message, status = "normal", hideClear, hideArrow, maxDisplay, border = true, filter, tip, filterPlaceholder = "...", popupProps, onSelect, onChange, ...restProps } = props;
|
|
4774
4777
|
const [filterValue, setFilterValue] = useState("");
|
|
4775
4778
|
const [selectedValue, setSelectedValue] = useState(value);
|
|
4776
4779
|
const [active, setActive] = useState(false);
|
|
@@ -4785,9 +4788,7 @@ const Select = (props) => {
|
|
|
4785
4788
|
if (!fv || !filter)
|
|
4786
4789
|
return formattedOptions;
|
|
4787
4790
|
const lowerFv = fv.toLowerCase();
|
|
4788
|
-
const filterFn = typeof filter === "function"
|
|
4789
|
-
? filter
|
|
4790
|
-
: (opt) => opt._value.includes(lowerFv) || opt._label.includes(lowerFv);
|
|
4791
|
+
const filterFn = typeof filter === "function" ? filter : (opt) => opt._value.includes(lowerFv) || opt._label.includes(lowerFv);
|
|
4791
4792
|
return formattedOptions.filter(filterFn);
|
|
4792
4793
|
}, [formattedOptions, filter, filterValue]);
|
|
4793
4794
|
const changeValue = (v) => {
|
|
@@ -4836,9 +4837,7 @@ const Select = (props) => {
|
|
|
4836
4837
|
useEffect(() => {
|
|
4837
4838
|
setSelectedValue(value);
|
|
4838
4839
|
}, [value]);
|
|
4839
|
-
const hasValue = multiple
|
|
4840
|
-
? selectedValue.length > 0
|
|
4841
|
-
: !!selectedValue;
|
|
4840
|
+
const hasValue = multiple ? selectedValue.length > 0 : !!selectedValue;
|
|
4842
4841
|
const clearable = !hideClear && active && hasValue;
|
|
4843
4842
|
const text = message ?? tip;
|
|
4844
4843
|
return (jsxs("label", { className: classNames("i-input-label", className, {
|
|
@@ -4855,7 +4854,7 @@ const Select = (props) => {
|
|
|
4855
4854
|
multiple,
|
|
4856
4855
|
maxDisplay,
|
|
4857
4856
|
onSelect: handleSelect,
|
|
4858
|
-
}) })) : (jsx("input", { className: "i-input i-select", placeholder: placeholder, readOnly: true }))) : null, !multiple &&
|
|
4857
|
+
}) })) : (jsx("input", { className: "i-input i-select", placeholder: placeholder, readOnly: true }))) : null, !multiple && jsx("input", { value: active ? filterValue : displayLabel, className: "i-input i-select", placeholder: displayLabel || placeholder, onChange: handleInputChange, readOnly: !filter }), jsx(Helpericon, { active: !hideArrow, icon: clearable ? undefined : jsx(UnfoldMoreRound, {}), onClick: handleHelperClick }), append] }) }), text && jsx("span", { className: "i-input-message", children: text })] }));
|
|
4859
4858
|
};
|
|
4860
4859
|
|
|
4861
4860
|
const ColorMethods = {
|
|
@@ -5325,6 +5324,214 @@ const DateRange = (props) => {
|
|
|
5325
5324
|
return (jsx(Popup, { visible: active, trigger: 'click', position: 'bottom', arrow: false, align: 'start', onVisibleChange: handleVisibleChange, content: jsx(DoublePanel, { value: dayJsValue, weeks: weeks, unitYear: unitYear, unitMonth: unitMonth, renderDate: renderDate, renderMonth: renderMonth, renderYear: renderYear, disabledDate: disabledDate, onSelected: handleSelected }), children: jsx(Input, { value: inputValue, placeholder: placeholder, readOnly: true, clear: clear, onClear: handleClear, append: jsx(Icon, { icon: jsx(CalendarMonthTwotone, {}), className: 'i-datepicker-icon', size: '1em' }), className: classNames("i-datepicker-label", className), ...restProps }) }));
|
|
5326
5325
|
};
|
|
5327
5326
|
|
|
5327
|
+
const CreateTag = memo(function CreateTag(props) {
|
|
5328
|
+
const { isEditing, isLoading, createTagProps, tagProps, onBlur, onKeyDown, onStartCreate } = props;
|
|
5329
|
+
if (isEditing) {
|
|
5330
|
+
return (jsx(Tag, { ...createTagProps, className: classNames("i-pill", tagProps?.className, "i-pill-editing"), contentEditable: true, suppressContentEditableWarning: true, onBlur: () => onBlur(-1), onKeyDown: (e) => onKeyDown(e, -1), children: isLoading && jsx(Loading, { size: ".86em", className: "ml-4" }) }, "pill-editing"));
|
|
5331
|
+
}
|
|
5332
|
+
return (jsx(Tag, { ...createTagProps, className: classNames("i-pill", tagProps?.className, "i-pill-create"), onClick: onStartCreate, children: jsx("b", { children: "\uFF0B" }) }, "pill-create"));
|
|
5333
|
+
});
|
|
5334
|
+
|
|
5335
|
+
const TagItem = memo(function TagItem(props) {
|
|
5336
|
+
const { item, index, isEditing, isLoading, tagProps, editable, readonly, renderItem, onClose, onClick, onBlur, onKeyDown } = props;
|
|
5337
|
+
const isClickable = !isEditing && editable && !readonly;
|
|
5338
|
+
if (renderItem) {
|
|
5339
|
+
return renderItem({
|
|
5340
|
+
value: item,
|
|
5341
|
+
index,
|
|
5342
|
+
editing: isEditing,
|
|
5343
|
+
loading: isLoading,
|
|
5344
|
+
readonly: !!readonly,
|
|
5345
|
+
remove: () => onClose(index),
|
|
5346
|
+
});
|
|
5347
|
+
}
|
|
5348
|
+
return (jsxs(Tag, { ...tagProps, className: classNames("i-pill", tagProps?.className, { "i-pill-editing": isEditing }), contentEditable: isEditing, suppressContentEditableWarning: true, onClose: !isEditing && !isLoading && !readonly ? () => onClose(index) : undefined, onClick: isClickable ? (e) => onClick(e, index) : undefined, onBlur: isEditing ? () => onBlur(index) : undefined, onKeyDown: isEditing ? (e) => onKeyDown(e, index) : undefined, children: [item, isLoading && jsx(Loading, { size: ".86em", className: "ml-4" })] }));
|
|
5349
|
+
});
|
|
5350
|
+
|
|
5351
|
+
function Pill(props) {
|
|
5352
|
+
const { value = [], tagProps, max, icon = jsx(AddRound, {}), className, label, labelInline, readonly, editable, onChange, onUpdate, validator, format, renderItem, hideCreate, ...restProps } = props;
|
|
5353
|
+
const [editingIndex, setEditingIndex] = useState(null);
|
|
5354
|
+
const [loadingSet, setLoadingSet] = useState(new Set());
|
|
5355
|
+
const instRef = useRef({
|
|
5356
|
+
props,
|
|
5357
|
+
editingIndex,
|
|
5358
|
+
loadingSet,
|
|
5359
|
+
setEditingIndex,
|
|
5360
|
+
setLoadingSet,
|
|
5361
|
+
});
|
|
5362
|
+
instRef.current.props = props;
|
|
5363
|
+
instRef.current.editingIndex = editingIndex;
|
|
5364
|
+
instRef.current.loadingSet = loadingSet;
|
|
5365
|
+
instRef.current.setEditingIndex = setEditingIndex;
|
|
5366
|
+
instRef.current.setLoadingSet = setLoadingSet;
|
|
5367
|
+
useEffect(() => {
|
|
5368
|
+
if (editingIndex !== null) {
|
|
5369
|
+
const el = document.querySelector(".i-pill-editing");
|
|
5370
|
+
el?.focus();
|
|
5371
|
+
}
|
|
5372
|
+
}, [editingIndex]);
|
|
5373
|
+
const cleanTagProps = useMemo(() => {
|
|
5374
|
+
if (!tagProps)
|
|
5375
|
+
return {};
|
|
5376
|
+
const { onClose, dot, dotClass, ...rest } = tagProps;
|
|
5377
|
+
return rest;
|
|
5378
|
+
}, [tagProps]);
|
|
5379
|
+
const handleClose = useCallback((index) => {
|
|
5380
|
+
const inst = instRef.current;
|
|
5381
|
+
if (inst.props.readonly)
|
|
5382
|
+
return;
|
|
5383
|
+
const hasAsync = !!inst.props.onUpdate;
|
|
5384
|
+
if (hasAsync)
|
|
5385
|
+
inst.setLoadingSet((prev) => new Set(prev).add(index));
|
|
5386
|
+
setTimeout(async () => {
|
|
5387
|
+
try {
|
|
5388
|
+
const { props } = instRef.current;
|
|
5389
|
+
const values = props.value ?? [];
|
|
5390
|
+
const item = values[index];
|
|
5391
|
+
if (item === undefined)
|
|
5392
|
+
return;
|
|
5393
|
+
const result = props.onUpdate?.(undefined, item, "delete");
|
|
5394
|
+
if (result instanceof Promise) {
|
|
5395
|
+
const ok = await result;
|
|
5396
|
+
if (ok === false)
|
|
5397
|
+
return;
|
|
5398
|
+
}
|
|
5399
|
+
const next = [...values];
|
|
5400
|
+
next.splice(index, 1);
|
|
5401
|
+
props.onChange?.(next);
|
|
5402
|
+
}
|
|
5403
|
+
finally {
|
|
5404
|
+
if (hasAsync) {
|
|
5405
|
+
instRef.current.setLoadingSet((prev) => {
|
|
5406
|
+
const s = new Set(prev);
|
|
5407
|
+
s.delete(index);
|
|
5408
|
+
return s;
|
|
5409
|
+
});
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
}, 0);
|
|
5413
|
+
}, []);
|
|
5414
|
+
const handleItemClick = useCallback((e, index) => {
|
|
5415
|
+
if (e.target.closest(".i-helpericon"))
|
|
5416
|
+
return;
|
|
5417
|
+
const inst = instRef.current;
|
|
5418
|
+
if (inst.props.readonly)
|
|
5419
|
+
return;
|
|
5420
|
+
if (index === -1 && inst.props.max !== undefined && (inst.props.value?.length ?? 0) >= inst.props.max)
|
|
5421
|
+
return;
|
|
5422
|
+
inst.setEditingIndex(index);
|
|
5423
|
+
}, []);
|
|
5424
|
+
const commitEdit = useCallback((index, text) => {
|
|
5425
|
+
const inst = instRef.current;
|
|
5426
|
+
const formatted = inst.props.format ? inst.props.format(text) : text;
|
|
5427
|
+
const hasAsync = !!(inst.props.validator || inst.props.onUpdate);
|
|
5428
|
+
if (hasAsync)
|
|
5429
|
+
inst.setLoadingSet((prev) => new Set(prev).add(index));
|
|
5430
|
+
setTimeout(async () => {
|
|
5431
|
+
try {
|
|
5432
|
+
const { props } = instRef.current;
|
|
5433
|
+
if (props.validator) {
|
|
5434
|
+
const valid = await Promise.resolve(props.validator(formatted));
|
|
5435
|
+
if (!valid)
|
|
5436
|
+
return;
|
|
5437
|
+
}
|
|
5438
|
+
const values = props.value ?? [];
|
|
5439
|
+
if (index === -1) {
|
|
5440
|
+
if (values.includes(formatted))
|
|
5441
|
+
return;
|
|
5442
|
+
const result = props.onUpdate?.(formatted, undefined, "create");
|
|
5443
|
+
if (result instanceof Promise) {
|
|
5444
|
+
const ok = await result;
|
|
5445
|
+
if (ok === false)
|
|
5446
|
+
return;
|
|
5447
|
+
}
|
|
5448
|
+
props.onChange?.([...values, formatted]);
|
|
5449
|
+
}
|
|
5450
|
+
else {
|
|
5451
|
+
const oldValue = values[index];
|
|
5452
|
+
if (oldValue === formatted)
|
|
5453
|
+
return;
|
|
5454
|
+
const result = props.onUpdate?.(formatted, oldValue, "update");
|
|
5455
|
+
if (result instanceof Promise) {
|
|
5456
|
+
const ok = await result;
|
|
5457
|
+
if (ok === false)
|
|
5458
|
+
return;
|
|
5459
|
+
}
|
|
5460
|
+
const next = [...values];
|
|
5461
|
+
next[index] = formatted;
|
|
5462
|
+
props.onChange?.(next);
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
finally {
|
|
5466
|
+
if (hasAsync) {
|
|
5467
|
+
instRef.current.setLoadingSet((prev) => {
|
|
5468
|
+
const s = new Set(prev);
|
|
5469
|
+
s.delete(index);
|
|
5470
|
+
return s;
|
|
5471
|
+
});
|
|
5472
|
+
}
|
|
5473
|
+
instRef.current.setEditingIndex(null);
|
|
5474
|
+
}
|
|
5475
|
+
}, 0);
|
|
5476
|
+
}, []);
|
|
5477
|
+
const handleBlur = useCallback((index) => {
|
|
5478
|
+
const inst = instRef.current;
|
|
5479
|
+
if (inst.loadingSet.has(index))
|
|
5480
|
+
return;
|
|
5481
|
+
const el = document.querySelector(".i-pill-editing");
|
|
5482
|
+
const text = el?.textContent?.trim();
|
|
5483
|
+
if (!text) {
|
|
5484
|
+
if (index !== -1) {
|
|
5485
|
+
handleClose(index);
|
|
5486
|
+
}
|
|
5487
|
+
else {
|
|
5488
|
+
inst.setEditingIndex(null);
|
|
5489
|
+
}
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
commitEdit(index, text);
|
|
5493
|
+
}, []);
|
|
5494
|
+
const handleKeyDown = useCallback((e, index) => {
|
|
5495
|
+
const inst = instRef.current;
|
|
5496
|
+
if (inst.loadingSet.has(index))
|
|
5497
|
+
return;
|
|
5498
|
+
if (e.key === "Enter") {
|
|
5499
|
+
e.preventDefault();
|
|
5500
|
+
const text = e.currentTarget.textContent?.trim();
|
|
5501
|
+
if (!text) {
|
|
5502
|
+
if (index !== -1) {
|
|
5503
|
+
handleClose(index);
|
|
5504
|
+
}
|
|
5505
|
+
else {
|
|
5506
|
+
inst.setEditingIndex(null);
|
|
5507
|
+
}
|
|
5508
|
+
return;
|
|
5509
|
+
}
|
|
5510
|
+
commitEdit(index, text);
|
|
5511
|
+
}
|
|
5512
|
+
else if (e.key === "Escape") {
|
|
5513
|
+
e.preventDefault();
|
|
5514
|
+
if (index !== -1) {
|
|
5515
|
+
const original = inst.props.value?.[index];
|
|
5516
|
+
if (original !== undefined) {
|
|
5517
|
+
e.currentTarget.textContent = original;
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
inst.setEditingIndex(null);
|
|
5521
|
+
}
|
|
5522
|
+
}, []);
|
|
5523
|
+
const handleStartCreate = useCallback(() => {
|
|
5524
|
+
const inst = instRef.current;
|
|
5525
|
+
if (inst.props.readonly)
|
|
5526
|
+
return;
|
|
5527
|
+
if (inst.props.max !== undefined && (inst.props.value?.length ?? 0) >= inst.props.max)
|
|
5528
|
+
return;
|
|
5529
|
+
inst.setEditingIndex(-1);
|
|
5530
|
+
}, []);
|
|
5531
|
+
const canCreate = !hideCreate && !readonly && (max === undefined || value.length < max);
|
|
5532
|
+
return (jsxs("div", { className: classNames("i-pills i-input-label", { "i-input-inline": labelInline }, className), ...restProps, children: [label && jsx("span", { className: "i-input-label-text", children: label }), jsxs("div", { className: "i-pill-list", children: [value.map((item, i) => (jsx(TagItem, { item: item, index: i, isEditing: editingIndex === i, isLoading: loadingSet.has(i), tagProps: tagProps, editable: editable, readonly: readonly, renderItem: renderItem, onClose: handleClose, onClick: handleItemClick, onBlur: handleBlur, onKeyDown: handleKeyDown }, i))), canCreate && jsx(CreateTag, { isEditing: editingIndex === -1, isLoading: loadingSet.has(-1), createTagProps: cleanTagProps, tagProps: tagProps, onBlur: handleBlur, onKeyDown: handleKeyDown, onStartCreate: handleStartCreate })] })] }));
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5328
5535
|
const defaultOk = {
|
|
5329
5536
|
children: "确定",
|
|
5330
5537
|
};
|
|
@@ -6976,4 +7183,4 @@ const useTheme = (props) => {
|
|
|
6976
7183
|
};
|
|
6977
7184
|
};
|
|
6978
7185
|
|
|
6979
|
-
export { Affix, Badge, Button,
|
|
7186
|
+
export { Affix, Badge, Button, Checkbox, Collapse, ColorPicker, Datagrid, Datepicker as DatePicker, DateRange, Description, Drawer, Dropdown, Editor, Flex, Form, Icon, MemoImage as Image, Input, List$1 as List, Loading, Message, Modal, Pagination, Pill, Popconfirm, Popup, Progress, Radio, Resizable, River, Scroll, Select, Step, Swiper, Tabs, Tag, Text, TimePicker, Tree, Upload, Video, usePreview, useTheme };
|
|
@@ -10,7 +10,7 @@ declare class IFormInstance {
|
|
|
10
10
|
set(field: any, value?: any): void;
|
|
11
11
|
delete(field: string): void;
|
|
12
12
|
clear(): void;
|
|
13
|
-
validate(field?: string): Promise<
|
|
13
|
+
validate(field?: string): Promise<false | Record<string, any>>;
|
|
14
14
|
}
|
|
15
15
|
declare function useForm(form?: IFormInstance): IFormInstance;
|
|
16
16
|
|
|
@@ -6,7 +6,7 @@ interface IModal extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
|
6
6
|
visible?: boolean;
|
|
7
7
|
title?: ReactNode;
|
|
8
8
|
footer?: ReactNode;
|
|
9
|
-
closable?: boolean;
|
|
9
|
+
closable?: boolean | (() => boolean | Promise<boolean>);
|
|
10
10
|
hideCloseButton?: boolean;
|
|
11
11
|
hideBackdrop?: boolean;
|
|
12
12
|
backdropClosable?: boolean;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { ITag } from '../tag/type.js';
|
|
3
|
+
|
|
4
|
+
interface IPill extends Omit<HTMLAttributes<HTMLDivElement>, "onChange" | "value"> {
|
|
5
|
+
value?: any[];
|
|
6
|
+
onChange?: (value: any[]) => void;
|
|
7
|
+
tagProps?: Partial<ITag>;
|
|
8
|
+
max?: number;
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
editable?: boolean;
|
|
11
|
+
readonly?: boolean;
|
|
12
|
+
label?: ReactNode;
|
|
13
|
+
labelInline?: boolean;
|
|
14
|
+
validator?: (value: any) => boolean | Promise<boolean>;
|
|
15
|
+
format?: (value: any) => any;
|
|
16
|
+
onUpdate?: (newValue: any, oldValue: any, type: "delete" | "create" | "update") => void | Promise<boolean>;
|
|
17
|
+
hideCreate?: boolean;
|
|
18
|
+
renderItem?: (context: {
|
|
19
|
+
value: any;
|
|
20
|
+
index: number;
|
|
21
|
+
editing: boolean;
|
|
22
|
+
loading: boolean;
|
|
23
|
+
readonly: boolean;
|
|
24
|
+
remove: () => void;
|
|
25
|
+
}) => ReactNode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type { IPill };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { default as Affix } from './components/affix/affix.js';
|
|
2
2
|
export { default as Badge } from './components/badge/badge.js';
|
|
3
3
|
export { default as Button } from './components/button/button.js';
|
|
4
|
-
export { default as Card } from './components/card/card.js';
|
|
5
4
|
export { default as Checkbox } from './components/checkbox/checkbox.js';
|
|
6
5
|
export { default as Collapse } from './components/collapse/collapse.js';
|
|
7
6
|
export { default as Datagrid } from './components/datagrid/datagrid.js';
|
|
@@ -23,6 +22,7 @@ export { default as ColorPicker } from './components/picker/colors/index.js';
|
|
|
23
22
|
export { default as TimePicker } from './components/picker/time/index.js';
|
|
24
23
|
export { default as DateRange } from './components/picker/daterange/daterange.js';
|
|
25
24
|
export { default as DatePicker } from './components/picker/dates/index.js';
|
|
25
|
+
export { default as Pill } from './components/pill/pill.js';
|
|
26
26
|
export { default as Popconfirm } from './components/popconfirm/popconfirm.js';
|
|
27
27
|
export { default as Popup } from './components/popup/popup.js';
|
|
28
28
|
export { default as Progress } from './components/progress/progress.js';
|
package/package.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import classNames from 'classnames';
|
|
3
|
-
|
|
4
|
-
const Card = (props) => {
|
|
5
|
-
const { hideShadow, border, className, children, header, footer, ...restProps } = props;
|
|
6
|
-
return (jsxs("div", { className: classNames("i-card", className, {
|
|
7
|
-
shadow: !hideShadow,
|
|
8
|
-
"i-card-bordered": border,
|
|
9
|
-
}), ...restProps, children: [header && jsx("div", { className: 'i-card-header', children: header }), children && jsx("div", { className: 'i-card-content', children: children }), footer && jsx("div", { className: 'i-card-footer', children: footer })] }));
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export { Card as default };
|