@ioca/react 1.0.4 → 1.0.6

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/index.js CHANGED
@@ -1,3537 +1 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import classNames from 'classnames';
3
- import { debounce, uid, throttle, pick, title } from 'radash';
4
- import { forwardRef, createElement, useEffect, useCallback, isValidElement, cloneElement, useState, useMemo, Children, useRef, useLayoutEffect, Fragment as Fragment$1, createContext, useContext, useImperativeHandle } from 'react';
5
- import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, VisibilityRound, VisibilityOffRound, SyncAltRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, CalendarMonthTwotone, PauseRound, PlayArrowRound, StopRound, FullscreenExitRound, FullscreenRound, VolumeOffRound, VolumeDownRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, HideImageTwotone, MoreHorizRound, InfoOutlined, SearchRound, CheckRound, UnfoldMoreRound, KeyboardArrowDownRound, ListAltRound, CloudUploadTwotone, PlusSharp } from '@ricons/material';
6
- import { useReactive, useMemoizedFn, useClickAway, useCreation, useSize } from 'ahooks';
7
- import { createRoot } from 'react-dom/client';
8
- import ScrollContainer from 'react-custom-scrollbars-2';
9
- import dayjs from 'dayjs';
10
- import customParseFormat from 'dayjs/plugin/customParseFormat';
11
- import { createPortal } from 'react-dom';
12
- import VList from 'rc-virtual-list';
13
- import PubSub from 'pubsub-js';
14
1
 
15
- const TIMEOUT = 500;
16
- const useRipple = () => {
17
- if (document.documentElement.dataset["useripple"])
18
- return;
19
- document.documentElement.dataset["useripple"] = "enable";
20
- document.addEventListener("mousedown", listener);
21
- };
22
- function listener(e) {
23
- const target = e.target;
24
- const parent = target.closest("[data-ripple]");
25
- if (!target || !parent)
26
- return;
27
- triggerRipple(parent, e);
28
- }
29
- function triggerRipple(target, e) {
30
- const [$box, $ripple] = createRipple();
31
- const rect = target.getBoundingClientRect();
32
- const size = Math.max(rect.width, rect.height) * 2;
33
- $ripple.style.cssText = `
34
- left: ${e.pageX - rect.left}px;
35
- top: ${e.pageY - rect.top}px;
36
- width: ${size}px;
37
- height: ${size}px;
38
- transition: all ${TIMEOUT / 1000}s;
39
- `;
40
- target.insertAdjacentElement("afterbegin", $box);
41
- target.offsetHeight;
42
- $ripple.classList.add("i-ripple-active");
43
- setTimeout(() => {
44
- $box.remove();
45
- }, TIMEOUT);
46
- }
47
- function createRipple() {
48
- const $box = document.createElement("SPAN");
49
- const $ripple = document.createElement("SPAN");
50
- $box.className = "i-ripple-container";
51
- $ripple.className = "i-ripple";
52
- $box.append($ripple);
53
- return [$box, $ripple];
54
- }
55
-
56
- const Loading = (props) => {
57
- const { icon, text, size, style, className, ...restProps } = props;
58
- return (jsxs("div", { className: classNames("i-loading-container", className), style: { fontSize: size, ...style }, ...restProps, children: [icon ?? (jsx("svg", { width: '24', height: '24', stroke: '#000', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg', className: 'i-loading-icon', children: jsx("circle", { cx: '12', cy: '12', r: '9.5', fill: 'none', strokeWidth: '3', strokeLinecap: 'round', strokeDasharray: 40, strokeDashoffset: 0 }) })), text] }));
59
- };
60
-
61
- const formatClass = ({ outline, flat, loading, disabled, size = "normal", block, round, square, secondary, className, }) => classNames("i-btn", className, {
62
- "i-btn-outline": outline,
63
- "i-btn-flat": flat,
64
- "i-btn-block": block,
65
- "i-btn-loading": loading,
66
- "i-btn-square": square,
67
- "i-btn-secondary": secondary,
68
- [`i-btn-${size}`]: size !== "normal",
69
- round,
70
- disabled,
71
- });
72
- const Button = forwardRef((props, ref) => {
73
- const { as: As = "a", children, className, loading, flat, outline, square, secondary, size, round, href, ripple = true, onClick, ...restProps } = props;
74
- const handleClick = (e) => {
75
- if (loading || restProps.disabled) {
76
- e.stopPropagation();
77
- return;
78
- }
79
- onClick?.(e);
80
- };
81
- if (!children)
82
- return jsx(Fragment, {});
83
- const childNodes = [
84
- loading && jsx(Loading, {}, 'loading'),
85
- createElement("span", { key: "content", className: "i-btn-content" }, children),
86
- ];
87
- const attrs = {
88
- className: formatClass(props),
89
- ["data-ripple"]: ripple,
90
- onClick: handleClick,
91
- };
92
- useEffect(() => {
93
- ripple && useRipple();
94
- }, [ripple]);
95
- if (typeof As === "string") {
96
- return createElement(As, {
97
- ref,
98
- href,
99
- ...attrs,
100
- ...restProps,
101
- }, childNodes);
102
- }
103
- return createElement(As, {
104
- to: href || "",
105
- ...attrs,
106
- ...restProps,
107
- }, childNodes);
108
- });
109
-
110
- function Group(props) {
111
- const { children, vertical, className, style } = props;
112
- return (jsx("div", { className: classNames(className, vertical ? "i-btn-group-vertical" : "i-btn-group-horizonal"), style: style, children: children }));
113
- }
114
-
115
- function Toggle(props) {
116
- const { active, activeClass, after, disabled, children, className, onClick, onToggle, ...restProps } = props;
117
- const state = useReactive({
118
- active,
119
- done: true,
120
- });
121
- const toggle = useCallback(() => {
122
- const hasAfter = after !== undefined;
123
- const nextActive = !state.active;
124
- Object.assign(state, {
125
- active: nextActive,
126
- done: !hasAfter,
127
- });
128
- onToggle?.(nextActive);
129
- hasAfter &&
130
- setTimeout(() => {
131
- state.done = true;
132
- }, 16);
133
- }, [after]);
134
- const handleClick = (e) => {
135
- onClick?.(e);
136
- !disabled && toggle();
137
- };
138
- useEffect(() => {
139
- state.active !== active && toggle();
140
- }, [active]);
141
- return (jsx(Button, { className: classNames(className, { [activeClass || ""]: state.active }, "i-btn-toggle"), ...restProps, onClick: handleClick, children: jsx("div", { className: classNames("i-btn-toggle-content", {
142
- "i-btn-toggle-active": state.done,
143
- }), children: state.active ? after ?? children : children }) }));
144
- }
145
-
146
- Button.Toggle = Toggle;
147
- Button.Group = Group;
148
-
149
- const Icon = forwardRef((props, ref) => {
150
- const { icon, size = "1.425em", rotate, style, className, ...restProps } = props;
151
- if (!isValidElement(icon))
152
- return icon;
153
- const elProps = {
154
- ref,
155
- style: {
156
- transform: rotate ? `rotate(${rotate}deg)` : undefined,
157
- ...style,
158
- width: size,
159
- height: size,
160
- },
161
- className: classNames("i-icon", className),
162
- ...restProps,
163
- };
164
- return cloneElement(icon, elProps);
165
- });
166
-
167
- function ToTop(props) {
168
- const { style, className, onClick } = props;
169
- return (jsx(Button, { square: true, className: classNames("i-affix-totop", className), style: { ...style }, onClick: onClick, children: jsx(Icon, { icon: jsx(SkipPreviousRound, {}), rotate: 90 }) }));
170
- }
171
-
172
- const Affix = (props) => {
173
- const { position = "fixed", left, top, right, bottom, offset, style, className, children, getContainer = () => document.body, } = props;
174
- const [hidden, setHidden] = useState(false);
175
- const hijackChildren = useMemo(() => Children.map(children, (node) => {
176
- if (node.type === ToTop) {
177
- const { onClick } = node.props;
178
- return cloneElement(node, {
179
- key: node.key,
180
- ...node.props,
181
- onClick: (e) => {
182
- const container = getContainer();
183
- onClick?.(e);
184
- container?.scrollTo({
185
- top: 0,
186
- left: 0,
187
- behavior: "smooth",
188
- });
189
- },
190
- });
191
- }
192
- return node;
193
- }), [children, getContainer]);
194
- useEffect(() => {
195
- const container = getContainer();
196
- if (!offset || !container)
197
- return;
198
- const listener = debounce({ delay: 160 }, () => {
199
- const top = container.scrollTop;
200
- setHidden(top < offset);
201
- });
202
- container.addEventListener("scroll", listener);
203
- return () => {
204
- container.removeEventListener("scroll", listener);
205
- };
206
- }, [offset, getContainer]);
207
- return (jsx("div", { className: classNames("i-affix", className, {
208
- "i-affix-hidden": hidden,
209
- }), style: {
210
- ...style,
211
- position,
212
- left,
213
- top,
214
- right,
215
- bottom,
216
- }, children: hijackChildren }));
217
- };
218
- Affix.ToTop = ToTop;
219
-
220
- const Badge = (props) => {
221
- const { content, contentClass, dot, dotSize, round, disabled, style, className, children, } = props;
222
- return (jsxs("div", { style: style, className: classNames("i-badge", { rounded: round }, className), children: [children, jsx("div", { className: classNames("i-badge-content", contentClass, {
223
- "i-badge-dot": dot,
224
- "i-badge-hidden": disabled,
225
- }), style: { fontSize: dotSize }, children: content })] }));
226
- };
227
-
228
- const Area = (name) => (props) => (jsx("div", { style: props.style, className: classNames("i-card-" + name, props.className), children: props.children }));
229
- const Header$2 = Area("header");
230
- const Footer = Area("footer");
231
- const Banner = Area("banner");
232
- const Tailer = Area("tail");
233
-
234
- const Card = (props) => {
235
- const { shadow = true, border, style, className, children } = props;
236
- const slots = useMemo(() => {
237
- const nodes = {};
238
- Children.map(children, (child) => {
239
- const type = child.type;
240
- switch (type) {
241
- case Header$2:
242
- nodes["header"] = child;
243
- break;
244
- case Footer:
245
- nodes["footer"] = child;
246
- break;
247
- case Banner:
248
- nodes["banner"] = child;
249
- break;
250
- case Tailer:
251
- nodes["tailer"] = child;
252
- break;
253
- default:
254
- nodes["content"] = [...(nodes["content"] || []), child];
255
- break;
256
- }
257
- });
258
- return nodes;
259
- }, [children]);
260
- const { header, banner, content, footer, tailer } = slots;
261
- return (jsxs("div", { style: style, className: classNames("i-card", className, {
262
- shadow,
263
- "i-card-bordered": border,
264
- }), children: [banner, header, content && jsx("div", { className: 'i-card-content', children: content }), footer, tailer] }));
265
- };
266
- Card.Header = Header$2;
267
- Card.Footer = Footer;
268
- Card.Banner = Banner;
269
- Card.Tailer = Tailer;
270
-
271
- var TFileType;
272
- (function (TFileType) {
273
- TFileType["IMAGE"] = "IMAGE";
274
- TFileType["VIDEO"] = "VIDEO";
275
- TFileType["AUDIO"] = "AUDIO";
276
- TFileType["PDF"] = "PDF";
277
- TFileType["EXCEL"] = "EXCEL";
278
- TFileType["TXT"] = "TXT";
279
- TFileType["UNKNOWN"] = "UNKNOWN";
280
- })(TFileType || (TFileType = {}));
281
-
282
- function getPosition($source, $popup, options = {}) {
283
- const { refWindow, gap = 0, offset = 0, position = "top", align } = options;
284
- if (!$source || !$popup)
285
- return [
286
- 0,
287
- 0,
288
- {
289
- arrowX: 0,
290
- arrowY: 0,
291
- arrowPos: "bottom",
292
- },
293
- ];
294
- const rectT = $source.getBoundingClientRect();
295
- const rectC = $popup.getBoundingClientRect();
296
- let w = window.innerWidth;
297
- let h = window.innerHeight;
298
- let { left: tl, top: tt, right: tr, bottom: tb, width: tw, height: th, } = rectT;
299
- const { height: ch, width: cw } = rectC;
300
- if (!refWindow) {
301
- const rectPa = $source.offsetParent?.getBoundingClientRect();
302
- w = rectPa?.width || w;
303
- h = rectPa?.height || h;
304
- tl = $source.offsetLeft;
305
- tt = $source.offsetTop;
306
- tr = tl + rectT.width;
307
- tb = tt + rectT.height;
308
- }
309
- let y = 0;
310
- let x = 0;
311
- let arrowX = 0;
312
- let arrowY = 0;
313
- let arrowPos = "bottom";
314
- switch (position) {
315
- case "left":
316
- case "right":
317
- y =
318
- th !== ch
319
- ? computePosition({
320
- containerSize: h,
321
- targetSize: th,
322
- targetOffset: tt,
323
- contentSize: ch,
324
- gap,
325
- align,
326
- })
327
- : tt;
328
- arrowY = y < tt ? tt - y + th / 2 : th / 2;
329
- const xl = tl - offset - cw;
330
- const xr = tr + offset + cw;
331
- if (position === "left") {
332
- x = xl < 0 ? tr + offset : xl;
333
- arrowX = xl < 0 ? 0 : cw;
334
- arrowPos = xl < 0 ? "left" : "right";
335
- }
336
- else {
337
- x = w > xr ? tr + offset : xl;
338
- arrowX = w > xr ? 0 : cw;
339
- arrowPos = w > xr ? "left" : "right";
340
- }
341
- break;
342
- case "top":
343
- case "bottom":
344
- x =
345
- tw !== cw
346
- ? computePosition({
347
- containerSize: w,
348
- targetOffset: tl,
349
- targetSize: tw,
350
- contentSize: cw,
351
- gap,
352
- align,
353
- })
354
- : tl;
355
- arrowX = x > tl ? cw / 2 : tl - x + tw / 2;
356
- const yt = tt - offset - ch;
357
- const yb = tb + offset + ch;
358
- if (position === "top") {
359
- y = yt < 0 ? tb + offset : yt;
360
- arrowY = yt < 0 ? 0 : ch;
361
- arrowPos = yt < 0 ? "top" : "bottom";
362
- }
363
- else {
364
- y = h > yb ? tb + offset : yt;
365
- arrowY = h > yb ? 0 : ch;
366
- arrowPos = h > yb ? "top" : "bottom";
367
- }
368
- break;
369
- }
370
- return [
371
- x,
372
- y,
373
- {
374
- arrowX,
375
- arrowY,
376
- arrowPos,
377
- },
378
- ];
379
- }
380
- function getPointPosition(e, content) {
381
- const { pageX: x, pageY: y } = e;
382
- const { width: w, height: h } = content.getBoundingClientRect();
383
- const { innerHeight: wh, innerWidth: ww } = window;
384
- const left = x + w >= ww ? (x - w > 0 ? x - w : x) : x;
385
- const top = y + h >= wh ? (y - h > 0 ? y - h : y) : y;
386
- return [left, top];
387
- }
388
- function computePosition({ containerSize, targetSize, targetOffset, contentSize, gap, align = "center", }) {
389
- const centerPoint = targetOffset + targetSize / 2;
390
- switch (align) {
391
- case "start":
392
- return targetOffset + contentSize > containerSize
393
- ? targetOffset + targetSize
394
- : targetOffset;
395
- case "center":
396
- if (targetSize >= contentSize) {
397
- return centerPoint - contentSize / 2;
398
- }
399
- if (centerPoint + contentSize / 2 + gap > containerSize) {
400
- return targetOffset + targetSize - contentSize;
401
- }
402
- if (centerPoint - contentSize / 2 - gap < 0) {
403
- return gap;
404
- }
405
- return centerPoint - contentSize / 2;
406
- case "end":
407
- const result = targetOffset + targetSize - contentSize;
408
- return result > 0 ? result : gap;
409
- default:
410
- return centerPoint - contentSize / 2;
411
- }
412
- }
413
- function formatOption(options) {
414
- return options.map((option) => ["string", "number"].includes(typeof option)
415
- ? { label: option, value: option }
416
- : option);
417
- }
418
- function animate(from, to, duration = 1000, callback, easing = (t) => 1 - Math.pow(1 - t, 4)) {
419
- const start = performance.now();
420
- const diff = to - from;
421
- let raf = requestAnimationFrame(loop);
422
- function loop() {
423
- raf = requestAnimationFrame(loop);
424
- const past = performance.now() - start;
425
- let percent = past / duration;
426
- if (percent >= 1) {
427
- percent = 1;
428
- cancelAnimationFrame(raf);
429
- }
430
- const pass = diff * easing(percent);
431
- callback?.(pass);
432
- }
433
- }
434
- function formatNumber(value, options = {}) {
435
- const { precision, thousand } = options;
436
- const result = value.toFixed(precision);
437
- if (!thousand)
438
- return result;
439
- const points = result.split(".");
440
- const integer = points[0].replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, `$&${thousand}`);
441
- if (points.length === 1)
442
- return integer;
443
- return `${integer}.${points[1]}`;
444
- }
445
- function renderNode(node, container = document.body) {
446
- const div = document.createElement("div");
447
- container.append(div);
448
- const root = createRoot(div);
449
- const sto = setTimeout(() => {
450
- root?.render(node);
451
- }, 0);
452
- return () => {
453
- div?.remove();
454
- root?.unmount();
455
- sto && clearTimeout(sto);
456
- };
457
- }
458
- function getSuffixByUrl(url) {
459
- return url.match(/\.([^\./\?]+)($|\?)/)?.[1];
460
- }
461
- function getFileType(suffix, type) {
462
- switch (true) {
463
- case ["jpg", "jpeg", "png", "webp", "svg"].includes(suffix) ||
464
- type?.startsWith("image/"):
465
- return TFileType["IMAGE"];
466
- case ["mp4", "avi"].includes(suffix) || type?.startsWith("video/"):
467
- return TFileType["VIDEO"];
468
- default:
469
- return TFileType["UNKNOWN"];
470
- }
471
- }
472
- function fullScreen(el) {
473
- el.requestFullscreen?.();
474
- }
475
- function exitFullScreen() {
476
- document.exitFullscreen?.();
477
- }
478
- function formatTime(time, options) {
479
- const result = [];
480
- const { zero = true, units = ["", ":", ":"] } = options || {};
481
- const l = units.length;
482
- let i = 0;
483
- while (i < l) {
484
- if (time <= 0 && i > 1)
485
- break;
486
- const n = Math.round(time % 60);
487
- time = Math.floor(time / 60);
488
- result.unshift((zero && n < 10 ? `0${n}` : n) + units[i++]);
489
- }
490
- return result.join("");
491
- }
492
- function getNextSorter(prevSortBy, prevSortType, sortBy) {
493
- const types = ["desc", "asc"];
494
- if (prevSortBy === sortBy) {
495
- const i = types.findIndex((t) => t === prevSortType) + 1;
496
- const type = types[i] || "";
497
- const by = type === "" ? "" : sortBy;
498
- return [by, type];
499
- }
500
- return [sortBy, "desc"];
501
- }
502
- function formatBytes(bytes, decimals = 2) {
503
- if (!+bytes)
504
- return "0 Bytes";
505
- const k = 1024;
506
- const dm = decimals < 0 ? 0 : decimals;
507
- const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
508
- const i = Math.floor(Math.log(bytes) / Math.log(k));
509
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
510
- }
511
- function clamp(value, min, max) {
512
- return value < min ? min : value > max ? max : value;
513
- }
514
-
515
- function CheckboxItem(props) {
516
- const { type = "default", label, name, value = false, className, status = "normal", message, disabled, partof, children, onChange, ...restProps } = props;
517
- const state = useReactive({
518
- value,
519
- status,
520
- message,
521
- });
522
- const handleChange = useMemoizedFn((e) => {
523
- const checked = e.target.checked;
524
- Object.assign(state, {
525
- value: checked,
526
- status,
527
- message,
528
- });
529
- onChange?.(checked, e);
530
- });
531
- useEffect(() => {
532
- state.value = value;
533
- }, [value]);
534
- return (jsxs("label", { className: classNames("i-checkbox-item", {
535
- [`i-checkbox-${state.status}`]: state.status !== "normal",
536
- disabled,
537
- }, className), ...restProps, children: [jsx("input", { type: 'checkbox', name: name, className: classNames("i-checkbox-input", {
538
- [`i-checkbox-${type}`]: !partof,
539
- "i-checkbox-partof": partof,
540
- }), checked: state.value, disabled: disabled, onChange: handleChange }), jsx("span", { className: 'i-checkbox-text', children: children || label }), state.message && (jsxs("span", { className: 'i-checkbox-message', children: ["*", state.message] }))] }));
541
- }
542
-
543
- function Checkbox(props) {
544
- const { label, name, options = [], value = "", type = "default", optionInline = true, labelInline, disabled, status = "normal", message, required, className, onChange, ...restProps } = props;
545
- const state = useReactive({
546
- value,
547
- });
548
- const formattedOptions = useMemo(() => formatOption(options), [options]);
549
- const handleChange = useMemoizedFn((checked, opt, e) => {
550
- const group = [...state.value];
551
- const i = group.findIndex((item) => item === opt.value);
552
- if (checked && i < 0) {
553
- group.push(opt.value);
554
- }
555
- else if (!checked && i > -1) {
556
- group.splice(i, 1);
557
- }
558
- state.value = group;
559
- onChange?.(group, opt, e);
560
- });
561
- useEffect(() => {
562
- state.value = value;
563
- }, [value]);
564
- return (jsxs("div", { className: classNames("i-checkbox i-input-label", {
565
- [`i-checkbox-${status}`]: status !== "normal",
566
- "i-input-inline": labelInline,
567
- }, 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", {
568
- "i-options-block": !optionInline,
569
- "i-checkbox-options-button": type === "button",
570
- }), children: formattedOptions.map((option) => {
571
- return (jsx(CheckboxItem, { name: name, value: state.value.includes(option.value), type: type, disabled: disabled || option.disabled, onChange: (checked, e) => handleChange(checked, option, e), children: option.label }, option.value));
572
- }) })] }));
573
- }
574
- Checkbox.Item = CheckboxItem;
575
-
576
- const Helpericon = (props) => {
577
- const { as = "a", active, className, icon = jsx(CloseRound, {}), ...restProps } = props;
578
- if (!active)
579
- return jsx(Fragment, {});
580
- return createElement(as, {
581
- className: classNames("i-helpericon", className),
582
- ...restProps,
583
- }, [
584
- createElement(Icon, {
585
- key: uid(3),
586
- icon,
587
- }),
588
- ]);
589
- };
590
-
591
- function Item$5(props) {
592
- return jsx(Fragment, {});
593
- }
594
-
595
- const Collapse = (props) => {
596
- const { active, items, multiple, border, headerClickable, className, children, renderToggle = (active) => active ? jsx(MinusRound, {}) : jsx(PlusRound, {}), onCollapse, ...restProps } = props;
597
- const state = useReactive({
598
- active,
599
- });
600
- const collapses = useMemo(() => {
601
- if (!items) {
602
- if (!children)
603
- return [];
604
- return (Children.map(children, (node, i) => {
605
- const { key, props: nodeProps } = node;
606
- const { title, children, content, disabled, ...restProps } = nodeProps;
607
- return {
608
- ...restProps,
609
- key: key || i,
610
- title,
611
- content: children || content,
612
- disabled,
613
- };
614
- }) || []);
615
- }
616
- return items;
617
- }, [children]);
618
- const handleHeaderClick = (item) => {
619
- if (!headerClickable)
620
- return;
621
- handleToggle(item);
622
- };
623
- const handleToggle = (item) => {
624
- const { key, disabled } = item;
625
- if (disabled)
626
- return;
627
- if (!multiple) {
628
- state.active = state.active === key ? undefined : key;
629
- onCollapse?.(key, state.active !== undefined);
630
- return;
631
- }
632
- if (!Array.isArray(state.active))
633
- state.active = [];
634
- const i = state.active.findIndex((k) => k === key);
635
- if (i > -1) {
636
- state.active.splice(i, 1);
637
- }
638
- else {
639
- key !== undefined && state.active.push(key);
640
- }
641
- onCollapse?.(key, i < 0);
642
- };
643
- return (jsx("div", { className: classNames("i-collapse", {
644
- "i-collapse-bordered": border,
645
- }, className), ...restProps, children: collapses.map((item) => {
646
- const { key, title, content, disabled, className, ...restProps } = item;
647
- const isActive = multiple
648
- ? (state.active || []).includes(key)
649
- : state.active === key;
650
- return (jsxs("div", { className: classNames("i-collapse-item", className, {
651
- "i-collapse-active": isActive,
652
- "i-collapse-disabled": disabled,
653
- }), ...restProps, children: [jsxs("div", { className: 'i-collapse-header', onClick: () => handleHeaderClick(item), children: [title, jsx(Helpericon, { active: true, className: 'i-collapse-toggle', icon: renderToggle(isActive), onClick: () => handleToggle(item) })] }), jsx("div", { className: 'i-collapse-content', children: content })] }, key));
654
- }) }));
655
- };
656
- Collapse.Item = Item$5;
657
-
658
- function Empty(props) {
659
- const { className, ...restProps } = props;
660
- return (jsx(InboxTwotone, { className: classNames("i-empty", className), ...restProps }));
661
- }
662
-
663
- function getCellStyle({ justify, col, row, colSpan = 1, rowSpan = 1, }) {
664
- const style = {
665
- "--datagrid-justify": justify,
666
- gridArea: `${row + 1} / ${col + 1} / ${row + 1 + rowSpan} / ${col + 1 + colSpan}`,
667
- insetInline: `var(--datagrid-cell-inset-${col})`,
668
- };
669
- return style;
670
- }
671
- function Cell(props) {
672
- const { column, row, col, data, onCellClick, onCellDoubleClick } = props;
673
- const { id, fixed, justify, rowSpan, render } = column;
674
- const style = getCellStyle({ justify, fixed, col, row, rowSpan });
675
- return (jsx("div", { className: classNames("i-datagrid-cell", {
676
- [`i-datagrid-cell-fixed-${fixed}`]: fixed,
677
- }), "data-col": id, style: style, onClick: (e) => onCellClick?.(data, column, row, col, e), onDoubleClick: (e) => onCellDoubleClick?.(data, column, row, col, e), children: render?.(data[id], data, col) ?? (jsx("div", { className: 'i-datagrid-cell-content', children: data[id] })) }));
678
- }
679
-
680
- const MouseMoveEvents = new Set();
681
- const MouseUpEvents = new Set();
682
- document.addEventListener("mousemove", (e) => {
683
- for (const listener of MouseMoveEvents.values()) {
684
- listener(e);
685
- }
686
- });
687
- document.addEventListener("mouseup", (e) => {
688
- for (const listener of MouseUpEvents.values()) {
689
- listener(e);
690
- }
691
- });
692
- function useMouseMove(listener) {
693
- useEffect(() => {
694
- MouseMoveEvents.add(listener);
695
- return () => {
696
- MouseMoveEvents.delete(listener);
697
- };
698
- }, [listener]);
699
- }
700
- function useMouseUp(listener) {
701
- useEffect(() => {
702
- MouseUpEvents.add(listener);
703
- return () => {
704
- MouseUpEvents.delete(listener);
705
- };
706
- }, [listener]);
707
- }
708
- function useIntersectionObserver(configs) {
709
- const WM = new WeakMap();
710
- const IO = new IntersectionObserver((entries) => {
711
- entries.map((entry) => {
712
- const callback = WM.get(entry.target);
713
- callback?.(entry.target, entry.isIntersecting);
714
- });
715
- }, configs);
716
- function observe(target, callback) {
717
- if (WM.get(target))
718
- return;
719
- WM.set(target, callback);
720
- target && IO.observe(target);
721
- }
722
- function unobserve(target) {
723
- target && IO.unobserve(target);
724
- WM.delete(target);
725
- }
726
- function disconnect() {
727
- IO.disconnect();
728
- }
729
- return {
730
- observe,
731
- unobserve,
732
- disconnect,
733
- };
734
- }
735
- function useResizeObserver() {
736
- const WM = new WeakMap();
737
- const IO = new ResizeObserver((entries) => {
738
- entries.map((entry) => {
739
- const callback = WM.get(entry.target);
740
- callback?.(entry.target);
741
- });
742
- });
743
- function observe(target, callback) {
744
- if (WM.get(target))
745
- return;
746
- target && IO.observe(target);
747
- WM.set(target, callback);
748
- }
749
- function unobserve(target) {
750
- target && IO.unobserve(target);
751
- WM.delete(target);
752
- }
753
- function disconnect() {
754
- IO.disconnect();
755
- }
756
- return {
757
- observe,
758
- unobserve,
759
- disconnect,
760
- };
761
- }
762
-
763
- function Resize(props) {
764
- const { index, onWidthChange } = props;
765
- const state = useReactive({
766
- resizing: false,
767
- x: 0,
768
- width: 0,
769
- });
770
- const handleMouseDown = useMemoizedFn((e) => {
771
- const tar = e.target;
772
- const width = tar.offsetParent.offsetWidth;
773
- Object.assign(state, {
774
- x: e.pageX,
775
- resizing: true,
776
- width,
777
- });
778
- });
779
- const handleMouseMove = useMemoizedFn((e) => {
780
- if (!state.resizing)
781
- return;
782
- e.preventDefault();
783
- const after = state.width + e.pageX - state.x;
784
- if (after <= 24)
785
- return;
786
- onWidthChange(index, after);
787
- });
788
- const handleMouseUp = () => {
789
- if (!state.resizing)
790
- return;
791
- state.resizing = false;
792
- };
793
- useMouseMove(handleMouseMove);
794
- useMouseUp(handleMouseUp);
795
- return (jsx("i", { className: 'i-datagrid-resizor', onMouseDown: handleMouseDown, onClick: (e) => e.stopPropagation() }));
796
- }
797
-
798
- const Arrow = (props) => (jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', ...props, children: jsx("g", { fill: 'none', children: jsx("path", { d: 'M9 17.898c0 1.074 1.265 1.648 2.073.941l6.31-5.522a1.75 1.75 0 0 0 0-2.634l-6.31-5.522C10.265 4.454 9 5.028 9 6.102v11.796z', fill: 'currentColor' }) }) }));
799
- function Sorter(props) {
800
- const { type } = props;
801
- return (jsxs("a", { className: classNames("i-datagrid-sorter", {
802
- [`i-datagrid-sorter-${type}`]: type,
803
- }), children: [jsx(Arrow, { className: 'i-datagrid-sorter-caret' }), jsx(Arrow, { className: 'i-datagrid-sorter-caret' })] }));
804
- }
805
-
806
- function Row(props) {
807
- const { row, data, columns, onRowClick, onCellClick, onCellDoubleClick } = props;
808
- return (jsx("div", { className: 'i-datagrid-row', onClick: () => onRowClick?.(data, row), children: columns.map((col, i) => (jsx(Cell, { column: col, col: i, row: row, data: data, onCellClick: onCellClick, onCellDoubleClick: onCellDoubleClick }, i))) }));
809
- }
810
- function Header$1(props) {
811
- const { columns, resizable, sortBy, sortType, onWidthChange, onHeaderClick, } = props;
812
- return (jsx("div", { className: 'i-datagrid-header i-datagrid-row', children: columns.map((column, col) => {
813
- const { id, title, fixed, colSpan, sorter, justify, renderHeader, } = column;
814
- const style = getCellStyle({
815
- justify,
816
- row: 0,
817
- col,
818
- colSpan,
819
- });
820
- const order = sortBy === id ? sortType : "";
821
- return (jsxs("div", { "data-col": id, className: classNames("i-datagrid-cell", {
822
- "i-datagrid-has-sorter": sorter,
823
- "i-datagrid-cell-fixed": fixed,
824
- }), style: { ...style, insetBlockStart: 0 }, onClick: (e) => onHeaderClick?.(column, e), children: [renderHeader?.(column, col) ?? (jsx("div", { className: 'i-datagrid-cell-content', children: title || id })), sorter && jsx(Sorter, { type: order }), resizable && (jsx(Resize, { index: col, onWidthChange: onWidthChange }))] }, col));
825
- }) }));
826
- }
827
-
828
- const Datagrid = (props) => {
829
- const { data = [], columns = [], border, striped, header = true, resizable, cellPadding = ".5em", empty = jsx(Empty, {}), loading, height = "unset", style, className, renderLoading = () => jsx(Loading, { size: '1.5em', className: 'color-3' }), onCellClick, onRowClick, onHeaderClick, onSort, onScroll, onResize, } = props;
830
- const container = useRef(null);
831
- const scrollbar = useRef(null);
832
- const state = useReactive({
833
- rows: data,
834
- widths: columns.map((col) => col.width ?? "auto"),
835
- sortBy: "",
836
- sortType: "",
837
- });
838
- const styles = useMemo(() => {
839
- const { widths } = state;
840
- const o = {
841
- ...style,
842
- "--grid-template-columns": widths
843
- .map((w) => {
844
- return typeof w === "number" ? `${w}px` : w;
845
- })
846
- .join(" "),
847
- };
848
- if (!resizable)
849
- return o;
850
- const fws = columns.map((col, i) => {
851
- const { fixed } = col;
852
- if (!fixed)
853
- return 0;
854
- return widths[i];
855
- });
856
- columns.map((col, i) => {
857
- const { fixed } = col;
858
- if (!fixed)
859
- return;
860
- if (i === 0) {
861
- o[`--datagrid-cell-inset-0`] = 0;
862
- }
863
- else if (i === fws.length - 1) {
864
- o[`--datagrid-cell-inset-${fws.length - 1}`] = "auto 0";
865
- }
866
- else {
867
- const isLeft = fixed === "left";
868
- const before = isLeft ? fws.slice(0, i) : fws.slice(i + 1);
869
- const sum = before.reduce((pre, cur) => pre + cur) + "px";
870
- const result = isLeft ? `${sum} auto` : `auto ${sum}`;
871
- o[`--datagrid-cell-inset-${i}`] = result;
872
- }
873
- });
874
- return o;
875
- }, [state.widths, resizable]);
876
- const handleWidthChange = useCallback((i, w) => {
877
- if (!resizable)
878
- return;
879
- const [...ws] = state.widths;
880
- ws[i] = w;
881
- state.widths = ws;
882
- onResize?.(columns[i], w);
883
- }, [resizable]);
884
- const handleHeaderClick = useCallback((column, e) => {
885
- if (column?.sorter) {
886
- const [sortBy, sortType] = getNextSorter(state.sortBy, state.sortType, column.id);
887
- Object.assign(state, {
888
- sortBy,
889
- sortType,
890
- });
891
- onSort?.(sortBy, sortType);
892
- }
893
- onHeaderClick?.(column, e);
894
- }, []);
895
- const rows = useMemo(() => {
896
- const { sortBy, sortType } = state;
897
- if (sortBy && !onSort) {
898
- const sorter = columns.find((col) => col.id === sortBy)?.sorter;
899
- const sortFn = typeof sorter === "function"
900
- ? sorter
901
- : (a, b) => b[sortBy] - a[sortBy];
902
- const sorted = [...data].sort(sortFn);
903
- return sortType === "desc" ? sorted : sorted.reverse();
904
- }
905
- return data;
906
- }, [data, columns, state.sortBy, state.sortType]);
907
- useEffect(() => {
908
- if (!container.current)
909
- return;
910
- const { current: div } = container;
911
- const tds = div.querySelector(".i-datagrid-row")?.children;
912
- if (!tds?.length)
913
- return;
914
- state.widths = Array.from(tds).map((node) => node.offsetWidth);
915
- }, [columns, resizable]);
916
- useEffect(() => {
917
- loading && scrollbar.current?.scrollTop(0);
918
- }, [loading]);
919
- const scrollBarStyle = {
920
- "--padding": cellPadding,
921
- ...styles,
922
- };
923
- return (jsxs(ScrollContainer, { ref: scrollbar, autoHide: true, autoHeight: true, autoHeightMax: height, style: scrollBarStyle, className: classNames("i-datagrid-container", className, {
924
- "i-datagrid-bordered": border,
925
- "i-datagrid-striped": striped,
926
- }), renderView: (props) => (jsx("div", { ...props, className: classNames({ "i-datagrid-loading": loading }) })), onScroll: onScroll, children: [jsxs("div", { ref: container, className: 'i-datagrid', children: [header && (jsx(Header$1, { columns: columns, resizable: resizable, sortType: state.sortType, sortBy: state.sortBy, onWidthChange: handleWidthChange, onHeaderClick: handleHeaderClick })), rows.map((row, i) => (jsx(Row, { row: i + (header ? 1 : 0), data: row, columns: columns, onCellClick: onCellClick, onRowClick: onRowClick }, i))), rows.length < 1 && empty] }), loading && renderLoading()] }));
927
- };
928
-
929
- function InputContainer(props) {
930
- const { label, className, labelInline, style, children, status, tip, required, } = props;
931
- return (jsxs("label", { className: classNames("i-input-label", className, {
932
- "i-input-inline": labelInline,
933
- }), style: style, children: [label && (jsxs("span", { className: 'i-input-label-text', children: [required && jsx("span", { className: 'error', children: "*" }), label] })), children, tip && (jsx("span", { className: classNames("i-input-message", {
934
- [`i-input-${status}`]: status !== "normal",
935
- }), children: tip }))] }));
936
- }
937
-
938
- const Input = forwardRef((props, ref) => {
939
- const { type = "text", label, name, value = props.initValue ?? "", initValue = "", prepend, append, labelInline, className, status = "normal", message, tip, clear, hideVisible, border, required, onChange, onEnter, style, ...restProps } = props;
940
- const state = useReactive({
941
- value,
942
- type,
943
- visible: false,
944
- });
945
- const handleChange = useCallback((e) => {
946
- const v = e.target.value;
947
- state.value = v;
948
- onChange?.(v, e);
949
- }, []);
950
- const handleKeydown = useMemoizedFn((e) => {
951
- e.code === "Enter" && onEnter?.();
952
- });
953
- const handleHelperClick = () => {
954
- if (type === "password" && !hideVisible) {
955
- Object.assign(state, {
956
- visible: !state.visible,
957
- type: !state.visible ? "text" : "password",
958
- });
959
- return;
960
- }
961
- const v = "";
962
- onChange?.(v);
963
- };
964
- const HelperIcon = useMemo(() => {
965
- if (type === "password") {
966
- return state.visible ? jsx(VisibilityRound, {}) : jsx(VisibilityOffRound, {});
967
- }
968
- return undefined;
969
- }, [state.visible]);
970
- useEffect(() => {
971
- state.value = value;
972
- }, [value]);
973
- const inputProps = {
974
- ref,
975
- type: state.type,
976
- name,
977
- value: state.value,
978
- className: classNames("i-input", `i-input-${type}`),
979
- onChange: handleChange,
980
- onKeyDown: handleKeydown,
981
- ...restProps,
982
- };
983
- const clearable = clear && state.value;
984
- const showHelper = type === "password" && !!state.value;
985
- return (jsx(InputContainer, { label: label, labelInline: labelInline, className: className, style: style, tip: message ?? tip, status: status, required: required, children: jsxs("div", { className: classNames("i-input-item", {
986
- [`i-input-${status}`]: status !== "normal",
987
- "i-input-borderless": !border,
988
- }), children: [prepend && jsx("div", { className: 'i-input-prepend', children: prepend }), jsx("input", { ...inputProps }), jsx(Helpericon, { active: !!clearable || showHelper, icon: HelperIcon, onClick: handleHelperClick }), append && jsx("div", { className: 'i-input-append', children: append })] }) }));
989
- });
990
-
991
- const Number$2 = forwardRef((props, ref) => {
992
- const { label, name, value = props.initValue ?? "", initValue, labelInline, step = 1, min = -Infinity, max = Infinity, thousand, precision, type, className, status = "normal", append, border, prepend, message, tip, hideControl, style, onChange, onEnter, onInput, onBlur, ...rest } = props;
993
- const state = useReactive({
994
- value,
995
- });
996
- const getRangeNumber = useCallback((v) => clamp(v, min, max), [min, max]);
997
- const getFormatNumber = useCallback((v) => formatNumber(v, { precision, thousand }), [precision, thousand]);
998
- const formatInputValue = useCallback((v) => {
999
- if (!v)
1000
- return "";
1001
- if (typeof v === "number" || !thousand)
1002
- return v;
1003
- return v.replaceAll(thousand, "");
1004
- }, [thousand]);
1005
- const handleChange = useMemoizedFn((e) => {
1006
- const { value } = e.target;
1007
- const v = formatInputValue(value.replace(/[^\d\.-]/g, ""));
1008
- state.value = v;
1009
- onChange?.(+v, e);
1010
- });
1011
- const handleOperate = useMemoizedFn((param) => {
1012
- const value = formatInputValue(state.value) ?? 0;
1013
- const result = getRangeNumber(+value + param);
1014
- state.value = getFormatNumber(result);
1015
- onChange?.(result);
1016
- });
1017
- useEffect(() => {
1018
- state.value = value;
1019
- }, [value]);
1020
- const inputProps = {
1021
- ref,
1022
- name,
1023
- value: state.value,
1024
- className: "i-input i-input-number",
1025
- onChange: handleChange,
1026
- ...rest,
1027
- };
1028
- return (jsx(InputContainer, { label: label, labelInline: labelInline, className: className, style: style, tip: message ?? tip, status: status, children: jsxs("div", { className: classNames("i-input-item", {
1029
- [`i-input-${status}`]: status !== "normal",
1030
- "i-input-borderless": !border,
1031
- }), children: [prepend && jsx("div", { className: 'i-input-prepend', children: prepend }), !hideControl && (jsx(Helpericon, { active: true, icon: jsx(MinusRound, {}), onClick: () => handleOperate(-step) })), jsx("input", { ...inputProps }), !hideControl && (jsx(Helpericon, { active: true, icon: jsx(PlusRound, {}), onClick: () => handleOperate(step) })), append && jsx("div", { className: 'i-input-append', children: append })] }) }));
1032
- });
1033
-
1034
- const Range = (props) => {
1035
- const { label, name, value = props.initValue ?? "", initValue, labelInline, min = -Infinity, max = Infinity, type, className, status = "normal", message, tip, append, prepend, step = 1, thousand, precision, hideControl, placeholder, border, onChange, onBlur, style, ...restProps } = props;
1036
- const state = useReactive({
1037
- value,
1038
- });
1039
- const getRangeNumber = useCallback((v) => clamp(v, min, max), [min, max]);
1040
- const getFormatNumber = useCallback((v) => formatNumber(v, { precision, thousand }), [precision, thousand]);
1041
- const formatInputValue = useCallback((v) => {
1042
- if (!v)
1043
- return "";
1044
- if (typeof v === "number" || !thousand)
1045
- return v;
1046
- return v.replaceAll(thousand, "");
1047
- }, [thousand]);
1048
- const handleChange = useMemoizedFn((e, i) => {
1049
- const { value } = e.target;
1050
- const v = formatInputValue(value.replace(/[^\d\.-]/g, ""));
1051
- const range = Array.isArray(state.value) ? state.value : [];
1052
- range[i] = +v;
1053
- state.value = range;
1054
- onChange?.(range, e);
1055
- });
1056
- const handleOperate = useMemoizedFn((e, param, i) => {
1057
- e.preventDefault();
1058
- e.stopPropagation();
1059
- const range = Array.isArray(state.value) ? state.value : [];
1060
- const value = formatInputValue(range[i]) ?? 0;
1061
- const result = getRangeNumber(+value + param);
1062
- range[i] = getFormatNumber(result);
1063
- state.value = range;
1064
- onChange?.(range, e);
1065
- });
1066
- const handleSwitch = useMemoizedFn((e) => {
1067
- e.preventDefault();
1068
- e.stopPropagation();
1069
- const range = state.value ?? [];
1070
- const v = range[0];
1071
- range[0] = range[1];
1072
- range[1] = v;
1073
- state.value = range;
1074
- onChange?.(range);
1075
- });
1076
- useEffect(() => {
1077
- state.value = value;
1078
- }, [value]);
1079
- const inputProps = {
1080
- name,
1081
- className: "i-input i-input-number",
1082
- ...restProps,
1083
- };
1084
- return (jsx(InputContainer, { label: label, labelInline: labelInline, className: className, style: style, tip: message ?? tip, status: status, children: jsxs("div", { className: classNames("i-input-item", {
1085
- [`i-input-${status}`]: status !== "normal",
1086
- "i-input-borderless": !border,
1087
- }), 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: state.value?.[0] || "", placeholder: placeholder?.[0], ...inputProps, 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: state.value?.[1] || "", placeholder: placeholder?.[1], ...inputProps, 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 })] }) }));
1088
- };
1089
-
1090
- const Textarea = forwardRef((props, ref) => {
1091
- const { label, name, value = props.initValue ?? "", initValue, labelInline, className, status = "normal", message, tip, autoSize, border, style, onChange, onEnter, ...restProps } = props;
1092
- const state = useReactive({
1093
- value,
1094
- });
1095
- const refTextarea = useRef(null);
1096
- const handleChange = useCallback((e) => {
1097
- const v = e.target.value;
1098
- state.value = v;
1099
- const ta = refTextarea.current?.firstChild;
1100
- if (autoSize && ta) {
1101
- // ta.style.height = "inherit";
1102
- ta.style.height = `${ta.scrollHeight}px`;
1103
- }
1104
- onChange?.(v, e);
1105
- }, []);
1106
- const handleKeydown = useMemoizedFn((e) => {
1107
- e.code === "Enter" && onEnter?.();
1108
- });
1109
- useEffect(() => {
1110
- state.value = value;
1111
- }, [value]);
1112
- const inputProps = {
1113
- ref,
1114
- name,
1115
- value: state.value,
1116
- className: "i-input i-textarea",
1117
- onChange: handleChange,
1118
- onKeyDown: handleKeydown,
1119
- ...restProps,
1120
- };
1121
- return (jsx(InputContainer, { label: label, labelInline: labelInline, className: className, style: style, tip: message ?? tip, status: status, children: jsx("div", { ref: refTextarea, className: classNames("i-input-item", {
1122
- [`i-input-${status}`]: status !== "normal",
1123
- "i-input-borderless": !border,
1124
- }), children: jsx("textarea", { ...inputProps }) }) }));
1125
- });
1126
-
1127
- Input.Textarea = Textarea;
1128
- Input.Number = Number$2;
1129
- Input.Range = Range;
1130
-
1131
- const Content$1 = forwardRef((props, ref) => {
1132
- const { getContainer = (trigger) => trigger?.offsetParent ?? document.body, trigger, arrow, arrowProps = {}, className, children, ...restProps } = props;
1133
- const arrowCSS = useMemo(() => {
1134
- let { left, top, pos } = arrowProps;
1135
- let transform = "";
1136
- switch (pos) {
1137
- case "left":
1138
- left += 2;
1139
- transform = `translate(-100%, -50%) rotate(180deg)`;
1140
- break;
1141
- case "right":
1142
- left -= 2;
1143
- transform = `translate(0, -50%)`;
1144
- break;
1145
- case "top":
1146
- top -= 2;
1147
- transform = `translate(-50%, -50%) rotate(-90deg)`;
1148
- break;
1149
- case "bottom":
1150
- top += 2;
1151
- transform = `translate(-50%, -50%) rotate(90deg)`;
1152
- break;
1153
- }
1154
- return {
1155
- left,
1156
- top,
1157
- transform,
1158
- };
1159
- }, [arrowProps]);
1160
- const content = (jsxs("div", { ref: ref, className: classNames("i-popup", className), ...restProps, children: [arrow && (jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', className: 'i-popup-arrow', style: arrowCSS, children: jsx("path", { d: 'M0.5 0L1.5 0C1.5 4, 3 5.5, 5 7.5S8,10 8,12S7 14.5, 5 16.5S1.5,20 1.5,24L0.5 24L0.5 0z' }) })), children] }));
1161
- return createPortal(content, getContainer(trigger));
1162
- });
1163
-
1164
- function Popup(props) {
1165
- const { visible = false, content, trigger = "hover", gap = 12, offset = 8, fixed, position = "top", showDelay = 16, hideDelay = 12, touchable, arrow = true, align, fitSize, watchResize, clickOutside = true, disabled, referToWindow, style, className, getContainer, children, onVisibleChange, } = props;
1166
- const triggerRef = useRef(null);
1167
- const contentRef = useRef(null);
1168
- const state = useReactive({
1169
- show: false,
1170
- toggling: false,
1171
- style: { position: fixed ? "fixed" : "absolute" },
1172
- arrowProps: {},
1173
- });
1174
- useClickAway((e) => {
1175
- if (!clickOutside)
1176
- return;
1177
- const tar = e.target;
1178
- const isContain = triggerRef.current?.contains(tar);
1179
- if (!state.show)
1180
- return;
1181
- (!isContain || trigger === "contextmenu") && handleToggle(false);
1182
- }, contentRef);
1183
- const handleShow = () => {
1184
- if (disabled)
1185
- return;
1186
- if (state.show &&
1187
- (trigger !== "hover" || (trigger === "hover" && !touchable))) {
1188
- return;
1189
- }
1190
- state.show = true;
1191
- state.toggling && clearTimeout(state.toggling);
1192
- state.toggling = setTimeout(() => {
1193
- const [left, top, { arrowX, arrowY, arrowPos }] = getPosition(triggerRef.current, contentRef.current, {
1194
- position,
1195
- gap,
1196
- offset,
1197
- align,
1198
- refWindow: referToWindow,
1199
- });
1200
- state.style = {
1201
- ...state.style,
1202
- opacity: 1,
1203
- transform: "none",
1204
- left,
1205
- top,
1206
- };
1207
- state.arrowProps = {
1208
- left: arrowX,
1209
- top: arrowY,
1210
- pos: arrowPos,
1211
- };
1212
- state.toggling && clearTimeout(state.toggling);
1213
- onVisibleChange?.(true);
1214
- }, showDelay);
1215
- };
1216
- const handleHide = () => {
1217
- if (!state.show)
1218
- return;
1219
- state.toggling = setTimeout(() => {
1220
- state.style = {
1221
- ...state.style,
1222
- opacity: 0,
1223
- transform: "translate(0, 2px)",
1224
- };
1225
- setTimeout(() => {
1226
- state.show = false;
1227
- state.toggling && clearTimeout(state.toggling);
1228
- onVisibleChange?.(false);
1229
- }, 160);
1230
- }, hideDelay);
1231
- };
1232
- const handleToggle = (action) => {
1233
- if (action !== undefined) {
1234
- action ? handleShow() : handleHide();
1235
- return;
1236
- }
1237
- state.show ? handleHide() : handleShow();
1238
- };
1239
- const eventMaps = useCreation(() => ({
1240
- click: {
1241
- onClick: () => handleToggle(true),
1242
- },
1243
- hover: {
1244
- onMouseEnter: () => handleToggle(true),
1245
- onMouseLeave: () => handleToggle(false),
1246
- },
1247
- focus: {
1248
- onFocus: () => handleToggle(true),
1249
- onBlur: () => handleToggle(false),
1250
- },
1251
- contextmenu: {
1252
- onContextMenu: (e) => {
1253
- e.preventDefault();
1254
- e.stopPropagation();
1255
- if (state.show) {
1256
- const [left, top] = getPointPosition(e, contentRef.current);
1257
- state.style = {
1258
- ...state.style,
1259
- left,
1260
- top,
1261
- };
1262
- return;
1263
- }
1264
- state.show = true;
1265
- state.toggling = setTimeout(() => {
1266
- const [left, top] = getPointPosition(e, contentRef.current);
1267
- state.style = {
1268
- ...state.style,
1269
- opacity: 1,
1270
- transform: "none",
1271
- left,
1272
- top,
1273
- };
1274
- state.toggling && clearTimeout(state.toggling);
1275
- onVisibleChange?.(true);
1276
- }, showDelay);
1277
- },
1278
- },
1279
- none: {},
1280
- }), []);
1281
- const contentTouch = useMemo(() => {
1282
- if (!touchable)
1283
- return {};
1284
- const events = {};
1285
- if (trigger === "hover") {
1286
- events["onMouseEnter"] = () => handleToggle(true);
1287
- events["onMouseLeave"] = () => handleToggle(false);
1288
- }
1289
- return events;
1290
- }, [touchable, trigger]);
1291
- const computePosition = () => {
1292
- if (!state.show)
1293
- return;
1294
- const [left, top, { arrowX, arrowY, arrowPos }] = getPosition(triggerRef.current, contentRef.current, {
1295
- position,
1296
- gap,
1297
- offset,
1298
- align,
1299
- refWindow: referToWindow,
1300
- });
1301
- Object.assign(state, {
1302
- style: { ...state.style, left, top },
1303
- arrowProps: { left: arrowX, top: arrowY, pos: arrowPos },
1304
- });
1305
- };
1306
- useEffect(() => {
1307
- if (trigger === "contextmenu")
1308
- return;
1309
- const { observe, unobserve, disconnect } = useResizeObserver();
1310
- triggerRef.current && observe(triggerRef.current, computePosition);
1311
- if (!watchResize || !contentRef.current)
1312
- return;
1313
- observe(contentRef.current, computePosition);
1314
- return () => {
1315
- if (!watchResize || !contentRef.current)
1316
- return;
1317
- unobserve(contentRef.current);
1318
- triggerRef.current && unobserve(triggerRef.current);
1319
- disconnect();
1320
- };
1321
- }, [watchResize, contentRef.current, triggerRef.current]);
1322
- useLayoutEffect(() => {
1323
- if (!fitSize || !state.show)
1324
- return;
1325
- const vertical = ["top", "bottom"].includes(position);
1326
- const size = triggerRef.current?.[vertical ? "offsetWidth" : "offsetHeight"];
1327
- state.style = { ...state.style, [vertical ? "width" : "height"]: size };
1328
- }, [state.show, fitSize]);
1329
- useLayoutEffect(() => {
1330
- handleToggle(visible);
1331
- }, [visible]);
1332
- return (jsxs(Fragment, { children: [Children.map(children, (child) => {
1333
- if (!isValidElement(child))
1334
- return;
1335
- const { type, props } = child;
1336
- if (typeof type === "function")
1337
- return child;
1338
- const { className, ...rest } = props;
1339
- return cloneElement(child, {
1340
- ref: triggerRef,
1341
- className,
1342
- ...eventMaps[trigger],
1343
- ...rest,
1344
- });
1345
- }), state.show && (jsx(Content$1, { ref: contentRef, arrow: arrow && trigger !== "contextmenu", style: { ...style, ...state.style }, arrowProps: state.arrowProps, className: className, ...contentTouch, trigger: triggerRef.current, getContainer: getContainer, children: content }))] }));
1346
- }
1347
-
1348
- const Dates = (props) => {
1349
- const { value, month, weeks = ["一", "二", "三", "四", "五", "六", "日"], renderDate = (date) => date.date(), disabledDate, onDateClick, } = props;
1350
- const today = dayjs();
1351
- const dates = useMemo(() => {
1352
- const dates = [];
1353
- const lastDateOfLastMonth = month.add(-1, "month").endOf("month");
1354
- let { $W, $D } = lastDateOfLastMonth;
1355
- if ($W !== 0) {
1356
- const lastMonthDates = Array.from({ length: $W }).map((whatever, i) => lastDateOfLastMonth.add(i + 1 - $W, "day"));
1357
- dates.push(...lastMonthDates);
1358
- }
1359
- const lastDate = month.endOf("month");
1360
- $D = lastDate.$D;
1361
- $W = lastDate.$W;
1362
- dates.push(...Array.from({ length: $D }).map((whatever, i) => lastDate.add(i + 1 - $D, "day")));
1363
- if ($W !== 0) {
1364
- dates.push(...Array.from({ length: 7 - $W }).map((whatever, i) => lastDate.add(i + 1, "day")));
1365
- }
1366
- return dates;
1367
- }, [month]);
1368
- const handleDateClick = useMemoizedFn((date) => {
1369
- if (disabledDate?.(date))
1370
- return;
1371
- onDateClick?.(date);
1372
- });
1373
- return (jsxs(Fragment, { children: [jsx("div", { className: 'i-datepicker-weeks', children: weeks.map((week, i) => (jsx("span", { className: 'i-datepicker-week', children: week }, i))) }), jsx("div", { className: 'i-datepicker-dates', children: dates.map((date, i) => {
1374
- const active = date.isSame(value, "day");
1375
- const isSameMonth = date.isSame(month, "month");
1376
- const isToday = date.isSame(today, "day");
1377
- const disabled = disabledDate?.(date);
1378
- return (jsx("div", { className: classNames("i-datepicker-item", {
1379
- "i-datepicker-active": active,
1380
- "i-datepicker-same-month": isSameMonth,
1381
- "i-datepicker-today": isToday,
1382
- "i-datepicker-disabled": disabled,
1383
- }), onClick: () => handleDateClick(date), children: renderDate(date) }, i));
1384
- }) })] }));
1385
- };
1386
-
1387
- const MONTHS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
1388
- const YearMonth = (props) => {
1389
- const { value, unitMonth = "月", unitYear = "年", renderYear, renderMonth, onClick, } = props;
1390
- return (jsxs("a", { className: 'i-datepicker-action', "data-ripple": true, onClick: onClick, children: [jsx("span", { children: renderYear?.(value.year()) }), unitYear, jsx("span", { children: renderMonth?.(value.month() + 1) }), unitMonth] }));
1391
- };
1392
- const Panel = (props) => {
1393
- const { value, unitYear, unitMonth, renderDate, renderMonth = (m) => m, renderYear = (y) => y, disabledDate, onDateClick, } = props;
1394
- const state = useReactive({
1395
- today: value,
1396
- month: value || dayjs(),
1397
- selectedYear: dayjs(),
1398
- years: [],
1399
- selectable: false,
1400
- });
1401
- const $years = useRef(null);
1402
- const handleOperateMonth = useCallback((next) => {
1403
- state.month = state.month[next ? "add" : "subtract"](1, "month");
1404
- }, []);
1405
- const handleChangeDate = (date) => {
1406
- if (date.isSame(state.today, "day"))
1407
- return;
1408
- if (!date.isSame(state.month, "month")) {
1409
- state.month = date;
1410
- }
1411
- state.today = date;
1412
- onDateClick?.(date);
1413
- };
1414
- const handleChangeMonth = (month) => {
1415
- state.month = state.month
1416
- .year(state.selectedYear.year())
1417
- .month(month - 1);
1418
- state.selectable = false;
1419
- };
1420
- const getMoreYears = throttle({ interval: 100 }, (e) => {
1421
- const isUp = e.deltaY < 0;
1422
- state.years = state.years.map((y) => (y += isUp ? -1 : 1));
1423
- });
1424
- useEffect(() => {
1425
- if (!state.selectable)
1426
- return;
1427
- state.selectedYear = state.month;
1428
- const y = state.selectedYear.year();
1429
- const years = Array.from({ length: 7 }).map((_, i) => y - 3 + i);
1430
- state.years = [...years];
1431
- }, [state.selectable]);
1432
- useEffect(() => {
1433
- state.today = value;
1434
- state.month = value || dayjs();
1435
- }, [value]);
1436
- return (jsxs("div", { className: 'i-datepicker', children: [jsxs("div", { className: 'i-datepicker-units', children: [jsx(YearMonth, { value: state.month, unitYear: unitYear, unitMonth: unitMonth, renderMonth: renderMonth, renderYear: renderYear, onClick: () => (state.selectable = true) }), jsx("a", { className: 'ml-auto i-datepicker-action', "data-ripple": true, onClick: () => handleOperateMonth(false), children: jsx(Icon, { icon: jsx(KeyboardArrowLeftRound, {}) }) }), jsx("a", { className: 'i-datepicker-action', "data-ripple": true, onClick: () => handleOperateMonth(true), children: jsx(Icon, { icon: jsx(KeyboardArrowRightRound, {}) }) })] }), jsxs("div", { className: classNames("i-datepicker-ym", {
1437
- "i-datepicker-active": state.selectable,
1438
- }), children: [jsx(Helpericon, { active: true, className: 'i-datepicker-close', onClick: () => (state.selectable = false) }), jsx("div", { ref: $years, className: 'i-datepicker-years', onWheel: getMoreYears, children: state.years.map((y) => (jsx("a", { className: classNames("i-datepicker-year", {
1439
- "i-datepicker-active": y === state.selectedYear.year(),
1440
- }), onClick: () => (state.selectedYear =
1441
- state.selectedYear.year(y)), children: renderYear(y) }, y))) }), jsx("div", { className: 'i-datepicker-months', children: MONTHS.map((m) => {
1442
- return (jsx("a", { className: classNames("i-datepicker-month", {
1443
- "i-datepicker-active": m === state.month.month() + 1,
1444
- }), onClick: () => handleChangeMonth(m), children: renderMonth(m) }, m));
1445
- }) })] }), jsx(Dates, { value: state.today, month: state.month, disabledDate: disabledDate, onDateClick: handleChangeDate, renderDate: renderDate })] }));
1446
- };
1447
-
1448
- dayjs.extend(customParseFormat);
1449
- const FORMATTYPES = ["YYYY-MM-DD", "YYYY-M-D", "YYYY/MM/DD", "YYYY/M/D"];
1450
- const Datepicker = (props) => {
1451
- const { name, value, weeks, format = "YYYY-MM-DD", renderDate, renderMonth, renderYear, popupProps, disabledDate, onDateClick, onChange, onBlur, ...restProps } = props;
1452
- const state = useReactive({
1453
- value,
1454
- });
1455
- const [active, setActive] = useState(false);
1456
- const dayJsValue = useMemo(() => {
1457
- if (!state.value)
1458
- return null;
1459
- const date = dayjs(state.value, format, true);
1460
- if (date.isValid())
1461
- return date;
1462
- return null;
1463
- }, [state.value]);
1464
- const handleDateClick = (date) => {
1465
- handleChange(date.format(format));
1466
- };
1467
- const handleChange = (v) => {
1468
- state.value = v;
1469
- onChange?.(v);
1470
- };
1471
- const handleSetDate = () => {
1472
- if (!state.value)
1473
- return;
1474
- const date = dayjs(state.value, FORMATTYPES, true);
1475
- if (date.isValid()) {
1476
- handleChange(date.format(format));
1477
- return;
1478
- }
1479
- handleChange("");
1480
- };
1481
- const handleBlur = (e) => {
1482
- onBlur?.(e);
1483
- handleSetDate();
1484
- };
1485
- useEffect(() => {
1486
- state.value = value;
1487
- }, [value]);
1488
- return (jsx(Popup, { visible: active, trigger: 'click', position: 'bottom', arrow: false, align: 'start', content: jsx(Panel, { value: dayJsValue, weeks: weeks, renderDate: renderDate, renderMonth: renderMonth, renderYear: renderYear, disabledDate: disabledDate, onDateClick: handleDateClick }), ...popupProps, watchResize: true, onVisibleChange: setActive, children: jsx(Input, { value: state.value, prepend: jsx(Icon, { icon: jsx(CalendarMonthTwotone, {}), className: 'i-datepicker-icon', size: '1em' }), onChange: handleChange, onBlur: handleBlur, onEnter: handleSetDate, ...restProps }) }));
1489
- };
1490
-
1491
- const Description = (props) => {
1492
- const { data, colon, columns = 1, gap = ".5em", align, labelWidth, labelAlign, vertical, equally, style, className, } = props;
1493
- return (jsx("div", { className: classNames("i-description", className), style: {
1494
- ["--description-label-width"]: labelWidth,
1495
- gridTemplateColumns: `repeat(${columns}, ${equally ? "1fr" : "auto"})`,
1496
- gap,
1497
- textAlign: align,
1498
- ...style,
1499
- }, children: data.map((item, i) => {
1500
- const { label, value, style, hidden, rowSpan = 1, colSpan = 1, } = item;
1501
- if (hidden)
1502
- return jsx(Fragment$1, {}, i);
1503
- return (jsxs("div", { className: classNames("i-description-item", {
1504
- "i-description-item-vertical": vertical,
1505
- }), style: {
1506
- gridColumn: `span ${colSpan}`,
1507
- gridRow: `span ${rowSpan}`,
1508
- ...style,
1509
- }, children: [label && (jsxs("div", { className: 'i-description-label', style: { textAlign: labelAlign }, children: [label, colon] })), jsx("div", { className: 'i-description-value', children: value })] }, i));
1510
- }) }));
1511
- };
1512
-
1513
- function Drawer(props) {
1514
- const { visible, position = "left", header, footer, backdropClosable = true, hideCloseButton, className, children, onVisibleChange, onClose, ...restProps } = props;
1515
- const [show, setShow] = useState(visible);
1516
- const [active, setActive] = useState(visible);
1517
- const toggable = useRef(true);
1518
- useEffect(() => {
1519
- visible ? handleShow() : handleHide();
1520
- }, [visible]);
1521
- const handleShow = useCallback(() => {
1522
- if (!toggable.current)
1523
- return;
1524
- setShow(true);
1525
- onVisibleChange?.(true);
1526
- toggable.current = false;
1527
- setTimeout(() => {
1528
- setActive(true);
1529
- toggable.current = true;
1530
- }, 24);
1531
- }, []);
1532
- const handleHide = useCallback(() => {
1533
- if (!toggable.current)
1534
- return;
1535
- toggable.current = false;
1536
- setActive(false);
1537
- setTimeout(() => {
1538
- setShow(false);
1539
- onVisibleChange?.(false);
1540
- toggable.current = true;
1541
- onClose?.();
1542
- }, 240);
1543
- }, []);
1544
- const handleBackdropClick = useCallback(function () {
1545
- backdropClosable && handleHide();
1546
- }, []);
1547
- return createPortal(show && (jsx("div", { className: classNames("i-backdrop-drawer", className, {
1548
- "i-active": active,
1549
- }), onClick: handleBackdropClick, ...restProps, children: jsxs("div", { className: classNames("i-drawer", `i-drawer-${position}`), onClick: (e) => e.stopPropagation(), children: [jsxs("header", { className: 'i-drawer-header', children: [header, jsx(Helpericon, { active: !hideCloseButton, className: 'i-drawer-close', onClick: handleHide })] }), jsx("div", { className: 'i-drawer-content', children: children }), jsx("div", { className: 'i-drawer-footer', children: footer })] }) })), document.body);
1550
- }
1551
-
1552
- const Item$4 = forwardRef((props, ref) => {
1553
- const { active, type, align, disabled, label, style, className, children, ...restProps } = props;
1554
- return (jsxs("li", { ref: ref, className: classNames("i-list-item", className, {
1555
- "i-list-item-active": active,
1556
- "i-list-option": type === "option",
1557
- disabled,
1558
- }), style: { alignItems: align, ...style }, ...restProps, children: [label !== undefined && (jsx("span", { className: 'i-list-item-label', children: label })), children] }));
1559
- });
1560
-
1561
- const Virtual = (props) => {
1562
- const { data, itemKey, renderItem, ...restProps } = props;
1563
- return (jsx(VList, { data: data, itemKey: itemKey, ...restProps, children: (item, i) => {
1564
- return renderItem?.(item, i);
1565
- } }));
1566
- };
1567
-
1568
- const List$1 = (props) => {
1569
- const { label, type, className, children, ...restProps } = props;
1570
- return (jsx("ul", { className: classNames("i-list", className), ...restProps, children: Children.map(children, (node, i) => {
1571
- const renderLabel = typeof label === "function" ? label(i) : label;
1572
- return cloneElement(node, {
1573
- label: renderLabel,
1574
- ...node.props,
1575
- type,
1576
- });
1577
- }) }));
1578
- };
1579
- List$1.Virtual = Virtual;
1580
- List$1.Item = Item$4;
1581
-
1582
- const { Item: ListItem } = List$1;
1583
- const Item$3 = (props) => {
1584
- const { more, moreProps, ...restProps } = props;
1585
- const Li = jsx(ListItem, { ...restProps });
1586
- if (!more)
1587
- return Li;
1588
- return (jsx(Popup, { position: 'right', touchable: true, arrow: false, align: 'start', offset: 10, ...moreProps, content: jsx(List$1, { className: 'i-dropdown-content', onClick: (e) => e.stopPropagation(), children: more }), children: Li }));
1589
- };
1590
-
1591
- const Dropdown = (props) => {
1592
- const { width, content, children, ...restProps } = props;
1593
- return (jsx(Popup, { trigger: 'click', position: 'bottom', touchable: true, content: jsx(List$1, { className: 'i-dropdown-content', style: { minWidth: width }, children: content }), ...restProps, children: children }));
1594
- };
1595
- Dropdown.Item = Item$3;
1596
-
1597
- const Flex = (props) => {
1598
- const { as: Component = "div", align, justify, direction, wrap, gap, columns, className, style, ...restProps } = props;
1599
- const gridColumns = useMemo(() => {
1600
- if (!columns)
1601
- return;
1602
- if (typeof columns === "number")
1603
- return `repeat(${columns}, 1fr)`;
1604
- return columns;
1605
- }, [columns]);
1606
- return (jsx(Component, { ...restProps, style: {
1607
- alignItems: align,
1608
- justifyContent: justify,
1609
- gap,
1610
- flexDirection: direction,
1611
- flexWrap: wrap === true ? "wrap" : wrap,
1612
- gridTemplateColumns: gridColumns,
1613
- ...style,
1614
- }, className: classNames(className, {
1615
- [columns ? "grid" : "flex"]: true,
1616
- }) }));
1617
- };
1618
-
1619
- const Context = createContext({});
1620
-
1621
- function Field(props) {
1622
- const { name, required, children } = props;
1623
- const state = useReactive({
1624
- value: undefined,
1625
- status: "normal",
1626
- message: undefined,
1627
- update: 0,
1628
- });
1629
- const form = useContext(Context);
1630
- const { id } = form;
1631
- const handleChange = useCallback((v) => {
1632
- if (!name)
1633
- return;
1634
- form.set(name, v);
1635
- }, [name]);
1636
- const hijackChildren = useMemo(() => {
1637
- return Children.map(children, (node) => {
1638
- if (!isValidElement(node))
1639
- return null;
1640
- const { onChange } = node.props;
1641
- const { value, status, message } = state;
1642
- return cloneElement(node, {
1643
- value,
1644
- status,
1645
- message,
1646
- required,
1647
- onChange: (...args) => {
1648
- handleChange(args[0]);
1649
- onChange?.(...args);
1650
- Object.assign(state, {
1651
- status: "normal",
1652
- message: undefined,
1653
- });
1654
- },
1655
- });
1656
- });
1657
- }, [children, state.update]);
1658
- useEffect(() => {
1659
- if (!name)
1660
- return;
1661
- PubSub.subscribe(`${id}:set:${name}`, (evt, v) => {
1662
- state.value = v;
1663
- state.update += 1;
1664
- });
1665
- PubSub.subscribe(`${id}:invalid:${name}`, (evt, v) => {
1666
- Object.assign(state, v);
1667
- state.update += 1;
1668
- });
1669
- setTimeout(() => {
1670
- form.data[name] = form.data[name] ?? undefined;
1671
- }, 0);
1672
- return () => {
1673
- PubSub.unsubscribe(`${id}:set:${name}`);
1674
- PubSub.unsubscribe(`${id}:invalid:${name}`);
1675
- };
1676
- }, [name]);
1677
- if (!name)
1678
- return children;
1679
- return hijackChildren;
1680
- }
1681
-
1682
- class IFormInstance {
1683
- id;
1684
- data = {};
1685
- rules = {};
1686
- constructor() {
1687
- this.id = uid(8);
1688
- this.data = {};
1689
- }
1690
- get(field) {
1691
- return field ? this.data[field] : this.data;
1692
- }
1693
- set(field, value) {
1694
- const id = this.id;
1695
- if (!this.data)
1696
- return;
1697
- if (typeof field === "string") {
1698
- this.data[field] = value;
1699
- PubSub.publish(`${id}:set:${field}`, value);
1700
- return;
1701
- }
1702
- Object.keys(field).map((name) => {
1703
- this.data[name] = field[name];
1704
- PubSub.publish(`${id}:set:${name}`, field[name]);
1705
- });
1706
- }
1707
- clear() {
1708
- if (!this.data)
1709
- return;
1710
- Object.keys(this.data).map((name) => {
1711
- PubSub.publish(`${this.id}:set:${name}`, undefined);
1712
- this.data[name] = undefined;
1713
- });
1714
- }
1715
- async validate(field) {
1716
- const { id, rules, data } = this;
1717
- if (!rules)
1718
- return data;
1719
- if (field) {
1720
- const o = rules[field];
1721
- const rule = {
1722
- validator: (v) => Array.isArray(v)
1723
- ? v.length > 0
1724
- : ![undefined, null, ""].includes(v),
1725
- message: undefined,
1726
- };
1727
- if (typeof o === "function") {
1728
- rule.validator = o;
1729
- }
1730
- else if (o === true) {
1731
- rule.message = "required";
1732
- }
1733
- else {
1734
- Object.assign(rule, o);
1735
- }
1736
- const isValid = rule.validator?.(data[field], this);
1737
- if (!isValid) {
1738
- PubSub.publish(`${id}:invalid:${field}`, {
1739
- message: rule.message,
1740
- status: "error",
1741
- });
1742
- return false;
1743
- }
1744
- PubSub.publish(`${id}:invalid:${name}`, {
1745
- message: null,
1746
- status: "normal",
1747
- });
1748
- return true;
1749
- }
1750
- let isAllValid = true;
1751
- Object.keys(data).map((name) => {
1752
- const o = rules[name];
1753
- if (o === undefined)
1754
- return;
1755
- const rule = {
1756
- validator: (v) => (Array.isArray(v) ? v.length > 0 : !!v),
1757
- message: undefined,
1758
- };
1759
- if (typeof o === "function") {
1760
- rule.validator = o;
1761
- }
1762
- else if (o === true) {
1763
- rule.message = "required";
1764
- }
1765
- else {
1766
- Object.assign(rule, o);
1767
- }
1768
- const isValid = rule.validator?.(data[name], this);
1769
- if (!isValid) {
1770
- PubSub.publish(`${id}:invalid:${name}`, {
1771
- message: rule.message,
1772
- status: "error",
1773
- });
1774
- isAllValid = false;
1775
- }
1776
- else {
1777
- PubSub.publish(`${id}:invalid:${name}`, {
1778
- message: null,
1779
- status: "normal",
1780
- });
1781
- }
1782
- });
1783
- return isAllValid ? data : false;
1784
- }
1785
- }
1786
- function useForm(form) {
1787
- const formRef = useRef();
1788
- if (!formRef.current) {
1789
- formRef.current = form ?? new IFormInstance();
1790
- }
1791
- return formRef.current;
1792
- }
1793
-
1794
- const Form = (props) => {
1795
- const { form = {}, rules = {}, initialValues = {}, style, className, width, children, ...rest } = props;
1796
- useEffect(() => {
1797
- Object.assign(form, {
1798
- data: { ...initialValues },
1799
- rules: { ...rules },
1800
- });
1801
- }, [form, rules]);
1802
- return (jsx(Context.Provider, { value: form, children: jsx("form", { style: { ...style, width }, className: classNames("i-form", className), ...rest, children: children }) }));
1803
- };
1804
- Form.useForm = useForm;
1805
- Form.Field = Field;
1806
-
1807
- function DefaultContent(props) {
1808
- const { title, footer, hideCloseButton, footerLeft, okButtonProps = {
1809
- children: "确定",
1810
- onClick: props.onOk,
1811
- }, cancelButtonProps = {
1812
- secondary: true,
1813
- children: "关闭",
1814
- onClick: props.onClose,
1815
- }, children, onClose, } = props;
1816
- const showHeader = title || !hideCloseButton;
1817
- const renderFooter = useMemo(() => {
1818
- if (footer || footer === null)
1819
- return footer;
1820
- const propsOk = Object.assign({}, okButtonProps);
1821
- const propsCancel = Object.assign({}, cancelButtonProps);
1822
- return (jsxs(Fragment, { children: [footerLeft, jsx(Button, { ...propsOk }), jsx(Button, { ...propsCancel })] }));
1823
- }, [footer, okButtonProps, cancelButtonProps]);
1824
- return (jsxs(Fragment, { children: [showHeader && (jsxs("header", { className: 'i-modal-header', children: [title && jsx("b", { children: title }), jsx(Helpericon, { active: !hideCloseButton, className: 'i-modal-close', onClick: onClose })] })), jsx("div", { className: 'i-modal-content', children: children }), jsx("footer", { className: 'i-modal-footer', children: renderFooter })] }));
1825
- }
1826
- function Modal(props) {
1827
- const { visible, title, footer, okButtonProps, cancelButtonProps, closable = true, hideBackdrop, backdropClosable = true, hideCloseButton, width, height, customized, fixed, shadow = true, children, style, className, footerLeft, onVisibleChange, onClose, onOk, ...restProps } = props;
1828
- const [show, setShow] = useState(visible);
1829
- const [active, setActive] = useState(false);
1830
- const [bounced, setBounced] = useState(false);
1831
- const toggable = useRef(true);
1832
- const handleShow = useCallback(() => {
1833
- if (!toggable.current)
1834
- return;
1835
- setShow(true);
1836
- toggable.current = false;
1837
- setTimeout(() => {
1838
- setActive(true);
1839
- onVisibleChange?.(true);
1840
- toggable.current = true;
1841
- }, 24);
1842
- }, []);
1843
- const handleHide = useCallback(() => {
1844
- if (!toggable.current)
1845
- return;
1846
- toggable.current = false;
1847
- if (!closable) {
1848
- setBounced(true);
1849
- setTimeout(() => {
1850
- setBounced(false);
1851
- toggable.current = true;
1852
- }, 400);
1853
- return;
1854
- }
1855
- setActive(false);
1856
- setTimeout(() => {
1857
- setShow(false);
1858
- toggable.current = true;
1859
- onVisibleChange?.(false);
1860
- onClose?.();
1861
- }, 240);
1862
- }, [closable]);
1863
- const handleBackdropClick = useCallback(function () {
1864
- backdropClosable && handleHide();
1865
- }, [closable, backdropClosable]);
1866
- useEffect(() => {
1867
- visible ? handleShow() : handleHide();
1868
- }, [visible]);
1869
- if (!show)
1870
- return null;
1871
- return createPortal(jsx("div", { className: classNames("i-modal-container", {
1872
- "i-modal-backdrop": !hideBackdrop,
1873
- "i-modal-customized": customized,
1874
- "i-modal-active": active,
1875
- fixed,
1876
- }, className), style: style, onClick: handleBackdropClick, children: jsxs("div", { className: classNames("i-modal", {
1877
- bounced,
1878
- shadow,
1879
- }), style: {
1880
- width,
1881
- height,
1882
- }, onClick: (e) => e.stopPropagation(), ...restProps, children: [customized && children, !customized && (jsx(DefaultContent, { title: title, hideCloseButton: hideCloseButton, footer: footer, okButtonProps: okButtonProps, cancelButtonProps: cancelButtonProps, children: children, footerLeft: footerLeft, onOk: onOk, onClose: handleHide }))] }) }), document.body);
1883
- }
1884
-
1885
- const HookModal = forwardRef((props, ref) => {
1886
- const state = useReactive({});
1887
- useImperativeHandle(ref, () => ({
1888
- update: (config = {}) => {
1889
- Object.assign(state, config);
1890
- },
1891
- }));
1892
- return jsx(Modal, { ...props, ...state });
1893
- });
1894
-
1895
- const Text = (props) => {
1896
- const { as: Tag = "span", size, weight, decoration, gradient, style, className, children, } = props;
1897
- const gradients = useMemo(() => {
1898
- if (!gradient || !Array.isArray(gradient))
1899
- return {};
1900
- return {
1901
- background: `-webkit-linear-gradient(${gradient.join(",")})`,
1902
- backgroundClip: "text",
1903
- };
1904
- }, [gradient]);
1905
- return (jsx(Tag, { style: {
1906
- fontSize: size,
1907
- fontWeight: weight,
1908
- textDecoration: decoration,
1909
- ...gradients,
1910
- ...style,
1911
- }, className: classNames(className, {
1912
- "i-text-gradient": gradient,
1913
- }), children: children }));
1914
- };
1915
-
1916
- function Number$1(props) {
1917
- const { count, to, decimal, thousand = ",", duration = 2400, easing, ...restProps } = props;
1918
- const [n, setN] = useState(count);
1919
- const number = useMemo(() => {
1920
- if (n === undefined)
1921
- return;
1922
- const z = n.toFixed(decimal);
1923
- if (!thousand)
1924
- return z;
1925
- return formatNumber(n, { precision: decimal, thousand });
1926
- }, [n, thousand]);
1927
- useEffect(() => {
1928
- if (count === undefined || to === undefined)
1929
- return;
1930
- animate(count, to, duration, (v) => setN(count + v), easing);
1931
- }, [to]);
1932
- useEffect(() => setN(count), [count]);
1933
- return jsx(Text, { ...restProps, children: number });
1934
- }
1935
-
1936
- function Number(props) {
1937
- const { seconds, zero, units, ...restProps } = props;
1938
- const text = useMemo(() => {
1939
- if (seconds === undefined)
1940
- return "";
1941
- return formatTime(seconds, {
1942
- zero,
1943
- units,
1944
- });
1945
- }, [seconds]);
1946
- return jsx(Text, { ...restProps, children: text });
1947
- }
1948
-
1949
- Text.Number = Number$1;
1950
- Text.Time = Number;
1951
-
1952
- function Circle(props) {
1953
- const { value, height = 40, size = 8 } = props;
1954
- return (jsxs("div", { className: 'i-progress-circle', children: [jsxs("svg", { width: height, height: height, children: [jsx("circle", { cx: height / 2, cy: height / 2, r: height / 2 - size / 2, fill: 'none', stroke: 'var(--background-opacity-2)', strokeWidth: size }), jsx("circle", { cx: height / 2, cy: height / 2, r: height / 2 - size / 2, fill: 'none', stroke: 'var(--color-main)', strokeWidth: size, strokeDasharray: 100, pathLength: 100, className: 'i-progress-circle-path', strokeLinecap: 'round', style: {
1955
- strokeDashoffset: `calc(100 - ${value})`,
1956
- } })] }), jsxs("span", { className: 'i-progress-circle-value', children: [jsx("span", { children: value }), jsx(Text, { size: '.81em', className: 'color-7', children: "%" })] })] }));
1957
- }
1958
-
1959
- const Line = forwardRef((props, ref) => {
1960
- const { value, size, barClass, dragging, renderCursor, onMouseDown } = props;
1961
- return (jsx("div", { ref: ref, className: 'i-progress', style: { height: size }, onMouseDown: onMouseDown, children: jsx("div", { className: classNames("i-progress-bar", barClass, {
1962
- "no-transition": dragging,
1963
- }), style: { width: `${value}%` }, children: renderCursor && (jsx("a", { className: 'i-progress-cursor', children: renderCursor(value ?? 0) })) }) }));
1964
- });
1965
-
1966
- const Progress = (props) => {
1967
- const { value = 0, size = 8, height = 40, precision = 0, style, draggable = true, type = "line", barClass, label, labelInline, className, renderCursor, onChange, } = props;
1968
- const ref = useRef(null);
1969
- const state = useReactive({
1970
- value,
1971
- dragging: false,
1972
- width: 0,
1973
- start: 0,
1974
- });
1975
- const toFixedValue = useMemo(() => {
1976
- let value = +state.value.toFixed(precision);
1977
- value = Math.min(100, value);
1978
- value = Math.max(0, value);
1979
- return value;
1980
- }, [state.value, precision]);
1981
- const handleMouseDown = useMemoizedFn((e) => {
1982
- if (!ref.current || !draggable)
1983
- return;
1984
- const rect = ref.current.getBoundingClientRect();
1985
- const value = ((e.pageX - rect.left) * 100) / rect.width;
1986
- Object.assign(state, {
1987
- value,
1988
- width: rect.width,
1989
- start: rect.left,
1990
- dragging: true,
1991
- });
1992
- });
1993
- const handleMouseMove = useMemoizedFn((e) => {
1994
- if (!state.dragging || !draggable)
1995
- return;
1996
- e.preventDefault();
1997
- const { start, width } = state;
1998
- const offset = e.pageX - start;
1999
- if (offset < 0 || offset > width)
2000
- return;
2001
- state.value = ((e.pageX - start) * 100) / width;
2002
- });
2003
- const handleMouseUp = useMemoizedFn(() => {
2004
- if (!state.dragging || !draggable)
2005
- return;
2006
- onChange?.(toFixedValue);
2007
- state.dragging = false;
2008
- });
2009
- useMouseMove(handleMouseMove);
2010
- useMouseUp(handleMouseUp);
2011
- useEffect(() => {
2012
- if (value > 100) {
2013
- state.value = 100;
2014
- return;
2015
- }
2016
- if (value < 0) {
2017
- state.value = 0;
2018
- return;
2019
- }
2020
- state.value = value;
2021
- }, [value]);
2022
- return (jsxs("div", { className: classNames("i-input-label", className, {
2023
- "i-input-inline": labelInline,
2024
- }), style: style, children: [label && jsx("span", { className: 'i-input-label-text', children: label }), type === "line" && (jsx(Line, { ref: ref, size: size, barClass: barClass, dragging: state.dragging, value: state.value, renderCursor: renderCursor, onMouseDown: handleMouseDown })), type === "circle" && (jsx(Circle, { value: state.value, height: height, size: size }))] }));
2025
- };
2026
-
2027
- const Video = forwardRef((props, ref) => {
2028
- const { style, hideControls, autoplay, muted, volume = 50, height, width, useOriginControls, timeProgressProps = {
2029
- barClass: "bg-2",
2030
- }, volumeProgressProps = {
2031
- barClass: "bg-1",
2032
- }, className, onFullScreenChange, ...restProps } = props;
2033
- const state = useReactive({
2034
- playing: autoplay,
2035
- volume: muted ? 0 : volume,
2036
- volumeCache: 0,
2037
- muted,
2038
- current: 0,
2039
- duration: 0,
2040
- isFullscreen: false,
2041
- });
2042
- const $v = useRef(null);
2043
- const timeUpdateListener = (e) => {
2044
- const tar = e.target;
2045
- if (tar.paused)
2046
- return;
2047
- Object.assign(state, {
2048
- current: tar.currentTime,
2049
- });
2050
- };
2051
- const playChangeListener = (e) => {
2052
- state.playing = !e.target.paused;
2053
- };
2054
- const fsChangeListener = () => {
2055
- const tar = $v.current?.parentElement;
2056
- if (!tar)
2057
- return;
2058
- state.isFullscreen = document.fullscreenElement === tar;
2059
- };
2060
- const volumeChangeListener = (e) => {
2061
- const tar = e.target;
2062
- Object.assign(state, {
2063
- volume: tar.volume * 100,
2064
- muted: tar.volume === 0,
2065
- });
2066
- };
2067
- const handlePlay = () => {
2068
- const v = $v.current;
2069
- if (!v)
2070
- return;
2071
- v.paused ? v.play() : v.pause();
2072
- };
2073
- const handleReady = (e) => {
2074
- const tar = e.target;
2075
- Object.assign(state, {
2076
- duration: tar.duration,
2077
- current: tar.currentTime,
2078
- });
2079
- tar.volume = state.volume / 100;
2080
- };
2081
- const handleMuted = () => {
2082
- const v = $v.current;
2083
- if (!v)
2084
- return;
2085
- if (v.volume > 0) {
2086
- state.volumeCache = v.volume;
2087
- v.volume = 0;
2088
- return;
2089
- }
2090
- v.volume = state.volumeCache === 0 ? 0.5 : state.volumeCache;
2091
- };
2092
- const handleStop = () => {
2093
- const v = $v.current;
2094
- if (!v)
2095
- return;
2096
- v.currentTime = 0;
2097
- v.pause();
2098
- };
2099
- const handleFullscreen = () => {
2100
- const tar = $v.current?.parentElement;
2101
- if (!tar)
2102
- return;
2103
- state.isFullscreen ? exitFullScreen() : fullScreen(tar);
2104
- onFullScreenChange?.(!state.isFullscreen);
2105
- };
2106
- const handleUpdateTime = (t) => {
2107
- const v = $v.current;
2108
- if (!v)
2109
- return;
2110
- v.currentTime = (state.duration * t) / 100;
2111
- };
2112
- const handleUpdateVolume = (t) => {
2113
- const v = $v.current;
2114
- if (!v)
2115
- return;
2116
- v.volume = t / 100;
2117
- };
2118
- useImperativeHandle(ref, () => ({
2119
- play: () => {
2120
- const v = $v.current;
2121
- if (!v)
2122
- return;
2123
- v.play();
2124
- },
2125
- pause: () => {
2126
- const v = $v.current;
2127
- if (!v)
2128
- return;
2129
- v.pause();
2130
- },
2131
- stop: handleStop,
2132
- fullscreen: handleFullscreen,
2133
- getVideo: () => $v.current,
2134
- }));
2135
- useEffect(() => {
2136
- const v = $v.current;
2137
- if (!v)
2138
- return;
2139
- v.addEventListener("timeupdate", timeUpdateListener);
2140
- v.addEventListener("play", playChangeListener);
2141
- v.addEventListener("pause", playChangeListener);
2142
- v.addEventListener("volumechange", volumeChangeListener);
2143
- document.addEventListener("fullscreenchange", fsChangeListener);
2144
- return () => {
2145
- v.removeEventListener("timeupdate", timeUpdateListener);
2146
- v.removeEventListener("play", playChangeListener);
2147
- v.removeEventListener("pause", playChangeListener);
2148
- v.removeEventListener("volumechange", volumeChangeListener);
2149
- document.removeEventListener("fullscreenchange", fsChangeListener);
2150
- };
2151
- }, []);
2152
- return (jsxs("div", { className: classNames("i-video", className), style: { height, width, ...style }, onClick: handlePlay, onDoubleClick: () => handleFullscreen(), children: [jsx("video", { ref: $v, onCanPlay: handleReady, ...restProps, controls: useOriginControls }), !hideControls && !useOriginControls && (jsxs("div", { className: 'i-video-controls', onClick: (e) => e.stopPropagation(), children: [jsxs("div", { className: 'i-video-control flex-1', children: [jsx(Button.Toggle, { className: 'i-video-btn', flat: true, square: true, after: jsx(Icon, { icon: jsx(PauseRound, {}) }), active: state.playing, onClick: handlePlay, children: jsx(Icon, { icon: jsx(PlayArrowRound, {}) }) }), jsx(Button, { className: 'i-video-btn', flat: true, square: true, onClick: handleStop, children: jsx(Icon, { icon: jsx(StopRound, {}) }) }), jsxs("span", { className: 'i-video-times', children: [jsx(Text.Time, { seconds: state.current }), " /", jsx(Text.Time, { seconds: state.duration })] }), jsx(Progress, { className: 'mr-8', ...timeProgressProps, value: (state.current / state.duration) * 100, onChange: handleUpdateTime })] }), jsx("div", { className: 'i-video-control', children: jsx(Button.Toggle, { className: 'i-video-btn', flat: true, square: true, after: jsx(Icon, { icon: jsx(FullscreenExitRound, {}) }), active: state.isFullscreen, onClick: () => handleFullscreen(), children: jsx(Icon, { icon: jsx(FullscreenRound, {}) }) }) }), jsxs("div", { className: 'i-video-control', children: [jsx(Button.Toggle, { className: 'i-video-btn', flat: true, square: true, active: state.volume <= 0, after: jsx(Icon, { icon: jsx(VolumeOffRound, {}), size: '1.2em' }), onClick: handleMuted, children: jsx(Icon, { icon: jsx(VolumeDownRound, {}) }) }), jsx(Progress, { style: { width: 100 }, className: 'mr-8', ...volumeProgressProps, value: state.volume, onChange: handleUpdateVolume })] })] }))] }));
2153
- });
2154
-
2155
- function renderFile(props) {
2156
- const { name, suffix, type } = props;
2157
- switch (type) {
2158
- case TFileType.IMAGE:
2159
- return jsx("img", { src: props.src });
2160
- case TFileType.VIDEO:
2161
- return jsx(Video, { ...props });
2162
- default:
2163
- return (jsxs("div", { className: 'i-preview-unknown', children: [jsx(Icon, { icon: jsx(FeedOutlined, {}), size: '3em' }), jsx("h5", { className: 'mt-4', children: name || suffix || "?" })] }));
2164
- }
2165
- }
2166
-
2167
- function Content(props) {
2168
- const { items = [], initial = 0, renderFile: renderFile$1 = renderFile, onRotate, onChange, onClose, onZoom, } = props;
2169
- const state = useReactive({
2170
- current: initial,
2171
- rotate: 0,
2172
- scale: 1,
2173
- translate: `0, 0`,
2174
- });
2175
- const box = useRef(null);
2176
- const files = useMemo(() => {
2177
- return items.map((item) => {
2178
- const o = {
2179
- src: "",
2180
- };
2181
- if (typeof item === "string") {
2182
- o.src = item;
2183
- }
2184
- else {
2185
- Object.assign(o, item);
2186
- }
2187
- o.suffix = getSuffixByUrl(o.src) || "";
2188
- o.type = getFileType(o.suffix, item["type"]);
2189
- return o;
2190
- });
2191
- }, [items]);
2192
- const { file, content } = useMemo(() => {
2193
- const file = files[state.current];
2194
- const content = renderFile$1(file);
2195
- return {
2196
- file,
2197
- content,
2198
- };
2199
- }, [state.current, items]);
2200
- const isImage = file.type === TFileType.IMAGE;
2201
- const handleSwitch = (next) => {
2202
- const l = files.length;
2203
- const { current: before } = state;
2204
- if (next >= l) {
2205
- state.current = 0;
2206
- }
2207
- else if (next < 0) {
2208
- state.current = l - 1;
2209
- }
2210
- else {
2211
- state.current = next;
2212
- }
2213
- onChange?.(state.current, before);
2214
- state.rotate = files[state.current].rotate || 0;
2215
- if (state.scale !== 1) {
2216
- state.scale = 1;
2217
- onZoom?.(1);
2218
- }
2219
- onRotate?.(state.rotate);
2220
- };
2221
- const handleRotate = (deg) => {
2222
- state.rotate += deg;
2223
- onRotate?.(state.rotate);
2224
- };
2225
- const handleMouseWheel = throttle({ interval: 60 }, (e) => {
2226
- if (!isImage)
2227
- return;
2228
- let after = state.scale + (e.deltaY < 0 ? 0.05 : -0.05);
2229
- if (after > 2)
2230
- after = 2;
2231
- if (after < 0.25)
2232
- after = 0.25;
2233
- onZoom?.(after);
2234
- state.scale = after;
2235
- });
2236
- return (jsxs(Fragment, { children: [jsx("div", { ref: box, className: 'i-preview-content', style: {
2237
- transform: `rotate(${state.rotate}deg) scale(${state.scale})`,
2238
- }, onWheel: handleMouseWheel, onClick: (e) => e.stopPropagation(), children: content }), jsxs("div", { className: 'i-preview-controls', children: [jsx(Button, { square: true, flat: true, onClick: onClose, children: jsx(Icon, { icon: jsx(CloseRound, {}) }) }), files.length > 1 && (jsxs("span", { className: 'px-8', children: [state.current + 1, " / ", files.length] })), state.scale !== 1 && (jsxs(Button, { flat: true, onClick: () => (state.scale = 1), children: [jsx(Icon, { icon: jsx(AspectRatioRound, {}) }), jsxs("span", { className: 'mt-4', children: [(state.scale * 100).toFixed(0), "%"] })] })), jsx(Button, { square: true, flat: true, href: file.src, target: '_blank', children: jsx(Icon, { icon: jsx(OpenInNewRound, {}) }) }), jsx(Button, { square: true, flat: true, href: file.src, download: true, target: '_blank', children: jsx(Icon, { icon: jsx(FileDownloadOutlined, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleRotate(90), children: jsx(Icon, { icon: jsx(RotateRightRound, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleRotate(-90), children: jsx(Icon, { icon: jsx(RotateLeftRound, {}) }) }), files.length > 1 && (jsxs(Fragment, { children: [jsx(Button, { square: true, flat: true, onClick: () => handleSwitch(state.current - 1), children: jsx(Icon, { icon: jsx(KeyboardArrowLeftRound, {}) }) }), jsx(Button, { square: true, flat: true, onClick: () => handleSwitch(state.current + 1), children: jsx(Icon, { icon: jsx(KeyboardArrowRightRound, {}) }) })] }))] })] }));
2239
- }
2240
-
2241
- function usePreview() {
2242
- const ref = useRef(null);
2243
- const preview = (config) => {
2244
- const { items, modalProps, onClose, ...restProps } = config;
2245
- const handleClose = () => {
2246
- onClose?.();
2247
- unMount?.();
2248
- };
2249
- const unMount = renderNode(jsx(HookModal, { ref: ref, visible: true, className: 'i-preview', customized: true, shadow: false, ...modalProps, children: jsx(Content, { ...restProps, items: items, onClose: () => {
2250
- ref.current?.update({ visible: false });
2251
- } }), fixed: true, onClose: handleClose }));
2252
- };
2253
- return preview;
2254
- }
2255
-
2256
- const Image = (props) => {
2257
- const { src, round, size, initSize, lazyload, fallback = (jsx(Icon, { icon: jsx(HideImageTwotone, {}), size: '2em', className: 'color-5' })), fit, style, className, children, usePreview: previewable, onLoad, onError, onClick, ...restProps } = props;
2258
- const state = useReactive({
2259
- status: "loading",
2260
- });
2261
- const ref = useRef(null);
2262
- const wh = fit ? "100%" : undefined;
2263
- const { observe, unobserve } = useIntersectionObserver();
2264
- const preview = usePreview();
2265
- const handleError = (err) => {
2266
- onError?.(err);
2267
- state.status = "error";
2268
- };
2269
- const handleLoad = (e) => {
2270
- onLoad?.(e);
2271
- state.status = undefined;
2272
- };
2273
- const handleClick = (e) => {
2274
- onClick?.(e);
2275
- previewable &&
2276
- src &&
2277
- preview({
2278
- items: [
2279
- {
2280
- src,
2281
- type: TFileType.IMAGE,
2282
- },
2283
- ],
2284
- });
2285
- };
2286
- useEffect(() => {
2287
- if (!src)
2288
- return;
2289
- state.status = "loading";
2290
- if (!lazyload || !ref.current)
2291
- return;
2292
- observe(ref.current, (tar, visible) => {
2293
- if (!visible)
2294
- return;
2295
- tar.setAttribute("src", tar.dataset.src || "");
2296
- unobserve(tar);
2297
- });
2298
- return () => {
2299
- ref.current && unobserve(ref.current);
2300
- };
2301
- }, [src]);
2302
- restProps[lazyload ? "data-src" : "src"] = src;
2303
- const iSize = state.status === "loading" ? initSize : undefined;
2304
- return (jsx("div", { style: {
2305
- width: size ?? iSize,
2306
- height: size ?? iSize,
2307
- ...style,
2308
- }, className: classNames("i-image", className, {
2309
- rounded: round,
2310
- [`i-image-${state.status}`]: state.status,
2311
- }), children: state.status === "error" ? (fallback) : (jsxs(Fragment, { children: [src && (jsx("img", { ref: ref, style: { objectFit: fit, width: wh, height: wh }, ...restProps, onLoad: handleLoad, onError: handleError, onClick: handleClick })), src && state.status === "loading" && jsx(Loading, {}), children && jsx("div", { className: 'i-image-text', children: children })] })) }));
2312
- };
2313
-
2314
- function List(props) {
2315
- const { items = [], gap = 8, columns, wrap, direction, usePreview: previewable, onClick, ...restProps } = props;
2316
- const preview = usePreview();
2317
- const files = useMemo(() => {
2318
- return items.map((item) => {
2319
- const o = {
2320
- src: "",
2321
- };
2322
- if (typeof item === "string") {
2323
- o.src = item;
2324
- }
2325
- else {
2326
- Object.assign(o, item);
2327
- }
2328
- o.suffix = getSuffixByUrl(o.src) || "";
2329
- o.type = getFileType(o.suffix, item["type"]);
2330
- return o;
2331
- });
2332
- }, [items]);
2333
- const handleClick = (e, i) => {
2334
- onClick?.(e);
2335
- previewable &&
2336
- preview({
2337
- items: files,
2338
- initial: i,
2339
- });
2340
- };
2341
- if (!files.length)
2342
- return "";
2343
- return (jsx(Flex, { className: 'i-image-list', gap: gap, columns: columns, wrap: wrap, direction: direction, children: files.map((img, i) => {
2344
- return (jsx(Image, { src: img.src, size: 60, usePreview: false, onClick: (e) => handleClick(e, i), ...restProps }, i));
2345
- }) }));
2346
- }
2347
-
2348
- Image.List = List;
2349
-
2350
- const AlignMap = {
2351
- left: "flex-start",
2352
- center: "center",
2353
- right: "flex-end",
2354
- };
2355
- const GlobalConfig = {
2356
- align: "center",
2357
- offset: "12px",
2358
- gap: 12,
2359
- max: 0,
2360
- };
2361
- const ItemDefaultConfig = {
2362
- duration: 3000,
2363
- closable: true,
2364
- active: false,
2365
- };
2366
- const container = createContainer();
2367
- const handler = {
2368
- callout(item) { },
2369
- close() { },
2370
- };
2371
- const queue = {
2372
- left: [],
2373
- center: [],
2374
- right: [],
2375
- };
2376
- const heights = {
2377
- left: [],
2378
- center: [],
2379
- right: [],
2380
- };
2381
- createRoot(container).render(jsx(Messages, {}));
2382
- const MessageItem = forwardRef(function ({ active, content, top, className, style, onClick }, ref) {
2383
- return (jsx("div", { ref: ref, className: classNames("i-message", className, {
2384
- "i-message-active": active,
2385
- }), style: {
2386
- ...style,
2387
- top,
2388
- }, onClick: onClick, children: content }));
2389
- });
2390
- function Messages() {
2391
- const ref = useRef(null);
2392
- const state = useReactive({
2393
- items: {
2394
- left: [],
2395
- center: [],
2396
- right: [],
2397
- },
2398
- tops: {
2399
- left: [],
2400
- center: [],
2401
- right: [],
2402
- },
2403
- });
2404
- const offsetTop = {
2405
- left: 0,
2406
- center: 0,
2407
- right: 0,
2408
- };
2409
- useEffect(() => {
2410
- Object.assign(handler, {
2411
- callout: function (item) {
2412
- const { align = "center", unshift, onShow } = item;
2413
- const size = queue[align][unshift ? "unshift" : "push"](item);
2414
- state.items[align] = [...queue[align]];
2415
- setTimeout(() => {
2416
- const h = ref.current?.offsetHeight || 0;
2417
- queue[align][unshift ? 0 : size - 1].active = true;
2418
- state.items[align] = [...queue[align]];
2419
- heights[align][unshift ? "unshift" : "push"](h);
2420
- state.tops[align] = [...heights[align]];
2421
- onShow?.();
2422
- }, 0);
2423
- item.duration !== 0 &&
2424
- setTimeout(this.close.bind(item), item.duration);
2425
- },
2426
- close: function () {
2427
- const item = this;
2428
- const { align = "center", onHide } = item;
2429
- const index = queue[align].findIndex((i) => i.id === item.id);
2430
- if (index < 0)
2431
- return;
2432
- queue[align][index].active = false;
2433
- state.items[align] = [...queue[align]];
2434
- item.timer = setTimeout(() => {
2435
- const index = queue[align].findIndex((i) => i.id === item.id);
2436
- queue[align].splice(index, 1);
2437
- heights[align].splice(index, 1);
2438
- state.tops[align] = [...heights[align]];
2439
- state.items[align] = [...queue[align]];
2440
- item.timer && clearTimeout(item.timer);
2441
- onHide?.();
2442
- }, 240);
2443
- },
2444
- });
2445
- }, []);
2446
- const renderItems = (item, i) => {
2447
- if (!item)
2448
- return jsx(Fragment, {});
2449
- const { id, active, content, align = "center", className } = item;
2450
- offsetTop[align] += state.tops[align][i - 1] || 0;
2451
- const top = GlobalConfig.gap * i + offsetTop[align];
2452
- return (jsx(MessageItem, { ref: ref, active: active, content: content, top: top, className: className, style: { alignSelf: AlignMap[align] }, onClick: handler.close.bind(item) }, id));
2453
- };
2454
- return (jsxs("div", { className: 'i-messages', children: [state.items.left.map(renderItems), state.items.center.map(renderItems), state.items.right.map(renderItems)] }));
2455
- }
2456
- function message(config) {
2457
- if (["string", "number"].includes(typeof config) ||
2458
- isValidElement(config)) {
2459
- config = { content: config };
2460
- }
2461
- config = Object.assign({ id: uid(7) }, ItemDefaultConfig, config);
2462
- handler.callout(config);
2463
- return handler.close.bind(config);
2464
- }
2465
- function createContainer(direction) {
2466
- const container = document.createElement("div");
2467
- container.dataset.id = "messages";
2468
- document.body.append(container);
2469
- return container;
2470
- }
2471
-
2472
- function useModal() {
2473
- const ref = useRef(null);
2474
- const handleOpen = (props) => {
2475
- const unMount = renderNode(jsx(HookModal, { ref: ref, visible: true, ...props, onClose: () => {
2476
- props.onClose?.();
2477
- unMount?.();
2478
- } }));
2479
- };
2480
- const handleUpdate = (props) => {
2481
- if (!ref.current)
2482
- return;
2483
- const { update } = ref.current;
2484
- update(props);
2485
- };
2486
- return {
2487
- open: handleOpen,
2488
- update: handleUpdate,
2489
- };
2490
- }
2491
-
2492
- Modal.useModal = useModal;
2493
-
2494
- const Page = (props) => {
2495
- const { page, active, children, onChange } = props;
2496
- const [loading, setLoading] = useState(false);
2497
- const handleClick = async () => {
2498
- if (active || loading)
2499
- return;
2500
- setLoading(true);
2501
- await onChange?.(page);
2502
- setLoading(false);
2503
- };
2504
- return (jsxs("a", { className: classNames("i-page", {
2505
- "i-page-active": active,
2506
- "i-page-loading": loading,
2507
- "i-page-disabled": false,
2508
- }), "data-page": page, onClick: handleClick, children: [loading && jsx(Loading, {}), children] }));
2509
- };
2510
-
2511
- const Pagination = (props) => {
2512
- const { page: defaultPage = 1, size = 10, total = 0, sibling = 2, prev = jsx(Icon, { icon: jsx(KeyboardArrowLeftRound, {}) }), next = jsx(Icon, { icon: jsx(KeyboardArrowRightRound, {}) }), simple, jumper, className, renderEllipsis = () => (jsx(Icon, { icon: jsx(MoreHorizRound, {}), className: 'color-7' })), renderPage = (i) => i, onChange, ...restProps } = props;
2513
- const [page, setPage] = useState(defaultPage);
2514
- const [loading, setLoading] = useState(false);
2515
- const totalPage = useMemo(() => Math.ceil(total / size), [size, total]);
2516
- const [start, end, loop] = useMemo(() => {
2517
- const start = Math.max(1, page - sibling);
2518
- const end = Math.min(totalPage, page + sibling);
2519
- return [
2520
- start,
2521
- end,
2522
- Array.from({ length: end - start + 1 }).map((n, i) => start + i),
2523
- ];
2524
- }, [page, totalPage, sibling]);
2525
- if (totalPage <= page && page === 1)
2526
- return jsx(Fragment, {});
2527
- const handlePageChange = async (p) => {
2528
- if (!onChange || loading)
2529
- return;
2530
- setLoading(true);
2531
- return new Promise(async (resolve) => {
2532
- if (p === undefined)
2533
- return;
2534
- await onChange(p);
2535
- setPage(p);
2536
- setLoading(false);
2537
- resolve();
2538
- });
2539
- };
2540
- useEffect(() => setPage(defaultPage), [defaultPage]);
2541
- return (jsxs("div", { className: classNames("i-pagination", className), ...restProps, children: [prev && (jsx(Page, { page: page - 1 || 1, onChange: handlePageChange, children: prev })), start > 1 && (jsx(Page, { page: 1, onChange: handlePageChange, children: renderPage(1) })), start > 2 && renderEllipsis(), loop.map((p) => {
2542
- return (jsx(Page, { page: p, active: p === page, onChange: handlePageChange, children: renderPage(p) }, p));
2543
- }), end < totalPage - 1 && renderEllipsis(), end < totalPage && (jsx(Page, { page: totalPage, onChange: handlePageChange, children: renderPage(totalPage) })), next && (jsx(Page, { page: page + 1, onChange: handlePageChange, children: next }))] }));
2544
- };
2545
-
2546
- const defaultOk = {
2547
- children: "确定",
2548
- };
2549
- const defaultCancel = {
2550
- children: "取消",
2551
- secondary: true,
2552
- };
2553
- const Popconfirm = (props) => {
2554
- const { trigger = "click", visible, icon = jsx(Icon, { icon: jsx(InfoOutlined, {}), className: 'error' }), content, okButtonProps, cancelButtonProps, children, align = "end", position = "top", offset = 12, extra, onOk, onClose, ...restProps } = props;
2555
- const state = useReactive({
2556
- loading: false,
2557
- visible,
2558
- });
2559
- const ok = okButtonProps
2560
- ? Object.assign({}, defaultOk, okButtonProps)
2561
- : defaultOk;
2562
- const cancel = cancelButtonProps
2563
- ? Object.assign({}, defaultCancel, cancelButtonProps)
2564
- : defaultCancel;
2565
- const handleVisibleChange = (v) => {
2566
- state.visible = v;
2567
- restProps.onVisibleChange?.(v);
2568
- };
2569
- const handleOk = async (e) => {
2570
- state.loading = true;
2571
- ok.onClick?.(e);
2572
- await onOk?.();
2573
- state.loading = false;
2574
- state.visible = false;
2575
- };
2576
- const handleCancel = async (e) => {
2577
- cancel.onClick?.(e);
2578
- await onClose?.();
2579
- state.visible = false;
2580
- };
2581
- const popconfirmContent = (jsxs("div", { className: 'i-popconfirm', children: [jsxs(Flex, { gap: 12, children: [icon, jsx("div", { className: 'i-popconfirm-content', children: content })] }), jsxs(Flex, { gap: 12, justify: 'flex-end', className: 'mt-8 i-popconfirm-footer', children: [cancelButtonProps !== null && (jsx(Button, { ...cancel, onClick: handleCancel })), extra, okButtonProps !== null && (jsx(Button, { loading: state.loading, ...ok, onClick: handleOk }))] })] }));
2582
- return (jsx(Popup, { content: popconfirmContent, ...restProps, trigger: trigger, visible: state.visible, align: align, offset: offset, position: position, onVisibleChange: handleVisibleChange, children: children }));
2583
- };
2584
-
2585
- function RadioItem(props) {
2586
- const { type = "default", name, value, checked, disabled, children, onChange, } = props;
2587
- const handleChange = (e) => {
2588
- onChange?.(value, e);
2589
- };
2590
- return (jsxs("label", { className: classNames("i-radio-item", { disabled }), children: [jsx("input", { type: 'radio', name: name, checked: checked, className: classNames("i-radio-input", `i-radio-${type}`), disabled: disabled, onChange: handleChange }), jsx("span", { className: 'i-radio-text', children: children })] }));
2591
- }
2592
-
2593
- function Radio(props) {
2594
- const { label, name, options, value, type = "default", status = "normal", message, optionInline = true, labelInline, disabled, required, className, onChange, } = props;
2595
- const state = useReactive({
2596
- value,
2597
- });
2598
- const formattedOptions = useMemo(() => formatOption(options), [options]);
2599
- const handleChange = useMemoizedFn((value, e) => {
2600
- state.value = value;
2601
- onChange?.(value, e);
2602
- });
2603
- useEffect(() => {
2604
- state.value = value;
2605
- }, [value]);
2606
- return (jsxs("div", { className: classNames("i-radio i-input-label", {
2607
- [`i-radio-${status}`]: status !== "normal",
2608
- "i-input-inline": labelInline,
2609
- }, className), children: [label && (jsxs("span", { className: 'i-input-label-text', children: [required && jsx("span", { className: 'error', children: "*" }), label, message && jsx("p", { className: 'i-radio-message', children: message })] })), jsx("div", { className: classNames("i-radio-options", {
2610
- "i-options-block": !optionInline,
2611
- "i-radio-options-button": type === "button",
2612
- }), children: formattedOptions.map((option) => (jsx(RadioItem, { name: name, value: option.value, checked: state.value === option.value, type: type, disabled: disabled || option.disabled, onChange: handleChange, children: option.label }, option.value))) })] }));
2613
- }
2614
- Radio.Item = RadioItem;
2615
-
2616
- const Tag = (props) => {
2617
- const { dot, dotClass, outline, round, size = "normal", className, children, onClose, onClick, ...restProps } = props;
2618
- return (jsxs("span", { className: classNames("i-tag", {
2619
- "i-tag-outline": outline,
2620
- "i-tag-clickable": onClick,
2621
- [`i-tag-${size}`]: size !== "normal",
2622
- round,
2623
- }, className), onClick: onClick, ...restProps, children: [dot && jsx("span", { className: classNames("i-tag-dot", dotClass) }), children, onClose && (jsx(Helpericon, { active: true, className: 'i-tag-close', onClick: onClose }))] }));
2624
- };
2625
-
2626
- const Options = (props) => {
2627
- const { value: val, options, filter, filterPlaceholder, multiple, empty = jsx(Empty, {}), onSelect, onFilter, } = props;
2628
- return (jsxs("div", { className: classNames("i-select-options", {
2629
- "i-select-options-multiple": multiple,
2630
- }), 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) => {
2631
- const { label, value, disabled } = option;
2632
- const isActive = multiple
2633
- ? val?.includes(value)
2634
- : val === value;
2635
- 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));
2636
- })] }));
2637
- };
2638
- const activeOptions = (options = [], value = [], max = 3) => {
2639
- const total = options.flatMap((opt) => value.includes(opt.value) ? [opt] : []);
2640
- if (max >= total.length)
2641
- return total;
2642
- const rest = total.length - max;
2643
- const after = total.slice(0, max);
2644
- after.push(rest);
2645
- return after;
2646
- };
2647
- const displayValue = (config) => {
2648
- const { options, value, maxDisplay, multiple, onSelect } = config;
2649
- if (multiple) {
2650
- return activeOptions(options, value, maxDisplay).map((opt, i) => {
2651
- if (typeof opt === "number")
2652
- return jsxs(Tag, { children: ["+", opt] }, i);
2653
- const { label, value } = opt;
2654
- return (jsx(Tag, { onClose: (e) => {
2655
- e?.stopPropagation();
2656
- onSelect?.(value, opt);
2657
- }, children: label }, value));
2658
- });
2659
- }
2660
- return options.find((opt) => opt.value === value)?.label;
2661
- };
2662
-
2663
- const Select = forwardRef((props, ref) => {
2664
- const { type = "text", name, label, value = "", placeholder, options = [], multiple, prepend, append, labelInline, style, className, message, status = "normal", hideClear, maxDisplay, border, filter, tip, filterPlaceholder = "...", popupProps, onSelect, onChange, ...restProps } = props;
2665
- const state = useReactive({
2666
- inputValue: "",
2667
- filterValue: "",
2668
- value,
2669
- loading: false,
2670
- });
2671
- const [active, setActive] = useState(false);
2672
- const formattedOptions = useMemo(() => formatOption(options), [options]);
2673
- const filterOptions = useMemo(() => {
2674
- const { filterValue: fv } = state;
2675
- if (!fv || !filter)
2676
- return formattedOptions;
2677
- const filterFn = typeof filter === "function"
2678
- ? filter
2679
- : (opt) => opt.value.includes(fv) || opt.label.includes(fv);
2680
- return formattedOptions.filter(filterFn);
2681
- }, [formattedOptions, filter, state.filterValue]);
2682
- const changeValue = (v) => {
2683
- state.value = v;
2684
- onChange?.(v);
2685
- };
2686
- const handleSelect = useCallback((value, option) => {
2687
- onSelect?.(value, option);
2688
- if (multiple) {
2689
- const values = [...state.value];
2690
- const i = values.findIndex((v) => v === value);
2691
- i > -1 ? values.splice(i, 1) : values.push(value);
2692
- changeValue(values);
2693
- return;
2694
- }
2695
- setActive(false);
2696
- changeValue(value);
2697
- }, []);
2698
- const handleVisibleChange = (visible) => {
2699
- setActive(visible);
2700
- if (!filter)
2701
- return;
2702
- state.filterValue = "";
2703
- };
2704
- const handleHelperClick = (e) => {
2705
- if (!active)
2706
- return;
2707
- changeValue(multiple ? [] : "");
2708
- };
2709
- const handleFilterChange = useMemoizedFn(debounce({ delay: 400 }, (e) => {
2710
- const v = e.target.value;
2711
- state.filterValue = v;
2712
- }));
2713
- const handleInputChange = (e) => {
2714
- state.filterValue = e.target.value;
2715
- };
2716
- useEffect(() => {
2717
- state.value = value;
2718
- }, [value]);
2719
- const hasValue = multiple
2720
- ? state.value.length > 0
2721
- : !!state.value;
2722
- const clearable = !hideClear && active && hasValue;
2723
- const text = message ?? tip;
2724
- return (jsxs("label", { className: classNames("i-input-label", className, {
2725
- "i-input-inline": labelInline,
2726
- }), style: style, children: [label && jsx("span", { className: 'i-input-label-text', children: label }), jsx(Popup, { position: 'bottom', arrow: false, fitSize: true, ...popupProps, visible: active, trigger: 'none', onVisibleChange: handleVisibleChange, content: jsx(Options, { options: filterOptions, value: state.value, multiple: multiple, filter: !!filter, filterPlaceholder: filterPlaceholder, onSelect: handleSelect, onFilter: handleFilterChange }), children: jsxs("div", { className: classNames("i-input-item", {
2727
- [`i-input-${status}`]: status !== "normal",
2728
- "i-input-borderless": !border,
2729
- "i-input-focus": active,
2730
- }), onClick: () => setActive(true), children: [prepend, jsx("input", { ref: ref, type: 'hidden', value: state.value, ...restProps }), multiple ? (hasValue ? (jsx("div", { className: classNames("i-input i-select", {
2731
- "i-select-multiple": multiple,
2732
- }), children: displayValue({
2733
- options: formattedOptions,
2734
- value: state.value,
2735
- multiple,
2736
- maxDisplay,
2737
- onSelect: handleSelect,
2738
- }) })) : (jsx("input", { className: 'i-input i-select', placeholder: placeholder, readOnly: true }))) : null, !multiple && (jsx("input", { value: active ? state.filterValue : state.value, className: 'i-input i-select', placeholder: state.value || placeholder, onChange: handleInputChange, readOnly: !filter })), jsx(Helpericon, { active: true, icon: clearable ? undefined : jsx(UnfoldMoreRound, {}), onClick: handleHelperClick }), append] }) }), text && jsx("span", { className: 'i-input-message', children: text })] }));
2739
- });
2740
-
2741
- function Divider() {
2742
- return jsx("i", { className: 'i-step-divider' });
2743
- }
2744
-
2745
- const STATUS = ["finished", "active", "pending"];
2746
- function defaultRenderIcon(i, status) {
2747
- return (jsx("span", { className: 'i-step-item-index', children: status === "finished" ? (jsx(CheckRound, { style: { width: "1em", height: "1.5em" } })) : (i + 1) }));
2748
- }
2749
- function Item$2(props) {
2750
- const { index = 0, active = 0, renderIcon = defaultRenderIcon, title, vertical, line = jsx(Divider, {}), style, className, children, onClick, } = props;
2751
- const status = STATUS[index === active ? 1 : index < active ? 0 : 2];
2752
- const handleClick = () => {
2753
- onClick?.(index);
2754
- };
2755
- return (jsx("div", { style: style, className: classNames("i-step-item", `i-step-item-${status}`, className), onClick: handleClick, children: vertical ? (jsxs(Fragment, { children: [jsxs("div", { className: 'i-step-item-left', children: [renderIcon?.(index, status), line] }), jsxs("div", { className: 'i-step-item-right', children: [jsx("div", { className: 'i-step-item-title', children: title }), children && (jsx("div", { className: 'i-step-item-content', children: children }))] })] })) : (jsxs(Fragment, { children: [jsxs("div", { className: 'i-step-item-title', children: [renderIcon?.(index, status), jsx("span", { children: title }), line] }), children && (jsx("div", { className: 'i-step-item-content', children: children }))] })) }));
2756
- }
2757
-
2758
- const Step = (props) => {
2759
- const { active = 0, vertical, renderIcon, line, style, className, children, onClick, } = props;
2760
- const steps = useMemo(() => {
2761
- const nodes = [];
2762
- let index = 0;
2763
- Children.map(children, (el) => {
2764
- if (!el || el.type !== Item$2)
2765
- return;
2766
- const { props: elProps } = el;
2767
- nodes.push({
2768
- ...el,
2769
- props: {
2770
- renderIcon,
2771
- line,
2772
- onClick,
2773
- ...elProps,
2774
- vertical,
2775
- active,
2776
- index: index++,
2777
- },
2778
- });
2779
- });
2780
- return nodes;
2781
- }, [active, children]);
2782
- return (jsx("div", { className: classNames("i-step", { "i-step-vertical": vertical }, className), style: style, children: steps }));
2783
- };
2784
- Step.Item = Item$2;
2785
-
2786
- function Item$1(props) {
2787
- const { index = 0, active, type, transition, gap = 0, itemHeight, vertical, style, className, children, } = props;
2788
- const selfStyle = useMemo(() => {
2789
- if (type === "normal") {
2790
- return {
2791
- [vertical ? "paddingBlock" : "paddingInline"]: gap / 2,
2792
- height: itemHeight,
2793
- };
2794
- }
2795
- return {
2796
- transform: `translate(-${index * 100}%, 0)`,
2797
- transition,
2798
- };
2799
- }, [index, gap, itemHeight, vertical, type]);
2800
- return (jsx("div", { style: { ...style, ...selfStyle }, className: classNames("i-swiper-item", className, {
2801
- "i-swiper-active": active,
2802
- }), "data-index": index, children: children }));
2803
- }
2804
-
2805
- const Swiper = forwardRef((props, ref) => {
2806
- const { type = "normal", initial = 0, display = 1, scroll = 1, loop = true, vertical, prev = jsx(Icon, { icon: jsx(KeyboardArrowLeftRound, {}), size: '2em' }), next = jsx(Icon, { icon: jsx(KeyboardArrowRightRound, {}), size: '2em' }), duration = 600, interval = 3000, autoplay, pauseOnHover, arrow = true, reverse, draggable, dragOffset = 40, gap = 0, itemHeight, indicator, style, className, children, renderIndicator, onBeforeSwipe, onAfterSwipe, } = props;
2807
- const listRef = useRef(null);
2808
- const timerRef = useRef(null);
2809
- const transition = `all ${duration / 1000}s`;
2810
- const state = useReactive({
2811
- current: initial,
2812
- swipable: true,
2813
- transition: type === "fade" ? "none" : transition,
2814
- dragStart: 0,
2815
- dragging: false,
2816
- initialized: false,
2817
- });
2818
- const items = useMemo(() => {
2819
- return Children.map(children, (node) => {
2820
- if (node.type !== Item$1)
2821
- return;
2822
- return node;
2823
- });
2824
- }, [children]);
2825
- const [displayItems, extra, size, total, listSize] = useMemo(() => {
2826
- const extra = type === "normal" && loop && items.length > display
2827
- ? display + 1
2828
- : 0;
2829
- let list = [];
2830
- if (extra <= 0) {
2831
- list = [...items];
2832
- }
2833
- else {
2834
- const head = items.slice(0, extra);
2835
- const tail = items.slice(-extra);
2836
- list = [...tail, ...items, ...head];
2837
- }
2838
- const listSize = `${(list.length / display) * 100}%`;
2839
- return [list, extra, items.length, list.length, listSize];
2840
- }, [display, loop, type, items]);
2841
- const offsetPercent = useMemo(() => (-100 * (state.current + extra)) / total, [state.current, total]);
2842
- const position = useMemo(() => {
2843
- if (size <= display || type === "fade")
2844
- return;
2845
- const offset = vertical
2846
- ? `0, ${offsetPercent}%`
2847
- : `${offsetPercent}%, 0`;
2848
- return `translate3d(${offset}, 0)`;
2849
- }, [offsetPercent, vertical, display, size, type]);
2850
- const trackStyle = useMemo(() => {
2851
- if (!vertical || !itemHeight)
2852
- return;
2853
- return {
2854
- height: itemHeight * display,
2855
- };
2856
- }, [vertical, itemHeight, display]);
2857
- const indicatorsLoop = useMemo(() => {
2858
- return Array.from({
2859
- length: Math.ceil((size - display) / scroll) + 1,
2860
- });
2861
- }, [loop, indicator]);
2862
- const clearTimer = () => {
2863
- clearTimeout(timerRef.current);
2864
- timerRef.current = null;
2865
- };
2866
- const swipeTo = useCallback((i) => {
2867
- if (!state.swipable || i === state.current)
2868
- return;
2869
- state.swipable = false;
2870
- onBeforeSwipe?.(state.current);
2871
- let reset = false;
2872
- let next = i;
2873
- const lastDisplay = size - display;
2874
- if (loop) {
2875
- if (i > lastDisplay) {
2876
- reset = true;
2877
- i = size;
2878
- next = 0;
2879
- }
2880
- else if (i < 0) {
2881
- reset = true;
2882
- i = -display;
2883
- next = lastDisplay;
2884
- }
2885
- }
2886
- else {
2887
- next = clamp(next, 0, lastDisplay);
2888
- i = next;
2889
- }
2890
- setTimeout(() => {
2891
- state.swipable = true;
2892
- }, duration + 32);
2893
- if (type === "fade") {
2894
- state.current = next;
2895
- onAfterSwipe?.(next);
2896
- return;
2897
- }
2898
- state.current = i;
2899
- if (!reset) {
2900
- if (autoplay) {
2901
- timerRef.current = setTimeout(swipeNext, interval);
2902
- }
2903
- setTimeout(() => {
2904
- onAfterSwipe?.(next);
2905
- }, duration + 12);
2906
- return;
2907
- }
2908
- setTimeout(() => {
2909
- state.transition = "none";
2910
- state.current = next;
2911
- onAfterSwipe?.(next);
2912
- if (autoplay) {
2913
- timerRef.current = setTimeout(swipeNext, interval);
2914
- }
2915
- setTimeout(() => {
2916
- state.transition = transition;
2917
- }, 60);
2918
- }, duration + 20);
2919
- }, [duration, autoplay]);
2920
- const swipeNext = () => {
2921
- swipeTo(reverse ? state.current - scroll : state.current + scroll);
2922
- };
2923
- const swipePrev = () => {
2924
- swipeTo(reverse ? state.current + scroll : state.current - scroll);
2925
- };
2926
- const handleMouseDown = useCallback((e) => {
2927
- if (!draggable || !state.swipable || type === "fade")
2928
- return;
2929
- e.stopPropagation();
2930
- Object.assign(state, {
2931
- dragStart: vertical ? e.clientY : e.clientX,
2932
- dragging: true,
2933
- transition: "none",
2934
- });
2935
- }, [draggable, vertical]);
2936
- const handleMouseMove = useCallback((e) => {
2937
- if (!state.dragging || !listRef.current)
2938
- return;
2939
- e.preventDefault();
2940
- const dragEnd = vertical ? e.clientY : e.clientX;
2941
- const offset = ((dragEnd - state.dragStart) * 61.8) /
2942
- listRef.current[vertical ? "offsetHeight" : "offsetWidth"] +
2943
- offsetPercent;
2944
- listRef.current.style.transform = `translate3d(${vertical ? `0, ${offset}%` : `${offset}%, 0`}, 0)`;
2945
- }, [vertical, listRef.current, offsetPercent]);
2946
- const handleMouseUp = useCallback((e) => {
2947
- if (!state.dragging || !listRef.current)
2948
- return;
2949
- const dragEnd = vertical ? e.clientY : e.clientX;
2950
- const part = listRef.current[vertical ? "offsetHeight" : "offsetWidth"] /
2951
- total;
2952
- const offset = (dragEnd - state.dragStart) * 0.618;
2953
- const absOffset = Math.abs(offset);
2954
- if (absOffset > dragOffset) {
2955
- const base = Math.floor(absOffset / part);
2956
- const mod = (absOffset % part) - dragOffset > 0 ? 1 : 0;
2957
- const p = base + mod;
2958
- let to = state.current + (offset > 0 ? -p : p);
2959
- swipeTo(to);
2960
- }
2961
- listRef.current.style.transform = position || "";
2962
- Object.assign(state, {
2963
- dragging: false,
2964
- transition,
2965
- });
2966
- }, [vertical, listRef.current, offsetPercent]);
2967
- const handleMouseOver = () => {
2968
- if (!pauseOnHover)
2969
- return;
2970
- clearTimer();
2971
- };
2972
- const handleMouseLeave = () => {
2973
- if (!pauseOnHover)
2974
- return;
2975
- clearTimer();
2976
- timerRef.current = setTimeout(swipeNext, interval);
2977
- };
2978
- useMouseMove(handleMouseMove);
2979
- useMouseUp(handleMouseUp);
2980
- useImperativeHandle(ref, () => ({
2981
- swipeTo,
2982
- swipeNext,
2983
- swipePrev,
2984
- }));
2985
- useEffect(() => {
2986
- if (!autoplay)
2987
- return;
2988
- timerRef.current = setTimeout(swipeNext, interval);
2989
- return () => {
2990
- clearTimeout(timerRef.current);
2991
- timerRef.current = null;
2992
- };
2993
- }, [autoplay, interval]);
2994
- return (jsxs("div", { style: style, className: classNames("i-swiper", {
2995
- "i-swiper-vertical": vertical,
2996
- "i-swiper-initialized": state.initialized,
2997
- }, className), children: [jsxs("div", { className: 'i-swiper-track', style: trackStyle, onMouseOver: handleMouseOver, onMouseLeave: handleMouseLeave, children: [jsx("div", { ref: listRef, className: classNames("i-swiper-list", {
2998
- "i-swiper-fade": type === "fade",
2999
- }), style: {
3000
- [vertical ? "height" : "width"]: listSize,
3001
- transform: position,
3002
- transition: state.transition,
3003
- }, onMouseDown: handleMouseDown, children: displayItems.map((item, i) => {
3004
- const { props: itemProps } = item;
3005
- return (jsx(Item$1, { index: i, active: i - extra === state.current, type: type, gap: gap, transition: transition, itemHeight: itemHeight, vertical: vertical, ...itemProps }, i));
3006
- }) }), arrow && (jsxs(Fragment, { children: [jsx("a", { className: 'i-swiper-arrow i-swiper-prev', onClick: swipePrev, children: prev }), jsx("a", { className: 'i-swiper-arrow i-swiper-next', onClick: swipeNext, children: next })] }))] }), indicator && (jsx("div", { className: 'i-swiper-indicators', children: indicatorsLoop.map((whatever, i) => {
3007
- return (jsx("a", { className: classNames("i-swiper-indicator", {
3008
- "i-indicator-active": i ===
3009
- Math[loop ? "floor" : "ceil"](((state.current + size) % size) /
3010
- scroll),
3011
- }), onClick: () => swipeTo(i * scroll), children: renderIndicator?.(i) }, i));
3012
- }) }))] }));
3013
- });
3014
- Swiper.Item = Item$1;
3015
-
3016
- const Item = (props) => {
3017
- return jsx(Fragment, {});
3018
- };
3019
-
3020
- const Tabs = forwardRef((props, ref) => {
3021
- const { active, tabs: items, type = "default", prepend, append, children, className, vertical, toggable, bar = true, hideMore, barClass, renderMore = () => (jsx(Button, { flat: true, square: true, size: 'small', children: jsx(Icon, { icon: jsx(MoreHorizRound, {}) }) })), onTabChange, ...rest } = props;
3022
- const navRefs = useRef([]);
3023
- const barRef = useRef(null);
3024
- const navsRef = useRef(null);
3025
- const state = useReactive({
3026
- active,
3027
- prevActive: undefined,
3028
- barStyle: {},
3029
- cachedTabs: [],
3030
- overflow: false,
3031
- more: [],
3032
- tabs: [],
3033
- });
3034
- const { observe, unobserve } = useIntersectionObserver();
3035
- const size = useSize(navsRef);
3036
- useEffect(() => {
3037
- if (!items) {
3038
- if (!children) {
3039
- state.tabs = [];
3040
- return;
3041
- }
3042
- state.tabs = Children.map(children, (node, i) => {
3043
- const { key, props: nodeProps } = node;
3044
- const { title, children, content, keepDOM } = nodeProps;
3045
- const cloned = children
3046
- ? pick(children, ["props", "type", "$$typeof", "ref"])
3047
- : content;
3048
- return {
3049
- key: key || String(i),
3050
- title,
3051
- content: cloned,
3052
- keepDOM,
3053
- };
3054
- });
3055
- return;
3056
- }
3057
- state.tabs = items.map((item, i) => {
3058
- if (["string", "number"].includes(typeof item)) {
3059
- return { key: item, title: item };
3060
- }
3061
- if (item.key === undefined) {
3062
- item.key = i;
3063
- }
3064
- return item;
3065
- });
3066
- }, [children, items]);
3067
- const add = (tab) => {
3068
- const { key } = tab;
3069
- const i = state.tabs.findIndex((t) => t.key === key);
3070
- if (i > -1) {
3071
- open(state.tabs[i].key ?? i);
3072
- return;
3073
- }
3074
- const l = state.tabs.length;
3075
- const tkey = tab.key ?? l;
3076
- state.tabs.push({ ...tab, key: tkey });
3077
- open(tkey);
3078
- };
3079
- const close = (key) => {
3080
- const i = state.tabs.findIndex((t) => t.key === key);
3081
- if (i < 0)
3082
- return;
3083
- state.tabs.splice(i, 1);
3084
- if (state.active !== key)
3085
- return;
3086
- const next = state.tabs[i] || state.tabs[i - 1];
3087
- open(state.prevActive ?? next?.key ?? "");
3088
- };
3089
- const open = (key) => {
3090
- if (key === state.active) {
3091
- if (!toggable)
3092
- return;
3093
- onTabChange?.(undefined, key);
3094
- state.active = undefined;
3095
- state.barStyle = {
3096
- height: 0,
3097
- width: 0,
3098
- };
3099
- return;
3100
- }
3101
- state.prevActive = state.active;
3102
- onTabChange?.(key, state.active);
3103
- state.active = key;
3104
- };
3105
- const handleMouseWheel = (e) => {
3106
- e.stopPropagation();
3107
- e.preventDefault();
3108
- if (vertical)
3109
- return;
3110
- navsRef.current?.scrollBy({
3111
- left: e.deltaY + e.deltaX,
3112
- });
3113
- };
3114
- useEffect(() => {
3115
- if (!size || hideMore)
3116
- return;
3117
- const { scrollHeight, scrollWidth } = navsRef.current;
3118
- const { width, height } = size;
3119
- state.overflow = scrollHeight > height || scrollWidth > width;
3120
- if (!state.overflow)
3121
- return;
3122
- navRefs.current.map((nav, i) => {
3123
- if (!nav)
3124
- return;
3125
- observe(nav, (tar, visible) => {
3126
- if (!state.tabs[i])
3127
- return;
3128
- state.tabs[i].intersecting = visible;
3129
- state.more = state.tabs.filter((tab) => !tab.intersecting);
3130
- });
3131
- });
3132
- }, [size, hideMore, state.tabs.length]);
3133
- useEffect(() => {
3134
- if (!bar || type === "pane" || state.active === undefined) {
3135
- return;
3136
- }
3137
- const index = state.tabs.findIndex((tab) => tab.key === state.active);
3138
- setTimeout(() => {
3139
- const nav = navRefs.current[index];
3140
- if (!nav)
3141
- return;
3142
- if (state.tabs[index].keepDOM && state.active) {
3143
- const i = state.cachedTabs.findIndex((k) => k === state.active);
3144
- i < 0 && state.cachedTabs.unshift(state.active);
3145
- }
3146
- const { offsetHeight, offsetLeft, offsetTop, offsetWidth } = nav;
3147
- const isLine = type === "line";
3148
- state.barStyle = {
3149
- height: !vertical && isLine ? ".25em" : offsetHeight,
3150
- width: vertical && isLine ? ".25em" : offsetWidth,
3151
- transform: `translate(${offsetLeft}px, ${offsetTop}px)`,
3152
- };
3153
- }, 16);
3154
- }, [state.active, bar]);
3155
- useEffect(() => {
3156
- if (active === undefined || state.active === active)
3157
- return;
3158
- open(active);
3159
- }, [active]);
3160
- useEffect(() => {
3161
- if (hideMore)
3162
- return;
3163
- return () => {
3164
- navRefs.current?.map(unobserve);
3165
- };
3166
- }, [state.tabs.length]);
3167
- useEffect(() => {
3168
- if (!navsRef.current || vertical)
3169
- return;
3170
- navsRef.current.addEventListener("wheel", handleMouseWheel, {
3171
- passive: false,
3172
- });
3173
- return () => {
3174
- if (!navsRef.current)
3175
- return;
3176
- navsRef.current.removeEventListener("wheel", handleMouseWheel);
3177
- };
3178
- }, [navsRef.current]);
3179
- useImperativeHandle(ref, () => ({
3180
- open,
3181
- close,
3182
- add,
3183
- navs: navsRef,
3184
- }));
3185
- return (jsxs("div", { className: classNames("i-tabs", { flex: vertical, [`i-tabs-${type}`]: type !== "default" }, className), ...rest, children: [jsxs("div", { className: classNames("i-tab-navs-container", {
3186
- "i-tab-navs-vertical": vertical,
3187
- }), children: [prepend, jsxs("div", { ref: navsRef, className: 'i-tab-navs', children: [state.tabs.map((tab, i) => {
3188
- const { title, key = i, closable } = tab;
3189
- return (jsxs("a", { ref: (ref) => (navRefs.current[i] =
3190
- ref), className: classNames("i-tab-nav", {
3191
- "i-tab-active": state.active === key,
3192
- }), onClick: () => open(key), children: [title, closable && (jsx(Helpericon, { as: 'i', active: true, className: 'i-tab-nav-close', onClick: (e) => {
3193
- e.stopPropagation();
3194
- close(key);
3195
- } }))] }, key));
3196
- }), bar && (jsx("span", { ref: barRef, className: classNames("i-tab-navs-bar", barClass), style: state.barStyle }))] }), !hideMore && state.overflow && state.more.length > 0 && (jsx(Popup, { arrow: false, position: vertical ? "right" : "bottom", align: 'end', touchable: true, hideDelay: 500, content: jsx("div", { className: 'i-tabs-morelist pd-4', children: state.more.map((tab, i) => {
3197
- const { key = i, title } = tab;
3198
- const isActive = state.active === key;
3199
- return (jsx("a", { className: classNames("i-tab-nav", {
3200
- "i-tab-active": isActive,
3201
- }), onClick: () => open(key), children: title }, key));
3202
- }) }), children: renderMore(state.more) })), append] }), jsx("div", { className: 'i-tab-contents', children: state.tabs.map((tab, i) => {
3203
- const { key = i, content } = tab;
3204
- const isActive = state.active === key;
3205
- const show = isActive ||
3206
- (key !== undefined && state.cachedTabs.includes(key));
3207
- return (show && (jsx("div", { className: classNames("i-tab-content", {
3208
- "i-tab-active": isActive,
3209
- }), children: content }, key)));
3210
- }) })] }));
3211
- });
3212
- Tabs.Item = Item;
3213
-
3214
- function TreeList(props) {
3215
- const { data = [], depth = 0, round, style, className, parent, nodeProps, ...restProps } = props;
3216
- const contents = data.map((item, i) => {
3217
- const { type } = item;
3218
- const title = item[nodeProps.title];
3219
- const itemKey = item[nodeProps.key] ||
3220
- (parent?.key !== undefined ? `${parent.key}-${i}` : `${i}`);
3221
- item.key = itemKey;
3222
- item.parent = parent;
3223
- if (type === "title") {
3224
- return (jsx("div", { className: 'i-tree-group-title', children: title }, i));
3225
- }
3226
- if (type && type !== "item") {
3227
- return (jsx("div", { className: `i-tree-type-${type}`, children: title }, i));
3228
- }
3229
- return (jsx(TreeItem, { index: i, item: item, depth: depth, nodeProps: nodeProps, ...restProps }, itemKey));
3230
- });
3231
- if (depth > 0)
3232
- return jsx(Fragment, { children: contents });
3233
- return (jsx("div", { className: classNames("i-tree", className, {
3234
- "i-tree-round": round,
3235
- }), style: style, children: contents }));
3236
- }
3237
- const Header = (props) => {
3238
- const { as: Tag = "a", href, selected, children, ...restProps } = props;
3239
- const className = classNames("i-tree-item-header", {
3240
- "i-tree-item-selected": selected,
3241
- });
3242
- if (typeof Tag === "string") {
3243
- return (jsx(Tag, { href: href, className: className, ...restProps, children: children }));
3244
- }
3245
- return (jsx(Tag, { to: href || "", className: className, ...restProps, children: children }));
3246
- };
3247
- const TreeItem = (props) => {
3248
- const { item, depth = 0, index, selected, checked = [], partofs = {}, checkable, nodeProps, renderExtra, onItemClick, onItemSelect, onItemCheck, ...restProps } = props;
3249
- const { as, key = "", href, icon, title, expanded, disabled } = item;
3250
- const children = item[nodeProps.children];
3251
- const [expand, setExpand] = useState(expanded);
3252
- const handleExpand = useMemoizedFn((e, fromToggle) => {
3253
- if (fromToggle) {
3254
- e.preventDefault();
3255
- e.stopPropagation();
3256
- }
3257
- if (disabled || !children?.length)
3258
- return;
3259
- setExpand((v) => !v);
3260
- });
3261
- const handleItemClick = useMemoizedFn((e) => {
3262
- if (disabled) {
3263
- e.preventDefault();
3264
- e.stopPropagation();
3265
- return;
3266
- }
3267
- handleExpand(e);
3268
- onItemClick?.(item, e);
3269
- onItemSelect?.(key, item);
3270
- });
3271
- const handleItemCheck = (checked) => onItemCheck?.(item, checked, []);
3272
- const itemChecked = checked.includes(key);
3273
- return (jsxs("div", { className: classNames("i-tree-item", {
3274
- "i-tree-expand": expand,
3275
- }), children: [jsxs(Header, { as: as, href: href, style: { paddingLeft: `${depth * 1.5 + 0.5}em` }, selected: selected === key, onClick: handleItemClick, children: [checkable && (jsx(Checkbox.Item, { value: itemChecked, partof: !itemChecked && partofs[key], className: 'i-tree-checkbox', onChange: handleItemCheck, onClick: (e) => e.stopPropagation() })), icon && jsx("span", { className: 'i-tree-item-icon', children: icon }), jsx("span", { className: 'i-tree-item-title', children: title }), renderExtra?.(item), children && (jsx(Icon, { icon: jsx(KeyboardArrowDownRound, {}), className: 'i-tree-toggle', onClick: (e) => handleExpand(e, true) }))] }), children?.length && (jsx("div", { className: 'i-tree-item-content', children: jsx(TreeList, { data: children, depth: depth + 1, selected: selected, checkable: checkable, parent: item, partofs: partofs, checked: checked, nodeProps: nodeProps, renderExtra: renderExtra, onItemClick: onItemClick, onItemSelect: onItemSelect, onItemCheck: onItemCheck, ...restProps }) }))] }));
3276
- };
3277
-
3278
- const defaultNodeProps = {
3279
- key: "key",
3280
- title: "title",
3281
- children: "children",
3282
- };
3283
- const Tree = forwardRef((props, ref) => {
3284
- const { data = [], selected, checked = [], disabledRelated, nodeProps, onItemSelect, onItemCheck, ...restProps } = props;
3285
- const state = useReactive({
3286
- selected,
3287
- checked,
3288
- partofs: {},
3289
- nodeMaps: new Map(),
3290
- });
3291
- const oNodeProps = Object.assign({}, defaultNodeProps, nodeProps);
3292
- const isChecked = (key) => state.checked.includes(key || "");
3293
- const checkItem = useMemoizedFn((item, checked, direction) => {
3294
- const { key = "", parent, children } = item;
3295
- const shouldChanged = { [key]: checked };
3296
- const partofs = { [key]: false };
3297
- if (disabledRelated)
3298
- return [shouldChanged];
3299
- if (checked) {
3300
- if (parent && direction !== "leaf") {
3301
- const hasUnchecked = parent.children?.some((o) => o.key !== key && !isChecked(o.key));
3302
- const [changes, parts] = checkItem(parent, true, "root");
3303
- if (!hasUnchecked) {
3304
- Object.assign(shouldChanged, changes);
3305
- }
3306
- Object.assign(partofs, hasUnchecked ? parts : {}, {
3307
- [parent.key]: true,
3308
- });
3309
- }
3310
- if (children?.length && direction !== "root") {
3311
- children.map((o) => {
3312
- if (isChecked(o.key))
3313
- return;
3314
- const [changes] = checkItem(o, true, "leaf");
3315
- Object.assign(shouldChanged, changes);
3316
- partofs[o.key] = false;
3317
- });
3318
- }
3319
- return [shouldChanged, partofs];
3320
- }
3321
- if (parent && direction !== "leaf") {
3322
- const [changes, parts] = checkItem(parent, false, "root");
3323
- Object.assign(shouldChanged, changes);
3324
- const hasChecked = parent.children?.some((o) => isChecked(o.key) && o.key !== key);
3325
- Object.assign(partofs, hasChecked ? {} : parts, {
3326
- [parent.key]: hasChecked,
3327
- [key]: false,
3328
- });
3329
- }
3330
- if (children?.length && direction !== "root") {
3331
- children.map((o) => {
3332
- const [changes] = checkItem(o, false, "leaf");
3333
- if (!isChecked(o.key))
3334
- return;
3335
- Object.assign(shouldChanged, changes);
3336
- partofs[o.key] = false;
3337
- });
3338
- }
3339
- return [shouldChanged, partofs];
3340
- });
3341
- const handleCheck = (item, checked) => {
3342
- const [shouldChanged, partofs] = checkItem(item, checked);
3343
- const changedKeys = Object.keys(shouldChanged);
3344
- state.checked = checked
3345
- ? Array.from(new Set([...state.checked, ...changedKeys]))
3346
- : state.checked.filter((k) => !changedKeys.includes(k));
3347
- Object.assign(state.partofs, partofs);
3348
- onItemCheck?.(item, checked, state.checked);
3349
- };
3350
- const handleSelect = (key, item) => {
3351
- if (!props.selectable)
3352
- return;
3353
- state.selected = key;
3354
- onItemSelect?.(key, item);
3355
- };
3356
- useEffect(() => {
3357
- if (selected === undefined)
3358
- return;
3359
- state.selected = selected;
3360
- }, [selected]);
3361
- useEffect(() => {
3362
- state.nodeMaps.clear();
3363
- const { key, children } = oNodeProps;
3364
- const recursive = (nodes) => {
3365
- nodes.map((o) => {
3366
- state.nodeMaps.set(o[key], o);
3367
- o[children]?.length > 0 && recursive(o[children]);
3368
- });
3369
- };
3370
- recursive(data);
3371
- }, [data]);
3372
- useImperativeHandle(ref, () => {
3373
- return {
3374
- getChecked: () => {
3375
- const items = [];
3376
- state.checked.map((k) => {
3377
- const item = state.nodeMaps.get(k);
3378
- items.push(item);
3379
- });
3380
- return [state.checked, items];
3381
- },
3382
- getSelected: () => {
3383
- const item = state.nodeMaps.get(state.selected);
3384
- return [state.selected, item];
3385
- },
3386
- getPartofs: () => {
3387
- const items = [];
3388
- const keys = Object.keys(state.partofs).filter((k) => {
3389
- const indeterminate = state.partofs[k];
3390
- if (indeterminate) {
3391
- const item = state.nodeMaps.get(k);
3392
- items.push(item);
3393
- }
3394
- return indeterminate;
3395
- });
3396
- return [keys, items];
3397
- },
3398
- };
3399
- });
3400
- return (jsx(TreeList, { data: data, selected: state.selected, checked: state.checked, partofs: state.partofs, nodeProps: oNodeProps, onItemCheck: handleCheck, onItemSelect: handleSelect, ...restProps }));
3401
- });
3402
-
3403
- function RenderFile(props) {
3404
- const { mode, index, file, onRemove, onPreview } = props;
3405
- if (!file)
3406
- return "";
3407
- const { name, size, url, src } = file;
3408
- const type = getFileType(name, file.type);
3409
- const CloseBtn = (jsx(Helpericon, { active: true, className: 'i-upload-delete', onClick: (e) => {
3410
- e.stopPropagation();
3411
- e.preventDefault();
3412
- onRemove(index);
3413
- } }));
3414
- switch (mode) {
3415
- case "card":
3416
- let node = jsx(Fragment, {});
3417
- switch (type) {
3418
- case TFileType.IMAGE:
3419
- node = (jsx(Image, { lazyload: true, src: url || src, fit: 'cover', usePreview: false }));
3420
- break;
3421
- case TFileType.VIDEO:
3422
- node = jsx("video", { src: url || src, preload: 'none' });
3423
- break;
3424
- default:
3425
- node = (jsxs(Fragment, { children: [jsx(Icon, { icon: jsx(ListAltRound, {}) }), jsx("span", { className: 'i-upload-file-name', children: title(name) })] }));
3426
- break;
3427
- }
3428
- return (jsxs("div", { title: name, className: 'i-upload-item-card', onClick: () => onPreview?.(index), children: [node, CloseBtn] }));
3429
- default:
3430
- return (jsxs("div", { className: 'i-upload-item', onClick: () => onPreview?.(index), children: [jsx("span", { children: name }), jsx("i", { className: 'i-upload-size', children: formatBytes(size ?? 0) }), CloseBtn] }));
3431
- }
3432
- }
3433
-
3434
- const Upload = forwardRef((props, ref) => {
3435
- const { label, labelInline, value, files = [], placeholder, status = "normal", message, className, style, children, mode = "default", cardSize = "4em", disabled, limit = props.multiple ? Infinity : 1, multiple, renderItem = RenderFile, shouldUpload = () => true, uploader, onChange, onFilesChange, onUpload, ...restProps } = props;
3436
- const state = useReactive({
3437
- files,
3438
- value,
3439
- status,
3440
- message,
3441
- update: 0,
3442
- });
3443
- const inputRef = useRef(null);
3444
- const preview = usePreview();
3445
- const trigger = useMemo(() => {
3446
- if (children)
3447
- return children;
3448
- switch (mode) {
3449
- case "card":
3450
- return (jsx(Button, { className: 'i-upload-card-btn color-5', square: true, flat: true, outline: true, disabled: disabled, children: jsx(Icon, { icon: jsx(PlusSharp, {}) }) }));
3451
- default:
3452
- return (jsxs(Button, { className: 'i-upload-btn', disabled: disabled, children: [jsx(Icon, { icon: jsx(CloudUploadTwotone, {}) }), " Upload"] }));
3453
- }
3454
- }, [mode, children]);
3455
- const handleChange = (e) => {
3456
- const files = Array.from(e.target.files || []);
3457
- const { files: before } = state;
3458
- const changed = [];
3459
- files.map((f) => {
3460
- const { name, size, type } = f;
3461
- const same = before.find((pf) => {
3462
- const { name: n, size: s, type: t } = pf;
3463
- return n === name && s === size && t === type;
3464
- });
3465
- const src = URL.createObjectURL(f);
3466
- f.src = src;
3467
- state.update += 1;
3468
- Object.assign(f, {
3469
- uid: uid(7),
3470
- src: f.src || f.name,
3471
- });
3472
- !same && changed.push(f);
3473
- });
3474
- const after = [...before, ...changed];
3475
- Object.assign(state, {
3476
- files: multiple ? after.slice(0, limit) : [after.at(-1)],
3477
- status,
3478
- message,
3479
- });
3480
- onFilesChange?.(state.files, changed, e);
3481
- onChange?.(state.files, e);
3482
- handleUpload(changed);
3483
- inputRef.current && (inputRef.current.value = "");
3484
- };
3485
- const handleRemove = (i) => {
3486
- const [...files] = state.files;
3487
- const changed = files.splice(i, 1);
3488
- URL.revokeObjectURL(changed[0]?.src || "");
3489
- state.files = files;
3490
- onFilesChange?.(files, changed);
3491
- onChange?.(files);
3492
- inputRef.current && (inputRef.current.value = "");
3493
- };
3494
- const handleUpload = async (files) => {
3495
- if (!uploader)
3496
- return;
3497
- files.forEach(async (file) => {
3498
- if (!shouldUpload(file))
3499
- return;
3500
- const result = await uploader(file);
3501
- const i = state.files.findIndex((f) => f.uid === result.uid);
3502
- i > -1 && (state.files[i] = result);
3503
- result?.status === "completed" && onUpload?.(result);
3504
- });
3505
- };
3506
- const handlePreview = (i) => {
3507
- preview({ items: state.files, initial: i });
3508
- };
3509
- useEffect(() => {
3510
- Object.assign(state, {
3511
- status,
3512
- message,
3513
- });
3514
- }, [status, message]);
3515
- useEffect(() => {
3516
- state.value = value;
3517
- }, [value]);
3518
- useImperativeHandle(ref, () => ({
3519
- getFileList: () => state.files,
3520
- }), []);
3521
- const { message: msg, files: currentFiles } = state;
3522
- return (jsxs(InputContainer, { label: label, labelInline: labelInline, className: classNames("i-input-label-file", className), style: style, children: [jsx("input", { ...restProps, disabled: disabled, ref: inputRef, type: 'file', className: 'i-input-file-hidden', multiple: multiple, onChange: handleChange }), jsxs("div", { className: classNames("i-upload-inner", {
3523
- [`i-upload-${mode}`]: mode !== "default",
3524
- }), style: { ["--upload-card-size"]: cardSize }, children: [jsx("div", { className: 'i-upload-list', onClick: (e) => {
3525
- e.stopPropagation();
3526
- e.preventDefault();
3527
- }, children: currentFiles?.map((file, i) => (jsx(Fragment$1, { children: RenderFile({
3528
- index: i,
3529
- file,
3530
- mode,
3531
- onRemove: handleRemove,
3532
- onPreview: handlePreview,
3533
- }) }, i))) }), msg && jsx("span", { className: 'i-upload-message', children: msg }), currentFiles.length < limit && trigger] })] }));
3534
- });
3535
-
3536
- export { Affix, Badge, Button, Card, Checkbox, Collapse, Datagrid, Datepicker, Description, Drawer, Dropdown, Flex, Form, Icon, Image, Input, List$1 as List, Loading, message as Message, Modal, Pagination, Popconfirm, Popup, Progress, Radio, Select, Step, Swiper, Tabs, Tag, Text, Tree, Upload, Video, usePreview };
3537
- //# sourceMappingURL=index.js.map