@ioca/react 1.5.27 → 1.5.29
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/datagrid/datagrid.js +23 -71
- package/lib/es/components/datagrid/helper.js +3 -11
- 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 +1 -1
- package/lib/es/components/select/select.js +8 -2
- package/lib/es/components/tag/tag.js +2 -4
- package/lib/es/components/tree/item.js +6 -6
- package/lib/es/components/tree/tree.js +7 -5
- package/lib/es/components/tree/virtual.js +2 -2
- package/lib/es/index.js +1 -0
- package/lib/index.js +268 -110
- 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 +27 -0
- package/lib/types/components/tag/type.d.ts +0 -1
- package/lib/types/components/tree/type.d.ts +3 -2
- package/lib/types/index.d.ts +1 -0
- package/package.json +1 -1
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, 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';
|
|
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';
|
|
@@ -1006,6 +1006,224 @@ const Collapse = (props) => {
|
|
|
1006
1006
|
};
|
|
1007
1007
|
Collapse.Item = Item$5;
|
|
1008
1008
|
|
|
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
|
+
|
|
1009
1227
|
function Empty(props) {
|
|
1010
1228
|
const { className, ...restProps } = props;
|
|
1011
1229
|
return (jsx(InboxTwotone, { className: classNames("i-empty", className), ...restProps }));
|
|
@@ -1037,16 +1255,8 @@ const sumLengths = (parts) => {
|
|
|
1037
1255
|
return `calc(${out.join(" + ")})`;
|
|
1038
1256
|
};
|
|
1039
1257
|
const toCssWidth = (w) => typeof w === "number" ? `${w}px` : w;
|
|
1040
|
-
const buildGridTemplateColumns = (widths, overrideIndex, overrideWidth) =>
|
|
1041
|
-
|
|
1042
|
-
for (let i = 0; i < widths.length; i++) {
|
|
1043
|
-
const w = i === overrideIndex && overrideWidth != null
|
|
1044
|
-
? overrideWidth
|
|
1045
|
-
: widths[i];
|
|
1046
|
-
out += (i ? " " : "") + toCssWidth(w);
|
|
1047
|
-
}
|
|
1048
|
-
return out;
|
|
1049
|
-
};
|
|
1258
|
+
const buildGridTemplateColumns = (widths, overrideIndex, overrideWidth) => buildCssWidths(widths, overrideIndex, overrideWidth).join(" ");
|
|
1259
|
+
const hasArrayChanged = (a, b) => a.length !== b.length || a.some((v, i) => v !== b[i]);
|
|
1050
1260
|
const buildCssWidths = (widths, overrideIndex, overrideWidth) => {
|
|
1051
1261
|
const out = new Array(widths.length);
|
|
1052
1262
|
for (let i = 0; i < widths.length; i++) {
|
|
@@ -1519,21 +1729,8 @@ const Datagrid = (props) => {
|
|
|
1519
1729
|
});
|
|
1520
1730
|
const previewRef = useRef({ index: -1, width: -1, template: "" });
|
|
1521
1731
|
useEffect(() => {
|
|
1522
|
-
const next = columns.map((col, i) =>
|
|
1523
|
-
|
|
1524
|
-
return col.width;
|
|
1525
|
-
return state.widths[i] ?? "min-content";
|
|
1526
|
-
});
|
|
1527
|
-
let changed = next.length !== state.widths.length;
|
|
1528
|
-
if (!changed) {
|
|
1529
|
-
for (let i = 0; i < next.length; i++) {
|
|
1530
|
-
if (next[i] !== state.widths[i]) {
|
|
1531
|
-
changed = true;
|
|
1532
|
-
break;
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
if (changed)
|
|
1732
|
+
const next = columns.map((col, i) => (col.width != null ? col.width : (state.widths[i] ?? "min-content")));
|
|
1733
|
+
if (hasArrayChanged(next, state.widths))
|
|
1537
1734
|
state.widths = next;
|
|
1538
1735
|
}, [columns, state]);
|
|
1539
1736
|
const styles = useMemo(() => {
|
|
@@ -1557,8 +1754,7 @@ const Datagrid = (props) => {
|
|
|
1557
1754
|
const el = wrapRef.current;
|
|
1558
1755
|
if (!el)
|
|
1559
1756
|
return;
|
|
1560
|
-
if (previewRef.current.index === i &&
|
|
1561
|
-
previewRef.current.width === w) {
|
|
1757
|
+
if (previewRef.current.index === i && previewRef.current.width === w) {
|
|
1562
1758
|
return;
|
|
1563
1759
|
}
|
|
1564
1760
|
const template = buildGridTemplateColumns(state.widths, i, w);
|
|
@@ -1597,9 +1793,7 @@ const Datagrid = (props) => {
|
|
|
1597
1793
|
const { sortBy, sortType } = state;
|
|
1598
1794
|
if (sortBy && !onSort) {
|
|
1599
1795
|
const sorter = columns.find((col) => col.id === sortBy)?.sorter;
|
|
1600
|
-
const sortFn = typeof sorter === "function"
|
|
1601
|
-
? sorter
|
|
1602
|
-
: (a, b) => b[sortBy] - a[sortBy];
|
|
1796
|
+
const sortFn = typeof sorter === "function" ? sorter : (a, b) => b[sortBy] - a[sortBy];
|
|
1603
1797
|
const sorted = [...data].sort(sortFn);
|
|
1604
1798
|
return sortType === "desc" ? sorted : sorted.reverse();
|
|
1605
1799
|
}
|
|
@@ -1612,66 +1806,36 @@ const Datagrid = (props) => {
|
|
|
1612
1806
|
return !!rowHeight && rowHeight > 0;
|
|
1613
1807
|
}, [virtual]);
|
|
1614
1808
|
useEffect(() => {
|
|
1615
|
-
if (!resizable)
|
|
1616
|
-
return;
|
|
1617
1809
|
if (!container.current)
|
|
1618
1810
|
return;
|
|
1619
|
-
|
|
1811
|
+
const hasUnmeasured = columns.some((col, i) => col.width == null && typeof state.widths[i] !== "number");
|
|
1812
|
+
if (!hasUnmeasured)
|
|
1620
1813
|
return;
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
let tries = 0;
|
|
1624
|
-
const run = () => {
|
|
1625
|
-
rafId = null;
|
|
1626
|
-
const div = container.current;
|
|
1627
|
-
if (!div)
|
|
1628
|
-
return;
|
|
1814
|
+
const div = container.current;
|
|
1815
|
+
const rafId = requestAnimationFrame(() => {
|
|
1629
1816
|
const headerRow = div.querySelector(".i-datagrid-header.i-datagrid-row");
|
|
1630
1817
|
const bodyRow = div.querySelector(".i-datagrid-row:not(.i-datagrid-header)");
|
|
1631
1818
|
const headerCells = headerRow ? Array.from(headerRow.children) : [];
|
|
1632
1819
|
const bodyCells = bodyRow ? Array.from(bodyRow.children) : [];
|
|
1633
1820
|
const cellCount = Math.max(headerCells.length, bodyCells.length);
|
|
1634
|
-
if (cellCount < 1)
|
|
1635
|
-
tries++;
|
|
1636
|
-
if (tries < 10)
|
|
1637
|
-
rafId = requestAnimationFrame(run);
|
|
1821
|
+
if (cellCount < 1)
|
|
1638
1822
|
return;
|
|
1639
|
-
}
|
|
1640
|
-
const measured = new Array(cellCount).fill(null);
|
|
1641
|
-
for (let i = 0; i < cellCount; i++) {
|
|
1642
|
-
const hw = headerCells[i]
|
|
1643
|
-
?.offsetWidth;
|
|
1644
|
-
const bw = bodyCells[i]
|
|
1645
|
-
?.offsetWidth;
|
|
1646
|
-
const w = Math.max(hw ?? 0, bw ?? 0);
|
|
1647
|
-
measured[i] = w > 0 ? w : null;
|
|
1648
|
-
}
|
|
1649
1823
|
const next = columns.map((col, i) => {
|
|
1650
1824
|
if (col.width != null)
|
|
1651
1825
|
return col.width;
|
|
1652
|
-
const
|
|
1653
|
-
if (typeof
|
|
1654
|
-
return
|
|
1655
|
-
|
|
1826
|
+
const prev = state.widths[i];
|
|
1827
|
+
if (typeof prev === "number")
|
|
1828
|
+
return prev;
|
|
1829
|
+
const hw = headerCells[i]?.offsetWidth ?? 0;
|
|
1830
|
+
const bw = bodyCells[i]?.offsetWidth ?? 0;
|
|
1831
|
+
const w = Math.max(hw, bw);
|
|
1832
|
+
return w > 0 ? w : "min-content";
|
|
1656
1833
|
});
|
|
1657
|
-
|
|
1658
|
-
if (!changed) {
|
|
1659
|
-
for (let i = 0; i < next.length; i++) {
|
|
1660
|
-
if (next[i] !== state.widths[i]) {
|
|
1661
|
-
changed = true;
|
|
1662
|
-
break;
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
if (changed)
|
|
1834
|
+
if (hasArrayChanged(next, state.widths))
|
|
1667
1835
|
state.widths = next;
|
|
1668
|
-
};
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
if (rafId != null)
|
|
1672
|
-
cancelAnimationFrame(rafId);
|
|
1673
|
-
};
|
|
1674
|
-
}, [columns, resizable, state, useVirtual]);
|
|
1836
|
+
});
|
|
1837
|
+
return () => cancelAnimationFrame(rafId);
|
|
1838
|
+
}, [columns, state]);
|
|
1675
1839
|
useEffect(() => {
|
|
1676
1840
|
if (!loading)
|
|
1677
1841
|
return;
|
|
@@ -1693,16 +1857,14 @@ const Datagrid = (props) => {
|
|
|
1693
1857
|
}, [rowKey]);
|
|
1694
1858
|
return (jsxs("div", { ref: wrapRef, style: {
|
|
1695
1859
|
maxHeight: height,
|
|
1696
|
-
...(useVirtual
|
|
1697
|
-
? { overflowX: "visible", overflowY: "hidden" }
|
|
1698
|
-
: null),
|
|
1860
|
+
...(useVirtual ? { overflowX: "visible", overflowY: "hidden" } : null),
|
|
1699
1861
|
...mergedStyle,
|
|
1700
1862
|
}, className: classNames("i-datagrid-container", className, {
|
|
1701
1863
|
"i-datagrid-bordered": border,
|
|
1702
1864
|
"i-datagrid-striped": striped,
|
|
1703
1865
|
}), children: [useVirtual && virtual ? (jsx(VirtualDatagrid, { virtual: virtual, columns: columns, rows: rows, header: header, sortBy: state.sortBy, sortType: state.sortType, height: height, loading: loading, resizable: resizable, striped: striped, cellEllipsis: cellEllipsis, empty: empty, wrapRef: wrapRef, containerRef: container, getRowKey: getRowKey, onHeaderClick: handleHeaderClick, onWidthChange: handleWidthChange, onRowClick: onRowClick, onCellClick: onCellClick, onCellDoubleClick: onCellDoubleClick, onScroll: onScroll })) : (jsxs("div", { ref: container, className: classNames("i-datagrid", {
|
|
1704
1866
|
"i-datagrid-loading": loading,
|
|
1705
|
-
}), onWheel: onScroll, children: [header &&
|
|
1867
|
+
}), onWheel: onScroll, children: [header && jsx(Header, { columns: columns, resizable: resizable, sortType: state.sortType, sortBy: state.sortBy, cellEllipsis: cellEllipsis, onWidthChange: handleWidthChange, onHeaderClick: handleHeaderClick }), rows.map((row, i) => (jsx(Row, { row: i + (header ? 1 : 0), data: row, cellEllipsis: cellEllipsis, columns: columns, onCellClick: onCellClick, onRowClick: onRowClick, onCellDoubleClick: onCellDoubleClick }, getRowKey(row, i) ?? i))), rows.length < 1 && empty] })), loading && renderLoading()] }));
|
|
1706
1868
|
};
|
|
1707
1869
|
|
|
1708
1870
|
const Description = (props) => {
|
|
@@ -4776,18 +4938,6 @@ const Pagination = (props) => {
|
|
|
4776
4938
|
}), 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 }))] }));
|
|
4777
4939
|
};
|
|
4778
4940
|
|
|
4779
|
-
const Tag = (props) => {
|
|
4780
|
-
const { dot, dotClass, outline, round, size = "normal", hoverShowClose, className, children, onClose, onClick, ...restProps } = props;
|
|
4781
|
-
return (jsxs("span", { className: classNames("i-tag", {
|
|
4782
|
-
"i-tag-outline": outline,
|
|
4783
|
-
"i-tag-clickable": onClick,
|
|
4784
|
-
[`i-tag-${size}`]: size !== "normal",
|
|
4785
|
-
round,
|
|
4786
|
-
}, className), onClick: onClick, ...restProps, children: [dot && jsx("span", { className: classNames("i-tag-dot", dotClass) }), children, onClose && (jsx(Helpericon, { active: true, className: classNames("i-tag-close", {
|
|
4787
|
-
"i-tag-hover-close": hoverShowClose,
|
|
4788
|
-
}), onClick: onClose }))] }));
|
|
4789
|
-
};
|
|
4790
|
-
|
|
4791
4941
|
const Options = (props) => {
|
|
4792
4942
|
const { value: val, options, filter, filterPlaceholder, multiple, empty = jsx(Empty, {}), onSelect, onFilter, } = props;
|
|
4793
4943
|
return (jsxs("div", { className: classNames("i-select-options", {
|
|
@@ -4816,7 +4966,7 @@ const displayValue = (config) => {
|
|
|
4816
4966
|
if (typeof opt === "number")
|
|
4817
4967
|
return jsxs(Tag, { children: ["+", opt] }, i);
|
|
4818
4968
|
const { label, value } = opt;
|
|
4819
|
-
return (jsx(Tag, {
|
|
4969
|
+
return (jsx(Tag, { onClose: (e) => {
|
|
4820
4970
|
e?.stopPropagation();
|
|
4821
4971
|
onSelect?.(value, opt);
|
|
4822
4972
|
}, children: label }, value));
|
|
@@ -4830,14 +4980,20 @@ const Select = (props) => {
|
|
|
4830
4980
|
const [filterValue, setFilterValue] = useState("");
|
|
4831
4981
|
const [selectedValue, setSelectedValue] = useState(value);
|
|
4832
4982
|
const [active, setActive] = useState(false);
|
|
4833
|
-
const formattedOptions = useMemo(() =>
|
|
4983
|
+
const formattedOptions = useMemo(() => {
|
|
4984
|
+
return formatOption(options).map((opt) => {
|
|
4985
|
+
const label = typeof opt.label === "string" ? opt.label : String(opt.value ?? "");
|
|
4986
|
+
return { ...opt, _label: label.toLowerCase(), _value: String(opt.value ?? "").toLowerCase() };
|
|
4987
|
+
});
|
|
4988
|
+
}, [options]);
|
|
4834
4989
|
const filterOptions = useMemo(() => {
|
|
4835
4990
|
const fv = filterValue;
|
|
4836
4991
|
if (!fv || !filter)
|
|
4837
4992
|
return formattedOptions;
|
|
4993
|
+
const lowerFv = fv.toLowerCase();
|
|
4838
4994
|
const filterFn = typeof filter === "function"
|
|
4839
4995
|
? filter
|
|
4840
|
-
: (opt) => opt.
|
|
4996
|
+
: (opt) => opt._value.includes(lowerFv) || opt._label.includes(lowerFv);
|
|
4841
4997
|
return formattedOptions.filter(filterFn);
|
|
4842
4998
|
}, [formattedOptions, filter, filterValue]);
|
|
4843
4999
|
const changeValue = (v) => {
|
|
@@ -6346,21 +6502,21 @@ const Tabs = ((props) => {
|
|
|
6346
6502
|
Tabs.Item = Item;
|
|
6347
6503
|
|
|
6348
6504
|
const TreeItemHeader = (props) => {
|
|
6349
|
-
const { as: Tag = "a", href, selected, children, ...restProps } = props;
|
|
6505
|
+
const { as: Tag = "a", href, selected, children, attrs, ...restProps } = props;
|
|
6350
6506
|
const className = classNames("i-tree-item-header", {
|
|
6351
6507
|
"i-tree-item-selected": selected,
|
|
6352
6508
|
});
|
|
6353
6509
|
if (typeof Tag === "string") {
|
|
6354
|
-
return (jsx(Tag, { href: href, className: className, ...restProps, children: children }));
|
|
6510
|
+
return (jsx(Tag, { href: href, className: className, ...attrs, ...restProps, children: children }));
|
|
6355
6511
|
}
|
|
6356
|
-
return (jsx(Tag, { to: href || "", className: className, ...restProps, children: children }));
|
|
6512
|
+
return (jsx(Tag, { to: href || "", className: className, ...attrs, ...restProps, children: children }));
|
|
6357
6513
|
};
|
|
6358
6514
|
function TreeRow(props) {
|
|
6359
6515
|
const { flatNode, wrapperStyle, virtualMode, selected, checkedSet, partofs = {}, checkable, nodeProps, renderExtra, loadingKeys, onExpand, onItemClick, onItemSelect, onItemCheck, } = props;
|
|
6360
6516
|
const { node, depth, isExpanded } = flatNode;
|
|
6361
|
-
const { key = "", as, href, icon, title, disabled, type } = node;
|
|
6517
|
+
const { key = "", as, href, icon, title, disabled, type, attrs } = node;
|
|
6362
6518
|
const children = node[nodeProps.children];
|
|
6363
|
-
const hasChildren = children instanceof Promise || (Array.isArray(children) && children.length > 0);
|
|
6519
|
+
const hasChildren = children instanceof Promise || typeof children === "function" || (Array.isArray(children) && children.length > 0);
|
|
6364
6520
|
const loading = loadingKeys?.includes(key);
|
|
6365
6521
|
if (type === "title") {
|
|
6366
6522
|
return jsx("div", { style: wrapperStyle, className: "i-tree-group-title", children: title });
|
|
@@ -6368,7 +6524,7 @@ function TreeRow(props) {
|
|
|
6368
6524
|
if (type && type !== "item") {
|
|
6369
6525
|
return jsx("div", { style: wrapperStyle, className: `i-tree-type-${type}`, children: title });
|
|
6370
6526
|
}
|
|
6371
|
-
return (jsx("div", { className: !virtualMode ? classNames("i-tree-item", { "i-tree-expand": isExpanded }) : undefined, style: wrapperStyle, children: jsxs(TreeItemHeader, { as: as, href: href, style: { paddingLeft: `${depth * 1.5 + 0.5}em` }, selected: selected === key, onClick: (e) => {
|
|
6527
|
+
return (jsx("div", { className: !virtualMode ? classNames("i-tree-item", { "i-tree-expand": isExpanded }) : undefined, style: wrapperStyle, children: jsxs(TreeItemHeader, { as: as, attrs: attrs, href: href, style: { paddingLeft: `${depth * 1.5 + 0.5}em` }, selected: selected === key, onClick: (e) => {
|
|
6372
6528
|
if (disabled) {
|
|
6373
6529
|
e.preventDefault();
|
|
6374
6530
|
e.stopPropagation();
|
|
@@ -6397,7 +6553,7 @@ function TreeList(props) {
|
|
|
6397
6553
|
}
|
|
6398
6554
|
|
|
6399
6555
|
function VirtualTree(props) {
|
|
6400
|
-
const { flatNodes, onExpand, selected, checkedSet, partofs = {}, checkable, nodeProps, renderExtra, loadingKeys, height,
|
|
6556
|
+
const { flatNodes, onExpand, selected, checkedSet, partofs = {}, checkable, nodeProps, renderExtra, loadingKeys, height, virtual, className, style, onItemClick, onItemSelect, onItemCheck, } = props;
|
|
6401
6557
|
const listRef = useRef(null);
|
|
6402
6558
|
const wrapRef = useRef(null);
|
|
6403
6559
|
const ro = useResizeObserver();
|
|
@@ -6424,7 +6580,7 @@ function VirtualTree(props) {
|
|
|
6424
6580
|
return null;
|
|
6425
6581
|
return (jsx(TreeRow, { flatNode: flatNode, wrapperStyle: style, virtualMode: true, selected: p.selected, checkedSet: p.checkedSet, partofs: p.partofs, checkable: p.checkable, nodeProps: p.nodeProps, renderExtra: p.renderExtra, loadingKeys: p.loadingKeys, onExpand: p.onExpand, onItemClick: p.onItemClick, onItemSelect: p.onItemSelect, onItemCheck: p.onItemCheck }));
|
|
6426
6582
|
}, []);
|
|
6427
|
-
return (jsx("div", { ref: wrapRef, className: classNames("i-tree", className), style: { display: "block", width: "100%", height: "100%", ...style }, children: jsx(List$2, { listRef: listRef, rowCount: flatNodes.length, rowHeight:
|
|
6583
|
+
return (jsx("div", { ref: wrapRef, className: classNames("i-tree", className), style: { display: "block", width: "100%", height: "100%", ...style }, children: jsx(List$2, { listRef: listRef, rowCount: flatNodes.length, rowHeight: virtual.rowHeight, overscanCount: Math.max(3, virtual.threshold ?? 8), rowProps: {}, style: {
|
|
6428
6584
|
width: "100%",
|
|
6429
6585
|
height: listHeight,
|
|
6430
6586
|
overflow: "auto",
|
|
@@ -6454,7 +6610,7 @@ function flattenTree(nodes, expandedMap, nodeProps, depth = 0, parentItem, async
|
|
|
6454
6610
|
return result;
|
|
6455
6611
|
}
|
|
6456
6612
|
const Tree = (props) => {
|
|
6457
|
-
const { data = [], ref, selected, checked = [], disabledRelated, nodeProps, height,
|
|
6613
|
+
const { data = [], ref, selected, checked = [], disabledRelated, nodeProps, height, virtual, onItemSelect, onItemCheck, ...restProps } = props;
|
|
6458
6614
|
const [selectedKey, setSelectedKey] = useState(selected);
|
|
6459
6615
|
const [checkedKeys, setCheckedKeys] = useState(checked);
|
|
6460
6616
|
const [partofs, setPartofs] = useState({});
|
|
@@ -6486,14 +6642,16 @@ const Tree = (props) => {
|
|
|
6486
6642
|
if (!item)
|
|
6487
6643
|
return;
|
|
6488
6644
|
const rawChildren = item[oNodeProps.children];
|
|
6489
|
-
const
|
|
6645
|
+
const isLazy = typeof rawChildren === "function";
|
|
6646
|
+
const isAsync = rawChildren instanceof Promise || isLazy;
|
|
6490
6647
|
const isExpanded = !!expandedMap[key];
|
|
6491
6648
|
if (isAsync && !isExpanded) {
|
|
6492
6649
|
flushSync(() => {
|
|
6493
6650
|
setLoadingMap((prev) => ({ ...prev, [key]: true }));
|
|
6494
6651
|
setExpandedMap((prev) => ({ ...prev, [key]: true }));
|
|
6495
6652
|
});
|
|
6496
|
-
rawChildren
|
|
6653
|
+
const promise = isLazy ? rawChildren() : rawChildren;
|
|
6654
|
+
promise
|
|
6497
6655
|
.then((resolved) => {
|
|
6498
6656
|
item[oNodeProps.children] = resolved;
|
|
6499
6657
|
setAsyncChildrenMap((prev) => ({ ...prev, [key]: resolved }));
|
|
@@ -6650,8 +6808,8 @@ const Tree = (props) => {
|
|
|
6650
6808
|
},
|
|
6651
6809
|
};
|
|
6652
6810
|
});
|
|
6653
|
-
if (
|
|
6654
|
-
return (jsx(VirtualTree, { flatNodes: flatNodes, onExpand: handleExpand, height: height,
|
|
6811
|
+
if (virtual) {
|
|
6812
|
+
return (jsx(VirtualTree, { flatNodes: flatNodes, onExpand: handleExpand, height: height, virtual: virtual, selected: selectedKey, checkedSet: checkedSet, partofs: partofs, nodeProps: oNodeProps, loadingKeys: loadingKeys, onItemCheck: handleCheck, onItemSelect: handleSelect, ...restProps }));
|
|
6655
6813
|
}
|
|
6656
6814
|
return (jsx(TreeList, { flatNodes: flatNodes, onExpand: handleExpand, selected: selectedKey, checkedSet: checkedSet, partofs: partofs, nodeProps: oNodeProps, loadingKeys: loadingKeys, onItemCheck: handleCheck, onItemSelect: handleSelect, ...restProps }));
|
|
6657
6815
|
};
|
|
@@ -7024,4 +7182,4 @@ const useTheme = (props) => {
|
|
|
7024
7182
|
};
|
|
7025
7183
|
};
|
|
7026
7184
|
|
|
7027
|
-
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, Popconfirm, Popup, Progress, Radio, Resizable, River, Scroll, Select, Step, Swiper, Tabs, Tag, Text, TimePicker, Tree, Upload, Video, usePreview, useTheme };
|
|
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 };
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
renderItem?: (context: {
|
|
18
|
+
value: any;
|
|
19
|
+
index: number;
|
|
20
|
+
editing: boolean;
|
|
21
|
+
loading: boolean;
|
|
22
|
+
readonly: boolean;
|
|
23
|
+
remove: () => void;
|
|
24
|
+
}) => ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type { IPill };
|
|
@@ -8,10 +8,11 @@ interface ITreeItem {
|
|
|
8
8
|
title: string | ReactNode;
|
|
9
9
|
icon?: ReactNode;
|
|
10
10
|
href?: string;
|
|
11
|
-
children?: ITreeItem[] | Promise<ITreeItem[]
|
|
11
|
+
children?: ITreeItem[] | Promise<ITreeItem[]> | (() => Promise<ITreeItem[]>);
|
|
12
12
|
expanded?: boolean;
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
checked?: boolean;
|
|
15
|
+
attrs?: Record<string, any>;
|
|
15
16
|
parent?: ITreeItem;
|
|
16
17
|
[key: string]: any;
|
|
17
18
|
}
|
|
@@ -34,7 +35,7 @@ interface ITree {
|
|
|
34
35
|
disabledRelated?: boolean;
|
|
35
36
|
round?: boolean;
|
|
36
37
|
height?: number | string;
|
|
37
|
-
|
|
38
|
+
virtual?: TVirtual;
|
|
38
39
|
style?: CSSProperties;
|
|
39
40
|
className?: string;
|
|
40
41
|
renderExtra?: (item: ITreeItem) => ReactNode;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { default as Button } from './components/button/button.js';
|
|
|
4
4
|
export { default as Card } from './components/card/card.js';
|
|
5
5
|
export { default as Checkbox } from './components/checkbox/checkbox.js';
|
|
6
6
|
export { default as Collapse } from './components/collapse/collapse.js';
|
|
7
|
+
export { default as Pill } from './components/pill/pill.js';
|
|
7
8
|
export { default as Datagrid } from './components/datagrid/datagrid.js';
|
|
8
9
|
export { default as Description } from './components/description/description.js';
|
|
9
10
|
export { default as Drawer } from './components/drawer/drawer.js';
|