shinsu-senju
Version:
Tree mapper utils
106 lines (91 loc) • 2.53 kB
text/typescript
import { customSort } from './sort.ts';
import { doCondition, getBy } from './utils.ts';
import type { Condition, Getter, UnknownObject } from './utils.ts';
declare global {
// eslint-disable-next-line vars-on-top
var DEBUG: boolean | undefined;
}
type TreeNodeBase = {
disabled?: boolean;
selectable?: boolean;
$original: UnknownObject;
$mapper: Mapper;
};
export type TreeNode = TreeNodeBase & {
[key: string]: unknown;
};
export type Tree = TreeNode[];
export type Mapper = {
[key: string]: Getter | Condition | undefined;
selectable?: Condition;
disabled?: Condition;
sortBy?: Getter;
children?: Getter;
childrenKey?: string;
label?: Getter;
ident?: string;
};
export function treeMapper(data: UnknownObject[], options: Mapper = {}): Tree {
if (data.length === 0) {
return [] as Tree;
}
if (Object.values(options).filter(Boolean).length === 0) {
return treeMapper(data, { label: 'name', value: 'id' });
}
const {
sortBy = options.label,
ident = options.value,
selectable,
disabled,
childrenKey = 'children',
children: _children,
...rest
} = options;
const tmp = data.map((node: UnknownObject): TreeNode => {
const childrenData = options.children
? getBy(node, options.children)
: node.children;
const children =
childrenData && Array.isArray(childrenData)
? treeMapper(childrenData, options)
: undefined;
const mapped: TreeNodeBase = {
...Object.fromEntries(
Object.entries(rest)
.filter(([key]) => key !== options.children)
.map(([key, getter]) => [
key,
getter && getBy(node, getter as Getter),
]),
),
...(children ? { [childrenKey]: children } : undefined),
...(selectable
? {
selectable: doCondition(node, selectable),
}
: undefined),
...(disabled
? {
disabled: doCondition(node, disabled),
}
: undefined),
...(ident ? { ident } : undefined),
$original: node,
$mapper: options,
};
if (!globalThis.DEBUG) {
Object.defineProperty(mapped, '$original', {
enumerable: false,
});
Object.defineProperty(mapped, '$mapper', {
enumerable: false,
});
}
return mapped;
});
return sortBy === undefined
? tmp
: tmp.toSorted((a: TreeNode, b: TreeNode) =>
customSort(getBy(a.$original, sortBy), getBy(b.$original, sortBy)),
);
}