UNPKG

@posva/splitpanes

Version:

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

312 lines (311 loc) 14 kB
import { defineComponent as G, useSlots as fe, ref as k, computed as h, watch as A, onMounted as X, onBeforeUnmount as Y, provide as g, createElementBlock as W, openBlock as I, normalizeClass as pe, renderSlot as J, nextTick as T, inject as E, getCurrentInstance as me, normalizeStyle as de, unref as j } from "vue"; const he = /* @__PURE__ */ G({ __name: "splitpanes", props: { horizontal: { type: Boolean, default: !1 }, pushOtherPanes: { type: Boolean, default: !0 }, maximizePanes: { type: Boolean, default: !0 }, // Maximize pane on splitter double click/tap. rtl: { type: Boolean, default: !1 }, // Right to left direction. firstSplitter: { type: Boolean, default: !1 } }, emits: [ "ready", "resize", "resized", "pane-click", "pane-maximize", "pane-add", "pane-remove", "splitter-click", "splitter-dblclick" ], setup(L, { emit: x }) { const y = x, c = L; fe(); const l = k([]), w = h( () => l.value.reduce((e, n) => (e[~~n.id] = n) && e, {}) ), z = h(() => l.value.length), f = k(null), S = k(!1), v = k({ mouseDown: !1, dragging: !1, activeSplitter: null, cursorOffset: 0 // Cursor offset within the splitter. }), d = k({ // Used to detect double click on touch devices. splitter: null, timeoutId: null }), _ = h(() => ({ [`splitpanes splitpanes--${c.horizontal ? "horizontal" : "vertical"}`]: !0, "splitpanes--dragging": v.value.dragging })); let P = () => { }; function R(e) { e.addEventListener("mousemove", M, { passive: !1 }), e.addEventListener("mouseup", u), "ontouchstart" in window && (e.addEventListener("touchmove", M, { passive: !1 }), e.addEventListener("touchend", u)), P = () => { e.removeEventListener("mousemove", M), e.removeEventListener("mouseup", u), "ontouchstart" in window && (e.removeEventListener("touchmove", M), e.removeEventListener("touchend", u)), P = () => { }; }; } const N = (e, n) => { const t = e.target.closest(".splitpanes__splitter"); if (t) { const { left: i, top: a } = t.getBoundingClientRect(), { clientX: o, clientY: s } = "touches" in e ? e.touches[0] : e; v.value.cursorOffset = c.horizontal ? s - a : o - i; } R(e.target.ownerDocument || document), v.value.mouseDown = !0, v.value.activeSplitter = n; }, M = (e) => { v.value.mouseDown && (e.preventDefault(), v.value.dragging = !0, requestAnimationFrame(() => { Z(Q(e)), m("resize", { event: e }, !0); })); }, u = (e) => { v.value.dragging && m("resized", { event: e }, !0), v.value.mouseDown = !1, v.value.activeSplitter = null, setTimeout(() => { v.value.dragging = !1, P(); }, 100); }, C = (e, n) => { "ontouchstart" in window && (e.preventDefault(), d.value.splitter === n ? (clearTimeout(d.value.timeoutId), d.value.timeoutId = null, B(e, n), d.value.splitter = null) : (d.value.splitter = n, d.value.timeoutId = setTimeout(() => d.value.splitter = null, 500))), v.value.dragging || m("splitter-click", { event: e, index: n }, !0); }, B = (e, n) => { if (m("splitter-dblclick", { event: e, index: n }, !0), c.maximizePanes) { let t = 0; l.value = l.value.map((i, a) => (i.size = a === n ? i.max : i.min, a !== n && (t += i.min), i)), l.value[n].size -= t, m("pane-maximize", { event: e, index: n, pane: l.value[n] }), m("resized", { event: e, index: n }, !0); } }, K = (e, n) => { m("pane-click", { event: e, index: w.value[n].index, pane: w.value[n] }); }, Q = (e) => { var a; const n = (a = f.value) == null ? void 0 : a.getBoundingClientRect(), { clientX: t, clientY: i } = "ontouchstart" in window && e.touches ? e.touches[0] : e; return { x: t - (c.horizontal ? 0 : v.value.cursorOffset) - n.left, y: i - (c.horizontal ? v.value.cursorOffset : 0) - n.top }; }, V = (e) => { e = e[c.horizontal ? "y" : "x"]; const n = f.value[c.horizontal ? "clientHeight" : "clientWidth"]; return c.rtl && !c.horizontal && (e = n - e), e * 100 / n; }, Z = (e) => { const n = v.value.activeSplitter; let t = { prevPanesSize: U(n), nextPanesSize: b(n), prevReachedMinPanes: 0, nextReachedMinPanes: 0 }; const i = 0 + (c.pushOtherPanes ? 0 : t.prevPanesSize), a = 100 - (c.pushOtherPanes ? 0 : t.nextPanesSize), o = Math.max(Math.min(V(e), a), i); let s = [n, n + 1], r = l.value[s[0]], p = l.value[s[1]]; if (!r || !p) return; const F = r.max < 100 && o >= r.max + t.prevPanesSize, ve = p.max < 100 && o <= 100 - (p.max + b(n + 1)); if (F || ve) { F ? (r.size = r.max, p.size = Math.max(100 - r.max - t.prevPanesSize - t.nextPanesSize, 0)) : (r.size = Math.max( 100 - p.max - t.prevPanesSize - b(n + 1), 0 ), p.size = p.max); return; } if (c.pushOtherPanes) { const H = ee(t, o); if (!H) return; ({ sums: t, panesToResize: s } = H), r = l.value[s[0]], p = l.value[s[1]]; } r && (r.size = Math.min( Math.max(o - t.prevPanesSize - t.prevReachedMinPanes, r.min), r.max )), p && (p.size = Math.min( Math.max(100 - o - t.nextPanesSize - t.nextReachedMinPanes, p.min), p.max )); }, ee = (e, n) => { const t = v.value.activeSplitter, i = [t, t + 1]; let a = l.value[i[0]], o = l.value[i[1]]; return a && n < e.prevPanesSize + a.min && (i[0] = ne(t).index, e.prevReachedMinPanes = 0, i[0] < t && l.value.forEach((s, r) => { r > i[0] && r <= t && (s.size = s.min, e.prevReachedMinPanes += s.min); }), e.prevPanesSize = U(i[0]), i[0] === void 0) ? (e.prevReachedMinPanes = 0, l.value[0].size = l.value[0].min, l.value.forEach((s, r) => { r > 0 && r <= t && (s.size = s.min, e.prevReachedMinPanes += s.min); }), o.size = 100 - e.prevReachedMinPanes - l.value[0].min - e.prevPanesSize - e.nextPanesSize, null) : n > 100 - e.nextPanesSize - l.value[i[1]].min && (i[1] = ie(t).index, e.nextReachedMinPanes = 0, i[1] > t + 1 && l.value.forEach((s, r) => { r > t && r < i[1] && (s.size = s.min, e.nextReachedMinPanes += s.min); }), e.nextPanesSize = b(i[1] - 1), i[1] === void 0) ? (e.nextReachedMinPanes = 0, l.value.forEach((s, r) => { r < z.value - 1 && r >= t + 1 && (s.size = s.min, e.nextReachedMinPanes += s.min); }), l.value[i[0]].size = 100 - e.prevPanesSize - b(i[0] - 1), null) : { sums: e, panesToResize: i }; }, U = (e) => l.value.reduce((n, t, i) => n + (i < e ? t.size : 0), 0), b = (e) => l.value.reduce((n, t, i) => n + (i > e + 1 ? t.size : 0), 0), ne = (e) => [...l.value].reverse().find((t) => t.index < e && t.size > t.min) || {}, ie = (e) => l.value.find((t) => t.index > e + 1 && t.size > t.min) || {}, te = () => { var n; const e = Array.from(((n = f.value) == null ? void 0 : n.children) || []); for (const t of e) { const i = t.classList.contains("splitpanes__pane"), a = t.classList.contains("splitpanes__splitter"); !i && !a && (t.remove(), console.warn( "Splitpanes: Only <pane> elements are allowed at the root of <splitpanes>. One of your DOM nodes was removed." )); } }, $ = (e, n, t = !1) => { const i = e - 1, a = document.createElement("div"); a.classList.add("splitpanes__splitter"), t || (a.onmousedown = (o) => N(o, i), typeof window < "u" && "ontouchstart" in window && (a.ontouchstart = (o) => N(o, i)), a.onclick = (o) => C(o, i + 1)), a.ondblclick = (o) => B(o, i + 1), n.parentNode.insertBefore(a, n); }, ae = (e) => { e.onmousedown = void 0, e.onclick = void 0, e.ondblclick = void 0, e.remove(); }, O = () => { var t; const e = Array.from(((t = f.value) == null ? void 0 : t.children) || []); for (const i of e) i.className.includes("splitpanes__splitter") && ae(i); let n = 0; for (const i of e) i.className.includes("splitpanes__pane") && (!n && c.firstSplitter ? $(n, i, !0) : n && $(n, i), n++); }, se = ({ uid: e, ...n }) => { const t = w.value[e]; for (const [i, a] of Object.entries(n)) t[i] = a; }, le = (e) => { var t; let n = -1; Array.from(((t = f.value) == null ? void 0 : t.children) || []).some((i) => (i.className.includes("splitpanes__pane") && n++, i.isSameNode(e.el))), l.value.splice(n, 0, { ...e, index: n }), l.value.forEach((i, a) => i.index = a), S.value && T(() => { O(), q({ addedPane: l.value[n] }), m("pane-add", { pane: l.value[n] }); }); }, oe = (e) => { const n = l.value.findIndex((i) => i.id === e); l.value[n].el = null; const t = l.value.splice(n, 1)[0]; l.value.forEach((i, a) => i.index = a), T(() => { O(), m("pane-remove", { pane: t }), q({ removedPane: { ...t } }); }); }, q = (e = {}) => { !e.addedPane && !e.removedPane ? ue() : l.value.some((n) => n.givenSize !== null || n.min || n.max < 100) ? ce(e) : re(), S.value && m("resized"); }, re = () => { const e = 100 / z.value; let n = 0; const t = [], i = []; for (const a of l.value) a.size = Math.max(Math.min(e, a.max), a.min), n -= a.size, a.size >= a.max && t.push(a.id), a.size <= a.min && i.push(a.id); n > 0.1 && D(n, t, i); }, ue = () => { let e = 100; const n = [], t = []; let i = 0; for (const o of l.value) e -= o.size, o.givenSize !== null && i++, o.size >= o.max && n.push(o.id), o.size <= o.min && t.push(o.id); let a = 100; if (e > 0.1) { for (const o of l.value) o.givenSize === null && (o.size = Math.max( Math.min(e / (z.value - i), o.max), o.min )), a -= o.size; a > 0.1 && D(a, n, t); } }, ce = ({ addedPane: e, removedPane: n } = {}) => { let t = 100 / z.value, i = 0; const a = [], o = []; ((e == null ? void 0 : e.givenSize) ?? null) !== null && (t = (100 - e.givenSize) / (z.value - 1)); for (const s of l.value) i -= s.size, s.size >= s.max && a.push(s.id), s.size <= s.min && o.push(s.id); if (!(Math.abs(i) < 0.1)) { for (const s of l.value) (e == null ? void 0 : e.givenSize) !== null && (e == null ? void 0 : e.id) === s.id || (s.size = Math.max(Math.min(t, s.max), s.min)), i -= s.size, s.size >= s.max && a.push(s.id), s.size <= s.min && o.push(s.id); i > 0.1 && D(i, a, o); } }, D = (e, n, t) => { let i; e > 0 ? i = e / (z.value - n.length) : i = e / (z.value - t.length), l.value.forEach((a, o) => { if (e > 0 && !n.includes(a.id)) { const s = Math.max(Math.min(a.size + i, a.max), a.min), r = s - a.size; e -= r, a.size = s; } else if (!t.includes(a.id)) { const s = Math.max(Math.min(a.size + i, a.max), a.min), r = s - a.size; e -= r, a.size = s; } }), Math.abs(e) > 0.1 && T(() => { S.value && console.warn("Splitpanes: Could not resize panes correctly due to their constraints."); }); }, m = (e, n = void 0, t = !1) => { const i = (n == null ? void 0 : n.index) ?? v.value.activeSplitter ?? null; y(e, { ...n, ...i !== null && { index: i }, ...t && i !== null && { prevPane: l.value[i - (c.firstSplitter ? 1 : 0)], nextPane: l.value[i + (c.firstSplitter ? 0 : 1)] }, panes: l.value.map((a) => ({ min: a.min, max: a.max, size: a.size })) }); }; return A( () => c.firstSplitter, () => O() ), X(() => { te(), O(), q(), m("ready"), S.value = !0; }), Y(() => S.value = !1), g("panes", l), g("indexedPanes", w), g( "horizontal", h(() => c.horizontal) ), g("requestUpdate", se), g("onPaneAdd", le), g("onPaneRemove", oe), g("onPaneClick", K), (e, n) => (I(), W("div", { class: pe(_.value), ref_key: "containerEl", ref: f }, [ J(e.$slots, "default") ], 2)); } }), xe = /* @__PURE__ */ G({ __name: "pane", props: { size: { type: [Number, String] }, minSize: { type: [Number, String], default: 0 }, maxSize: { type: [Number, String], default: 100 } }, setup(L) { var M; const x = L, y = E("requestUpdate"), c = E("onPaneAdd"), l = E("horizontal"), w = E("onPaneRemove"), z = E("onPaneClick"), f = (M = me()) == null ? void 0 : M.uid, S = E("indexedPanes"), v = h(() => S.value[f]), d = k(null), _ = h(() => { const u = isNaN(x.size) || x.size === void 0 ? 0 : parseFloat(x.size); return Math.max(Math.min(u, R.value), P.value); }), P = h(() => { const u = parseFloat(x.minSize); return isNaN(u) ? 0 : u; }), R = h(() => { const u = parseFloat(x.maxSize); return isNaN(u) ? 100 : u; }), N = h(() => { var u; return `${l.value ? "height" : "width"}: ${(u = v.value) == null ? void 0 : u.size}%`; }); return A( () => _.value, (u) => y({ uid: f, size: u }) ), A( () => P.value, (u) => y({ uid: f, min: u }) ), A( () => R.value, (u) => y({ uid: f, max: u }) ), X(() => { c({ id: f, el: d.value, min: P.value, max: R.value, // The given size (useful to know the user intention). givenSize: x.size === void 0 ? null : _.value, size: _.value // The computed current size at any time. }); }), Y(() => w(f)), (u, C) => (I(), W("div", { ref_key: "paneEl", ref: d, class: "splitpanes__pane", onClick: C[0] || (C[0] = (B) => j(z)(B, j(f))), style: de(N.value) }, [ J(u.$slots, "default") ], 4)); } }); export { xe as Pane, he as Splitpanes };