checkbox-tree-sync
Version:
A lightweight library for checkbox tree synchronization
197 lines (196 loc) • 4.91 kB
JavaScript
import { useRef as u, useState as C, useCallback as k } from "react";
var i = /* @__PURE__ */ ((h) => (h.Indeterminate = "indeterminate", h.Checked = "checked", h))(i || {});
class b {
constructor(e) {
this.params = e, this.flattenData = this.makeDataFlat(e.data), this.state = e.state ?? {};
}
flattenData;
variant = {};
state = {};
getCheckboxesModel() {
return {
checkboxes: this.createSubCheckboxesModel(this.params.data)
};
}
pickAll() {
const e = {};
Object.keys(this.flattenData).forEach((t) => {
e[t] = !0;
}), this.handleChangeState(e);
}
/**
* make data flat so we can easily find parent and children and retrieve data
*/
makeDataFlat(e, t) {
const { getCheckboxId: a, getSubCheckboxes: s } = this.params, n = {};
return e.forEach((c) => {
const r = s(c) ?? [], o = a(c), l = r.map(a);
if (n[o] = {
id: o,
data: c,
children: l,
parentId: t
}, r.length > 0) {
const d = this.makeDataFlat(r, o);
Object.assign(n, d);
}
}), n;
}
pickCheckboxParents(e) {
const t = {};
let a = e;
for (; a && this.flattenData[a]?.parentId; )
a = this.flattenData[a].parentId, t[a] = !0;
return t;
}
getCheckboxChildren(e) {
let t = this.flattenData[e]?.children ?? [];
const a = t.flatMap(
(s) => this.getCheckboxChildren(s)
);
return t.concat(a);
}
pickCheckboxChildren(e) {
const t = {};
return this.getCheckboxChildren(e).forEach((s) => {
t[s] = !0;
}), t;
}
removeCheckboxParents(e, t) {
const a = { ...t };
let s = this.flattenData[e].parentId;
for (; s; ) {
const n = this.flattenData[s];
n.children.some((r) => a[r]) ? s = void 0 : (delete a[s], s = n.parentId);
}
return a;
}
removeCheckboxChildren(e, t) {
const a = { ...t };
return this.getCheckboxChildren(e).forEach((n) => {
delete a[n];
}), a;
}
toggleCheckbox(e, t) {
const a = { ...this.state };
if (t)
return Object.assign(
a,
this.pickCheckboxParents(e),
this.pickCheckboxChildren(e),
{
[e]: !0
}
), a;
delete a[e];
const s = this.removeCheckboxParents(e, a);
return this.removeCheckboxChildren(e, s);
}
/**
* checkbox model for rendering
*/
createCheckboxModel(e) {
const t = this.params.getCheckboxId(e);
return {
id: t,
getIsChecked: () => !!this?.state?.[t],
getVariant: () => this.variant[t]?.type ?? i.Checked,
getToggleHandler: () => () => {
const a = this.state?.[t], s = this.toggleCheckbox(t, !a);
this.handleChangeState(s);
}
};
}
/**
* checkbox model for rendering
*/
createSubCheckboxesModel(e) {
return e.map((t) => {
const a = this.createCheckboxModel(t), s = this.params.getSubCheckboxes(t) ?? [], n = this.createSubCheckboxesModel(s);
return {
...a,
subCheckboxes: n
};
});
}
handleChangeState(e) {
this.params?.onStateChange ? this.params.onStateChange?.(e) : this.updateState(e);
}
updateVariant(e) {
this.variant = this.params?.getCheckboxVariantModel?.({
data: this.params.data,
state: e,
getCheckboxId: this.params.getCheckboxId,
getSubCheckboxes: this.params.getSubCheckboxes
}) ?? {};
}
updateParams(e) {
e.data && this.params?.data !== e.data && (this.flattenData = this.makeDataFlat(e.data)), this.params = e;
}
updateState(e) {
e !== void 0 && this.params.state !== e && (this.updateVariant(e), this.state = e);
}
}
const g = () => ({
data: h,
state: e,
getSubCheckboxes: t,
getCheckboxId: a
}) => {
const s = {};
return h.forEach((n) => {
const c = t(n) ?? [], r = a(n);
if (c && c.length) {
const o = g()({
data: c,
state: e,
getCheckboxId: a,
getSubCheckboxes: t
});
Object.assign(s, o);
const l = Object.values(o).every((d) => d.checked);
r && (s[r] = {
checked: l,
type: l ? i.Checked : i.Indeterminate
});
} else
r && (s[r] = {
checked: e[r],
type: i.Checked
});
}), s;
}, x = ({
controlled: h,
default: e
}) => {
const { current: t } = u(h !== void 0), [a, s] = C(e), n = t ? h : a, c = k(
(r) => {
t || s(r);
},
[t]
);
return [n, c];
}, f = ({
state: h,
...e
}) => {
const [t, a] = x({
controlled: h,
default: {}
}), s = u();
return s.current ? (s.current.updateParams(e), s.current.updateState(t)) : s.current = new b({
...e,
state: t,
onStateChange: (n) => {
e.onStateChange?.(n), a(n);
}
}), {
checkBoxTreeSync: s.current
};
};
export {
b as CheckboxTreeSync,
i as CheckboxVariantEnum,
g as getCheckboxVariantModel,
f as useCheckboxTreeSync
};