@ioca/react 1.5.17 → 1.5.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/lib/cjs/components/input/input.js +3 -1
  2. package/lib/cjs/components/input/input.js.map +1 -1
  3. package/lib/cjs/components/message/index.js +3 -2
  4. package/lib/cjs/components/message/index.js.map +1 -1
  5. package/lib/cjs/components/message/message.js +93 -50
  6. package/lib/cjs/components/message/message.js.map +1 -1
  7. package/lib/cjs/components/picker/daterange/daterange.js +60 -0
  8. package/lib/cjs/components/picker/daterange/daterange.js.map +1 -0
  9. package/lib/cjs/components/picker/daterange/index.js +11 -0
  10. package/lib/cjs/components/picker/daterange/index.js.map +1 -0
  11. package/lib/cjs/components/picker/daterange/panel.js +55 -0
  12. package/lib/cjs/components/picker/daterange/panel.js.map +1 -0
  13. package/lib/cjs/components/picker/dates/dates.js +12 -4
  14. package/lib/cjs/components/picker/dates/dates.js.map +1 -1
  15. package/lib/cjs/components/picker/dates/index.js +6 -2
  16. package/lib/cjs/components/picker/dates/index.js.map +1 -1
  17. package/lib/cjs/components/picker/dates/panel.js +22 -9
  18. package/lib/cjs/components/picker/dates/panel.js.map +1 -1
  19. package/lib/cjs/components/picker/time/index.js +6 -2
  20. package/lib/cjs/components/picker/time/index.js.map +1 -1
  21. package/lib/cjs/components/popconfirm/popconfirm.js +2 -4
  22. package/lib/cjs/components/popconfirm/popconfirm.js.map +1 -1
  23. package/lib/cjs/index.js +10 -8
  24. package/lib/cjs/index.js.map +1 -1
  25. package/lib/css/colors.css +2 -2
  26. package/lib/css/index.css +1 -1
  27. package/lib/css/index.css.map +1 -1
  28. package/lib/css/input.css +12 -0
  29. package/lib/es/components/input/input.js +3 -1
  30. package/lib/es/components/input/input.js.map +1 -1
  31. package/lib/es/components/message/index.js +3 -3
  32. package/lib/es/components/message/index.js.map +1 -1
  33. package/lib/es/components/message/message.js +93 -51
  34. package/lib/es/components/message/message.js.map +1 -1
  35. package/lib/es/components/picker/daterange/daterange.js +50 -0
  36. package/lib/es/components/picker/daterange/daterange.js.map +1 -0
  37. package/lib/es/components/picker/daterange/index.js +6 -0
  38. package/lib/es/components/picker/daterange/index.js.map +1 -0
  39. package/lib/es/components/picker/daterange/panel.js +47 -0
  40. package/lib/es/components/picker/daterange/panel.js.map +1 -0
  41. package/lib/es/components/picker/dates/dates.js +12 -4
  42. package/lib/es/components/picker/dates/dates.js.map +1 -1
  43. package/lib/es/components/picker/dates/index.js +6 -2
  44. package/lib/es/components/picker/dates/index.js.map +1 -1
  45. package/lib/es/components/picker/dates/panel.js +22 -9
  46. package/lib/es/components/picker/dates/panel.js.map +1 -1
  47. package/lib/es/components/picker/time/index.js +6 -2
  48. package/lib/es/components/picker/time/index.js.map +1 -1
  49. package/lib/es/components/popconfirm/popconfirm.js +2 -4
  50. package/lib/es/components/popconfirm/popconfirm.js.map +1 -1
  51. package/lib/es/index.js +2 -1
  52. package/lib/es/index.js.map +1 -1
  53. package/lib/index.js +223 -70
  54. package/lib/types/components/input/type.d.ts +1 -0
  55. package/lib/types/components/message/index.d.ts +5 -3
  56. package/lib/types/components/message/message.d.ts +5 -4
  57. package/lib/types/components/message/type.d.ts +25 -12
  58. package/lib/types/components/picker/daterange/daterange.d.ts +6 -0
  59. package/lib/types/components/picker/daterange/index.d.ts +5 -0
  60. package/lib/types/components/picker/type.d.ts +11 -2
  61. package/lib/types/index.d.ts +2 -1
  62. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import classNames from 'classnames';
3
3
  import { debounce, uid, crush, throttle } from 'radash';
4
4
  import { useState, useRef, useEffect, useCallback, useMemo, Children, cloneElement, createElement, isValidElement, memo, Fragment as Fragment$1, useTransition, forwardRef, useLayoutEffect, useContext, createContext, useImperativeHandle } from 'react';
5
- import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, UndoRound, RedoRound, FormatBoldRound, FormatItalicRound, FormatUnderlinedRound, StrikethroughSRound, ClearAllRound, PlayArrowRound, PauseRound, StopRound, VolumeDownRound, VolumeOffRound, FullscreenRound, FullscreenExitRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, KeyboardDoubleArrowUpRound, SyncAltRound, VisibilityRound, VisibilityOffRound, MoreHorizRound, SearchRound, CheckRound, UnfoldMoreRound, CalendarMonthTwotone, AccessTimeRound, InfoOutlined, KeyboardArrowDownRound, MoveToInboxTwotone, OutboxTwotone, FilePresentOutlined, DriveFolderUploadOutlined } from '@ricons/material';
5
+ import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, UndoRound, RedoRound, FormatBoldRound, FormatItalicRound, FormatUnderlinedRound, StrikethroughSRound, ClearAllRound, PlayArrowRound, PauseRound, StopRound, VolumeDownRound, VolumeOffRound, FullscreenRound, FullscreenExitRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, KeyboardDoubleArrowUpRound, SyncAltRound, VisibilityRound, VisibilityOffRound, MoreHorizRound, SearchRound, CheckRound, UnfoldMoreRound, CalendarMonthTwotone, AccessTimeRound, KeyboardArrowDownRound, MoveToInboxTwotone, OutboxTwotone, FilePresentOutlined, DriveFolderUploadOutlined } from '@ricons/material';
6
6
  import { createRoot } from 'react-dom/client';
7
7
  import { getScrollbarSize, List as List$2 } from 'react-window';
8
8
  import { createPortal } from 'react-dom';
@@ -4317,11 +4317,13 @@ const Input = ((props) => {
4317
4317
  }, [type]);
4318
4318
  const clearable = clear && inputValue;
4319
4319
  const showHelper = type === "password" && !!inputValue;
4320
+ const isClearBtn = clearable && !showHelper;
4320
4321
  return (jsx(InputContainer, { label: label, labelInline: labelInline, className: className, style: { width, ...style }, tip: message ?? tip, status: status, required: required, children: jsxs("div", { className: classNames("i-input-item", {
4322
+ "i-input-clearable": clearable,
4321
4323
  [`i-input-${status}`]: status !== "normal",
4322
4324
  "i-input-borderless": !border,
4323
4325
  "i-input-underline": underline,
4324
- }), children: [prepend && jsx("div", { className: 'i-input-prepend', children: prepend }), jsx("input", { ...inputProps }), maxLength && inputValue?.length > 0 && (jsxs("span", { className: 'color-8 pr-4 font-sm', children: [inputValue.length, " / ", maxLength] })), jsx(Helpericon, { active: !!clearable || showHelper, icon: HelperIcon, onClick: handleHelperClick }), append && jsx("div", { className: 'i-input-append', children: append })] }) }));
4326
+ }), children: [prepend && jsx("div", { className: 'i-input-prepend', children: prepend }), jsx("input", { ...inputProps }), maxLength && inputValue?.length > 0 && (jsxs("span", { className: 'color-8 pr-4 font-sm', children: [inputValue.length, " / ", maxLength] })), jsx(Helpericon, { active: !!clearable || showHelper, icon: HelperIcon, className: classNames({ "i-helpericon-clear": isClearBtn }), onClick: handleHelperClick }), append && jsx("div", { className: 'i-input-append', children: append })] }) }));
4325
4327
  });
4326
4328
  Input.Textarea = Textarea;
4327
4329
  Input.Number = Number$1;
@@ -4332,13 +4334,13 @@ const AlignMap = {
4332
4334
  center: "center",
4333
4335
  right: "flex-end",
4334
4336
  };
4335
- const GlobalConfig = {
4337
+ const ContainerConfig = {
4338
+ align: "center",
4339
+ fromBottom: false,
4340
+ unshift: false,
4336
4341
  gap: 12,
4337
- };
4338
- const ItemDefaultConfig = {
4342
+ offset: "12px",
4339
4343
  duration: 3000,
4340
- closable: true,
4341
- active: false,
4342
4344
  };
4343
4345
  const handler = {
4344
4346
  oneInstance: null,
@@ -4355,12 +4357,12 @@ const heights = {
4355
4357
  center: [],
4356
4358
  right: [],
4357
4359
  };
4358
- const MessageItem = function ({ ref, active, content, top, className, style, onClick, }) {
4360
+ const MessageItem = function ({ ref, active, content, top, bottom, className, style, onClick, }) {
4359
4361
  return (jsx("div", { ref: ref, className: classNames("i-message", className, {
4360
4362
  "i-message-active": active,
4361
4363
  }), style: {
4362
4364
  ...style,
4363
- top,
4365
+ ...(bottom !== undefined ? { bottom } : { top }),
4364
4366
  }, onClick: onClick, children: content }));
4365
4367
  };
4366
4368
  function Messages() {
@@ -4377,26 +4379,32 @@ function Messages() {
4377
4379
  right: [],
4378
4380
  },
4379
4381
  });
4380
- const offsetTop = {
4381
- left: 0,
4382
- center: 0,
4383
- right: 0,
4384
- };
4385
4382
  useEffect(() => {
4386
4383
  Object.assign(handler, {
4387
4384
  callout: function (item) {
4388
4385
  const { align = "center", unshift, onShow } = item;
4389
- const size = queue[align][unshift ? "unshift" : "push"](item);
4386
+ queue[align][unshift ? "unshift" : "push"](item);
4390
4387
  state.items[align] = [...queue[align]];
4391
4388
  item.close = this.close.bind(item);
4392
- setTimeout(() => {
4389
+ // Pre-fill 0-height placeholder so heights stays index-aligned with queue
4390
+ if (unshift) {
4391
+ heights[align].unshift(0);
4392
+ }
4393
+ else {
4394
+ heights[align].push(0);
4395
+ }
4396
+ state.tops[align] = [...heights[align]];
4397
+ requestAnimationFrame(() => {
4393
4398
  const h = ref.current?.offsetHeight || 0;
4394
- queue[align][unshift ? 0 : size - 1].active = true;
4399
+ const idx = queue[align].findIndex((i) => i.id === item.id);
4400
+ if (idx < 0)
4401
+ return;
4402
+ queue[align][idx].active = true;
4395
4403
  state.items[align] = [...queue[align]];
4396
- heights[align][unshift ? "unshift" : "push"](h);
4404
+ heights[align][idx] = h;
4397
4405
  state.tops[align] = [...heights[align]];
4398
4406
  onShow?.();
4399
- }, 12);
4407
+ });
4400
4408
  if (item.duration !== 0) {
4401
4409
  item.timer = setTimeout(() => {
4402
4410
  this.close.call(item);
@@ -4423,36 +4431,65 @@ function Messages() {
4423
4431
  },
4424
4432
  });
4425
4433
  }, []);
4426
- const renderItems = (item, i) => {
4427
- if (!item)
4428
- return jsx(Fragment, {});
4429
- const { id, active, content, align = "center", className } = item;
4430
- offsetTop[align] += state.tops[align][i - 1] || 0;
4431
- const top = GlobalConfig.gap * i + offsetTop[align];
4432
- return (jsx(MessageItem, { ref: ref, active: active, content: content, top: top, className: className, style: { alignSelf: AlignMap[align] }, onClick: handler.close.bind(item) }, id));
4433
- };
4434
- return (jsxs("div", { className: 'i-messages', children: [state.items.left.map(renderItems), state.items.center.map(renderItems), state.items.right.map(renderItems)] }));
4434
+ const renderGroup = (align) => {
4435
+ const items = state.items[align];
4436
+ const tops = state.tops[align];
4437
+ const gap = ContainerConfig.gap;
4438
+ if (ContainerConfig.fromBottom) {
4439
+ // bottom-up: calculate bottom-edge positions
4440
+ let offset = 0;
4441
+ const bottoms = [];
4442
+ for (let i = items.length - 1; i >= 0; i--) {
4443
+ bottoms[i] = offset;
4444
+ offset += (tops[i] || 0) + gap;
4445
+ }
4446
+ return items.map((item, i) => {
4447
+ if (!item)
4448
+ return jsx(Fragment, {});
4449
+ const { id, active, content, className, style: itemStyle } = item;
4450
+ return (jsx(MessageItem, { ref: ref, active: active, content: content, bottom: bottoms[i], className: className, style: {
4451
+ ...itemStyle,
4452
+ alignSelf: AlignMap[align],
4453
+ }, onClick: handler.close.bind(item) }, id));
4454
+ });
4455
+ }
4456
+ // top-down (default)
4457
+ let offset = 0;
4458
+ return items.map((item, i) => {
4459
+ if (!item)
4460
+ return jsx(Fragment, {});
4461
+ const { id, active, content, className, style: itemStyle } = item;
4462
+ const top = offset;
4463
+ offset += (tops[i] || 0) + gap;
4464
+ return (jsx(MessageItem, { ref: ref, active: active, content: content, top: top, className: className, style: {
4465
+ ...itemStyle,
4466
+ alignSelf: AlignMap[align],
4467
+ }, onClick: handler.close.bind(item) }, id));
4468
+ });
4469
+ };
4470
+ return (jsxs("div", { className: "i-messages", style: {
4471
+ margin: ContainerConfig.offset,
4472
+ }, children: [renderGroup("left"), renderGroup("center"), renderGroup("right")] }));
4435
4473
  }
4436
4474
  function message(config) {
4437
- if (["string", "number"].includes(typeof config) ||
4438
- isValidElement(config)) {
4475
+ if (typeof config !== "object" || isValidElement(config)) {
4439
4476
  config = { content: config };
4440
4477
  }
4441
- config = Object.assign({ id: uid(7) }, ItemDefaultConfig, config);
4442
- handler.callout(config);
4478
+ const msg = {
4479
+ id: uid(7),
4480
+ active: false,
4481
+ align: ContainerConfig.align,
4482
+ duration: ContainerConfig.duration,
4483
+ closable: true,
4484
+ unshift: ContainerConfig.unshift,
4485
+ ...config,
4486
+ };
4487
+ handler.callout(msg);
4443
4488
  return {
4444
- instance: config,
4445
- close: handler.close.bind(config),
4489
+ instance: msg,
4490
+ close: handler.close.bind(msg),
4446
4491
  };
4447
4492
  }
4448
- function createContainer() {
4449
- if (typeof document === "undefined")
4450
- return null;
4451
- const container = document.createElement("div");
4452
- container.dataset.id = "messages";
4453
- document.body.append(container);
4454
- return container;
4455
- }
4456
4493
  message.error = (content) => {
4457
4494
  return message({
4458
4495
  content,
@@ -4495,16 +4532,26 @@ message.one = (config) => {
4495
4532
  handler.oneInstance = instance;
4496
4533
  }
4497
4534
  };
4498
- // 初始化消息容器
4499
- let container = null;
4500
- let root = null;
4501
- if (typeof window !== "undefined") {
4502
- container = createContainer();
4503
- if (container) {
4504
- root = createRoot(container);
4505
- root.render(jsx(Messages, {}));
4506
- }
4535
+ function MessageContainer({ align = "center", fromBottom = false, unshift = false, gap = 12, offset = "12px", duration = 3000, }) {
4536
+ ContainerConfig.align = align;
4537
+ ContainerConfig.fromBottom = fromBottom;
4538
+ ContainerConfig.unshift = unshift;
4539
+ ContainerConfig.gap = gap;
4540
+ ContainerConfig.offset = offset;
4541
+ ContainerConfig.duration = duration;
4542
+ return null;
4507
4543
  }
4544
+ // 默认 Portal 到 document.body(仅在客户端执行,避免 SSR 问题)
4545
+ if (typeof document !== "undefined") {
4546
+ import('react-dom/client').then(({ createRoot }) => {
4547
+ const container = document.createElement("div");
4548
+ container.dataset.id = "messages";
4549
+ document.body.append(container);
4550
+ createRoot(container).render(jsx(Messages, {}));
4551
+ });
4552
+ }
4553
+
4554
+ const Message = Object.assign(message, { Container: MessageContainer });
4508
4555
 
4509
4556
  const Page = (props) => {
4510
4557
  const { page, active, disabled, children, onChange } = props;
@@ -4772,7 +4819,7 @@ function ColorPicker(props) {
4772
4819
  }
4773
4820
 
4774
4821
  const Dates = (props) => {
4775
- const { value, month, weeks = ["一", "二", "三", "四", "五", "六", "日"], renderDate = (date) => date.date(), disabledDate, onDateClick, } = props;
4822
+ const { value, month, rangeEnd, hoverDate, weeks = ["一", "二", "三", "四", "五", "六", "日"], renderDate = (date) => date.date(), disabledDate, onDateClick, onDateHover, } = props;
4776
4823
  const today = dayjs();
4777
4824
  const dates = useMemo(() => {
4778
4825
  const dates = [];
@@ -4796,17 +4843,25 @@ const Dates = (props) => {
4796
4843
  return;
4797
4844
  onDateClick?.(date);
4798
4845
  };
4799
- 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) => {
4800
- const active = date.isSame(value, "day");
4846
+ const handleMouseEnter = (date) => {
4847
+ if (disabledDate?.(date))
4848
+ return;
4849
+ onDateHover?.(date);
4850
+ };
4851
+ 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', onMouseLeave: () => onDateHover?.(null), children: dates.map((date, i) => {
4852
+ const active = date.isSame(value, "day") || (rangeEnd && date.isSame(rangeEnd, "day"));
4801
4853
  const isSameMonth = date.isSame(month, "month");
4802
4854
  const isToday = date.isSame(today, "day");
4803
4855
  const disabled = disabledDate?.(date);
4856
+ const isBetween = hoverDate && value && ((date.isAfter(value, "day") && date.isBefore(hoverDate, "day")) ||
4857
+ (date.isAfter(hoverDate, "day") && date.isBefore(value, "day")));
4804
4858
  return (jsx("div", { className: classNames("i-datepicker-item", {
4805
4859
  "i-datepicker-active": active,
4806
4860
  "i-datepicker-same-month": isSameMonth,
4807
4861
  "i-datepicker-today": isToday,
4808
4862
  "i-datepicker-disabled": disabled,
4809
- }), onClick: () => handleDateClick(date), children: renderDate(date) }, i));
4863
+ "i-daterange-between": isBetween,
4864
+ }), onMouseEnter: () => handleMouseEnter(date), onClick: () => handleDateClick(date), children: renderDate(date) }, i));
4810
4865
  }) })] }));
4811
4866
  };
4812
4867
 
@@ -4816,20 +4871,20 @@ const YearMonth = (props) => {
4816
4871
  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] }));
4817
4872
  };
4818
4873
  const Panel$1 = (props) => {
4819
- const { value, unitYear, unitMonth, renderDate, renderMonth = (m) => m, renderYear = (y) => y, disabledDate, onDateClick, } = props;
4874
+ const { value, month: controlledMonth, rangeEnd, hoverDate, onDateHover, onOperateMonth: onOperateMonthProp, unitYear, unitMonth, renderDate, renderMonth = (m) => m, renderYear = (y) => y, disabledDate, onDateClick, } = props;
4820
4875
  const [today, setToday] = useState(value);
4821
- const [month, setMonth] = useState(value || dayjs());
4876
+ const [month, setMonth] = useState(controlledMonth || value || dayjs());
4822
4877
  const [selectedYear, setSelectedYear] = useState(dayjs());
4823
4878
  const [years, setYears] = useState([]);
4824
4879
  const [selectable, setSelectable] = useState(false);
4825
4880
  const $years = useRef(null);
4826
- const handleOperateMonth = (next) => {
4881
+ const handleOperateMonth = onOperateMonthProp || ((next) => {
4827
4882
  setMonth((m) => m[next ? "add" : "subtract"](1, "month"));
4828
- };
4883
+ });
4829
4884
  const handleChangeDate = (date) => {
4830
4885
  if (date.isSame(today, "day"))
4831
4886
  return;
4832
- if (!date.isSame(month, "month")) {
4887
+ if (controlledMonth === undefined && !date.isSame(month, "month")) {
4833
4888
  setMonth(date);
4834
4889
  }
4835
4890
  setToday(date);
@@ -4853,27 +4908,40 @@ const Panel$1 = (props) => {
4853
4908
  }, [selectable, month]);
4854
4909
  useEffect(() => {
4855
4910
  setToday(value);
4856
- setMonth(value || dayjs());
4857
- }, [value]);
4911
+ if (controlledMonth === undefined) {
4912
+ setMonth(value || dayjs());
4913
+ }
4914
+ }, [value, controlledMonth]);
4915
+ useEffect(() => {
4916
+ const el = $years.current;
4917
+ if (!el)
4918
+ return;
4919
+ const onWheel = (e) => {
4920
+ e.preventDefault();
4921
+ getMoreYears(e);
4922
+ };
4923
+ el.addEventListener("wheel", onWheel, { passive: false });
4924
+ return () => el.removeEventListener("wheel", onWheel);
4925
+ }, []);
4858
4926
  return (jsxs("div", { className: 'i-datepicker', children: [jsxs("div", { className: 'i-datepicker-units', children: [jsx(YearMonth, { value: month, unitYear: unitYear, unitMonth: unitMonth, renderMonth: renderMonth, renderYear: renderYear, onClick: () => setSelectable(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", {
4859
4927
  "i-datepicker-active": selectable,
4860
4928
  }), children: [jsx(Helpericon, { active: true, className: 'i-datepicker-close', onClick: (e) => {
4861
4929
  e.stopPropagation();
4862
4930
  setSelectable(false);
4863
- } }), jsx("div", { ref: $years, className: 'i-datepicker-years', onWheel: getMoreYears, children: years.map((y) => (jsx("a", { className: classNames("i-datepicker-year", {
4931
+ } }), jsx("div", { ref: $years, className: 'i-datepicker-years', children: years.map((y) => (jsx("a", { className: classNames("i-datepicker-year", {
4864
4932
  "i-datepicker-active": y === selectedYear.year(),
4865
4933
  }), onClick: () => setSelectedYear((sy) => sy.year(y)), children: renderYear(y) }, y))) }), jsx("div", { className: 'i-datepicker-months', children: MONTHS.map((m) => {
4866
4934
  return (jsx("a", { className: classNames("i-datepicker-month", {
4867
4935
  "i-datepicker-active": m === month.month() + 1,
4868
4936
  }), onClick: () => handleChangeMonth(m), children: renderMonth(m) }, m));
4869
- }) })] }), jsx(Dates, { value: today, month: month, disabledDate: disabledDate, onDateClick: handleChangeDate, renderDate: renderDate })] }));
4937
+ }) })] }), jsx(Dates, { value: today, month: month, rangeEnd: rangeEnd, hoverDate: hoverDate, onDateHover: onDateHover, disabledDate: disabledDate, onDateClick: handleChangeDate, renderDate: renderDate })] }));
4870
4938
  };
4871
4939
 
4872
4940
  dayjs.extend(customParseFormat);
4873
4941
  const FORMATTYPES = ["YYYY-MM-DD", "YYYY-M-D", "YYYY/MM/DD", "YYYY/M/D"];
4874
4942
  const FORMAT$1 = "YYYY-MM-DD";
4875
4943
  const Datepicker = (props) => {
4876
- const { name, value, weeks, format = FORMAT$1, placeholder = props.format ?? FORMAT$1, className, renderDate, renderMonth, renderYear, popupProps, disabledDate, onDateClick, onChange, onBlur, ...restProps } = props;
4944
+ const { name, value, weeks, format = FORMAT$1, placeholder = props.format ?? FORMAT$1, className, renderDate, renderMonth, renderYear, popupProps, disabledDate, onDateClick, onChange, onBlur, clear, onClear: onClearProp, ...restProps } = props;
4877
4945
  const [inputValue, setInputValue] = useState(value);
4878
4946
  const [active, setActive] = useState(false);
4879
4947
  const dayJsValue = useMemo(() => {
@@ -4909,10 +4977,14 @@ const Datepicker = (props) => {
4909
4977
  popupProps?.onVisibleChange?.(v);
4910
4978
  setActive(v);
4911
4979
  };
4980
+ const handleClear = () => {
4981
+ setActive(false);
4982
+ onClearProp?.();
4983
+ };
4912
4984
  useEffect(() => {
4913
4985
  setInputValue(value);
4914
4986
  }, [value]);
4915
- return (jsx(Popup, { visible: active, trigger: 'click', position: 'bottom', arrow: false, align: 'start', onVisibleChange: handleVisibleChange, content: jsx(Panel$1, { value: dayJsValue, weeks: weeks, renderDate: renderDate, renderMonth: renderMonth, renderYear: renderYear, disabledDate: disabledDate, onDateClick: handleDateClick }), ...popupProps, children: jsx(Input, { value: inputValue, append: jsx(Icon, { icon: jsx(CalendarMonthTwotone, {}), className: 'i-datepicker-icon', size: '1em' }), placeholder: placeholder, onChange: handleChange, onBlur: handleBlur, onEnter: handleSetDate, className: classNames("i-datepicker-label", className), ...restProps }) }));
4987
+ return (jsx(Popup, { visible: active, trigger: 'click', position: 'bottom', arrow: false, align: 'start', onVisibleChange: handleVisibleChange, content: jsx(Panel$1, { value: dayJsValue, weeks: weeks, renderDate: renderDate, renderMonth: renderMonth, renderYear: renderYear, disabledDate: disabledDate, onDateClick: handleDateClick }), ...popupProps, children: jsx(Input, { value: inputValue, append: jsx(Icon, { icon: jsx(CalendarMonthTwotone, {}), className: 'i-datepicker-icon', size: '1em' }), placeholder: placeholder, clear: clear, onClear: handleClear, onChange: handleChange, onBlur: handleBlur, onEnter: handleSetDate, className: classNames("i-datepicker-label", className), ...restProps }) }));
4916
4988
  };
4917
4989
 
4918
4990
  function Items(props) {
@@ -5021,7 +5093,7 @@ function Panel(props) {
5021
5093
 
5022
5094
  const FORMAT = "hh:mm:ss";
5023
5095
  function TimePicker(props) {
5024
- const { name, value, format = FORMAT, periods, placeholder = props.format ?? FORMAT, className, renderItem, onChange, onBlur, popupProps, ...restProps } = props;
5096
+ const { name, value, format = FORMAT, periods, placeholder = props.format ?? FORMAT, className, renderItem, onChange, onBlur, popupProps, clear, onClear: onClearProp, ...restProps } = props;
5025
5097
  const [timeValue, setTimeValue] = useState(value);
5026
5098
  const [safeValue, setSafeValue] = useState(undefined);
5027
5099
  const [active, setActive] = useState(false);
@@ -5045,12 +5117,93 @@ function TimePicker(props) {
5045
5117
  popupProps?.onVisibleChange?.(v);
5046
5118
  setActive(v);
5047
5119
  };
5120
+ const handleClear = () => {
5121
+ setActive(false);
5122
+ onClearProp?.();
5123
+ };
5048
5124
  useEffect(() => {
5049
5125
  setTimeValue(value);
5050
5126
  }, [value]);
5051
- return (jsx(Popup, { visible: active, trigger: 'click', position: 'bottom', arrow: false, align: 'start', ...popupProps, onVisibleChange: handleVisibleChange, content: jsx(Panel, { value: timeValue, format: format, periods: periods, renderItem: renderItem, onChange: handleChange, onFallback: handleFallback }), children: jsx(Input, { value: timeValue, placeholder: placeholder, append: jsx(Icon, { icon: jsx(AccessTimeRound, {}), className: 'i-timepicker-icon', size: '1em' }), onChange: handleChange, onBlur: handleBlur, className: classNames("i-timepicker-label", className), ...restProps }) }));
5127
+ return (jsx(Popup, { visible: active, trigger: 'click', position: 'bottom', arrow: false, align: 'start', ...popupProps, onVisibleChange: handleVisibleChange, content: jsx(Panel, { value: timeValue, format: format, periods: periods, renderItem: renderItem, onChange: handleChange, onFallback: handleFallback }), children: jsx(Input, { value: timeValue, placeholder: placeholder, append: jsx(Icon, { icon: jsx(AccessTimeRound, {}), className: 'i-timepicker-icon', size: '1em' }), clear: clear, onClear: handleClear, onChange: handleChange, onBlur: handleBlur, className: classNames("i-timepicker-label", className), ...restProps }) }));
5052
5128
  }
5053
5129
 
5130
+ const DoublePanel = (props) => {
5131
+ const { value, weeks, unitYear, unitMonth, renderDate, renderMonth, renderYear, disabledDate, onSelected, } = props;
5132
+ const [baseMonth, setBaseMonth] = useState(value?.[0] || dayjs());
5133
+ const [startDate, setStartDate] = useState(value?.[0] || null);
5134
+ const [endDate, setEndDate] = useState(value?.[1] || null);
5135
+ const [selecting, setSelecting] = useState("start");
5136
+ const [hoverDate, setHoverDate] = useState(null);
5137
+ const nextMonth = useMemo(() => baseMonth.add(1, "month"), [baseMonth]);
5138
+ useEffect(() => {
5139
+ setStartDate(value?.[0] || null);
5140
+ setEndDate(value?.[1] || null);
5141
+ setBaseMonth(value?.[0] || dayjs());
5142
+ }, [value]);
5143
+ const handleDateClick = (date) => {
5144
+ if (selecting === "start") {
5145
+ setStartDate(date);
5146
+ setSelecting("end");
5147
+ }
5148
+ else {
5149
+ let start = startDate || date;
5150
+ let end = date;
5151
+ if (start.isAfter(end)) {
5152
+ [start, end] = [end, start];
5153
+ }
5154
+ setEndDate(end);
5155
+ setSelecting("start");
5156
+ onSelected?.([start, end]);
5157
+ }
5158
+ };
5159
+ const handleDateHover = (date) => {
5160
+ if (selecting === "end" && startDate) {
5161
+ setHoverDate(date);
5162
+ }
5163
+ };
5164
+ const handleOperateMonth = (next) => {
5165
+ setBaseMonth((m) => m[next ? "add" : "subtract"](1, "month"));
5166
+ };
5167
+ return (jsxs("div", { className: 'i-daterange-panel i-daterange-grids', children: [jsx("div", { className: 'i-daterange-col', children: jsx(Panel$1, { value: startDate, month: baseMonth, rangeEnd: endDate, hoverDate: hoverDate, onDateHover: handleDateHover, weeks: weeks, unitYear: unitYear, unitMonth: unitMonth, renderDate: renderDate, renderMonth: renderMonth, renderYear: renderYear, disabledDate: disabledDate, onDateClick: handleDateClick, onOperateMonth: handleOperateMonth }) }), jsx("div", { className: 'i-daterange-col', children: jsx(Panel$1, { value: startDate, month: nextMonth, rangeEnd: endDate, hoverDate: hoverDate, onDateHover: handleDateHover, weeks: weeks, unitYear: unitYear, unitMonth: unitMonth, renderDate: renderDate, renderMonth: renderMonth, renderYear: renderYear, disabledDate: disabledDate, onDateClick: handleDateClick, onOperateMonth: handleOperateMonth }) })] }));
5168
+ };
5169
+
5170
+ dayjs.extend(customParseFormat);
5171
+ const DateRange = (props) => {
5172
+ const { value, format = "YYYY-MM-DD", placeholder = "选择日期范围", className, disabledDate, onChange, clear, onClear: onClearProp, weeks, unitYear, unitMonth, renderDate, renderMonth, renderYear, ...restProps } = props;
5173
+ const [active, setActive] = useState(false);
5174
+ const inputValue = useMemo(() => {
5175
+ if (value?.[0] && value?.[1]) {
5176
+ return `${value[0]} ~ ${value[1]}`;
5177
+ }
5178
+ if (value?.[0]) {
5179
+ return `${value[0]} ~ `;
5180
+ }
5181
+ return "";
5182
+ }, [value]);
5183
+ const dayJsValue = useMemo(() => {
5184
+ const start = value?.[0] ? dayjs(value[0], format, true) : null;
5185
+ const end = value?.[1] ? dayjs(value[1], format, true) : null;
5186
+ if (start?.isValid() || end?.isValid()) {
5187
+ return [start?.isValid() ? start : null, end?.isValid() ? end : null];
5188
+ }
5189
+ return null;
5190
+ }, [value, format]);
5191
+ const handleSelected = (dates) => {
5192
+ const formatted = dates.map((d) => d.format(format));
5193
+ onChange?.([formatted[0], formatted[1]]);
5194
+ setActive(false);
5195
+ };
5196
+ const handleVisibleChange = (v) => {
5197
+ setActive(v);
5198
+ };
5199
+ const handleClear = () => {
5200
+ onChange?.([undefined, undefined]);
5201
+ setActive(false);
5202
+ onClearProp?.();
5203
+ };
5204
+ return (jsx(Popup, { visible: active, trigger: 'click', position: 'bottom', arrow: false, align: 'start', onVisibleChange: handleVisibleChange, content: jsx(DoublePanel, { value: dayJsValue, weeks: weeks, unitYear: unitYear, unitMonth: unitMonth, renderDate: renderDate, renderMonth: renderMonth, renderYear: renderYear, disabledDate: disabledDate, onSelected: handleSelected }), children: jsx(Input, { value: inputValue, placeholder: placeholder, readOnly: true, clear: clear, onClear: handleClear, append: jsx(Icon, { icon: jsx(CalendarMonthTwotone, {}), className: 'i-datepicker-icon', size: '1em' }), className: classNames("i-datepicker-label", className), ...restProps }) }));
5205
+ };
5206
+
5054
5207
  const defaultOk = {
5055
5208
  children: "确定",
5056
5209
  };
@@ -5059,7 +5212,7 @@ const defaultCancel = {
5059
5212
  flat: true,
5060
5213
  };
5061
5214
  const Popconfirm = (props) => {
5062
- const { trigger = "click", visible, icon = jsx(Icon, { icon: jsx(InfoOutlined, {}), className: "error", size: "1.2em" }), content, okButtonProps, cancelButtonProps, children, align, position = "top", offset = 12, extra, onOk, onClose, ...restProps } = props;
5215
+ const { trigger = "click", visible, icon, content, okButtonProps, cancelButtonProps, children, align, position = "top", offset = 12, extra, onOk, onClose, ...restProps } = props;
5063
5216
  const state = useReactive({
5064
5217
  loading: false,
5065
5218
  visible,
@@ -6531,4 +6684,4 @@ const useTheme = (props) => {
6531
6684
  };
6532
6685
  };
6533
6686
 
6534
- export { Affix, Badge, Button, Card, Checkbox, Collapse, ColorPicker, Datagrid, Datepicker as DatePicker, Description, Drawer, Dropdown, Editor, Flex, Form, Icon, MemoImage as Image, Input, List$1 as List, Loading, message as Message, Modal, Pagination, Popconfirm, Popup, Progress, Radio, Resizable, River, Scroll, Select, Step, Swiper, Tabs, Tag, Text, TimePicker, Tree, Upload, Video, usePreview, useTheme };
6687
+ export { Affix, Badge, Button, Card, Checkbox, Collapse, ColorPicker, Datagrid, Datepicker as DatePicker, DateRange, Description, Drawer, Dropdown, Editor, Flex, Form, Icon, MemoImage as Image, Input, List$1 as List, Loading, Message, Modal, Pagination, Popconfirm, Popup, Progress, Radio, Resizable, River, Scroll, Select, Step, Swiper, Tabs, Tag, Text, TimePicker, Tree, Upload, Video, usePreview, useTheme };
@@ -8,6 +8,7 @@ interface IInput extends BaseInput, Omit<InputHTMLAttributes<HTMLInputElement>,
8
8
  prepend?: ReactNode;
9
9
  append?: ReactNode;
10
10
  hideVisible?: boolean;
11
+ clear?: boolean;
11
12
  onClear?: () => void;
12
13
  }
13
14
  interface ITextarea extends Omit<BaseInput, "ref">, Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "value" | "defaultValue" | "onChange"> {
@@ -1,5 +1,7 @@
1
- import message from './message.js';
1
+ import message, { MessageContainer } from './message.js';
2
2
 
3
+ declare const Message: typeof message & {
4
+ Container: typeof MessageContainer;
5
+ };
3
6
 
4
-
5
- export { message as default };
7
+ export { MessageContainer, Message as default };
@@ -1,7 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
- import { IMessage } from './type.js';
2
+ import { IMessageConfig, IMessage, IMessageContainerProps } from './type.js';
3
3
 
4
- declare function message(config: IMessage | ReactNode): {
4
+ declare function message(config: IMessageConfig | ReactNode): {
5
5
  instance: IMessage;
6
6
  close: any;
7
7
  };
@@ -22,7 +22,8 @@ declare namespace message {
22
22
  instance: IMessage;
23
23
  close: any;
24
24
  };
25
- var one: (config: IMessage) => void;
25
+ var one: (config: IMessageConfig) => void;
26
26
  }
27
+ declare function MessageContainer({ align, fromBottom, unshift, gap, offset, duration, }: IMessageContainerProps): any;
27
28
 
28
- export { message as default };
29
+ export { MessageContainer, message as default };
@@ -1,21 +1,34 @@
1
- import { ReactNode } from 'react';
1
+ import { ReactNode, CSSProperties } from 'react';
2
2
 
3
- interface IMessage {
4
- id?: string;
3
+ /** User-facing callable config for message() */
4
+ interface IMessageConfig {
5
5
  content?: ReactNode;
6
- active?: boolean;
7
6
  duration?: number;
8
- gap?: number;
9
- offset?: string;
10
- max?: number;
11
- align?: "center" | "left" | "right";
7
+ style?: CSSProperties;
8
+ className?: string;
12
9
  unshift?: boolean;
13
10
  closable?: boolean;
14
- timer?: ReturnType<typeof setTimeout>;
15
- className?: string;
16
- close?: () => void;
17
11
  onShow?: () => void;
18
12
  onHide?: () => void;
19
13
  }
14
+ /** Internal runtime message (extends user config with system fields) */
15
+ interface IMessage extends IMessageConfig {
16
+ id: string;
17
+ active: boolean;
18
+ align?: string;
19
+ timer?: ReturnType<typeof setTimeout>;
20
+ close?: () => void;
21
+ }
22
+ /** Props for <Message.Container /> */
23
+ interface IMessageContainerProps {
24
+ align?: "center" | "left" | "right";
25
+ fromBottom?: boolean;
26
+ unshift?: boolean;
27
+ gap?: number;
28
+ offset?: string;
29
+ duration?: number;
30
+ className?: string;
31
+ style?: CSSProperties;
32
+ }
20
33
 
21
- export type { IMessage };
34
+ export type { IMessage, IMessageConfig, IMessageContainerProps };
@@ -0,0 +1,6 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { IDateRange } from '../type.js';
3
+
4
+ declare const DateRange: (props: IDateRange) => react_jsx_runtime.JSX.Element;
5
+
6
+ export { DateRange as default };
@@ -0,0 +1,5 @@
1
+ import DateRange from './daterange.js';
2
+
3
+
4
+
5
+ export { DateRange, DateRange as default };
@@ -1,6 +1,6 @@
1
1
  import { ColorPickerProps } from '@rc-component/color-picker';
2
+ import { ReactNode, CSSProperties } from 'react';
2
3
  import { Dayjs } from 'dayjs';
3
- import { ReactNode } from 'react';
4
4
  import { BaseInput } from '../../type/index.js';
5
5
  import { IInput } from '../input/type.js';
6
6
  import { IPopup } from '../popup/type.js';
@@ -20,6 +20,15 @@ interface IBaseDates {
20
20
  onDateClick?: (date: Dayjs) => void;
21
21
  disabledDate?: (date: Dayjs) => boolean;
22
22
  }
23
+ interface IDateRange extends Omit<IBaseDates, "value" | "onDateClick"> {
24
+ value?: [string | undefined, string | undefined];
25
+ placeholder?: string;
26
+ className?: string;
27
+ style?: CSSProperties;
28
+ clear?: boolean;
29
+ onChange?: (value: [string | undefined, string | undefined]) => void;
30
+ onClear?: () => void;
31
+ }
23
32
  interface ITimePicker extends BaseInput, IInput {
24
33
  value?: any;
25
34
  format?: string;
@@ -43,4 +52,4 @@ interface IColorPicker extends Omit<ColorPickerProps, "value" | "onChange"> {
43
52
  onChange?: (value: any) => void;
44
53
  }
45
54
 
46
- export type { IBaseDates, IColorPicker, IDatePicker, ITimePicker };
55
+ export type { IBaseDates, IColorPicker, IDatePicker, IDateRange, ITimePicker };
@@ -16,11 +16,12 @@ export { default as Image } from './components/image/image.js';
16
16
  export { default as Input } from './components/input/input.js';
17
17
  export { default as List } from './components/list/list.js';
18
18
  export { default as Loading } from './components/loading/loading.js';
19
- export { default as Message } from './components/message/message.js';
19
+ export { default as Message } from './components/message/index.js';
20
20
  export { default as Modal } from './components/modal/modal.js';
21
21
  export { default as Pagination } from './components/pagination/pagination.js';
22
22
  export { default as ColorPicker } from './components/picker/colors/index.js';
23
23
  export { default as TimePicker } from './components/picker/time/index.js';
24
+ export { default as DateRange } from './components/picker/daterange/daterange.js';
24
25
  export { default as DatePicker } from './components/picker/dates/index.js';
25
26
  export { default as Popconfirm } from './components/popconfirm/popconfirm.js';
26
27
  export { default as Popup } from './components/popup/popup.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ioca/react",
3
- "version": "1.5.17",
3
+ "version": "1.5.18",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite",