UNPKG

words

Version:

A collection of React hooks for toggles with dynamic noun and verb APIs

226 lines (225 loc) 8.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useWords = exports.useVerbs = exports.useNoun = exports.createNounFromState = exports.NounSetter = exports.nounState = exports.negativeVerbs = exports.positiveVerbs = exports.toggleVerbs = void 0; var react_1 = require("react"); // toggle verb pairs exports.toggleVerbs = { open: 'close', show: 'hide', turnOn: 'turnOff', check: 'uncheck', enable: 'disable', expand: 'collapse', activate: 'deactivate', start: 'end', connect: 'disconnect', focus: 'blur', mount: 'unmount', reveal: 'conceal', display: 'dismiss', lock: 'unlock', subscribe: 'unsubscribe' }; exports.positiveVerbs = Object.keys(exports.toggleVerbs); exports.negativeVerbs = Object.values(exports.toggleVerbs); // noun state mapping object. For any key that maps to true, // the getter will return getActive(), and for false it returns !getActive() exports.nounState = { isActive: true, isOpen: true, isClosed: false, isShown: true, isHidden: false, isVisible: true, isOn: true, isOff: false, isChecked: true, isEnabled: true, isDisabled: false, isExpanded: true, isCollapsed: false, isActivated: true, isDeactivated: false, hasStarted: true, hasEnded: false, isConnected: true, isDisconnected: false, isFocused: true, isBlurred: false, isMounted: true, isRevealed: true, isConcealed: false, isLocked: true, isUnlocked: false, isSubscribed: true }; exports.NounSetter = Symbol.for('NounSetter'); function createNounFromState(name, getActive, setActive) { return new Proxy({}, { get: function (_, prop) { if (typeof prop === 'symbol') { if (prop === exports.NounSetter) return setActive; return undefined; } if (typeof prop === 'string') { if (prop === 'name') return name; if (prop in exports.nounState) { return exports.nounState[prop] ? getActive() : !getActive(); } } return undefined; } }); } exports.createNounFromState = createNounFromState; function useNoun(initial) { var _a = (0, react_1.useState)(initial), active = _a[0], setActive = _a[1]; var activeRef = (0, react_1.useRef)(active); (0, react_1.useLayoutEffect)(function () { activeRef.current = active; }, [active]); var name = (0, react_1.useMemo)(function () { return Math.random().toString(36).substr(2, 6); }, []); var nounRef = (0, react_1.useRef)(); if (!nounRef.current) { nounRef.current = createNounFromState(name, function () { return activeRef.current; }, function (val) { // Update the ref immediately so that getActive returns the new value activeRef.current = val; setActive(val); }); } return nounRef.current; } exports.useNoun = useNoun; function useVerbs() { return (0, react_1.useMemo)(function () { var v = {}; var _loop_1 = function (positive) { var negative = exports.toggleVerbs[positive]; v[positive] = function (noun) { var setter = noun[exports.NounSetter]; if (typeof setter === 'function') { setter(true); } else { console.warn("No setter on noun ".concat(noun.name, " for ").concat(positive)); } }; v[negative] = function (noun) { console.log("useVerbs: ".concat(negative, " called on noun ").concat(noun.name)); var setter = noun[exports.NounSetter]; if (typeof setter === 'function') { setter(false); } else { console.warn("No setter on noun ".concat(noun.name, " for ").concat(negative)); } }; }; for (var _i = 0, positiveVerbs_1 = exports.positiveVerbs; _i < positiveVerbs_1.length; _i++) { var positive = positiveVerbs_1[_i]; _loop_1(positive); } v.toggle = function (noun) { var setter = noun[exports.NounSetter]; if (typeof setter === 'function') { setter(!noun.isActive); } else { console.warn("No setter on noun ".concat(noun.name, " for toggle")); } }; return v; }, []); } exports.useVerbs = useVerbs; function useWords() { var initialValues = []; for (var _i = 0; _i < arguments.length; _i++) { initialValues[_i] = arguments[_i]; } var rerender = (0, react_1.useReducer)(function () { return ({}); }, 0)[1]; var states = (0, react_1.useRef)({}); var nounCache = (0, react_1.useRef)({}); var index = (0, react_1.useRef)(0); // Updated verbs: try to use the noun's own setter if available. var verbs = (0, react_1.useMemo)(function () { var v = {}; for (var _i = 0, positiveVerbs_2 = exports.positiveVerbs; _i < positiveVerbs_2.length; _i++) { var positive = positiveVerbs_2[_i]; var negative = exports.toggleVerbs[positive]; v[positive] = function (noun) { var setter = noun[exports.NounSetter]; if (typeof setter === 'function') { setter(true); } else { states.current[noun.name] = true; rerender(); } }; v[negative] = function (noun) { var setter = noun[exports.NounSetter]; if (typeof setter === 'function') { setter(false); } else { states.current[noun.name] = false; rerender(); } }; } v.toggle = function (noun) { var setter = noun[exports.NounSetter]; if (typeof setter === 'function') { setter(!noun.isActive); } else { states.current[noun.name] = !states.current[noun.name]; rerender(); } }; return v; }, [rerender]); var nouns = (0, react_1.useMemo)(function () { return new Proxy({}, { get: function (_, prop) { var _a; if (typeof prop !== 'string') return undefined; if (prop in verbs) { if (process.env.NODE_ENV !== 'production') { throw new Error("Invalid noun name \"".concat(prop, "\": noun names must not conflict with verb names")); } else { console.error("Invalid noun name \"".concat(prop, "\": noun names must not conflict with verb names")); return undefined; } } if (nounCache.current[prop]) return nounCache.current[prop]; var initial = (_a = initialValues[index.current]) !== null && _a !== void 0 ? _a : false; states.current[prop] = initial; var noun = createNounFromState(prop, function () { return states.current[prop]; }, function (val) { states.current[prop] = val; rerender(); }); nounCache.current[prop] = noun; index.current++; return noun; }, ownKeys: function () { return Object.keys(states.current); }, getOwnPropertyDescriptor: function (_, prop) { if (prop in states.current) return { enumerable: true, configurable: true }; return undefined; } }); }, [initialValues, rerender]); return [nouns, verbs]; } exports.useWords = useWords; exports.default = useWords;