create-slots
Version:
Bring slots to React components
1 lines • 7.6 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.tsx","../src/utils.ts","../src/DevChildren.tsx","../src/SlotsManager.ts"],"sourcesContent":["import * as React from 'react'\n\nimport { createSlotsContext, getComponentName, hoistStatics } from './utils'\nimport { DevChildren } from './DevChildren'\nimport { createSlotsManager } from './SlotsManager'\n\ntype Slots = ReturnType<typeof createSlotsManager>\ntype Callback = (Slots: Slots) => JSX.Element | null\n\nconst SlotsContext = createSlotsContext<Slots | undefined>(undefined)\n\nconst Template = ({ children }: { children: () => ReturnType<Callback> }) => {\n return children()\n}\n\nexport const HostSlots = ({\n children,\n callback,\n}: {\n children: React.ReactNode\n callback: Callback\n}) => {\n const forceUpdate = React.useReducer(() => [], [])[1]\n const Slots = React.useMemo(\n () => createSlotsManager(forceUpdate),\n [forceUpdate]\n )\n\n return (\n <>\n <SlotsContext.Provider value={Slots}>\n {process.env.NODE_ENV === 'production' ? (\n children\n ) : (\n <DevChildren name=\"HostSlots\" forceUpdate={forceUpdate}>\n {children}\n </DevChildren>\n )}\n </SlotsContext.Provider>\n <Template>{() => callback(Slots)}</Template>\n </>\n )\n}\n\nexport const createHost = (children: React.ReactNode, callback: Callback) => {\n return <HostSlots children={children} callback={callback} />\n}\n\nexport const createSlot = <T extends React.ElementType>(Fallback?: T) => {\n const ForwardRef = (props: any, ref: any) => {\n const Slots = React.useContext(SlotsContext)\n if (!Slots) return Fallback ? <Fallback ref={ref} {...props} /> : null\n\n const element = <Slot ref={ref} {...props} />\n /* eslint-disable react-hooks/rules-of-hooks */\n React.useState(() => Slots.register(Slot, element))\n React.useEffect(() => Slots.update(Slot, element))\n React.useEffect(() => () => Slots.unmount(Slot), [Slots])\n /* eslint-enable react-hooks/rules-of-hooks */\n\n return null\n }\n ForwardRef.displayName = Fallback\n ? `Slot(${getComponentName(Fallback)})`\n : 'Slot'\n const Slot = React.forwardRef(ForwardRef) as unknown as T\n\n return Fallback ? hoistStatics(Slot, Fallback) : Slot\n}\n","import * as React from 'react'\n\nexport const useIsomorphicEffect =\n // istanbul ignore next\n typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect\n\nexport const createSlotsContext = <T>(defaultValue: T) => {\n const context = React.createContext(defaultValue)\n context.displayName = 'SlotsContext'\n return context\n}\n\nexport const getComponentName = (Component: React.ElementType) => {\n if (typeof Component === 'string') return Component\n // istanbul ignore next\n return Component.displayName || Component.name || 'Component'\n}\n\nconst REACT_STATICS = ['$$typeof', 'render', 'displayName', 'defaultProps']\n\nexport const hoistStatics = <T extends React.ElementType>(\n target: T,\n source: T\n) => {\n if (typeof source === 'string') return target\n\n const statics = Object.getOwnPropertyNames(source).reduce((obj, key) => {\n if (!REACT_STATICS.includes(key)) {\n obj[key] = (source as any)[key]\n }\n return obj\n }, {} as Record<string, any>)\n\n return Object.assign(target, statics)\n}\n","import * as React from 'react'\n\nimport { useIsomorphicEffect } from './utils'\n\nconst Wrapper = 'slots-wrapper' as 'span'\nconst wrapperRegexp = new RegExp(`^<${Wrapper}>.*</${Wrapper}>$`)\n\ntype DevChildrenProps = {\n name: string\n forceUpdate: () => void\n children: React.ReactNode\n}\n\nexport const DevChildren = ({\n name,\n forceUpdate,\n children,\n}: DevChildrenProps) => {\n const ref = React.useRef<HTMLSpanElement>(null)\n const warnedRef = React.useRef(false)\n // const forceUpdate = React.useReducer(() => [], [])[1]\n\n useIsomorphicEffect(() => {\n if (!warnedRef.current && ref.current?.innerHTML) {\n const content = ref.current.innerHTML\n if (content && !wrapperRegexp.test(content)) {\n console.warn(\n `Unwrapped children found in \"${name}\", either wrap them in slots or remove`\n )\n }\n }\n warnedRef.current = true\n forceUpdate()\n }, [name])\n\n return warnedRef.current ? (\n <>{children}</>\n ) : (\n <Wrapper ref={ref}>{children}</Wrapper>\n )\n}\n","import * as React from 'react'\n\ntype Slot = React.ElementType\n\nexport const createSlotsManager = (onChange: (slot: Slot) => void) => {\n const elementMap = new Map<Slot, React.ReactElement>()\n return {\n register(slot: Slot, element: React.ReactElement) {\n elementMap.set(slot, element)\n },\n update(slot: Slot, element: React.ReactElement) {\n elementMap.set(slot, element)\n onChange(slot)\n },\n unmount(slot: Slot) {\n elementMap.delete(slot)\n onChange(slot)\n },\n get<T extends Slot>(slot: T) {\n return elementMap.get(slot) as\n | React.ReactElement<React.ComponentProps<T>, T>\n | undefined\n },\n getProps<T extends Slot>(slot: T) {\n const element = elementMap.get(slot)\n if (!element) return undefined\n const { ref, props } = element as any\n return (ref ? { ...props, ref } : props) as React.ComponentProps<T>\n },\n }\n}\n"],"mappings":"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","names":[]}