splitpanes-ts
Version:
TypeScript version of A Vue.js reliable, simple and touch-ready panes splitter / resizer
258 lines (257 loc) • 13.6 kB
JavaScript
import { defineComponent as G, ref as w, computed as h, watch as B, onMounted as X, onBeforeUnmount as Y, provide as g, createElementBlock as I, openBlock as V, normalizeClass as de, renderSlot as W, nextTick as T, inject as M, getCurrentInstance as me, normalizeStyle as fe, unref as H } from "vue";
const ze = /* @__PURE__ */ G({
__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 }
},
emits: ["ready", "resize", "resized", "pane-click", "pane-maximize", "pane-add", "pane-remove", "splitter-click", "splitter-dblclick"],
setup(A, { emit: x }) {
const E = x, u = A, l = w([]), k = h(
() => l.value.reduce((e, n) => (e[n.id] = n, e), {})
), z = h(() => l.value.length), v = w(null), S = w(!1), c = w({
mouseDown: !1,
dragging: !1,
activeSplitter: null,
cursorOffset: 0
}), p = w({
splitter: null,
timeoutId: null
}), _ = h(() => ({
[`splitpanes splitpanes--${u.horizontal ? "horizontal" : "vertical"}`]: !0,
"splitpanes--dragging": c.value.dragging
})), R = () => {
document.addEventListener("mousemove", P, { passive: !1 }), document.addEventListener("mouseup", o), "ontouchstart" in window && (document.addEventListener("touchmove", P, { passive: !1 }), document.addEventListener("touchend", o));
}, y = () => {
document.removeEventListener("mousemove", P), document.removeEventListener("mouseup", o), "ontouchstart" in window && (document.removeEventListener("touchmove", P), document.removeEventListener("touchend", o));
}, C = (e, n) => {
var i;
const t = (i = e.target) == null ? void 0 : i.closest(".splitpanes__splitter");
if (t) {
const { left: a, top: s } = t.getBoundingClientRect(), { clientX: r, clientY: d } = "ontouchstart" in window && "touches" in e ? e.touches[0] : e;
c.value.cursorOffset = u.horizontal ? d - s : r - a;
}
R(), c.value.mouseDown = !0, c.value.activeSplitter = n;
}, P = (e) => {
c.value.mouseDown && (e.preventDefault(), c.value.dragging = !0, requestAnimationFrame(() => {
Z(K(e)), m("resize", { event: e }, !0);
}));
}, o = (e) => {
var n;
c.value.dragging && ((n = window.getSelection()) == null || n.removeAllRanges(), m("resized", { event: e }, !0)), c.value.mouseDown = !1, c.value.activeSplitter = null, setTimeout(() => {
c.value.dragging = !1, y();
}, 100);
}, N = (e, n) => {
"ontouchstart" in window && (e.preventDefault(), p.value.splitter === n ? (clearTimeout(p.value.timeoutId), p.value.timeoutId = null, D(e, n), p.value.splitter = null) : (p.value.splitter = n, p.value.timeoutId = setTimeout(() => p.value.splitter = null, 500))), c.value.dragging || m("splitter-click", { event: e, index: n }, !0);
}, D = (e, n) => {
if (m("splitter-dblclick", { event: e, index: n }, !0), u.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);
}
}, J = (e, n) => {
const t = k.value[n];
t && m("pane-click", {
event: e,
index: t.index,
pane: t
});
}, K = (e) => {
const n = v.value.getBoundingClientRect(), { clientX: t, clientY: i } = "ontouchstart" in window && "touches" in e ? e.touches[0] : e;
return {
x: t - (u.horizontal ? 0 : c.value.cursorOffset) - n.left,
y: i - (u.horizontal ? c.value.cursorOffset : 0) - n.top
};
}, Q = (e) => {
const n = e[u.horizontal ? "y" : "x"], t = v.value[u.horizontal ? "clientHeight" : "clientWidth"];
return (u.rtl && !u.horizontal ? t - n : n) * 100 / t;
}, Z = (e) => {
const n = c.value.activeSplitter;
if (n === null) return;
let t = {
prevPanesSize: U(n),
nextPanesSize: b(n),
prevReachedMinPanes: 0,
nextReachedMinPanes: 0
};
const i = 0 + (u.pushOtherPanes ? 0 : t.prevPanesSize), a = 100 - (u.pushOtherPanes ? 0 : t.nextPanesSize), s = Math.max(Math.min(Q(e), a), i);
let r = [n, n + 1], d = l.value[r[0]] || null, f = l.value[r[1]] || null;
const j = d.max < 100 && s >= d.max + t.prevPanesSize, ve = f.max < 100 && s <= 100 - (f.max + b(n + 1));
if (j || ve) {
j ? (d.size = d.max, f.size = Math.max(100 - d.max - t.prevPanesSize - t.nextPanesSize, 0)) : (d.size = Math.max(100 - f.max - t.prevPanesSize - b(n + 1), 0), f.size = f.max);
return;
}
if (u.pushOtherPanes) {
const F = ee(t, s);
if (!F) return;
({ sums: t, panesToResize: r } = F), d = l.value[r[0]] || null, f = l.value[r[1]] || null;
}
d !== null && (d.size = Math.min(Math.max(s - t.prevPanesSize - t.prevReachedMinPanes, d.min), d.max)), f !== null && (f.size = Math.min(Math.max(100 - s - t.nextPanesSize - t.nextReachedMinPanes, f.min), f.max));
}, ee = (e, n) => {
const t = c.value.activeSplitter;
if (t === null) return null;
const i = [t, t + 1];
return n < e.prevPanesSize + l.value[i[0]].min && (i[0] = ne(t).index || 0, e.prevReachedMinPanes = 0, i[0] < t && l.value.forEach((a, s) => {
s > i[0] && s <= t && (a.size = a.min, e.prevReachedMinPanes += a.min);
}), e.prevPanesSize = U(i[0]), i[0] === void 0) ? (e.prevReachedMinPanes = 0, l.value[0].size = l.value[0].min, l.value.forEach((a, s) => {
s > 0 && s <= t && (a.size = a.min, e.prevReachedMinPanes += a.min);
}), l.value[i[1]].size = 100 - e.prevReachedMinPanes - l.value[0].min - e.prevPanesSize - e.nextPanesSize, null) : n > 100 - e.nextPanesSize - l.value[i[1]].min && (i[1] = te(t).index || l.value.length - 1, e.nextReachedMinPanes = 0, i[1] > t + 1 && l.value.forEach((a, s) => {
s > t && s < i[1] && (a.size = a.min, e.nextReachedMinPanes += a.min);
}), e.nextPanesSize = b(i[1] - 1), i[1] === void 0) ? (e.nextReachedMinPanes = 0, l.value.forEach((a, s) => {
s < z.value - 1 && s >= t + 1 && (a.size = a.min, e.nextReachedMinPanes += a.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) || {}, te = (e) => l.value.find((t) => t.index > e + 1 && t.size > t.min) || {}, ie = () => {
var n;
const e = Array.from(((n = v.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) => {
var s;
const i = e - 1, a = document.createElement("div");
a.classList.add("splitpanes__splitter"), t || (a.onmousedown = (r) => C(r, i), typeof window < "u" && "ontouchstart" in window && (a.ontouchstart = (r) => C(r, i)), a.onclick = (r) => N(r, i + 1)), a.ondblclick = (r) => D(r, i + 1), (s = n.parentNode) == null || s.insertBefore(a, n);
}, ae = (e) => {
e.onmousedown = null, e.onclick = null, e.ondblclick = null, e.remove();
}, O = () => {
var t;
const e = Array.from(((t = v.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 && u.firstSplitter ? $(n, i, !0) : n && $(n, i), n++);
}, se = ({ uid: e, ...n }) => {
const t = k.value[e];
for (const [i, a] of Object.entries(n))
t[i] = a;
}, le = (e) => {
var t;
let n = -1;
Array.from(((t = v.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(), L({ 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 }), L({ removedPane: { ...t } });
});
}, L = (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 && q(n, t, i);
}, ue = () => {
let e = 100;
const n = [], t = [];
let i = 0;
for (const s of l.value)
e -= s.size, s.givenSize !== null && i++, s.size >= s.max && n.push(s.id), s.size <= s.min && t.push(s.id);
let a = 100;
if (e > 0.1) {
for (const s of l.value)
s.givenSize === null && (s.size = Math.max(Math.min(e / (z.value - i), s.max), s.min)), a -= s.size;
a > 0.1 && q(a, n, t);
}
}, ce = ({ addedPane: e } = {}) => {
let n = 100 / z.value, t = 0;
const i = [], a = [];
e && e.givenSize !== null && (n = (100 - e.givenSize) / (z.value - 1));
for (const s of l.value)
t -= s.size, s.size >= s.max && i.push(s.id), s.size <= s.min && a.push(s.id);
if (!(Math.abs(t) < 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(n, s.max), s.min)), t -= s.size, s.size >= s.max && i.push(s.id), s.size <= s.min && a.push(s.id);
t > 0.1 && q(t, i, a);
}
}, q = (e, n, t) => {
let i;
e > 0 ? i = e / (z.value - n.length) : i = e / (z.value - t.length), l.value.forEach((a) => {
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) ?? c.value.activeSplitter ?? null;
E(e, {
...n,
...i !== null && { index: i },
...t && i !== null && {
prevPane: l.value[i - (u.firstSplitter ? 1 : 0)],
nextPane: l.value[i + (u.firstSplitter ? 0 : 1)]
},
panes: l.value.map((a) => ({ min: a.min, max: a.max, size: a.size }))
});
};
return B(() => u.firstSplitter, () => O()), X(() => {
ie(), O(), L(), m("ready"), S.value = !0;
}), Y(() => S.value = !1), g("panes", l), g("indexedPanes", k), g("horizontal", h(() => u.horizontal)), g("requestUpdate", se), g("onPaneAdd", le), g("onPaneRemove", oe), g("onPaneClick", J), (e, n) => (V(), I("div", {
ref_key: "containerEl",
ref: v,
class: de(_.value)
}, [
W(e.$slots, "default")
], 2));
}
}), he = /* @__PURE__ */ G({
__name: "pane",
props: {
size: {},
minSize: { default: 0 },
maxSize: { default: 100 }
},
setup(A) {
var P;
const x = A, E = M("requestUpdate"), u = M("onPaneAdd"), l = M("horizontal"), k = M("onPaneRemove"), z = M("onPaneClick"), v = (P = me()) == null ? void 0 : P.uid, S = M("indexedPanes"), c = h(() => S.value[v]), p = w(null), _ = h(() => {
const o = isNaN(Number(x.size)) || x.size === void 0 ? 0 : parseFloat(String(x.size));
return Math.max(Math.min(o, y.value), R.value);
}), R = h(() => {
const o = parseFloat(String(x.minSize));
return isNaN(o) ? 0 : o;
}), y = h(() => {
const o = parseFloat(String(x.maxSize));
return isNaN(o) ? 100 : o;
}), C = h(() => {
var o;
return `${l.value ? "height" : "width"}: ${(o = c.value) == null ? void 0 : o.size}%`;
});
return B(() => _.value, (o) => E({ uid: v, size: o })), B(() => R.value, (o) => E({ uid: v, min: o })), B(() => y.value, (o) => E({ uid: v, max: o })), X(() => {
u({
id: v,
el: p.value,
min: R.value,
max: y.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(() => k(v)), (o, N) => (V(), I("div", {
ref_key: "paneEl",
ref: p,
class: "splitpanes__pane",
onClick: N[0] || (N[0] = (D) => H(z)(D, H(v))),
style: fe(C.value)
}, [
W(o.$slots, "default")
], 4));
}
});
export {
he as Pane,
ze as Splitpanes
};