klun-ui 0.1.3 → 0.1.5

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/CHANGELOG.md CHANGED
@@ -5,6 +5,33 @@ All notable changes to **klun-ui** are documented here. The format follows
5
5
  [Semantic Versioning](https://semver.org/) (pre-1.0: minor/patch bumps may carry
6
6
  small breaking changes).
7
7
 
8
+ ## [0.1.5] — 2026-06-21
9
+
10
+ ### Added
11
+ - **`Modal` aligned with Ant Design.** A default Cancel/OK footer driven by
12
+ `onOk` / `onCancel` / `okText` / `cancelText` / `confirmLoading` / `okType`
13
+ (set `footer={null}` to hide it) — convenient for submitting a `Form`
14
+ (`onOk={() => form.submit()}` + `confirmLoading`). New props: `centered`,
15
+ `maskClosable`, `keyboard`, `mask`, `okButtonProps` / `cancelButtonProps`,
16
+ `zIndex`, `afterClose`, `afterOpenChange`, and a **`draggable`** mode (drag by
17
+ the header).
18
+ - **`Drawer` aligned with Ant Design.** New `placement` (`top` / `right` /
19
+ `bottom` / `left`), `height` (for top/bottom), `size` (`default` 378 /
20
+ `large` 736), `closable`, `maskClosable`, `mask`, `keyboard`, `extra` header
21
+ actions, `zIndex` and `afterOpenChange`. The existing `side` prop still works
22
+ as an alias of `placement`.
23
+
24
+ ### Changed
25
+ - `Modal` default `width` is now `520` (was 440) and dialogs are **top-aligned**
26
+ by default; pass `centered` to vertically center (matches antd defaults).
27
+
28
+ ## [0.1.4] — 2026-06-21
29
+
30
+ ### Fixed
31
+ - **`Modal`** no longer draws the footer divider when there's no body (e.g.
32
+ confirmation dialogs), so the actions sit cleanly under the heading instead of
33
+ reading as an empty band.
34
+
8
35
  ## [0.1.3] — 2026-06-20
9
36
 
10
37
  ### Fixed
@@ -47,6 +74,8 @@ small breaking changes).
47
74
  `ConfigProvider`, i18n locales, and the full component set (layout, forms,
48
75
  navigation, data display, feedback, overlays, charts).
49
76
 
77
+ [0.1.5]: https://www.npmjs.com/package/klun-ui/v/0.1.5
78
+ [0.1.4]: https://www.npmjs.com/package/klun-ui/v/0.1.4
50
79
  [0.1.3]: https://www.npmjs.com/package/klun-ui/v/0.1.3
51
80
  [0.1.2]: https://www.npmjs.com/package/klun-ui/v/0.1.2
52
81
  [0.1.1]: https://www.npmjs.com/package/klun-ui/v/0.1.1
@@ -9838,29 +9838,83 @@ function Confirm(opts = {}) {
9838
9838
  root.render(/* @__PURE__ */ jsxRuntime.jsx(ConfirmDialog, { opts, onClose: close }));
9839
9839
  });
9840
9840
  }
9841
- var Drawer = react.forwardRef(function Drawer2({ open, onClose, title, side = "right", width = 400, children, footer, className, style, ...props }, ref) {
9841
+ var Drawer = react.forwardRef(function Drawer2({
9842
+ open,
9843
+ onClose,
9844
+ title,
9845
+ placement,
9846
+ side,
9847
+ width = 378,
9848
+ height = 378,
9849
+ size = "default",
9850
+ closable = true,
9851
+ maskClosable = true,
9852
+ mask = true,
9853
+ keyboard = true,
9854
+ extra,
9855
+ footer,
9856
+ zIndex,
9857
+ afterOpenChange,
9858
+ children,
9859
+ className,
9860
+ style,
9861
+ ...props
9862
+ }, ref) {
9842
9863
  const { drawer } = useLocale();
9864
+ const place = placement ?? side ?? "right";
9865
+ const horizontal = place === "left" || place === "right";
9866
+ const prevOpen = react.useRef(open);
9867
+ react.useEffect(() => {
9868
+ if (!open) return;
9869
+ const onKey = (e) => {
9870
+ if (e.key === "Escape" && keyboard && closable) onClose?.();
9871
+ };
9872
+ document.addEventListener("keydown", onKey);
9873
+ const prevOverflow = document.body.style.overflow;
9874
+ document.body.style.overflow = "hidden";
9875
+ return () => {
9876
+ document.removeEventListener("keydown", onKey);
9877
+ document.body.style.overflow = prevOverflow;
9878
+ };
9879
+ }, [open, keyboard, closable, onClose]);
9880
+ react.useEffect(() => {
9881
+ if (prevOpen.current !== open) {
9882
+ afterOpenChange?.(open);
9883
+ prevOpen.current = open;
9884
+ }
9885
+ }, [open]);
9843
9886
  if (!open) return null;
9844
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkTPGAXYFU_cjs.cx("klun-drawer", `klun-drawer--${side}`), onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs(
9887
+ const axis = size === "large" ? 736 : horizontal ? width : height;
9888
+ const panelStyle = horizontal ? { width: axis, height: "100%", ...style } : { height: axis, width: "100%", ...style };
9889
+ return /* @__PURE__ */ jsxRuntime.jsx(
9845
9890
  "div",
9846
9891
  {
9847
- ref,
9848
- role: "dialog",
9849
- "aria-modal": "true",
9850
- onClick: (e) => e.stopPropagation(),
9851
- className: chunkTPGAXYFU_cjs.cx("klun-drawer__panel", className),
9852
- style: { width, ...style },
9853
- ...props,
9854
- children: [
9855
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-drawer__header", children: [
9856
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-drawer__title", children: title }),
9857
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onClose, "aria-label": drawer.close, className: "klun-drawer__close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 5l10 10M15 5L5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })
9858
- ] }),
9859
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-drawer__body", children }),
9860
- footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-drawer__footer", children: footer }) : null
9861
- ]
9892
+ className: chunkTPGAXYFU_cjs.cx("klun-drawer", `klun-drawer--${place}`, !mask && "klun-drawer--no-mask"),
9893
+ style: zIndex != null ? { zIndex } : void 0,
9894
+ onClick: maskClosable ? onClose : void 0,
9895
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
9896
+ "div",
9897
+ {
9898
+ ref,
9899
+ role: "dialog",
9900
+ "aria-modal": "true",
9901
+ onClick: (e) => e.stopPropagation(),
9902
+ className: chunkTPGAXYFU_cjs.cx("klun-drawer__panel", className),
9903
+ style: panelStyle,
9904
+ ...props,
9905
+ children: [
9906
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-drawer__header", children: [
9907
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-drawer__title", children: title }),
9908
+ extra ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-drawer__extra", children: extra }) : null,
9909
+ closable ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onClose, "aria-label": drawer.close, className: "klun-drawer__close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 5l10 10M15 5L5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) }) : null
9910
+ ] }),
9911
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-drawer__body", children }),
9912
+ footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-drawer__footer", children: footer }) : null
9913
+ ]
9914
+ }
9915
+ )
9862
9916
  }
9863
- ) });
9917
+ );
9864
9918
  });
9865
9919
  Drawer.displayName = "Drawer";
9866
9920
  var Dropdown = react.forwardRef(function Dropdown2({ trigger, items: items2 = [], align = "left", width = 220, className, ...props }, ref) {
@@ -9918,46 +9972,133 @@ var Dropdown = react.forwardRef(function Dropdown2({ trigger, items: items2 = []
9918
9972
  ] });
9919
9973
  });
9920
9974
  Dropdown.displayName = "Dropdown";
9921
- var Modal = react.forwardRef(function Modal2({ open, onClose, title, description, icon, children, footer, width = 440, closable = true, closeOnBackdrop = true, className, style, ...props }, ref) {
9975
+ var Modal = react.forwardRef(function Modal2({
9976
+ open,
9977
+ onClose,
9978
+ onOk,
9979
+ onCancel,
9980
+ title,
9981
+ description,
9982
+ icon,
9983
+ footer,
9984
+ okText,
9985
+ cancelText,
9986
+ confirmLoading = false,
9987
+ okType = "primary",
9988
+ okButtonProps,
9989
+ cancelButtonProps,
9990
+ width = 520,
9991
+ centered = false,
9992
+ closable = true,
9993
+ maskClosable,
9994
+ closeOnBackdrop,
9995
+ keyboard = true,
9996
+ mask = true,
9997
+ draggable = false,
9998
+ zIndex,
9999
+ afterClose,
10000
+ afterOpenChange,
10001
+ children,
10002
+ className,
10003
+ style,
10004
+ ...props
10005
+ }, ref) {
9922
10006
  const { modal } = useLocale();
10007
+ const cancel = onCancel ?? onClose;
10008
+ const backdropClosable = maskClosable ?? closeOnBackdrop ?? true;
10009
+ const [pos, setPos] = react.useState({ x: 0, y: 0 });
10010
+ const drag = react.useRef(null);
10011
+ const prevOpen = react.useRef(open);
9923
10012
  react.useEffect(() => {
9924
10013
  if (!open) return;
9925
10014
  const onKey = (e) => {
9926
- if (e.key === "Escape" && closable) onClose?.();
10015
+ if (e.key === "Escape" && keyboard && closable) cancel?.();
9927
10016
  };
9928
10017
  document.addEventListener("keydown", onKey);
9929
- const prev = document.body.style.overflow;
10018
+ const prevOverflow = document.body.style.overflow;
9930
10019
  document.body.style.overflow = "hidden";
9931
10020
  return () => {
9932
10021
  document.removeEventListener("keydown", onKey);
9933
- document.body.style.overflow = prev;
10022
+ document.body.style.overflow = prevOverflow;
9934
10023
  };
9935
- }, [open, closable, onClose]);
10024
+ }, [open, keyboard, closable, cancel]);
10025
+ react.useEffect(() => {
10026
+ if (open) setPos({ x: 0, y: 0 });
10027
+ if (prevOpen.current !== open) {
10028
+ afterOpenChange?.(open);
10029
+ if (!open) afterClose?.();
10030
+ prevOpen.current = open;
10031
+ }
10032
+ }, [open]);
9936
10033
  if (!open) return null;
9937
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal", onClick: closeOnBackdrop && closable ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
10034
+ const startDrag = (e) => {
10035
+ if (!draggable) return;
10036
+ if (e.target.closest(".klun-modal__close")) return;
10037
+ drag.current = { sx: e.clientX, sy: e.clientY, ox: pos.x, oy: pos.y };
10038
+ const move = (ev) => {
10039
+ if (!drag.current) return;
10040
+ setPos({
10041
+ x: drag.current.ox + (ev.clientX - drag.current.sx),
10042
+ y: drag.current.oy + (ev.clientY - drag.current.sy)
10043
+ });
10044
+ };
10045
+ const up = () => {
10046
+ drag.current = null;
10047
+ document.removeEventListener("mousemove", move);
10048
+ document.removeEventListener("mouseup", up);
10049
+ };
10050
+ document.addEventListener("mousemove", move);
10051
+ document.addEventListener("mouseup", up);
10052
+ };
10053
+ const defaultFooter = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10054
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "neutral", appearance: "stroke", disabled: confirmLoading, onClick: cancel, ...cancelButtonProps, children: cancelText ?? "Cancel" }),
10055
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: okType === "danger" ? "error" : "primary", loading: confirmLoading, onClick: onOk, ...okButtonProps, children: okText ?? "OK" })
10056
+ ] });
10057
+ const resolvedFooter = footer === null ? null : footer !== void 0 ? footer : defaultFooter;
10058
+ const dialogStyle = {
10059
+ width,
10060
+ ...pos.x || pos.y ? { transform: `translate(${pos.x}px, ${pos.y}px)` } : null,
10061
+ ...style
10062
+ };
10063
+ return /* @__PURE__ */ jsxRuntime.jsx(
9938
10064
  "div",
9939
10065
  {
9940
- ref,
9941
- role: "dialog",
9942
- "aria-modal": "true",
9943
- onClick: (e) => e.stopPropagation(),
9944
- className: chunkTPGAXYFU_cjs.cx("klun-modal__dialog", className),
9945
- style: { width, ...style },
9946
- ...props,
9947
- children: [
9948
- title || icon ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-modal__header", children: [
9949
- icon ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__icon", children: icon }) : null,
9950
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-modal__heading", children: [
9951
- title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__title", children: title }) : null,
9952
- description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__description", children: description }) : null
9953
- ] }),
9954
- closable ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onClose, "aria-label": modal.close, className: "klun-modal__close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 5l10 10M15 5L5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) }) : null
9955
- ] }) : null,
9956
- children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__body", children }) : null,
9957
- footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__footer", children: footer }) : null
9958
- ]
10066
+ className: chunkTPGAXYFU_cjs.cx("klun-modal", centered && "klun-modal--centered", !mask && "klun-modal--no-mask"),
10067
+ style: zIndex != null ? { zIndex } : void 0,
10068
+ onClick: backdropClosable && closable ? cancel : void 0,
10069
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
10070
+ "div",
10071
+ {
10072
+ ref,
10073
+ role: "dialog",
10074
+ "aria-modal": "true",
10075
+ onClick: (e) => e.stopPropagation(),
10076
+ className: chunkTPGAXYFU_cjs.cx("klun-modal__dialog", className),
10077
+ style: dialogStyle,
10078
+ ...props,
10079
+ children: [
10080
+ title || icon ? /* @__PURE__ */ jsxRuntime.jsxs(
10081
+ "div",
10082
+ {
10083
+ className: chunkTPGAXYFU_cjs.cx("klun-modal__header", draggable && "klun-modal__header--draggable"),
10084
+ onMouseDown: startDrag,
10085
+ children: [
10086
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__icon", children: icon }) : null,
10087
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-modal__heading", children: [
10088
+ title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__title", children: title }) : null,
10089
+ description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__description", children: description }) : null
10090
+ ] }),
10091
+ closable ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: cancel, "aria-label": modal.close, className: "klun-modal__close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 5l10 10M15 5L5 15", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) }) : null
10092
+ ]
10093
+ }
10094
+ ) : null,
10095
+ children ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-modal__body", children }) : null,
10096
+ resolvedFooter ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkTPGAXYFU_cjs.cx("klun-modal__footer", !children && "klun-modal__footer--flush"), children: resolvedFooter }) : null
10097
+ ]
10098
+ }
10099
+ )
9959
10100
  }
9960
- ) });
10101
+ );
9961
10102
  });
9962
10103
  Modal.displayName = "Modal";
9963
10104
  var Popconfirm = react.forwardRef(function Popconfirm2({
@@ -10212,5 +10353,5 @@ exports.useSize = useSize;
10212
10353
  exports.viVN = viVN;
10213
10354
  exports.zhCN = zhCN;
10214
10355
  exports.zhTW = zhTW;
10215
- //# sourceMappingURL=chunk-SUH5CQ55.cjs.map
10216
- //# sourceMappingURL=chunk-SUH5CQ55.cjs.map
10356
+ //# sourceMappingURL=chunk-LDENBMG5.cjs.map
10357
+ //# sourceMappingURL=chunk-LDENBMG5.cjs.map