klun-ui 0.1.9 → 0.1.10

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,20 @@ 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.10] — 2026-06-21
9
+
10
+ ### Added
11
+ - **`ImageViewer` — a full-screen image lightbox.** Single image or gallery
12
+ (prev/next + counter), wheel & button zoom, rotate, drag-to-pan when zoomed,
13
+ double-click to toggle fit/100%, optional download, and keyboard control
14
+ (Esc / ←→ / `+` `-` / `0`). Portals to `<body>` and locks body scroll.
15
+ - **`ContextMenu` + `useContextMenu` — a cursor-positioned right-click menu.**
16
+ Nested submenus via `item.children` (open on hover or `→`, any depth, each
17
+ level flips to stay on-screen), clamps/flips to the viewport, keyboard
18
+ navigation (↑ ↓ → ← Enter Esc Home End), disabled items, shortcuts, dividers
19
+ and danger items. The `useContextMenu` hook wires up `onContextMenu` →
20
+ open-at-cursor.
21
+
8
22
  ## [0.1.9] — 2026-06-21
9
23
 
10
24
  ### Fixed
@@ -4372,9 +4372,9 @@ function useExpandedRows({
4372
4372
  list.forEach((row, i) => {
4373
4373
  const id = getRowId(row, i);
4374
4374
  const kids = row[childrenKey];
4375
- const hasChildren = Array.isArray(kids) && kids.length > 0;
4376
- out.push({ row, id, depth, hasChildren });
4377
- if (hasChildren && treeOpen.has(id)) walk(kids, depth + 1);
4375
+ const hasChildren2 = Array.isArray(kids) && kids.length > 0;
4376
+ out.push({ row, id, depth, hasChildren: hasChildren2 });
4377
+ if (hasChildren2 && treeOpen.has(id)) walk(kids, depth + 1);
4378
4378
  });
4379
4379
  };
4380
4380
  walk(pageRows, 0);
@@ -4912,7 +4912,7 @@ var Tree = react.forwardRef(function Tree2({
4912
4912
  className: chunkTPGAXYFU_cjs.cx("klun-tree", showLine && "klun-tree--line", className),
4913
4913
  ...props,
4914
4914
  children: rows.filter((r) => !r.hidden).map(({ node, level }) => {
4915
- const hasChildren = !!(node.children && node.children.length);
4915
+ const hasChildren2 = !!(node.children && node.children.length);
4916
4916
  const isExpanded = exp.includes(node.key);
4917
4917
  const selected = selectedKey === node.key;
4918
4918
  const cs = checkable ? checkState(node) : null;
@@ -4920,7 +4920,7 @@ var Tree = react.forwardRef(function Tree2({
4920
4920
  "div",
4921
4921
  {
4922
4922
  role: "treeitem",
4923
- "aria-expanded": hasChildren ? isExpanded : void 0,
4923
+ "aria-expanded": hasChildren2 ? isExpanded : void 0,
4924
4924
  "aria-selected": selected,
4925
4925
  className: chunkTPGAXYFU_cjs.cx(
4926
4926
  "klun-tree__item",
@@ -4939,12 +4939,12 @@ var Tree = react.forwardRef(function Tree2({
4939
4939
  {
4940
4940
  className: chunkTPGAXYFU_cjs.cx(
4941
4941
  "klun-tree__caret",
4942
- hasChildren && isExpanded && "klun-tree__caret--expanded"
4942
+ hasChildren2 && isExpanded && "klun-tree__caret--expanded"
4943
4943
  ),
4944
- "data-leaf": hasChildren ? void 0 : "",
4944
+ "data-leaf": hasChildren2 ? void 0 : "",
4945
4945
  onClick: (e) => {
4946
4946
  e.stopPropagation();
4947
- if (hasChildren) toggle(node.key);
4947
+ if (hasChildren2) toggle(node.key);
4948
4948
  },
4949
4949
  children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-arrow-right-s-line" })
4950
4950
  }
@@ -5920,7 +5920,7 @@ var Cascader = react.forwardRef(function Cascader2({
5920
5920
  ),
5921
5921
  children: col.map((opt) => {
5922
5922
  const selected = active[ci] === opt.value;
5923
- const hasChildren = !!(opt.children && opt.children.length);
5923
+ const hasChildren2 = !!(opt.children && opt.children.length);
5924
5924
  return /* @__PURE__ */ jsxRuntime.jsxs(
5925
5925
  "button",
5926
5926
  {
@@ -5931,7 +5931,7 @@ var Cascader = react.forwardRef(function Cascader2({
5931
5931
  "data-selected": selected || void 0,
5932
5932
  children: [
5933
5933
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "klun-cascader__option-label", children: opt.label }),
5934
- hasChildren ? /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-arrow-right-s-line klun-cascader__option-arrow" }) : selected ? /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-check-line klun-cascader__option-check" }) : null
5934
+ hasChildren2 ? /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-arrow-right-s-line klun-cascader__option-arrow" }) : selected ? /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-check-line klun-cascader__option-check" }) : null
5935
5935
  ]
5936
5936
  },
5937
5937
  opt.value
@@ -9838,6 +9838,203 @@ function Confirm(opts = {}) {
9838
9838
  root.render(/* @__PURE__ */ jsxRuntime.jsx(ConfirmDialog, { opts, onClose: close }));
9839
9839
  });
9840
9840
  }
9841
+ var isSelectable = (it) => !it.divider && !it.disabled;
9842
+ var hasChildren = (it) => !!it.children && it.children.length > 0;
9843
+ var GAP2 = 8;
9844
+ function place(w, h, opts) {
9845
+ const vw = window.innerWidth;
9846
+ const vh = window.innerHeight;
9847
+ let left;
9848
+ let top;
9849
+ if (opts.rect) {
9850
+ const r = opts.rect;
9851
+ left = r.right - 4 + w > vw - GAP2 ? r.left - w + 4 : r.right - 4;
9852
+ top = r.top - 4;
9853
+ } else {
9854
+ const p = opts.point ?? { x: 0, y: 0 };
9855
+ left = p.x + w > vw - GAP2 ? p.x - w : p.x;
9856
+ top = p.y + h > vh - GAP2 ? p.y - h : p.y;
9857
+ }
9858
+ left = Math.min(Math.max(GAP2, left), vw - w - GAP2);
9859
+ top = Math.min(Math.max(GAP2, top), vh - h - GAP2);
9860
+ return { left, top };
9861
+ }
9862
+ function MenuPanel({ items: items2, width, className, point, anchorEl, autoActive, depth, onCloseAll, onCloseSelf }) {
9863
+ const ref = react.useRef(null);
9864
+ const itemEls = react.useRef([]);
9865
+ const [pos, setPos] = react.useState(null);
9866
+ const [active, setActive] = react.useState(autoActive ? items2.findIndex(isSelectable) : -1);
9867
+ const [sub, setSub] = react.useState(null);
9868
+ const isLeaf = sub === null;
9869
+ react.useLayoutEffect(() => {
9870
+ const el = ref.current;
9871
+ const w = el?.offsetWidth ?? width;
9872
+ const h = el?.offsetHeight ?? 0;
9873
+ setPos(place(w, h, anchorEl ? { rect: anchorEl.getBoundingClientRect() } : { point }));
9874
+ }, [width, point?.x, point?.y, anchorEl, items2]);
9875
+ const move = react.useCallback(
9876
+ (dir) => {
9877
+ setActive((cur) => {
9878
+ const n = items2.length;
9879
+ for (let s = 1; s <= n; s++) {
9880
+ const i = (cur + dir * s + n * s) % n;
9881
+ if (isSelectable(items2[i])) return i;
9882
+ }
9883
+ return cur;
9884
+ });
9885
+ },
9886
+ [items2]
9887
+ );
9888
+ const activate = react.useCallback(
9889
+ (i, viaKeyboard) => {
9890
+ const it = items2[i];
9891
+ if (!it || !isSelectable(it)) return;
9892
+ if (hasChildren(it)) setSub({ i, kbd: viaKeyboard });
9893
+ else {
9894
+ onCloseAll();
9895
+ it.onClick?.();
9896
+ }
9897
+ },
9898
+ [items2, onCloseAll]
9899
+ );
9900
+ react.useEffect(() => {
9901
+ if (!isLeaf) return;
9902
+ const onKey = (e) => {
9903
+ if (e.key === "ArrowDown") {
9904
+ e.preventDefault();
9905
+ move(1);
9906
+ } else if (e.key === "ArrowUp") {
9907
+ e.preventDefault();
9908
+ move(-1);
9909
+ } else if (e.key === "Home") {
9910
+ e.preventDefault();
9911
+ setActive(items2.findIndex(isSelectable));
9912
+ } else if (e.key === "End") {
9913
+ e.preventDefault();
9914
+ for (let i = items2.length - 1; i >= 0; i--)
9915
+ if (isSelectable(items2[i])) {
9916
+ setActive(i);
9917
+ break;
9918
+ }
9919
+ } else if (e.key === "ArrowRight") {
9920
+ if (active >= 0 && hasChildren(items2[active])) {
9921
+ e.preventDefault();
9922
+ setSub({ i: active, kbd: true });
9923
+ }
9924
+ } else if (e.key === "ArrowLeft") {
9925
+ if (depth > 0) {
9926
+ e.preventDefault();
9927
+ onCloseSelf?.();
9928
+ }
9929
+ } else if (e.key === "Enter") {
9930
+ if (active >= 0) {
9931
+ e.preventDefault();
9932
+ activate(active, true);
9933
+ }
9934
+ } else if (e.key === "Escape") {
9935
+ e.preventDefault();
9936
+ onCloseAll();
9937
+ }
9938
+ };
9939
+ window.addEventListener("keydown", onKey, true);
9940
+ return () => window.removeEventListener("keydown", onKey, true);
9941
+ }, [isLeaf, active, items2, depth, move, activate, onCloseAll, onCloseSelf]);
9942
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9943
+ "div",
9944
+ {
9945
+ ref,
9946
+ role: "menu",
9947
+ className: chunkTPGAXYFU_cjs.cx("klun-context-menu", className),
9948
+ style: pos ? { left: pos.left, top: pos.top, width } : { left: -9999, top: -9999, width },
9949
+ onMouseDown: (e) => e.stopPropagation(),
9950
+ children: [
9951
+ items2.map(
9952
+ (it, i) => it.divider ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-context-menu__divider", role: "separator" }, i) : /* @__PURE__ */ jsxRuntime.jsxs(
9953
+ "button",
9954
+ {
9955
+ ref: (el) => {
9956
+ itemEls.current[i] = el;
9957
+ },
9958
+ type: "button",
9959
+ role: "menuitem",
9960
+ "aria-haspopup": hasChildren(it) || void 0,
9961
+ "aria-expanded": hasChildren(it) ? sub?.i === i : void 0,
9962
+ disabled: it.disabled,
9963
+ className: chunkTPGAXYFU_cjs.cx(
9964
+ "klun-context-menu__item",
9965
+ it.danger && "klun-context-menu__item--danger",
9966
+ i === active && "is-active"
9967
+ ),
9968
+ onMouseEnter: () => {
9969
+ setActive(i);
9970
+ if (isSelectable(it)) setSub(hasChildren(it) ? { i, kbd: false } : null);
9971
+ },
9972
+ onClick: () => activate(i, false),
9973
+ children: [
9974
+ it.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "klun-context-menu__icon", children: it.icon }) : null,
9975
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "klun-context-menu__label", children: it.label }),
9976
+ hasChildren(it) ? /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-arrow-right-s-line klun-context-menu__arrow" }) : it.shortcut ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "klun-context-menu__shortcut", children: it.shortcut }) : null
9977
+ ]
9978
+ },
9979
+ i
9980
+ )
9981
+ ),
9982
+ sub && items2[sub.i]?.children ? /* @__PURE__ */ jsxRuntime.jsx(
9983
+ MenuPanel,
9984
+ {
9985
+ items: items2[sub.i].children,
9986
+ width,
9987
+ anchorEl: itemEls.current[sub.i],
9988
+ autoActive: sub.kbd,
9989
+ depth: depth + 1,
9990
+ onCloseAll,
9991
+ onCloseSelf: () => setSub(null)
9992
+ },
9993
+ `sub-${sub.i}`
9994
+ ) : null
9995
+ ]
9996
+ }
9997
+ );
9998
+ }
9999
+ function ContextMenu2({ open, x, y, items: items2, onClose, width = 208, className }) {
10000
+ if (!open || typeof document === "undefined") return null;
10001
+ return reactDom.createPortal(
10002
+ /* @__PURE__ */ jsxRuntime.jsx(
10003
+ "div",
10004
+ {
10005
+ className: "klun-context-menu__layer",
10006
+ onMouseDown: onClose,
10007
+ onContextMenu: (e) => {
10008
+ e.preventDefault();
10009
+ onClose();
10010
+ },
10011
+ onWheel: onClose,
10012
+ children: /* @__PURE__ */ jsxRuntime.jsx(
10013
+ MenuPanel,
10014
+ {
10015
+ items: items2,
10016
+ width,
10017
+ className,
10018
+ point: { x, y },
10019
+ depth: 0,
10020
+ onCloseAll: onClose
10021
+ }
10022
+ )
10023
+ }
10024
+ ),
10025
+ document.body
10026
+ );
10027
+ }
10028
+ ContextMenu2.displayName = "ContextMenu";
10029
+ function useContextMenu() {
10030
+ const [state, setState] = react.useState(null);
10031
+ const open = react.useCallback((e, items2) => {
10032
+ e.preventDefault();
10033
+ setState({ x: e.clientX, y: e.clientY, items: items2 });
10034
+ }, []);
10035
+ const close = react.useCallback(() => setState(null), []);
10036
+ return { state, open, close };
10037
+ }
9841
10038
  var Drawer = react.forwardRef(function Drawer2({
9842
10039
  open,
9843
10040
  onClose,
@@ -9861,8 +10058,8 @@ var Drawer = react.forwardRef(function Drawer2({
9861
10058
  ...props
9862
10059
  }, ref) {
9863
10060
  const { drawer } = useLocale();
9864
- const place = placement ?? side ?? "right";
9865
- const horizontal = place === "left" || place === "right";
10061
+ const place2 = placement ?? side ?? "right";
10062
+ const horizontal = place2 === "left" || place2 === "right";
9866
10063
  const prevOpen = react.useRef(open);
9867
10064
  react.useEffect(() => {
9868
10065
  if (!open) return;
@@ -9889,7 +10086,7 @@ var Drawer = react.forwardRef(function Drawer2({
9889
10086
  return /* @__PURE__ */ jsxRuntime.jsx(
9890
10087
  "div",
9891
10088
  {
9892
- className: chunkTPGAXYFU_cjs.cx("klun-drawer", `klun-drawer--${place}`, !mask && "klun-drawer--no-mask"),
10089
+ className: chunkTPGAXYFU_cjs.cx("klun-drawer", `klun-drawer--${place2}`, !mask && "klun-drawer--no-mask"),
9893
10090
  style: zIndex != null ? { zIndex } : void 0,
9894
10091
  onClick: maskClosable ? onClose : void 0,
9895
10092
  children: /* @__PURE__ */ jsxRuntime.jsxs(
@@ -9972,6 +10169,146 @@ var Dropdown = react.forwardRef(function Dropdown2({ trigger, items: items2 = []
9972
10169
  ] });
9973
10170
  });
9974
10171
  Dropdown.displayName = "Dropdown";
10172
+ var MIN = 0.25;
10173
+ var MAX = 6;
10174
+ var clampScale = (s) => Math.min(MAX, Math.max(MIN, +s.toFixed(2)));
10175
+ function ImageViewer({
10176
+ open,
10177
+ src,
10178
+ alt,
10179
+ items: items2,
10180
+ index,
10181
+ onIndexChange,
10182
+ onClose,
10183
+ onDownload,
10184
+ title,
10185
+ zoomable = true,
10186
+ rotatable = true,
10187
+ closeOnBackdrop = true,
10188
+ className
10189
+ }) {
10190
+ const gallery = items2 && items2.length > 0 ? items2 : src != null ? [{ src, alt }] : [];
10191
+ const [innerIndex, setInnerIndex] = react.useState(0);
10192
+ const idx = index ?? innerIndex;
10193
+ const safeIdx = Math.min(Math.max(idx, 0), Math.max(0, gallery.length - 1));
10194
+ const current = gallery[safeIdx];
10195
+ const [scale, setScale] = react.useState(1);
10196
+ const [rot, setRot] = react.useState(0);
10197
+ const [pan, setPan] = react.useState({ x: 0, y: 0 });
10198
+ const drag = react.useRef(null);
10199
+ const reset = react.useCallback(() => {
10200
+ setScale(1);
10201
+ setRot(0);
10202
+ setPan({ x: 0, y: 0 });
10203
+ }, []);
10204
+ const goTo = react.useCallback(
10205
+ (next) => {
10206
+ if (gallery.length < 2) return;
10207
+ const n = (next + gallery.length) % gallery.length;
10208
+ if (index === void 0) setInnerIndex(n);
10209
+ onIndexChange?.(n);
10210
+ reset();
10211
+ },
10212
+ [gallery.length, index, onIndexChange, reset]
10213
+ );
10214
+ react.useEffect(() => {
10215
+ reset();
10216
+ }, [safeIdx, open, reset]);
10217
+ react.useEffect(() => {
10218
+ if (!open) return;
10219
+ const prev = document.body.style.overflow;
10220
+ document.body.style.overflow = "hidden";
10221
+ const onKey = (e) => {
10222
+ if (e.key === "Escape") onClose();
10223
+ else if (e.key === "ArrowLeft") goTo(safeIdx - 1);
10224
+ else if (e.key === "ArrowRight") goTo(safeIdx + 1);
10225
+ else if (zoomable && (e.key === "+" || e.key === "=")) setScale((s) => clampScale(s + 0.25));
10226
+ else if (zoomable && (e.key === "-" || e.key === "_")) setScale((s) => clampScale(s - 0.25));
10227
+ else if (e.key === "0") reset();
10228
+ };
10229
+ window.addEventListener("keydown", onKey);
10230
+ return () => {
10231
+ document.body.style.overflow = prev;
10232
+ window.removeEventListener("keydown", onKey);
10233
+ };
10234
+ }, [open, safeIdx, zoomable, onClose, goTo, reset]);
10235
+ if (!open || !current || typeof document === "undefined") return null;
10236
+ const onWheel = (e) => {
10237
+ if (!zoomable) return;
10238
+ e.preventDefault();
10239
+ setScale((s) => clampScale(s + (e.deltaY < 0 ? 0.2 : -0.2)));
10240
+ };
10241
+ const onImgDown = (e) => {
10242
+ if (scale <= 1) return;
10243
+ e.preventDefault();
10244
+ drag.current = { x: e.clientX, y: e.clientY, px: pan.x, py: pan.y };
10245
+ };
10246
+ const onMove = (e) => {
10247
+ if (!drag.current) return;
10248
+ setPan({ x: drag.current.px + (e.clientX - drag.current.x), y: drag.current.py + (e.clientY - drag.current.y) });
10249
+ };
10250
+ const endDrag = () => {
10251
+ drag.current = null;
10252
+ };
10253
+ const toggleZoom = () => scale > 1 ? reset() : setScale(2);
10254
+ const heading = title ?? current.title ?? current.alt;
10255
+ return reactDom.createPortal(
10256
+ /* @__PURE__ */ jsxRuntime.jsxs(
10257
+ "div",
10258
+ {
10259
+ className: chunkTPGAXYFU_cjs.cx("klun-image-viewer", className),
10260
+ onMouseDown: (e) => {
10261
+ if (closeOnBackdrop && e.target === e.currentTarget) onClose();
10262
+ },
10263
+ onMouseMove: onMove,
10264
+ onMouseUp: endDrag,
10265
+ onMouseLeave: endDrag,
10266
+ children: [
10267
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-image-viewer__bar", onMouseDown: (e) => e.stopPropagation(), children: [
10268
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-image-viewer__title", children: [
10269
+ heading,
10270
+ gallery.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "klun-image-viewer__counter", children: [
10271
+ safeIdx + 1,
10272
+ " / ",
10273
+ gallery.length
10274
+ ] }) : null
10275
+ ] }),
10276
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "klun-image-viewer__tools", children: [
10277
+ zoomable ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10278
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__btn", "aria-label": "Zoom out", onClick: () => setScale((s) => clampScale(s - 0.25)), children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-zoom-out-line" }) }),
10279
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "klun-image-viewer__zoom", children: [
10280
+ Math.round(scale * 100),
10281
+ "%"
10282
+ ] }),
10283
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__btn", "aria-label": "Zoom in", onClick: () => setScale((s) => clampScale(s + 0.25)), children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-zoom-in-line" }) })
10284
+ ] }) : null,
10285
+ rotatable ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__btn", "aria-label": "Rotate", onClick: () => setRot((r) => (r + 90) % 360), children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-anticlockwise-line" }) }) : null,
10286
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__btn", "aria-label": "Reset", onClick: reset, children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-aspect-ratio-line" }) }),
10287
+ onDownload ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__btn", "aria-label": "Download", onClick: () => onDownload(current, safeIdx), children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-download-2-line" }) }) : null,
10288
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__btn", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-close-line" }) })
10289
+ ] })
10290
+ ] }),
10291
+ gallery.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__nav klun-image-viewer__nav--prev", "aria-label": "Previous", onMouseDown: (e) => e.stopPropagation(), onClick: () => goTo(safeIdx - 1), children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-arrow-left-s-line" }) }) : null,
10292
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "klun-image-viewer__stage", onMouseDown: (e) => e.stopPropagation(), onWheel, children: /* @__PURE__ */ jsxRuntime.jsx(
10293
+ "img",
10294
+ {
10295
+ className: chunkTPGAXYFU_cjs.cx("klun-image-viewer__img", scale > 1 && "is-zoomed"),
10296
+ src: current.src,
10297
+ alt: current.alt ?? "",
10298
+ draggable: false,
10299
+ onDoubleClick: toggleZoom,
10300
+ onMouseDown: onImgDown,
10301
+ style: { transform: `translate(${pan.x}px, ${pan.y}px) scale(${scale}) rotate(${rot}deg)` }
10302
+ }
10303
+ ) }),
10304
+ gallery.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "klun-image-viewer__nav klun-image-viewer__nav--next", "aria-label": "Next", onMouseDown: (e) => e.stopPropagation(), onClick: () => goTo(safeIdx + 1), children: /* @__PURE__ */ jsxRuntime.jsx("i", { className: "ri-arrow-right-s-line" }) }) : null
10305
+ ]
10306
+ }
10307
+ ),
10308
+ document.body
10309
+ );
10310
+ }
10311
+ ImageViewer.displayName = "ImageViewer";
9975
10312
  var Modal = react.forwardRef(function Modal2({
9976
10313
  open,
9977
10314
  onClose,
@@ -10101,7 +10438,7 @@ var Modal = react.forwardRef(function Modal2({
10101
10438
  );
10102
10439
  });
10103
10440
  Modal.displayName = "Modal";
10104
- var GAP2 = 8;
10441
+ var GAP3 = 8;
10105
10442
  var MARGIN = 8;
10106
10443
  var Popconfirm = react.forwardRef(function Popconfirm2({
10107
10444
  title: titleProp,
@@ -10150,7 +10487,7 @@ var Popconfirm = react.forwardRef(function Popconfirm2({
10150
10487
  setPos(null);
10151
10488
  return;
10152
10489
  }
10153
- const place = () => {
10490
+ const place2 = () => {
10154
10491
  const trigger = innerRef.current;
10155
10492
  const bubble = bubbleRef.current;
10156
10493
  if (!trigger || !bubble) return;
@@ -10163,31 +10500,31 @@ var Popconfirm = react.forwardRef(function Popconfirm2({
10163
10500
  let left;
10164
10501
  switch (placement) {
10165
10502
  case "bottom":
10166
- top = tr.bottom + GAP2;
10503
+ top = tr.bottom + GAP3;
10167
10504
  left = tr.left + tr.width / 2 - bw / 2;
10168
10505
  break;
10169
10506
  case "left":
10170
- left = tr.left - bw - GAP2;
10507
+ left = tr.left - bw - GAP3;
10171
10508
  top = tr.top + tr.height / 2 - bh / 2;
10172
10509
  break;
10173
10510
  case "right":
10174
- left = tr.right + GAP2;
10511
+ left = tr.right + GAP3;
10175
10512
  top = tr.top + tr.height / 2 - bh / 2;
10176
10513
  break;
10177
10514
  default:
10178
- top = tr.top - bh - GAP2;
10515
+ top = tr.top - bh - GAP3;
10179
10516
  left = tr.left + tr.width / 2 - bw / 2;
10180
10517
  }
10181
10518
  left = Math.max(MARGIN, Math.min(left, vw - bw - MARGIN));
10182
10519
  top = Math.max(MARGIN, Math.min(top, vh - bh - MARGIN));
10183
10520
  setPos({ top, left });
10184
10521
  };
10185
- place();
10186
- window.addEventListener("scroll", place, true);
10187
- window.addEventListener("resize", place);
10522
+ place2();
10523
+ window.addEventListener("scroll", place2, true);
10524
+ window.addEventListener("resize", place2);
10188
10525
  return () => {
10189
- window.removeEventListener("scroll", place, true);
10190
- window.removeEventListener("resize", place);
10526
+ window.removeEventListener("scroll", place2, true);
10527
+ window.removeEventListener("resize", place2);
10191
10528
  };
10192
10529
  }, [open, placement, title, description]);
10193
10530
  const setRefs = (node) => {
@@ -10308,6 +10645,7 @@ exports.CompactSelectForInput = CompactSelectForInput;
10308
10645
  exports.ConfigProvider = ConfigProvider;
10309
10646
  exports.Confirm = Confirm;
10310
10647
  exports.ContentLabel = ContentLabel;
10648
+ exports.ContextMenu = ContextMenu2;
10311
10649
  exports.CopyButton = CopyButton;
10312
10650
  exports.CounterInput = CounterInput;
10313
10651
  exports.DatePicker = DatePicker;
@@ -10330,6 +10668,7 @@ exports.Grid = Grid;
10330
10668
  exports.Hint = Hint;
10331
10669
  exports.HorizontalFilter = HorizontalFilter;
10332
10670
  exports.ImageUpload = ImageUpload;
10671
+ exports.ImageViewer = ImageViewer;
10333
10672
  exports.InlineInput = InlineInput;
10334
10673
  exports.InlineSelect = InlineSelect;
10335
10674
  exports.Input = Input;
@@ -10409,6 +10748,7 @@ exports.toast = toast;
10409
10748
  exports.trTR = trTR;
10410
10749
  exports.useBreakpoint = useBreakpoint;
10411
10750
  exports.useConfig = useConfig;
10751
+ exports.useContextMenu = useContextMenu;
10412
10752
  exports.useForm = useForm;
10413
10753
  exports.useLocale = useLocale;
10414
10754
  exports.useMappedSize = useMappedSize;
@@ -10418,5 +10758,5 @@ exports.useSize = useSize;
10418
10758
  exports.viVN = viVN;
10419
10759
  exports.zhCN = zhCN;
10420
10760
  exports.zhTW = zhTW;
10421
- //# sourceMappingURL=chunk-PRDLMM4A.cjs.map
10422
- //# sourceMappingURL=chunk-PRDLMM4A.cjs.map
10761
+ //# sourceMappingURL=chunk-VFEV4TOL.cjs.map
10762
+ //# sourceMappingURL=chunk-VFEV4TOL.cjs.map