UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

78 lines (61 loc) 2.4 kB
'use strict'; var React = require('react'); var warning = require('../utils/warning.js'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React__default = /*#__PURE__*/_interopDefault(React); // slot config allows 2 options: // 1. Component to match, example: { leadingVisual: LeadingVisual } // 2. Component to match + a test function, example: { blockDescription: [Description, props => props.variant === 'block'] } // We don't know what the props are yet, we set them later based on slot config // eslint-disable-next-line @typescript-eslint/no-explicit-any // useful for narrowing types, third option is not possible /** * Extract components from `children` so we can render them in different places, * allowing us to implement components with SSR-compatible slot APIs. * Note: We can only extract direct children, not nested ones. */ function useSlots(children, config) { // Object mapping slot names to their elements const slots = mapValues(config, () => undefined); // Array of elements that are not slots const rest = []; const keys = Object.keys(config); const values = Object.values(config); // eslint-disable-next-line github/array-foreach React__default.default.Children.forEach(children, child => { if (! /*#__PURE__*/React__default.default.isValidElement(child)) { rest.push(child); return; } const index = values.findIndex(value => { if (Array.isArray(value)) { const [component, testFn] = value; return child.type === component && testFn(child.props); } else { return child.type === value; } }); // If the child is not a slot, add it to the `rest` array if (index === -1) { rest.push(child); return; } const slotKey = keys[index]; // If slot is already filled, ignore duplicates if (slots[slotKey]) { process.env.NODE_ENV !== "production" ? warning.warning(true, `Found duplicate "${String(slotKey)}" slot. Only the first will be rendered.`) : void 0; return; } // If the child is a slot, add it to the `slots` object slots[slotKey] = child; }); return [slots, rest]; } /** Map the values of an object */ function mapValues(obj, fn) { return Object.keys(obj).reduce((result, key) => { result[key] = fn(obj[key]); return result; }, {}); } exports.useSlots = useSlots;