UNPKG

@kobalte/core

Version:

Unstyled components and primitives for building accessible web apps and design systems with SolidJS.

76 lines (63 loc) 2.43 kB
/* * Portions of this file are based on code from ariakit. * MIT Licensed, Copyright (c) Diego Haz. * * Credits to the Ariakit team: * https://github.com/ariakit/ariakit/blob/da142672eddefa99365773ced72171facc06fdcb/packages/ariakit/src/collection/collection.tsx * https://github.com/ariakit/ariakit/blob/da142672eddefa99365773ced72171facc06fdcb/packages/ariakit/src/collection/collection-state.ts * https://github.com/ariakit/ariakit/blob/da142672eddefa99365773ced72171facc06fdcb/packages/ariakit/src/collection/collection-item.ts */ import { type MaybeAccessor, access, addItemToArray } from "@kobalte/utils"; import { type FlowComponent, createComponent } from "solid-js"; import { createControllableArraySignal } from "../index"; import { DomCollectionContext, type DomCollectionContextValue, } from "./dom-collection-context"; import type { DomCollectionItem } from "./types"; import { createSortBasedOnDOMPosition, findDOMIndex } from "./utils"; export interface CreateDomCollectionProps< T extends DomCollectionItem = DomCollectionItem, > { /** The controlled items state of the collection. */ items?: MaybeAccessor<Array<T> | undefined>; /** Event handler called when the items state of the collection changes. */ onItemsChange?: (items: Array<T>) => void; } export function createDomCollection< T extends DomCollectionItem = DomCollectionItem, >(props: CreateDomCollectionProps<T> = {}) { const [items, setItems] = createControllableArraySignal({ value: () => access(props.items), onChange: (value) => props.onItemsChange?.(value), }); createSortBasedOnDOMPosition(items, setItems); const registerItem = (item: T) => { setItems((prevItems) => { // Finds the item group based on the DOM hierarchy const index = findDOMIndex(prevItems, item); return addItemToArray(prevItems, item, index); }); return () => { setItems((prevItems) => { const nextItems = prevItems.filter( (prevItem) => prevItem.ref() !== item.ref(), ); if (prevItems.length === nextItems.length) { // The item isn't registered, so do nothing return prevItems; } return nextItems; }); }; }; const DomCollectionProvider: FlowComponent = (props) => { return createComponent(DomCollectionContext.Provider, { value: { registerItem } as DomCollectionContextValue, get children() { return props.children; }, }); }; return { DomCollectionProvider }; }