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 +14 -0
- package/dist/{chunk-PRDLMM4A.cjs → chunk-VFEV4TOL.cjs} +366 -26
- package/dist/chunk-VFEV4TOL.cjs.map +1 -0
- package/dist/{chunk-N3A4NTMY.js → chunk-Y5BGEO2J.js} +364 -27
- package/dist/chunk-Y5BGEO2J.js.map +1 -0
- package/dist/index.cjs +156 -144
- package/dist/index.d.cts +83 -2
- package/dist/index.d.ts +83 -2
- package/dist/index.js +1 -1
- package/dist/styles.css +210 -0
- package/dist/templates/index.cjs +82 -82
- package/dist/templates/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-N3A4NTMY.js.map +0 -1
- package/dist/chunk-PRDLMM4A.cjs.map +0 -1
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
|
|
4376
|
-
out.push({ row, id, depth, hasChildren });
|
|
4377
|
-
if (
|
|
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
|
|
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":
|
|
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
|
-
|
|
4942
|
+
hasChildren2 && isExpanded && "klun-tree__caret--expanded"
|
|
4943
4943
|
),
|
|
4944
|
-
"data-leaf":
|
|
4944
|
+
"data-leaf": hasChildren2 ? void 0 : "",
|
|
4945
4945
|
onClick: (e) => {
|
|
4946
4946
|
e.stopPropagation();
|
|
4947
|
-
if (
|
|
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
|
|
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
|
-
|
|
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
|
|
9865
|
-
const horizontal =
|
|
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--${
|
|
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
|
|
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
|
|
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 +
|
|
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 -
|
|
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 +
|
|
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 -
|
|
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
|
-
|
|
10186
|
-
window.addEventListener("scroll",
|
|
10187
|
-
window.addEventListener("resize",
|
|
10522
|
+
place2();
|
|
10523
|
+
window.addEventListener("scroll", place2, true);
|
|
10524
|
+
window.addEventListener("resize", place2);
|
|
10188
10525
|
return () => {
|
|
10189
|
-
window.removeEventListener("scroll",
|
|
10190
|
-
window.removeEventListener("resize",
|
|
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-
|
|
10422
|
-
//# sourceMappingURL=chunk-
|
|
10761
|
+
//# sourceMappingURL=chunk-VFEV4TOL.cjs.map
|
|
10762
|
+
//# sourceMappingURL=chunk-VFEV4TOL.cjs.map
|