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
JavaScript
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);
}
}