UNPKG

@athosws/react-components

Version:

This is a set of useful ReactJS components developed by Athos.\n Email:ladiesman217.as@gmail.com

197 lines (155 loc) 4.94 kB
import type { UniqueIdentifier } from "@dnd-kit/core"; import { arrayMove } from "@dnd-kit/sortable"; import { FlattenedItem, TreeItem, TreeItems } from "./types"; export const iOS = /iPad|iPhone|iPod/.test(navigator.platform); function getDragDepth(offset: number, indentationWidth: number) { return Math.round(offset / indentationWidth); } export function getProjection( items: FlattenedItem[], activeId: UniqueIdentifier, overId: UniqueIdentifier, dragOffset: number, indentationWidth: number ) { const overItemIndex = items.findIndex(({ id }) => id === overId); const activeItemIndex = items.findIndex(({ id }) => id === activeId); const activeItem = items[activeItemIndex]; const newItems = arrayMove(items, activeItemIndex, overItemIndex); const previousItem = newItems[overItemIndex - 1]; const nextItem = newItems[overItemIndex + 1]; const dragDepth = getDragDepth(dragOffset, indentationWidth); const projectedDepth = activeItem.depth + dragDepth; const maxDepth = getMaxDepth({ previousItem, }); const minDepth = getMinDepth({ nextItem }); let depth = projectedDepth; if (projectedDepth >= maxDepth) { depth = maxDepth; } else if (projectedDepth < minDepth) { depth = minDepth; } return { depth, maxDepth, minDepth, parentId: getParentId() }; function getParentId() { if (depth === 0 || !previousItem) { return null; } if (depth === previousItem.depth) { return previousItem.parentId; } if (depth > previousItem.depth) { return previousItem.id; } const newParent = newItems .slice(0, overItemIndex) .reverse() .find((item) => item.depth === depth)?.parentId; return newParent ?? null; } } function getMaxDepth({ previousItem }: { previousItem: FlattenedItem }) { if (previousItem) { return previousItem.depth + 1; } return 0; } function getMinDepth({ nextItem }: { nextItem: FlattenedItem }) { if (nextItem) { return nextItem.depth; } return 0; } function flatten(items: TreeItems, parentId: UniqueIdentifier | null = null, depth = 0): FlattenedItem[] { return items.reduce<FlattenedItem[]>((acc, item, index) => { return [...acc, { ...item, parentId, depth, index }, ...flatten(item.children, item.id, depth + 1)]; }, []); } export function flattenTree(items: TreeItems): FlattenedItem[] { return flatten(items); } export function buildTree(flattenedItems: FlattenedItem[]): TreeItems { const root: TreeItem = { id: "root", children: [] }; const nodes: Record<string, TreeItem> = { [root.id]: root }; const items = flattenedItems.map((item) => ({ ...item, children: [] })); for (const item of items) { const { id, children } = item; const parentId = item.parentId ?? root.id; const parent = nodes[parentId] ?? findItem(items, parentId); nodes[id] = { id, children }; parent.children.push(item); } return root.children; } export function findItem(items: TreeItem[], itemId: UniqueIdentifier) { return items.find(({ id }) => id === itemId); } export function findItemDeep(items: TreeItems, itemId: UniqueIdentifier): TreeItem | undefined { for (const item of items) { const { id, children } = item; if (id === itemId) { return item; } if (children.length) { const child = findItemDeep(children, itemId); if (child) { return child; } } } return undefined; } export function removeItem(items: TreeItems, id: UniqueIdentifier) { const newItems = []; for (const item of items) { if (item.id === id) { continue; } if (item.children.length) { item.children = removeItem(item.children, id); } newItems.push(item); } return newItems; } export function setProperty<T extends keyof TreeItem>( items: TreeItems, id: UniqueIdentifier, property: T, setter: (value: TreeItem[T]) => TreeItem[T] ) { for (const item of items) { if (item.id === id) { item[property] = setter(item[property]); continue; } if (item.children.length) { item.children = setProperty(item.children, id, property, setter); } } return [...items]; } function countChildren(items: TreeItem[], count = 0): number { return items.reduce((acc, { children }) => { if (children.length) { return countChildren(children, acc + 1); } return acc + 1; }, count); } export function getChildCount(items: TreeItems, id: UniqueIdentifier) { const item = findItemDeep(items, id); return item ? countChildren(item.children) : 0; } export function removeChildrenOf(items: FlattenedItem[], ids: UniqueIdentifier[]) { const excludeParentIds = [...ids]; return items.filter((item) => { if (item.parentId && excludeParentIds.includes(item.parentId)) { if (item.children.length) { excludeParentIds.push(item.id); } return false; } return true; }); }