UNPKG

splitpanes

Version:

A Vue.js reliable, simple and touch-ready panes splitter / resizer

324 lines (323 loc) 14.1 kB
import { computed as e, createBlock as t, createElementBlock as n, getCurrentInstance as r, h as i, inject as a, nextTick as o, normalizeStyle as s, onBeforeUnmount as c, onMounted as l, openBlock as u, provide as d, ref as f, renderSlot as p, resolveDynamicComponent as m, unref as h, useAttrs as g, useSlots as _, watch as v } from "vue"; //#region src/components/splitpanes/splitpanes.vue var y = /* @__PURE__ */ Object.assign({ inheritAttrs: !1 }, { __name: "splitpanes", props: { horizontal: { type: Boolean, default: !1 }, pushOtherPanes: { type: Boolean, default: !0 }, maximizePanes: { type: Boolean, default: !0 }, rtl: { type: Boolean, default: !1 }, firstSplitter: { type: Boolean, default: !1 }, keyboardStep: { type: Number, default: 5 } }, emits: [ "ready", "resize", "resized", "pane-click", "pane-maximize", "pane-add", "pane-remove", "splitter-click", "splitter-dblclick", "direction-changed" ], setup(n, { emit: r }) { let a = r, s = n, p = g(), h = _(), y = f([]), b = e(() => y.value.reduce((e, t) => (e[~~t.id] = t) && e, {})), x = e(() => y.value.length), S = f(null), C = f(!1), w = f({ mouseDown: !1, dragging: !1, activeSplitter: null, cursorOffset: 0 }), T = f({ splitter: null, timeoutId: null }), E = e(() => ({ [`splitpanes splitpanes--${s.horizontal ? "horizontal" : "vertical"}`]: !0, "splitpanes--dragging": w.value.dragging, "splitpanes--ready": C.value })), ee = () => { document.addEventListener("mousemove", k, { passive: !1 }), document.addEventListener("mouseup", A), "ontouchstart" in window && (document.addEventListener("touchmove", k, { passive: !1 }), document.addEventListener("touchend", A)); }, D = () => { document.removeEventListener("mousemove", k, { passive: !1 }), document.removeEventListener("mouseup", A), "ontouchstart" in window && (document.removeEventListener("touchmove", k, { passive: !1 }), document.removeEventListener("touchend", A)); }, O = (e, t) => { let n = e.target.closest(".splitpanes__splitter"); if (n) { let { left: t, top: r } = n.getBoundingClientRect(), { clientX: i, clientY: a } = "ontouchstart" in window && e.touches ? e.touches[0] : e; w.value.cursorOffset = s.horizontal ? a - r : i - t; } ee(), w.value.mouseDown = !0, w.value.activeSplitter = t, document.documentElement.style.cursor = s.horizontal ? "row-resize" : "col-resize"; }, k = (e) => { w.value.mouseDown && (e.preventDefault(), w.value.dragging || (window.getSelection()?.removeAllRanges(), w.value.dragging = !0), requestAnimationFrame(() => { L(F(e)), $("resize", { event: e }, !0); })); }, A = (e) => { w.value.dragging && (window.getSelection()?.removeAllRanges(), $("resized", { event: e }, !0)), w.value.mouseDown = !1, w.value.activeSplitter = null, setTimeout(() => { w.value.dragging = !1, D(), document.documentElement.style.cursor = ""; }, 100); }, j = (e, t) => { "ontouchstart" in window && (e.preventDefault(), T.value.splitter === t ? (clearTimeout(T.value.timeoutId), T.value.timeoutId = null, M(e, t), T.value.splitter = null) : (T.value.splitter = t, T.value.timeoutId = setTimeout(() => T.value.splitter = null, 500))), w.value.dragging || $("splitter-click", { event: e, index: t }, !0); }, M = (e, t) => { if ($("splitter-dblclick", { event: e, index: t }, !0), s.maximizePanes) { let n = 0; y.value = y.value.map((e, r) => (e.size = r === t ? e.max : e.min, r !== t && (n += e.min), e)), y.value[t].size -= n, $("pane-maximize", { event: e, index: t, pane: y.value[t] }), $("resized", { event: e, index: t }, !0); } }, N = (e, t) => { if (!s.keyboardStep) return; let n = s.horizontal ? e.key === "ArrowDown" : e.key === "ArrowRight", r = s.horizontal ? e.key === "ArrowUp" : e.key === "ArrowLeft"; if (!n && !r) return; e.preventDefault(), w.value.activeSplitter = t; let i = (n ? 1 : -1) * (s.rtl && !s.horizontal ? -1 : 1), a = z(t) + y.value[t].size; R(Math.min(Math.max(a + i * s.keyboardStep, 0), 100)), $("resize", { event: e }, !0), $("resized", { event: e }, !0), w.value.activeSplitter = null; }, P = (e, t) => { let n = b.value[t]; n && $("pane-click", { event: e, index: n.index, pane: n }); }, F = (e) => { let t = S.value.getBoundingClientRect(), { clientX: n, clientY: r } = "ontouchstart" in window && e.touches ? e.touches[0] : e; return { x: n - (s.horizontal ? 0 : w.value.cursorOffset) - t.left, y: r - (s.horizontal ? w.value.cursorOffset : 0) - t.top }; }, I = (e) => { e = e[s.horizontal ? "y" : "x"]; let t = S.value[s.horizontal ? "clientHeight" : "clientWidth"]; return s.rtl && !s.horizontal && (e = t - e), e * 100 / t; }, L = (e) => { R(I(e)); }, R = (e) => { let t = w.value.activeSplitter; if (t === null || t >= y.value.length - 1) return; let n = { prevPanesSize: z(t), nextPanesSize: B(t), prevReachedMinPanes: 0, nextReachedMinPanes: 0 }, r = 0 + (s.pushOtherPanes ? 0 : n.prevPanesSize), i = 100 - (s.pushOtherPanes ? 0 : n.nextPanesSize); e = Math.max(Math.min(e, i), r); let a = [t, t + 1], o = y.value[a[0]] || null, c = y.value[a[1]] || null, l = o !== null && o.max < 100 && e >= o.max + n.prevPanesSize, u = c !== null && c.max < 100 && e <= 100 - (c.max + B(t + 1)); if (l || u) { l ? (o.size = o.max, c.size = Math.min(Math.max(100 - o.max - n.prevPanesSize - n.nextPanesSize, c.min), c.max)) : (o.size = Math.min(Math.max(100 - c.max - n.prevPanesSize - B(t + 1), o.min), o.max), c.size = c.max); return; } if (s.pushOtherPanes) { let t = te(n, e); if (!t) return; ({sums: n, panesToResize: a} = t), o = y.value[a[0]] || null, c = y.value[a[1]] || null; } o !== null && (o.size = Math.min(Math.max(e - n.prevPanesSize - n.prevReachedMinPanes, o.min), o.max)), c !== null && (c.size = Math.min(Math.max(100 - e - n.nextPanesSize - n.nextReachedMinPanes, c.min), c.max)); }, te = (e, t) => { let n = w.value.activeSplitter, r = [n, n + 1]; if (t < e.prevPanesSize + y.value[r[0]].min) { if (r[0] = V(n).index, e.prevReachedMinPanes = 0, r[0] < n && y.value.forEach((t, i) => { i > r[0] && i <= n && (t.size = t.min, e.prevReachedMinPanes += t.min); }), r[0] === void 0) return e.prevReachedMinPanes = 0, y.value[0].size = y.value[0].min, y.value.forEach((t, r) => { r > 0 && r <= n && (t.size = t.min, e.prevReachedMinPanes += t.min); }), y.value[r[1]].size = 100 - e.prevReachedMinPanes - y.value[0].min - e.prevPanesSize - e.nextPanesSize, null; e.prevPanesSize = z(r[0]); } return t > 100 - e.nextPanesSize - y.value[r[1]].min && (r[1] = H(n).index, e.nextReachedMinPanes = 0, r[1] > n + 1 && y.value.forEach((t, i) => { i > n && i < r[1] && (t.size = t.min, e.nextReachedMinPanes += t.min); }), e.nextPanesSize = r[1] === void 0 ? 0 : B(r[1] - 1), r[1] === void 0) ? (e.nextReachedMinPanes = 0, y.value.forEach((t, r) => { r >= n + 1 && (t.size = t.min, e.nextReachedMinPanes += t.min); }), r[0] !== void 0 && (y.value[r[0]].size = 100 - e.prevPanesSize - B(r[0] - 1)), null) : { sums: e, panesToResize: r }; }, z = (e) => y.value.reduce((t, n, r) => t + (r < e ? n.size : 0), 0), B = (e) => y.value.reduce((t, n, r) => t + (r > e + 1 ? n.size : 0), 0), V = (e) => [...y.value].reverse().find((t) => t.index < e && t.size > t.min) || {}, H = (e) => y.value.find((t) => t.index > e + 1 && t.size > t.min) || {}, U = () => { let e = Array.from(S.value?.children || []); for (let t of e) { let e = t.classList.contains("splitpanes__pane"), n = t.classList.contains("splitpanes__splitter"); !e && !n && (t.remove(), console.warn("Splitpanes: Only <pane> elements are allowed at the root of <splitpanes>. One of your DOM nodes was removed.")); } }, W = (e, t, n = !1) => { let r = e - 1, i = document.createElement("div"); i.classList.add("splitpanes__splitter"), n || (i.onmousedown = (e) => O(e, r), typeof window < "u" && "ontouchstart" in window && (i.ontouchstart = (e) => O(e, r)), i.onclick = (e) => j(e, r + 1), s.keyboardStep && (i.setAttribute("tabindex", "0"), i.setAttribute("role", "separator"), i.setAttribute("aria-orientation", s.horizontal ? "horizontal" : "vertical"), i.onkeydown = (e) => N(e, r))), i.ondblclick = (e) => M(e, r + 1), t.parentNode.insertBefore(i, t); }, G = (e) => { e.onmousedown = null, e.onclick = null, e.ondblclick = null, e.onkeydown = null, e.remove(); }, K = () => { let e = Array.from(S.value?.children || []); for (let t of e) t.className.includes("splitpanes__splitter") && G(t); let t = 0; for (let n of e) n.className.includes("splitpanes__pane") && (!t && s.firstSplitter ? W(t, n, !0) : t && W(t, n), t++); }, q = ({ uid: e, ...t }) => { let n = b.value[e]; for (let [e, r] of Object.entries(t)) n[e] = r; }, J = !1, Y = (e) => { let t = -1; Array.from(S.value?.children || []).some((n) => (n.className.includes("splitpanes__pane") && t++, n.isSameNode(e.el))), y.value.splice(t, 0, { ...e, index: t }), y.value.forEach((e, t) => e.index = t), C.value && !J && (J = !0, o(() => { K(), Z({ addedPane: y.value[t] }), $("pane-add", { pane: y.value[t] }), J = !1; })); }, X = (e) => { let t = y.value.findIndex((t) => t.id === e); y.value[t].el = null; let n = y.value.splice(t, 1)[0]; y.value.forEach((e, t) => e.index = t), o(() => { K(), $("pane-remove", { pane: n }), Z({ removedPane: { ...n, index: t } }); }); }, Z = (e = {}) => { !e.addedPane && !e.removedPane ? re() : y.value.some((e) => e.givenSize !== null || e.min || e.max < 100) ? ie(e) : ne(), C.value && $("resized"); }, ne = () => { let e = 100 / x.value, t = 100, n = [], r = []; for (let i of y.value) i.size = Math.max(Math.min(e, i.max), i.min), t -= i.size, i.size >= i.max && n.push(i.id), i.size <= i.min && r.push(i.id); Math.abs(t) > .1 && Q(t, n, r); }, re = () => { let e = 100, t = [], n = [], r = 0; for (let i of y.value) e -= i.size, i.givenSize !== null && r++, i.size >= i.max && t.push(i.id), i.size <= i.min && n.push(i.id); let i = 100; if (e > .1) { for (let t of y.value) t.givenSize === null && (t.size = Math.max(Math.min(e / (x.value - r), t.max), t.min)), i -= t.size; i > .1 && Q(i, t, n); } }, ie = ({ addedPane: e, removedPane: t } = {}) => { let n = y.value.reduce((e, t) => e + (t.givenSize === null ? 0 : t.givenSize), 0), r = y.value.filter((e) => e.givenSize === null).length, i = r > 0 ? (100 - n) / r : 0, a = 0, o = [], s = []; for (let e of y.value) a -= e.size, e.size >= e.max && o.push(e.id), e.size <= e.min && s.push(e.id); if (!(Math.abs(a) < .1)) { a = 100; for (let e of y.value) e.givenSize === null && (e.size = Math.max(Math.min(i, e.max), e.min)), a -= e.size, e.size >= e.max && o.push(e.id), e.size <= e.min && s.push(e.id); Math.abs(a) > .1 && Q(a, o, s); } }, Q = (e, t, n) => { let r; r = e > 0 ? e / (x.value - t.length) : e / (x.value - n.length), y.value.forEach((i, a) => { if (e > 0 && !t.includes(i.id)) { let t = Math.max(Math.min(i.size + r, i.max), i.min), n = t - i.size; e -= n, i.size = t; } else if (!n.includes(i.id)) { let t = Math.max(Math.min(i.size + r, i.max), i.min), n = t - i.size; e -= n, i.size = t; } }), Math.abs(e) > .1 && C.value && console.warn("Splitpanes: Could not resize panes correctly due to their constraints."); }, $ = (e, t = void 0, n = !1) => { let r = t?.index ?? w.value.activeSplitter ?? null; a(e, { ...t, ...r !== null && { index: r }, ...n && r !== null && { prevPane: y.value[r - +!!s.firstSplitter], nextPane: y.value[r + +!s.firstSplitter] }, panes: y.value.map((e) => ({ min: e.min, max: e.max, size: e.size })) }); }; v(() => s.firstSplitter, () => K()), v(() => s.horizontal, (e) => o(() => { a("direction-changed", { horizontal: e, panes: y.value.map((e) => ({ min: e.min, max: e.max, size: e.size })) }); })), l(() => { U(), K(), Z(), $("ready"), C.value = !0; }), c(() => C.value = !1); let ae = () => { let { class: e, ...t } = p; return i("div", { ref: S, class: [E.value, e], ...t }, h.default?.()); }; return d("panes", y), d("indexedPanes", b), d("horizontal", e(() => s.horizontal)), d("requestUpdate", q), d("onPaneAdd", Y), d("onPaneRemove", X), d("onPaneClick", P), (e, n) => (u(), t(m(ae))); } }), b = { __name: "pane", props: { size: { type: [Number, String] }, minSize: { type: [Number, String], default: 0 }, maxSize: { type: [Number, String], default: 100 } }, setup(t) { let i = t, o = a("requestUpdate"), d = a("onPaneAdd"), m = a("horizontal"), g = a("onPaneRemove"), _ = a("onPaneClick"), y = r()?.uid, b = a("indexedPanes"), x = e(() => b.value[y]), S = f(null), C = e(() => { let e = isNaN(i.size) || i.size === void 0 ? 0 : parseFloat(i.size); return Math.max(Math.min(e, T.value), w.value); }), w = e(() => { let e = parseFloat(i.minSize); return isNaN(e) ? 0 : e; }), T = e(() => { let e = parseFloat(i.maxSize); return isNaN(e) ? 100 : e; }), E = e(() => { let e = x.value?.size ?? (i.size === void 0 ? void 0 : C.value); return e === void 0 ? "" : `${m.value ? "height" : "width"}: ${e}%`; }); return v(() => C.value, (e) => o({ uid: y, size: e })), v(() => w.value, (e) => o({ uid: y, min: e })), v(() => T.value, (e) => o({ uid: y, max: e })), l(() => { d({ id: y, el: S.value, min: w.value, max: T.value, givenSize: i.size === void 0 ? null : C.value, size: C.value }); }), c(() => g(y)), (e, t) => (u(), n("div", { ref_key: "paneEl", ref: S, class: "splitpanes__pane", onClick: t[0] ||= (t) => h(_)(t, e._.uid), style: s(E.value) }, [p(e.$slots, "default")], 4)); } }; //#endregion export { b as Pane, y as Splitpanes };