UNPKG

nano-slots

Version:

A super lightweight modern alternative to [`react-slot-fill`](https://github.com/camwest/react-slot-fill) with familiar API.

72 lines (71 loc) 2.37 kB
var _a; import { createContext, createElement as h, useContext, useLayoutEffect, useState, Fragment, } from 'react'; import isServer from './is-server'; export var SlotsProvider = (_a = createSlots(), _a.Provider), Slot = _a.Slot, Fill = _a.Fill; export default function createSlots() { var SlotsContext = createContext(createEmitter()); function Provider(props) { var emitter = useState(createEmitter)[0]; return (h(SlotsContext.Provider, { value: emitter }, props.children)); } function Slot(props) { var emitter = useContext(SlotsContext); var _a = useState(), node = _a[0], setNode = _a[1]; useUniversalEffect(function () { setNode(emitter.get(props.name)); return emitter.on(props.name, setNode); }, [emitter, props.name]); var has = node !== undefined; useUniversalEffect(function () { if (props.onChange) props.onChange(has); }, [has, props.onChange]); return h(Fragment, null, has ? node : props.children); } function Fill(props) { var emitter = useContext(SlotsContext); useUniversalEffect(function () { return emitter.emit(props.name, props.children); }, [emitter, props.name, props.children]); return null; } return { Provider: Provider, Slot: Slot, Fill: Fill, }; } /** @private Don't use! */ export function createEmitter() { var cache = {}; var events = {}; function update(event, node) { var source = events[event]; if (source) source.forEach(function (cb) { return cb(node); }); cache[event] = node; } return { emit: function (event, node) { update(event, node); return function () { return update(event); }; }, get: function (event) { return cache[event]; }, on: function (event, cb) { var source = (events[event] = events[event] || []); source.push(cb); return function () { var index = source.indexOf(cb); if (index > -1) source.splice(index, 1); }; }, }; } function useUniversalEffect(effect, deps) { if (!isServer()) { useLayoutEffect(effect, deps); } }