UNPKG

@wener/console

Version:

Base console UI toolkit

90 lines (89 loc) 3.26 kB
import React, { memo, useCallback, useDebugValue, useEffect, useId } from "react"; import { useCompareEffect } from "@wener/reaction"; import { createStore } from "zustand"; import { immer } from "zustand/middleware/immer"; import { shallow } from "zustand/shallow"; import { createStoreContext } from "../../zustand/index.js"; const createSlotStore = () => createStore()(immer(() => ({ slots: {} }))); export function createSlotContext() { const { Provider, useStore, useStoreApi } = createStoreContext(); const SlotProvider = ({ children }) => { return /*#__PURE__*/ React.createElement(Provider, { createStore: createSlotStore }, children); }; const Slot = /*#__PURE__*/ memo(({ name, placement = "default", order = 0, children }) => { const api = useStoreApi(); const slotId = useId(); placement ||= "default"; order ||= 0; const slotPlace = `${name}/${placement}`; useCompareEffect(() => { // wrap children with key api.setState((s) => { const slots = s.slots[slotPlace] ||= []; const slot = slots.find((v) => v.id === slotId); if (slot) { Object.assign(slot, { name, placement, order, children }); } else { slots.push({ id: slotId, name, placement, order, children }); } slots.sort((a, b) => b.order - a.order); }); }, [ slotPlace, order, children ]); useEffect(() => { return () => { api.setState((s) => { s.slots[slotPlace] = s.slots[slotPlace]?.filter((v) => v.id !== slotId); }); }; }, []); useDebugValue(`Slot ${slotPlace}@{${slotId}`); return null; }); Slot.displayName = "Slot"; function useSlot({ name, placement }) { const slotPlace = `${name}/${placement || "default"}`; return useStore(useCallback((s) => s.slots[slotPlace] || [], [ slotPlace ]), shallow); } const SlotPlaceholder = /*#__PURE__*/ memo(({ name, placement, placeholder }) => { const slotPlace = `${name}/${placement || "default"}`; const slot = useStore(useCallback((s) => s.slots[slotPlace] || [], [ slotPlace ]), shallow); useDebugValue(`SlotPlaceholder ${name}${placement ? `@${placement}` : ""}`); const children = slot.length ? slot.map((v) => { return v.children; }) : placeholder; return /*#__PURE__*/ React.createElement(React.Fragment, null, children); }); SlotPlaceholder.displayName = "SlotPlaceholder"; return { Slot, useSlot, SlotPlaceholder, SlotProvider }; } export const { Slot, SlotPlaceholder, SlotProvider, useSlot } = createSlotContext(); //# sourceMappingURL=Slot.js.map