UNPKG

preact-context-provider

Version:

A generic <Provider /> for preact to put props into context

188 lines (172 loc) 6.3 kB
import { h } from 'preact'; /** * Adds all passed `props`, `children` into `context`, making them available to all descendants. * * To learn about `context`, see the [React Docs](https://facebook.github.io/react/docs/context.html). * * @name Provider * @param {Object} props All props are exposed as properties in `context`, except children * * @example * const Demo = (props, context) => { * console.log(context); // "{ a: 'b' }" * }; * render( * <Provider a="b"> * <Demo /> * </Provider> * ); * // "{ a: 'b' }" * * // lower-level providers override higher providers for any keys that they define * render( * <Provider a={key1: 'foo'} b={key2: 'bar'}> * <Provider a={key3: 'buz'} > * <Demo /> * </Provider> * </Provider> * ); * // "{ a: { key3: 'buz' }, b: { key2: 'bar' } }" */ var Provider = function Provider () {}; Provider.prototype.getChildContext = function getChildContext () { var context = {}; for (var i in this.props) { if (i !== 'children') { context[i] = this.props[i]; } } return context; }; Provider.prototype.render = function render (props) { return props.children[0]; }; /** * A simpler Object.assign * @private */ function assign(obj, props) { for (var i in props) { if (props.hasOwnProperty(i)) { obj[i] = props[i]; } } return obj; } /** * Recursively copy keys from `source` to `target`, skipping truthy values already in `target` so parent values can block child values * @private */ function deepAssign(target, source) { //if they aren't both objects, use target if it is defined (null/0/false/etc. are OK), otherwise use source if (!(target && source && typeof target==='object' && typeof source==='object')) { return typeof target !== 'undefined' ? target : source; } var out = assign({}, target); for (var i in source) { if (source.hasOwnProperty(i)) { out[i] = deepAssign(target[i], source[i]); } } return out; } /** * Similar to {@link Provider}, but allows a special `mergeProps` prop to allow parent supplied context keys with the same name as those * provided by the current `MergingProvider` to be deep merged, instead of replaced. * * To learn about `context`, see the [React Docs](https://facebook.github.io/react/docs/context.html). * * @name MergingProvider * @param {Object} props All props are exposed as properties in `context`, except `children` and `mergeProps` * @param {Array} [props.mergeProps] If not supplied, all supplied props will be merged with keys already in context. If supplied as an array of strings, * it will deep merge any prop names that are present in the array, and missing prop names be overriden by the child like {@link Provider}. * * @example * import Provider, { MergingProvider } from 'preact-context-provider'; * const Demo = (props, context) => { * console.log(context); // "{ a: 'b' }" * }; * * // with mergeProps unspecified, all parent context keys are merged with the ones presently supplied, parent values taking precedence * render( * <Provider a={key1: 'foo'}> * <MergingProvider a={key2: 'bar'}> * <Demo /> * </MergingProvider> * </Provider> * ); * // "{ a: { key1: 'foo', key2: 'bar' } }" * * // when mergeProps is an array, only specified keys are merged, non-specified keys get their value from current node * // in this example, only the 'a' context key is merged. 'b' is overwritten by the lower node * render( * <Provider a={key1: 'foo'} b={key2: 'bar'}> * <MergingProvider mergeProps={['a']} a={key3: 'baz'} b={key4: 'buz'}> * <Demo /> * </MergingProvider> * </Provider> * ); * // "{ a: { key1: 'foo', key3: 'baz' }, b: {key4: 'buz'} }" */ var MergingProvider = function MergingProvider () {}; MergingProvider.prototype.getChildContext = function getChildContext () { var context = {}, props=this.props, mergeProps = props.mergeProps, mergeIsArray=Array.isArray(mergeProps); for (var i in props) { if (i !== 'children' && i !== 'mergeProps') { context[i] = (!mergeIsArray || ~mergeProps.indexOf(i)) ? deepAssign(this.context[i], props[i]) : props[i]; } } return context; }; MergingProvider.prototype.render = function render (props) { return props.children[0]; }; /** * Higher Order Component that wraps components in a {@link Provider} for the given context. * * @name provide * @param {Object} ctx Properties to pass into context (passed to {@link Provider}) * @returns {Function} A function that, given a Child component, wraps it in a Provider component for the given context. * * @example * import {provide} from 'preact-context-provider'; * const Demo = (props, context) => { * console.log(context.a); // "b" * }; * const ProvidedDemo = provide({a: "b"})(Demo); * * ProvidedDemo.getWrappedComponent() === Demo; // true * * render( <ProvidedDemo /> ); */ var provide = function (ctx) { return function (Child) { var ProviderWrapper = function (props) { return h(Provider, ctx, h(Child, props)); }; ProviderWrapper.getWrappedComponent = Child && Child.getWrappedComponent || (function () { return Child; }); return ProviderWrapper; }; }; Provider.provide = provide; /** * Higher Order Component that wraps components in a {@link MergingProvider} for the given context. * * @name mergingProvide * @param {Object} ctx Properties to pass into context (passed to {@link MergingProvider}) * @returns {Function} A function that, given a Child component, wraps it in a {@link MergingProvider} component for the given context. * * @example * import {mergingProvide} from 'preact-context-provider'; * const Demo = (props, context) => { * console.log(context.a); * }; * const ProvidedDemo = mergingProvide({a: "b"})(Demo); * * ProvidedDemo.getWrappedComponent() === Demo; // true * * render( <ProvidedDemo /> ); // "b" */ var mergingProvide = function (ctx) { return function (Child) { var MergingProviderWrapper = function (props) { return h(MergingProvider, ctx, h(Child, props)); }; MergingProviderWrapper.getWrappedComponent = Child && Child.getWrappedComponent || (function () { return Child; }); return MergingProviderWrapper; }; }; export default Provider; export { MergingProvider, mergingProvide, provide }; //# sourceMappingURL=preact-context-provider.es.js.map