UNPKG

@dr.pogodin/react-themes

Version:
358 lines (340 loc) 12.7 kB
import { c as _c } from "react/compiler-runtime"; import { createContext, use, useMemo } from 'react'; // ----------------------------------------------------------------------------- // TypeScript interfaces & types, constants. // Note: Support of custom specifity-manipulation classes in TypeScript is too // cumbersome, thus although it remains a functional feature for pure JavaScript, // the TypeScript assumes these classes are always "ad", "hoc", and "context". // NOTE: Keep it as interface, to allow, in theory, consumer to redefine these // default keys. // eslint-disable-next-line @typescript-eslint/consistent-type-definitions // NOTE: KeyT should be a union of string literals - valid theme keys. // TODO: Revise, should we change it to type? // eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/consistent-indexed-object-style // TODO: Revise, should we change it to type? // eslint-disable-next-line @typescript-eslint/consistent-type-definitions // TODO: Revise, should we change it to type? // eslint-disable-next-line @typescript-eslint/consistent-type-definitions import { jsx as _jsx } from "react/jsx-runtime"; /** Supported theme composition modes. */ export let COMPOSE = /*#__PURE__*/function (COMPOSE) { COMPOSE["DEEP"] = "DEEP"; COMPOSE["SOFT"] = "SOFT"; COMPOSE["SWAP"] = "SWAP"; return COMPOSE; }({}); /** Supported theme priorities. */ export let PRIORITY = /*#__PURE__*/function (PRIORITY) { PRIORITY["ADHOC_CONTEXT_DEFAULT"] = "ADHOC_CONTEXT_DEFAULT"; PRIORITY["ADHOC_DEFAULT_CONTEXT"] = "ADHOC_DEFAULT_CONTEXT"; return PRIORITY; }({}); const INVALID_COMPOSE = 'Invalid composition mode'; const Context = /*#__PURE__*/createContext(undefined); // ----------------------------------------------------------------------------- // Here comes the logic. /** * Theme provider defines style contexts. It accepts a single property * `themes` (`theme` in compatibility modes). * * In case of nested context, the context theme from the closest context takes * the effect on a component. If the context theme for a component is not set in * the closest context, but it is set in an outer context, the theme from outer * context will be applied. * * @param props.children React content to render in-place of * <ThemeProvider> component. * * @param props.themes The mapping of between themeable component names * (the first parameter passed into themed() function for such components * registration), and context themes to apply to them within the context. * * @param props.theme Fallback mapping for backward compatibility * with `react-css-themr` and `react-css-super-themr` libraries. */ export const ThemeProvider = t0 => { const $ = _c(6); const { children, themes } = t0; const contextThemes = use(Context); let t1; if ($[0] !== contextThemes || $[1] !== themes) { var _ref; t1 = contextThemes && themes ? { ...contextThemes, ...themes } : (_ref = contextThemes !== null && contextThemes !== void 0 ? contextThemes : themes) !== null && _ref !== void 0 ? _ref : {}; $[0] = contextThemes; $[1] = themes; $[2] = t1; } else { t1 = $[2]; } const value = t1; let t2; if ($[3] !== children || $[4] !== value) { t2 = /*#__PURE__*/_jsx(Context, { value: value, children: children }); $[3] = children; $[4] = value; $[5] = t2; } else { t2 = $[5]; } return t2; }; /** * Composes two themes. * @param high High priorty theme. * @param low Low priority theme. * @param mode Composition mode. * @param tag Specifity tag(s). * @return Composed theme. */ function compose(high, low, mode, tag) { if (high && low) { switch (mode) { case COMPOSE.DEEP: { const res = { ...low }; const prefix = Array.isArray(tag) ? `${high[tag[0]] || ''} ${high[tag[1]] || ''}` : high[tag] || ''; for (const key in high) { if (res[key]) { res[key] = `${res[key]} ${prefix} ${high[key]}`; } else res[key] = high[key]; } return res; } case COMPOSE.SOFT: return { ...low, ...high }; case COMPOSE.SWAP: return high; default: throw new Error(INVALID_COMPOSE); } } else return high !== null && high !== void 0 ? high : low; } /** * @deprecated * * Registers a themeable component under given name, and with an optional * default theme. * @param componentName Themed component name, which should be used to * provide its context theme via <ThemeProvider>. * @param [defaultTheme] Default theme, in form of theme key to * CSS class name mapping. If you have CSS modules and SCSS loader correctly * configured, the import `import theme from 'some.theme.scss';` will result * in `theme` object you can pass here. In some cases, it might be also legit * to construct theme object in a diffent way. * @param [options] Additional parameters. * @param [options.composeAdhocTheme=COMPOSE.DEEP] Composition type for * _ad hoc_ theme, which is merged into the result of composition of lower * priority themes. Must be one of COMPOSE values. * @param [options.composeContextTheme=COMPOSE.DEEP] Composition type * for context theme into default theme (or vice verca, if opted by * `themePriority` override). Must be one of COMPOSE values. * @param [options.themePriority=ADHOC_CONTEXT_DEFAULT] Theme * priorities. Must be one of PRIORITY values. * @param [options.mapThemeProps] By default, the themeable * component * created by `themed()` does not pass into the original wrapped component any * properties introduced by this library. It only passes down properties it * does not recognize, alongside the composed `theme`, and forwarded DOM `ref`. * In case a different behavior is needed, the property mapper can be * specified with this option. It should be a function with * ThemePropsMapper signature, and if present the result from this * function will be passed down the wrapped component as its props. * @param [options.contextTag=context] Override of `context` theme * key. * @param [options.adhocTag=ad.hoc] Override of `ad.hoc` theme key. * @param [options.composeTheme] Compatibility compose mode. * @param [options.mapThemrProps] Compatibility prop mapper. * @return Themeable component, registered under * given name. */ function themedImpl(componentName, defaultTheme, options = {}) { const { adhocTag = 'ad.hoc', composeAdhocTheme: oComposeAdhocTheme, composeContextTheme: oComposeContextTheme, contextTag = 'context', mapThemeProps: oMapThemeProps, themePriority: oThemePriority } = options; const aTag = adhocTag.split('.'); // TODO: Should we remove this runtime safeguard, assuming by now all // host projects should use TypeScript, which should prevent the error // we safeguard against here? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (aTag.length !== 2 || !aTag[0] || !aTag[1]) { throw new Error('Invalid adhoc theme tag'); } return ThemeableComponent => { const Component = properties => { var _ref2; const $ = _c(22); let children; let composeAdhocTheme; let composeContextTheme; let mapThemeProps; let ref; let rest; let theme; let themePriority; if ($[0] !== properties) { ({ children, composeAdhocTheme, composeContextTheme, mapThemeProps, ref, theme, themePriority, ...rest } = properties); $[0] = properties; $[1] = children; $[2] = composeAdhocTheme; $[3] = composeContextTheme; $[4] = mapThemeProps; $[5] = ref; $[6] = rest; $[7] = theme; $[8] = themePriority; } else { children = $[1]; composeAdhocTheme = $[2]; composeContextTheme = $[3]; mapThemeProps = $[4]; ref = $[5]; rest = $[6]; theme = $[7]; themePriority = $[8]; } const context = use(Context); const contextTheme = context === null || context === void 0 ? void 0 : context[componentName]; const mapper = mapThemeProps !== null && mapThemeProps !== void 0 ? mapThemeProps : oMapThemeProps; const priority = (_ref2 = themePriority !== null && themePriority !== void 0 ? themePriority : oThemePriority) !== null && _ref2 !== void 0 ? _ref2 : PRIORITY.ADHOC_CONTEXT_DEFAULT; const composeAdhoc = composeAdhocTheme || oComposeAdhocTheme || COMPOSE.DEEP; const composeContext = composeContextTheme || oComposeContextTheme || COMPOSE.DEEP; let t0; if ($[9] !== composeAdhoc || $[10] !== composeContext || $[11] !== contextTheme || $[12] !== mapper || $[13] !== priority || $[14] !== properties || $[15] !== ref || $[16] !== rest || $[17] !== theme) { var _compose; let res = priority === PRIORITY.ADHOC_DEFAULT_CONTEXT ? compose(defaultTheme, contextTheme, composeContext, contextTag) : compose(contextTheme, defaultTheme, composeContext, contextTag); res = (_compose = compose(theme, res, composeAdhoc, aTag)) !== null && _compose !== void 0 ? _compose : {}; t0 = mapper ? mapper(properties, res) : { ...rest, ref, theme: res }; $[9] = composeAdhoc; $[10] = composeContext; $[11] = contextTheme; $[12] = mapper; $[13] = priority; $[14] = properties; $[15] = ref; $[16] = rest; $[17] = theme; $[18] = t0; } else { t0 = $[18]; } const p = t0; let t1; if ($[19] !== children || $[20] !== p) { t1 = /*#__PURE__*/_jsx(ThemeableComponent, { ...p, children: children }); $[19] = children; $[20] = p; $[21] = t1; } else { t1 = $[21]; } return t1; }; return Component; }; } /** @deprecated */ function themed( // 1st argument. componentOrComponentName, // 2nd argument. componentNameOrDefaultTheme, // 3rd argument. defaultThemeOrOptions, // 4th argument. options) { let component; let componentName; let defaultTheme; let ops; if (typeof componentOrComponentName === 'string') { // 1st argument: component name. componentName = componentOrComponentName; // 2nd argument: default theme. if (typeof componentNameOrDefaultTheme === 'string') { throw Error('Second argument is not expected to be a string'); } defaultTheme = componentNameOrDefaultTheme; // 3rd argument: options. ops = defaultThemeOrOptions; // 4th argument: none. if (options) throw Error('4th argument is not expected'); } else { // 1st argument: component. component = componentOrComponentName; // 2nd argument: component name. if (typeof componentNameOrDefaultTheme !== 'string') { throw Error('Second argument is not a string'); } componentName = componentNameOrDefaultTheme; // 3rd argument: default theme. defaultTheme = defaultThemeOrOptions; // 4th argument: options. ops = options; } const impl = themedImpl(componentName, defaultTheme, ops); return component ? impl(component) : impl; } /** @deprecated */ export default themed; /** * React hook for theme composition. */ export function useTheme(componentName, defaultTheme, adHocTheme, options) { var _compose2; const { adhocTag = 'ad.hoc', composeAdhocTheme = COMPOSE.DEEP, composeContextTheme = COMPOSE.DEEP, contextTag = 'context', themePriority = PRIORITY.ADHOC_CONTEXT_DEFAULT } = options !== null && options !== void 0 ? options : {}; const aTag = adhocTag.split('.'); // TODO: Should we remove this runtime safeguard, assuming by now all // host projects should use TypeScript, which should prevent the error // we safeguard against here? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (aTag.length !== 2 || !aTag[0] || !aTag[1]) { throw new Error('Invalid adhoc theme tag'); } const context = use(Context); const contextTheme = context === null || context === void 0 ? void 0 : context[componentName]; let res = themePriority === PRIORITY.ADHOC_DEFAULT_CONTEXT ? compose(defaultTheme, contextTheme, composeContextTheme, contextTag) : compose(contextTheme, defaultTheme, composeContextTheme, contextTag); res = (_compose2 = compose(adHocTheme, res, composeAdhocTheme, aTag)) !== null && _compose2 !== void 0 ? _compose2 : {}; return res; } //# sourceMappingURL=index.js.map