@ioca/react 1.5.29 → 1.5.31

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.
Files changed (35) hide show
  1. package/lib/css/index.css +1 -1
  2. package/lib/es/components/checkbox/checkbox.js +1 -1
  3. package/lib/es/components/divider/divider.js +12 -0
  4. package/lib/es/components/divider/index.js +5 -0
  5. package/lib/es/components/dropdown/dropdown.js +8 -12
  6. package/lib/es/components/dropdown/item.js +15 -9
  7. package/lib/es/components/form/useForm.js +3 -4
  8. package/lib/es/components/input/input.js +2 -2
  9. package/lib/es/components/input/number.js +3 -5
  10. package/lib/es/components/input/range.js +2 -2
  11. package/lib/es/components/input/textarea.js +1 -1
  12. package/lib/es/components/list/item.js +5 -1
  13. package/lib/es/components/list/list.js +11 -4
  14. package/lib/es/components/modal/hookModal.js +11 -1
  15. package/lib/es/components/modal/modal.js +24 -16
  16. package/lib/es/components/pill/pill.js +2 -2
  17. package/lib/es/components/select/options.js +5 -7
  18. package/lib/es/components/select/select.js +4 -8
  19. package/lib/es/index.js +2 -2
  20. package/lib/index.js +319 -297
  21. package/lib/types/components/divider/divider.d.ts +6 -0
  22. package/lib/types/components/divider/index.d.ts +5 -0
  23. package/lib/types/components/divider/type.d.ts +9 -0
  24. package/lib/types/components/dropdown/type.d.ts +3 -1
  25. package/lib/types/components/form/useForm.d.ts +1 -1
  26. package/lib/types/components/list/type.d.ts +1 -0
  27. package/lib/types/components/modal/type.d.ts +1 -1
  28. package/lib/types/components/pill/type.d.ts +1 -0
  29. package/lib/types/index.d.ts +2 -2
  30. package/package.json +1 -1
  31. package/lib/es/components/card/card.js +0 -12
  32. package/lib/es/components/card/index.js +0 -5
  33. package/lib/types/components/card/card.d.ts +0 -6
  34. package/lib/types/components/card/index.d.ts +0 -5
  35. 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, AddRound, 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
- "i-checkbox-options-button": type === "button",
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
  }) })] }));
@@ -1006,224 +998,6 @@ const Collapse = (props) => {
1006
998
  };
1007
999
  Collapse.Item = Item$5;
1008
1000
 
1009
- const Tag = (props) => {
1010
- const { dot, dotClass, outline, round, size = "normal", className, children, onClose, onClick, ...restProps } = props;
1011
- return (jsxs("span", { className: classNames("i-tag", {
1012
- "i-tag-outline": outline,
1013
- "i-tag-clickable": onClick,
1014
- [`i-tag-${size}`]: size !== "normal",
1015
- round,
1016
- }, 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 }))] }));
1017
- };
1018
-
1019
- const CreateTag = memo(function CreateTag(props) {
1020
- const { isEditing, isLoading, createTagProps, tagProps, onBlur, onKeyDown, onStartCreate } = props;
1021
- if (isEditing) {
1022
- 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"));
1023
- }
1024
- return (jsx(Tag, { ...createTagProps, className: classNames("i-pill", tagProps?.className, "i-pill-create"), onClick: onStartCreate, children: jsx("b", { children: "\uFF0B" }) }, "pill-create"));
1025
- });
1026
-
1027
- const TagItem = memo(function TagItem(props) {
1028
- const { item, index, isEditing, isLoading, tagProps, editable, readonly, renderItem, onClose, onClick, onBlur, onKeyDown } = props;
1029
- const isClickable = !isEditing && editable && !readonly;
1030
- if (renderItem) {
1031
- return renderItem({
1032
- value: item,
1033
- index,
1034
- editing: isEditing,
1035
- loading: isLoading,
1036
- readonly: !!readonly,
1037
- remove: () => onClose(index),
1038
- });
1039
- }
1040
- 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" })] }));
1041
- });
1042
-
1043
- function Pill(props) {
1044
- const { value = [], tagProps, max, icon = jsx(AddRound, {}), className, label, labelInline, readonly, editable, onChange, onUpdate, validator, format, renderItem, ...restProps } = props;
1045
- const [editingIndex, setEditingIndex] = useState(null);
1046
- const [loadingSet, setLoadingSet] = useState(new Set());
1047
- const instRef = useRef({
1048
- props,
1049
- editingIndex,
1050
- loadingSet,
1051
- setEditingIndex,
1052
- setLoadingSet,
1053
- });
1054
- instRef.current.props = props;
1055
- instRef.current.editingIndex = editingIndex;
1056
- instRef.current.loadingSet = loadingSet;
1057
- instRef.current.setEditingIndex = setEditingIndex;
1058
- instRef.current.setLoadingSet = setLoadingSet;
1059
- useEffect(() => {
1060
- if (editingIndex !== null) {
1061
- const el = document.querySelector(".i-pill-editing");
1062
- el?.focus();
1063
- }
1064
- }, [editingIndex]);
1065
- const cleanTagProps = useMemo(() => {
1066
- if (!tagProps)
1067
- return {};
1068
- const { onClose, dot, dotClass, ...rest } = tagProps;
1069
- return rest;
1070
- }, [tagProps]);
1071
- const handleClose = useCallback((index) => {
1072
- const inst = instRef.current;
1073
- if (inst.props.readonly)
1074
- return;
1075
- const hasAsync = !!inst.props.onUpdate;
1076
- if (hasAsync)
1077
- inst.setLoadingSet((prev) => new Set(prev).add(index));
1078
- setTimeout(async () => {
1079
- try {
1080
- const { props } = instRef.current;
1081
- const values = props.value ?? [];
1082
- const item = values[index];
1083
- if (item === undefined)
1084
- return;
1085
- const result = props.onUpdate?.(undefined, item, "delete");
1086
- if (result instanceof Promise) {
1087
- const ok = await result;
1088
- if (ok === false)
1089
- return;
1090
- }
1091
- const next = [...values];
1092
- next.splice(index, 1);
1093
- props.onChange?.(next);
1094
- }
1095
- finally {
1096
- if (hasAsync) {
1097
- instRef.current.setLoadingSet((prev) => {
1098
- const s = new Set(prev);
1099
- s.delete(index);
1100
- return s;
1101
- });
1102
- }
1103
- }
1104
- }, 0);
1105
- }, []);
1106
- const handleItemClick = useCallback((e, index) => {
1107
- if (e.target.closest(".i-helpericon"))
1108
- return;
1109
- const inst = instRef.current;
1110
- if (inst.props.readonly)
1111
- return;
1112
- if (index === -1 && inst.props.max !== undefined && (inst.props.value?.length ?? 0) >= inst.props.max)
1113
- return;
1114
- inst.setEditingIndex(index);
1115
- }, []);
1116
- const commitEdit = useCallback((index, text) => {
1117
- const inst = instRef.current;
1118
- const formatted = inst.props.format ? inst.props.format(text) : text;
1119
- const hasAsync = !!(inst.props.validator || inst.props.onUpdate);
1120
- if (hasAsync)
1121
- inst.setLoadingSet((prev) => new Set(prev).add(index));
1122
- setTimeout(async () => {
1123
- try {
1124
- const { props } = instRef.current;
1125
- if (props.validator) {
1126
- const valid = await Promise.resolve(props.validator(formatted));
1127
- if (!valid)
1128
- return;
1129
- }
1130
- const values = props.value ?? [];
1131
- if (index === -1) {
1132
- if (values.includes(formatted))
1133
- return;
1134
- const result = props.onUpdate?.(formatted, undefined, "create");
1135
- if (result instanceof Promise) {
1136
- const ok = await result;
1137
- if (ok === false)
1138
- return;
1139
- }
1140
- props.onChange?.([...values, formatted]);
1141
- }
1142
- else {
1143
- const oldValue = values[index];
1144
- if (oldValue === formatted)
1145
- return;
1146
- const result = props.onUpdate?.(formatted, oldValue, "update");
1147
- if (result instanceof Promise) {
1148
- const ok = await result;
1149
- if (ok === false)
1150
- return;
1151
- }
1152
- const next = [...values];
1153
- next[index] = formatted;
1154
- props.onChange?.(next);
1155
- }
1156
- }
1157
- finally {
1158
- if (hasAsync) {
1159
- instRef.current.setLoadingSet((prev) => {
1160
- const s = new Set(prev);
1161
- s.delete(index);
1162
- return s;
1163
- });
1164
- }
1165
- instRef.current.setEditingIndex(null);
1166
- }
1167
- }, 0);
1168
- }, []);
1169
- const handleBlur = useCallback((index) => {
1170
- const inst = instRef.current;
1171
- if (inst.loadingSet.has(index))
1172
- return;
1173
- const el = document.querySelector(".i-pill-editing");
1174
- const text = el?.textContent?.trim();
1175
- if (!text) {
1176
- if (index !== -1) {
1177
- handleClose(index);
1178
- }
1179
- else {
1180
- inst.setEditingIndex(null);
1181
- }
1182
- return;
1183
- }
1184
- commitEdit(index, text);
1185
- }, []);
1186
- const handleKeyDown = useCallback((e, index) => {
1187
- const inst = instRef.current;
1188
- if (inst.loadingSet.has(index))
1189
- return;
1190
- if (e.key === "Enter") {
1191
- e.preventDefault();
1192
- const text = e.currentTarget.textContent?.trim();
1193
- if (!text) {
1194
- if (index !== -1) {
1195
- handleClose(index);
1196
- }
1197
- else {
1198
- inst.setEditingIndex(null);
1199
- }
1200
- return;
1201
- }
1202
- commitEdit(index, text);
1203
- }
1204
- else if (e.key === "Escape") {
1205
- e.preventDefault();
1206
- if (index !== -1) {
1207
- const original = inst.props.value?.[index];
1208
- if (original !== undefined) {
1209
- e.currentTarget.textContent = original;
1210
- }
1211
- }
1212
- inst.setEditingIndex(null);
1213
- }
1214
- }, []);
1215
- const handleStartCreate = useCallback(() => {
1216
- const inst = instRef.current;
1217
- if (inst.props.readonly)
1218
- return;
1219
- if (inst.props.max !== undefined && (inst.props.value?.length ?? 0) >= inst.props.max)
1220
- return;
1221
- inst.setEditingIndex(-1);
1222
- }, []);
1223
- const canCreate = !readonly && (max === undefined || value.length < max);
1224
- 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 })] })] }));
1225
- }
1226
-
1227
1001
  function Empty(props) {
1228
1002
  const { className, ...restProps } = props;
1229
1003
  return (jsx(InboxTwotone, { className: classNames("i-empty", className), ...restProps }));
@@ -1951,20 +1725,31 @@ function Drawer(props) {
1951
1725
 
1952
1726
  const Item$4 = (props) => {
1953
1727
  const { ref, active, type, align, disabled, label, style, border, className, children, ...restProps } = props;
1728
+ const handlers = {};
1729
+ if (disabled) {
1730
+ handlers.onClick = (e) => { e.preventDefault(); e.stopPropagation(); };
1731
+ }
1954
1732
  return (jsxs("li", { ref: ref, className: classNames("i-list-item", className, {
1955
1733
  "i-list-item-active": active,
1956
1734
  "i-list-option": type === "option",
1957
1735
  "i-list-item-bordered": border,
1958
1736
  disabled,
1959
- }), style: { alignItems: align, ...style }, ...restProps, children: [label !== undefined && (jsx("span", { className: 'i-list-item-label', children: label })), children] }));
1737
+ }), style: { alignItems: align, ...style }, "aria-disabled": disabled || undefined, tabIndex: disabled ? -1 : undefined, ...restProps, ...handlers, children: [label !== undefined && (jsx("span", { className: 'i-list-item-label', children: label })), children] }));
1960
1738
  };
1961
1739
 
1962
1740
  const List$1 = forwardRef((props, ref) => {
1963
- const { label, type, border, className, children, ...restProps } = props;
1964
- return (jsx("ul", { ref: ref, className: classNames("i-list", className), ...restProps, children: Children.map(children, (node, i) => {
1741
+ const { label, type, border, padding, className, style, children, ...restProps } = props;
1742
+ return (jsx("ul", { ref: ref, className: classNames("i-list", className, {
1743
+ "i-list-option-type": type === "option",
1744
+ }), style: {
1745
+ ...(padding !== undefined
1746
+ ? { "--option-gap": typeof padding === "number" ? `${padding}px` : padding }
1747
+ : undefined),
1748
+ ...style,
1749
+ }, ...restProps, children: Children.map(children, (node, i) => {
1965
1750
  const renderLabel = typeof label === "function" ? label(i) : label;
1966
- const { type, props: nodeProps } = node;
1967
- if (type === Item$4) {
1751
+ const { type: elementType, props: nodeProps } = node;
1752
+ if (elementType === Item$4) {
1968
1753
  return cloneElement(node, {
1969
1754
  label: renderLabel,
1970
1755
  ...nodeProps,
@@ -2514,50 +2299,52 @@ function Popup(props) {
2514
2299
 
2515
2300
  const { Item: ListItem } = List$1;
2516
2301
  const Item$3 = (props) => {
2517
- const { more, moreProps, onClick, ref: itemRef, children, ...restProps } = props;
2518
- const close = useContext(DropdownCloseCtx);
2302
+ const { more, moreProps, onClick, ref: itemRef, type = "option", children, ...restProps } = props;
2303
+ const close = useContext(DropdownContext);
2519
2304
  const liRef = useRef(null);
2520
2305
  const [position, setPosition] = useState("right");
2521
2306
  const { position: morePosition, onVisibleChange: moreOnVisibleChange, width: moreWidth, ...restMoreProps } = moreProps ?? {};
2522
2307
  const effectivePosition = morePosition ?? position;
2308
+ const rafRef = useRef(0);
2523
2309
  const handleVisibleChange = (v) => {
2524
2310
  if (v && liRef.current) {
2525
- const rect = liRef.current.getBoundingClientRect();
2526
- setPosition(rect.left > window.innerWidth / 2 ? "left" : "right");
2311
+ cancelAnimationFrame(rafRef.current);
2312
+ rafRef.current = requestAnimationFrame(() => {
2313
+ if (!liRef.current)
2314
+ return;
2315
+ const rect = liRef.current.getBoundingClientRect();
2316
+ setPosition(rect.left > window.innerWidth / 2 ? "left" : "right");
2317
+ });
2527
2318
  }
2528
2319
  moreOnVisibleChange?.(v);
2529
2320
  };
2530
- const Li = (jsx(ListItem, { ref: itemRef ?? liRef, onClick: (e) => {
2321
+ const Li = useMemo(() => (jsx(ListItem, { ref: itemRef ?? liRef, role: "menuitem", "aria-haspopup": more ? "menu" : undefined, onClick: (e) => {
2531
2322
  e.stopPropagation();
2532
2323
  if (!more)
2533
2324
  close?.();
2534
2325
  onClick?.(e);
2535
- }, ...restProps, children: children }));
2326
+ }, type: type, ...restProps, children: children })), [itemRef, liRef, more, close, onClick, type, restProps, children]);
2536
2327
  if (!more)
2537
2328
  return Li;
2538
- return (jsx(Popup, { ...restMoreProps, position: effectivePosition, touchable: true, arrow: false, align: "start", offset: 11, hideDelay: 240, onVisibleChange: handleVisibleChange, content: jsx(List$1, { className: "i-dropdown-content", style: { minWidth: moreWidth }, onClick: (e) => e.stopPropagation(), children: more }), children: Li }));
2329
+ return (jsx(Popup, { ...restMoreProps, position: effectivePosition, touchable: true, arrow: false, align: "start", offset: 8, hideDelay: 240, onVisibleChange: handleVisibleChange, content: jsx(List$1, { type: "option", padding: 4, className: "i-dropdown-content", style: { minWidth: moreWidth }, onClick: (e) => e.stopPropagation(), children: more }), children: Li }));
2539
2330
  };
2540
2331
 
2541
- const DropdownCloseCtx = createContext(null);
2332
+ const DropdownContext = createContext(null);
2542
2333
  const Dropdown = (props) => {
2543
2334
  const { visible, width, content, children, ...restProps } = props;
2544
2335
  const [active, setActive] = useState(visible);
2545
2336
  if (!content) {
2546
2337
  return children;
2547
2338
  }
2548
- const close = () => setActive(false);
2549
- const handleVisibleChange = (v) => {
2339
+ const close = useCallback(() => setActive(false), []);
2340
+ const handleVisibleChange = useCallback((v) => {
2550
2341
  setActive(v);
2551
- if (props.onVisibleChange) {
2552
- props.onVisibleChange(v);
2553
- }
2554
- };
2342
+ props.onVisibleChange?.(v);
2343
+ }, [props.onVisibleChange]);
2555
2344
  useEffect(() => {
2556
2345
  setActive(visible);
2557
2346
  }, [visible]);
2558
- return (jsx(Popup, { trigger: 'click', position: 'bottom', content: jsx(DropdownCloseCtx.Provider, { value: close, children: jsx(List$1, { className: 'i-dropdown-content', style: { minWidth: width }, children: typeof content === "function"
2559
- ? content(close)
2560
- : content }) }), ...restProps, touchable: true, visible: active, onVisibleChange: handleVisibleChange, children: children }));
2347
+ return (jsx(Popup, { trigger: "click", position: "bottom", content: jsx(DropdownContext.Provider, { value: close, children: jsx(List$1, { type: "option", className: "i-dropdown-content", style: { minWidth: width }, role: "menu", children: typeof content === "function" ? content(close) : content }) }), ...restProps, touchable: true, visible: active, onVisibleChange: handleVisibleChange, children: children }));
2561
2348
  };
2562
2349
  Dropdown.Item = Item$3;
2563
2350
 
@@ -3302,8 +3089,7 @@ class IFormInstance {
3302
3089
  for (let i = 1; i < parts.length; i++) {
3303
3090
  const ancestor = parts.slice(0, i).join(".");
3304
3091
  if (ancestor in this.data) {
3305
- console.warn(`[ioca-form] Field "${field}" conflicts with "${ancestor}". ` +
3306
- "Nested representation in form.get() may be inconsistent.");
3092
+ console.warn(`[ioca-form] Field "${field}" conflicts with "${ancestor}". ` + "Nested representation in form.get() may be inconsistent.");
3307
3093
  }
3308
3094
  }
3309
3095
  setDeep(this.data, field, value);
@@ -3355,7 +3141,7 @@ class IFormInstance {
3355
3141
  if (!field && o === undefined)
3356
3142
  return;
3357
3143
  const rule = {
3358
- validator: (v) => Array.isArray(v) ? v.length > 0 : ![undefined, null, ""].includes(v),
3144
+ validator: (v) => (Array.isArray(v) ? v.length > 0 : ![undefined, null, ""].includes(v)),
3359
3145
  };
3360
3146
  if (typeof o === "function")
3361
3147
  rule.validator = o;
@@ -3380,7 +3166,7 @@ class IFormInstance {
3380
3166
  });
3381
3167
  }
3382
3168
  });
3383
- return field ? isAllValid : isAllValid ? data : false;
3169
+ return isAllValid ? data : false;
3384
3170
  }
3385
3171
  }
3386
3172
  function useForm(form) {
@@ -3647,25 +3433,33 @@ function Modal(props) {
3647
3433
  if (!toggable.current)
3648
3434
  return;
3649
3435
  toggable.current = false;
3650
- if (!closable) {
3651
- setBounced(true);
3436
+ const canClose = typeof closable === 'function' ? closable() : closable;
3437
+ const exec = (result) => {
3438
+ if (!result) {
3439
+ setBounced(true);
3440
+ const timer = setTimeout(() => {
3441
+ setBounced(false);
3442
+ toggable.current = true;
3443
+ }, 400);
3444
+ return () => clearTimeout(timer);
3445
+ }
3446
+ setActive(false);
3447
+ updateVisible(mid, false);
3652
3448
  const timer = setTimeout(() => {
3653
- setBounced(false);
3449
+ if (!keepDOM)
3450
+ setShow(false);
3654
3451
  toggable.current = true;
3655
- }, 400);
3452
+ onVisibleChange?.(false);
3453
+ onClose?.();
3454
+ }, 240);
3656
3455
  return () => clearTimeout(timer);
3456
+ };
3457
+ if (canClose instanceof Promise) {
3458
+ canClose.then(exec);
3459
+ return;
3657
3460
  }
3658
- setActive(false);
3659
- updateVisible(mid, false);
3660
- const timer = setTimeout(() => {
3661
- if (!keepDOM)
3662
- setShow(false);
3663
- toggable.current = true;
3664
- onVisibleChange?.(false);
3665
- onClose?.();
3666
- }, 240);
3667
- return () => clearTimeout(timer);
3668
- }, [closable, keepDOM, onClose, onVisibleChange]);
3461
+ return exec(canClose);
3462
+ }, [closable, keepDOM, mid, onClose, onVisibleChange]);
3669
3463
  const handleBackdropClick = () => {
3670
3464
  backdropClosable && handleHide();
3671
3465
  };
@@ -3725,7 +3519,7 @@ function Modal(props) {
3725
3519
  e.stopPropagation();
3726
3520
  handleClick();
3727
3521
  onClick?.(e);
3728
- }, 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());
3522
+ }, 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());
3729
3523
  }
3730
3524
  Modal.useModal = useModal;
3731
3525
 
@@ -3739,7 +3533,17 @@ const HookModal = (props) => {
3739
3533
  },
3740
3534
  close: () => {
3741
3535
  state.visible = false;
3742
- if (mergedProps.closable ?? true)
3536
+ const canClose = typeof mergedProps.closable === 'function'
3537
+ ? mergedProps.closable()
3538
+ : (mergedProps.closable ?? true);
3539
+ if (canClose instanceof Promise) {
3540
+ canClose.then((result) => {
3541
+ if (!result)
3542
+ state.visible = true;
3543
+ });
3544
+ return;
3545
+ }
3546
+ if (canClose)
3743
3547
  return;
3744
3548
  Promise.resolve().then(() => {
3745
3549
  state.visible = true;
@@ -4385,7 +4189,7 @@ function InputContainer(props) {
4385
4189
  }
4386
4190
 
4387
4191
  const Number$1 = (props) => {
4388
- 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;
4192
+ 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;
4389
4193
  const [inputValue, setInputValue] = useState(value === undefined || value === null ? "" : String(value));
4390
4194
  const formatOut = (num) => {
4391
4195
  const v = clamp(num, min, max);
@@ -4398,9 +4202,7 @@ const Number$1 = (props) => {
4398
4202
  const body = negative ? s.slice(1) : s;
4399
4203
  const [integer, decimal] = body.split(".");
4400
4204
  const withThousand = integer.replace(/\B(?=(\d{3})+(?!\d))/g, thousand);
4401
- return decimal
4402
- ? `${negative ? "-" : ""}${withThousand}.${decimal}`
4403
- : `${negative ? "-" : ""}${withThousand}`;
4205
+ return decimal ? `${negative ? "-" : ""}${withThousand}.${decimal}` : `${negative ? "-" : ""}${withThousand}`;
4404
4206
  };
4405
4207
  const sanitizeNumberInput = (raw) => {
4406
4208
  const hasMinus = raw.startsWith("-");
@@ -4481,11 +4283,11 @@ const Number$1 = (props) => {
4481
4283
  [`i-input-${status}`]: status !== "normal",
4482
4284
  "i-input-borderless": !border,
4483
4285
  "i-input-underline": underline,
4484
- }), 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 })] }) }));
4286
+ }), 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 })] }) }));
4485
4287
  };
4486
4288
 
4487
4289
  const Range = (props) => {
4488
- 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;
4290
+ 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;
4489
4291
  const [rangeValue, setRangeValue] = useState(value);
4490
4292
  const getRangeNumber = (v) => clamp(v, min, max);
4491
4293
  const getFormatNumber = (v) => formatNumber(v, { precision, thousand });
@@ -4545,11 +4347,11 @@ const Range = (props) => {
4545
4347
  [`i-input-${status}`]: status !== "normal",
4546
4348
  "i-input-borderless": !border,
4547
4349
  "i-input-underline": underline,
4548
- }), 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 })] }) }));
4350
+ }), 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 })] }) }));
4549
4351
  };
4550
4352
 
4551
4353
  const Textarea = (props) => {
4552
- const { ref, label, name, value = "", labelInline, className, status = "normal", message, tip, autoSize, border, width, style, resize, onChange, onEnter, ...restProps } = props;
4354
+ const { ref, label, name, value = "", labelInline, className, status = "normal", message, tip, autoSize, border = true, width, style, resize, onChange, onEnter, ...restProps } = props;
4553
4355
  const [textareaValue, setTextareaValue] = useState(value);
4554
4356
  const refTextarea = useRef(null);
4555
4357
  const syncTextareaHeight = () => {
@@ -4598,7 +4400,7 @@ const Textarea = (props) => {
4598
4400
  };
4599
4401
 
4600
4402
  const Input = ((props) => {
4601
- 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;
4403
+ 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;
4602
4404
  const [inputValue, setInputValue] = useState(value);
4603
4405
  const [inputType, setInputType] = useState(type);
4604
4406
  const [visible, setVisible] = useState(false);
@@ -4656,12 +4458,20 @@ const Input = ((props) => {
4656
4458
  [`i-input-${status}`]: status !== "normal",
4657
4459
  "i-input-borderless": !border,
4658
4460
  "i-input-underline": underline,
4659
- }), 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 })] }) }));
4461
+ }), 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 })] }) }));
4660
4462
  });
4661
4463
  Input.Textarea = Textarea;
4662
4464
  Input.Number = Number$1;
4663
4465
  Input.Range = Range;
4664
4466
 
4467
+ const Divider$1 = ({ className, children, color, width, style, ...restProps }) => {
4468
+ return (jsx("li", { role: "separator", className: classNames("i-divider", className), style: {
4469
+ ...(color !== undefined ? { "--divider-color": color } : undefined),
4470
+ ...(width !== undefined ? { "--divider-width": typeof width === "number" ? `${width}px` : width } : undefined),
4471
+ ...style,
4472
+ }, ...restProps, children: children && jsx("div", { className: "i-divider-content", children: children }) }));
4473
+ };
4474
+
4665
4475
  const AlignMap = {
4666
4476
  left: "flex-start",
4667
4477
  center: "center",
@@ -4938,20 +4748,28 @@ const Pagination = (props) => {
4938
4748
  }), end < totalPage - 1 && renderEllipsis(), end < totalPage && (jsx(Page, { page: totalPage, onChange: handlePageChange, children: renderPage(totalPage) })), next && (jsx(Page, { page: page + 1, disabled: page === totalPage, onChange: handlePageChange, children: next }))] }));
4939
4749
  };
4940
4750
 
4751
+ const Tag = (props) => {
4752
+ const { dot, dotClass, outline, round, size = "normal", className, children, onClose, onClick, ...restProps } = props;
4753
+ return (jsxs("span", { className: classNames("i-tag", {
4754
+ "i-tag-outline": outline,
4755
+ "i-tag-clickable": onClick,
4756
+ [`i-tag-${size}`]: size !== "normal",
4757
+ round,
4758
+ }, 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 }))] }));
4759
+ };
4760
+
4941
4761
  const Options = (props) => {
4942
- const { value: val, options, filter, filterPlaceholder, multiple, empty = jsx(Empty, {}), onSelect, onFilter, } = props;
4762
+ const { value: val, options, filter, filterPlaceholder, multiple, empty = jsx(Empty, {}), onSelect, onFilter } = props;
4943
4763
  return (jsxs("div", { className: classNames("i-select-options", {
4944
4764
  "i-select-options-multiple": multiple,
4945
- }), 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) => {
4765
+ }), 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) => {
4946
4766
  const { label, value, disabled } = option;
4947
- const isActive = multiple
4948
- ? val?.includes(value)
4949
- : val === value;
4950
- 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));
4767
+ const isActive = multiple ? val?.includes(value) : val === value;
4768
+ 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));
4951
4769
  })] }));
4952
4770
  };
4953
4771
  const activeOptions = (options = [], value = [], max = 3) => {
4954
- const total = options.flatMap((opt) => value.includes(opt.value) ? [opt] : []);
4772
+ const total = options.flatMap((opt) => (value.includes(opt.value) ? [opt] : []));
4955
4773
  if (max >= total.length)
4956
4774
  return total;
4957
4775
  const rest = total.length - max;
@@ -4976,7 +4794,7 @@ const displayValue = (config) => {
4976
4794
  };
4977
4795
 
4978
4796
  const Select = (props) => {
4979
- 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;
4797
+ 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;
4980
4798
  const [filterValue, setFilterValue] = useState("");
4981
4799
  const [selectedValue, setSelectedValue] = useState(value);
4982
4800
  const [active, setActive] = useState(false);
@@ -4991,9 +4809,7 @@ const Select = (props) => {
4991
4809
  if (!fv || !filter)
4992
4810
  return formattedOptions;
4993
4811
  const lowerFv = fv.toLowerCase();
4994
- const filterFn = typeof filter === "function"
4995
- ? filter
4996
- : (opt) => opt._value.includes(lowerFv) || opt._label.includes(lowerFv);
4812
+ const filterFn = typeof filter === "function" ? filter : (opt) => opt._value.includes(lowerFv) || opt._label.includes(lowerFv);
4997
4813
  return formattedOptions.filter(filterFn);
4998
4814
  }, [formattedOptions, filter, filterValue]);
4999
4815
  const changeValue = (v) => {
@@ -5042,9 +4858,7 @@ const Select = (props) => {
5042
4858
  useEffect(() => {
5043
4859
  setSelectedValue(value);
5044
4860
  }, [value]);
5045
- const hasValue = multiple
5046
- ? selectedValue.length > 0
5047
- : !!selectedValue;
4861
+ const hasValue = multiple ? selectedValue.length > 0 : !!selectedValue;
5048
4862
  const clearable = !hideClear && active && hasValue;
5049
4863
  const text = message ?? tip;
5050
4864
  return (jsxs("label", { className: classNames("i-input-label", className, {
@@ -5061,7 +4875,7 @@ const Select = (props) => {
5061
4875
  multiple,
5062
4876
  maxDisplay,
5063
4877
  onSelect: handleSelect,
5064
- }) })) : (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 })] }));
4878
+ }) })) : (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 })] }));
5065
4879
  };
5066
4880
 
5067
4881
  const ColorMethods = {
@@ -5531,6 +5345,214 @@ const DateRange = (props) => {
5531
5345
  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 }) }));
5532
5346
  };
5533
5347
 
5348
+ const CreateTag = memo(function CreateTag(props) {
5349
+ const { isEditing, isLoading, createTagProps, tagProps, onBlur, onKeyDown, onStartCreate } = props;
5350
+ if (isEditing) {
5351
+ 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"));
5352
+ }
5353
+ return (jsx(Tag, { ...createTagProps, className: classNames("i-pill", tagProps?.className, "i-pill-create"), onClick: onStartCreate, children: jsx("b", { children: "\uFF0B" }) }, "pill-create"));
5354
+ });
5355
+
5356
+ const TagItem = memo(function TagItem(props) {
5357
+ const { item, index, isEditing, isLoading, tagProps, editable, readonly, renderItem, onClose, onClick, onBlur, onKeyDown } = props;
5358
+ const isClickable = !isEditing && editable && !readonly;
5359
+ if (renderItem) {
5360
+ return renderItem({
5361
+ value: item,
5362
+ index,
5363
+ editing: isEditing,
5364
+ loading: isLoading,
5365
+ readonly: !!readonly,
5366
+ remove: () => onClose(index),
5367
+ });
5368
+ }
5369
+ 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" })] }));
5370
+ });
5371
+
5372
+ function Pill(props) {
5373
+ const { value = [], tagProps, max, icon = jsx(AddRound, {}), className, label, labelInline, readonly, editable, onChange, onUpdate, validator, format, renderItem, hideCreate, ...restProps } = props;
5374
+ const [editingIndex, setEditingIndex] = useState(null);
5375
+ const [loadingSet, setLoadingSet] = useState(new Set());
5376
+ const instRef = useRef({
5377
+ props,
5378
+ editingIndex,
5379
+ loadingSet,
5380
+ setEditingIndex,
5381
+ setLoadingSet,
5382
+ });
5383
+ instRef.current.props = props;
5384
+ instRef.current.editingIndex = editingIndex;
5385
+ instRef.current.loadingSet = loadingSet;
5386
+ instRef.current.setEditingIndex = setEditingIndex;
5387
+ instRef.current.setLoadingSet = setLoadingSet;
5388
+ useEffect(() => {
5389
+ if (editingIndex !== null) {
5390
+ const el = document.querySelector(".i-pill-editing");
5391
+ el?.focus();
5392
+ }
5393
+ }, [editingIndex]);
5394
+ const cleanTagProps = useMemo(() => {
5395
+ if (!tagProps)
5396
+ return {};
5397
+ const { onClose, dot, dotClass, ...rest } = tagProps;
5398
+ return rest;
5399
+ }, [tagProps]);
5400
+ const handleClose = useCallback((index) => {
5401
+ const inst = instRef.current;
5402
+ if (inst.props.readonly)
5403
+ return;
5404
+ const hasAsync = !!inst.props.onUpdate;
5405
+ if (hasAsync)
5406
+ inst.setLoadingSet((prev) => new Set(prev).add(index));
5407
+ setTimeout(async () => {
5408
+ try {
5409
+ const { props } = instRef.current;
5410
+ const values = props.value ?? [];
5411
+ const item = values[index];
5412
+ if (item === undefined)
5413
+ return;
5414
+ const result = props.onUpdate?.(undefined, item, "delete");
5415
+ if (result instanceof Promise) {
5416
+ const ok = await result;
5417
+ if (ok === false)
5418
+ return;
5419
+ }
5420
+ const next = [...values];
5421
+ next.splice(index, 1);
5422
+ props.onChange?.(next);
5423
+ }
5424
+ finally {
5425
+ if (hasAsync) {
5426
+ instRef.current.setLoadingSet((prev) => {
5427
+ const s = new Set(prev);
5428
+ s.delete(index);
5429
+ return s;
5430
+ });
5431
+ }
5432
+ }
5433
+ }, 0);
5434
+ }, []);
5435
+ const handleItemClick = useCallback((e, index) => {
5436
+ if (e.target.closest(".i-helpericon"))
5437
+ return;
5438
+ const inst = instRef.current;
5439
+ if (inst.props.readonly)
5440
+ return;
5441
+ if (index === -1 && inst.props.max !== undefined && (inst.props.value?.length ?? 0) >= inst.props.max)
5442
+ return;
5443
+ inst.setEditingIndex(index);
5444
+ }, []);
5445
+ const commitEdit = useCallback((index, text) => {
5446
+ const inst = instRef.current;
5447
+ const formatted = inst.props.format ? inst.props.format(text) : text;
5448
+ const hasAsync = !!(inst.props.validator || inst.props.onUpdate);
5449
+ if (hasAsync)
5450
+ inst.setLoadingSet((prev) => new Set(prev).add(index));
5451
+ setTimeout(async () => {
5452
+ try {
5453
+ const { props } = instRef.current;
5454
+ if (props.validator) {
5455
+ const valid = await Promise.resolve(props.validator(formatted));
5456
+ if (!valid)
5457
+ return;
5458
+ }
5459
+ const values = props.value ?? [];
5460
+ if (index === -1) {
5461
+ if (values.includes(formatted))
5462
+ return;
5463
+ const result = props.onUpdate?.(formatted, undefined, "create");
5464
+ if (result instanceof Promise) {
5465
+ const ok = await result;
5466
+ if (ok === false)
5467
+ return;
5468
+ }
5469
+ props.onChange?.([...values, formatted]);
5470
+ }
5471
+ else {
5472
+ const oldValue = values[index];
5473
+ if (oldValue === formatted)
5474
+ return;
5475
+ const result = props.onUpdate?.(formatted, oldValue, "update");
5476
+ if (result instanceof Promise) {
5477
+ const ok = await result;
5478
+ if (ok === false)
5479
+ return;
5480
+ }
5481
+ const next = [...values];
5482
+ next[index] = formatted;
5483
+ props.onChange?.(next);
5484
+ }
5485
+ }
5486
+ finally {
5487
+ if (hasAsync) {
5488
+ instRef.current.setLoadingSet((prev) => {
5489
+ const s = new Set(prev);
5490
+ s.delete(index);
5491
+ return s;
5492
+ });
5493
+ }
5494
+ instRef.current.setEditingIndex(null);
5495
+ }
5496
+ }, 0);
5497
+ }, []);
5498
+ const handleBlur = useCallback((index) => {
5499
+ const inst = instRef.current;
5500
+ if (inst.loadingSet.has(index))
5501
+ return;
5502
+ const el = document.querySelector(".i-pill-editing");
5503
+ const text = el?.textContent?.trim();
5504
+ if (!text) {
5505
+ if (index !== -1) {
5506
+ handleClose(index);
5507
+ }
5508
+ else {
5509
+ inst.setEditingIndex(null);
5510
+ }
5511
+ return;
5512
+ }
5513
+ commitEdit(index, text);
5514
+ }, []);
5515
+ const handleKeyDown = useCallback((e, index) => {
5516
+ const inst = instRef.current;
5517
+ if (inst.loadingSet.has(index))
5518
+ return;
5519
+ if (e.key === "Enter") {
5520
+ e.preventDefault();
5521
+ const text = e.currentTarget.textContent?.trim();
5522
+ if (!text) {
5523
+ if (index !== -1) {
5524
+ handleClose(index);
5525
+ }
5526
+ else {
5527
+ inst.setEditingIndex(null);
5528
+ }
5529
+ return;
5530
+ }
5531
+ commitEdit(index, text);
5532
+ }
5533
+ else if (e.key === "Escape") {
5534
+ e.preventDefault();
5535
+ if (index !== -1) {
5536
+ const original = inst.props.value?.[index];
5537
+ if (original !== undefined) {
5538
+ e.currentTarget.textContent = original;
5539
+ }
5540
+ }
5541
+ inst.setEditingIndex(null);
5542
+ }
5543
+ }, []);
5544
+ const handleStartCreate = useCallback(() => {
5545
+ const inst = instRef.current;
5546
+ if (inst.props.readonly)
5547
+ return;
5548
+ if (inst.props.max !== undefined && (inst.props.value?.length ?? 0) >= inst.props.max)
5549
+ return;
5550
+ inst.setEditingIndex(-1);
5551
+ }, []);
5552
+ const canCreate = !hideCreate && !readonly && (max === undefined || value.length < max);
5553
+ 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 })] })] }));
5554
+ }
5555
+
5534
5556
  const defaultOk = {
5535
5557
  children: "确定",
5536
5558
  };
@@ -7182,4 +7204,4 @@ const useTheme = (props) => {
7182
7204
  };
7183
7205
  };
7184
7206
 
7185
- export { Affix, Badge, Button, Card, 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 };
7207
+ export { Affix, Badge, Button, Checkbox, Collapse, ColorPicker, Datagrid, Datepicker as DatePicker, DateRange, Description, Divider$1 as Divider, 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 };