UNPKG

@curi/react-dom

Version:

React DOM components to use with Curi

318 lines (295 loc) 12.9 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : (global = global || self, factory(global.CuriReactDOM = {}, global.React)); }(this, function (exports, React) { 'use strict'; React = React && React.hasOwnProperty('default') ? React['default'] : React; function find(name, children) { return children.some(function (node) { if (node.name === name) { return true; } else if (node.children) { return find(name, node.children); } return false; }); } function active(route, response, options) { if (options === void 0) { options = {}; } if (response.name !== route.name && (!options.partial || !find(response.name, route.children))) { return false; } var keys = route.keys; if (keys.length) { if (!options.params) { return false; } for (var r = 0, length = keys.length; r < length; r++) { var key = keys[r]; var param = options.params[key]; if (!param || param !== response.params[key]) { return false; } } } if (options.components) { return options.components(response.location); } return true; } var key = { response: null, navigation: null }; var responseContext = React.createContext(key); var ResponseProvider = responseContext.Provider, ResponseConsumer = responseContext.Consumer; var routerContext = React.createContext(null); var RouterProvider = routerContext.Provider, RouterConsumer = routerContext.Consumer; function createRouterComponent(router) { function initialState() { return router.current(); } return function Router(props) { var _a = React.useState(initialState), response = _a[0], setResponse = _a[1]; React.useEffect(function () { var removed = false; var stop = router.observe(function (_a) { var response = _a.response, navigation = _a.navigation; if (!removed) { setResponse({ response: response, navigation: navigation }); } }, { initial: false }); return function () { removed = true; stop(); }; }, []); return (React.createElement(RouterProvider, { value: router }, React.createElement(ResponseProvider, { value: response }, props.children))); }; } function useRouter() { return React.useContext(routerContext); } function useResponse() { return React.useContext(responseContext); } function useActive(props) { var router = useRouter(); var response = useResponse().response; var route = router.route(props.name); if (!route) { return false; } return active(route, response, { params: props.params, partial: props.partial, components: props.components }); } function useURL(props) { var router = useRouter(); return router.url(props); } function useNavigating() { var router = useRouter(); var _a = React.useState(undefined), cancel = _a[0], setCancel = _a[1]; React.useEffect(function () { var removed = false; var stop = router.cancel(function (callback) { if (!removed) { setCancel(function () { return callback; }); } }); return function () { removed = true; stop(); }; }, []); return cancel; } function defaultCanNavigate() { return true; } function useNavigationHandler(props) { var url = props.url, onNav = props.onNav, method = props.method, target = props.target, state = props.state, _a = props.canNavigate, canNavigate = _a === void 0 ? defaultCanNavigate : _a; var router = useRouter(); var eventHandler = React.useCallback(function eventHandler(event) { if (onNav) { onNav(event); } if (canNavigate(event, target)) { event.preventDefault(); router.navigate({ url: url, state: state, method: method }); } }, [url, method, state, onNav, target]); return { url: url, eventHandler: eventHandler }; } function useStatefulNavigationHandler(props) { var url = props.url, onNav = props.onNav, method = props.method, target = props.target, state = props.state, _a = props.canNavigate, canNavigate = _a === void 0 ? defaultCanNavigate : _a; var router = useRouter(); var removeCallbacks = React.useRef(undefined); var _b = React.useState(false), navigating = _b[0], setNavigating = _b[1]; React.useEffect(function () { return function () { if (removeCallbacks.current) { removeCallbacks.current(); } }; }, []); var eventHandler = React.useCallback(function eventHandler(event) { if (onNav) { onNav(event); } if (canNavigate(event, target)) { event.preventDefault(); var done = function () { removeCallbacks.current = undefined; setNavigating(false); }; setNavigating(true); removeCallbacks.current = router.navigate({ url: url, state: state, method: method, cancelled: done, finished: done }); } }, [url, method, state, onNav, target]); return { url: url, eventHandler: eventHandler, navigating: navigating }; } function useConfirm(fn) { var router = useRouter(); React.useEffect(function () { router.history.confirm(fn); return function () { router.history.confirm(); }; }, [fn]); } function useNavigationFocus(ref, props) { if (props === void 0) { props = {}; } // The response isn't actually used, but the app should only // re-focus when the response changes. The preserve and preventScroll // values are used, but not used in the comparison array because // changing these values would steal the app's focus even though // the location hasn't changed. var response = useResponse().response; var preserve = props.preserve, _a = props.preventScroll, preventScroll = _a === void 0 ? false : _a; React.useEffect(function () { var ele = ref.current; if (ele === null) { { console.warn("There is no element to focus. Did you forget to add the ref to an element?"); } return; } if (preserve && ele.contains(document.activeElement)) { return; } { if (!ele.hasAttribute("tabIndex") && ele.tabIndex === -1) { console.warn('The component that is passed the ref must have a "tabIndex" prop or be focusable by default in order to be focused. ' + "Otherwise, the document's <body> will be focused instead."); } } // @ts-ignore ele.focus({ preventScroll: preventScroll }); }, [response]); } /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) t[p[i]] = s[p[i]]; return t; } function canNavigate(event, target) { return (!event.defaultPrevented && !target && event.button === 0 && !(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)); } var Link = React.forwardRef(function (props, ref) { var _a; var // url name = props.name, params = props.params, query = props.query, hash = props.hash, // navigation state = props.state, onNav = props.onNav, method = props.method, // props children = props.children, Anchor = (_a = props.anchor, _a === void 0 ? "a" : _a), rest = __rest(props, ["name", "params", "query", "hash", "state", "onNav", "method", "children", "anchor"]); var url = useURL({ name: name, params: params, query: query, hash: hash }); var eventHandler = useNavigationHandler({ url: url, state: state, onNav: onNav, method: method, canNavigate: canNavigate, target: rest.target }).eventHandler; return (React.createElement(Anchor, __assign({}, rest, { onClick: eventHandler, href: url, ref: ref }), children)); }); var AsyncLink = React.forwardRef(function (props, ref) { var _a, _b; var // url name = props.name, params = props.params, query = props.query, hash = props.hash, // navigation state = props.state, onNav = props.onNav, method = props.method, // props children = props.children, Anchor = (_a = props.anchor, _a === void 0 ? "a" : _a), rest = __rest(props, ["name", "params", "query", "hash", "state", "onNav", "method", "children", "anchor"]); var url = useURL({ name: name, params: params, query: query, hash: hash }); var eventHandler = (_b = useStatefulNavigationHandler({ url: url, state: state, onNav: onNav, method: method, canNavigate: canNavigate, target: rest.target }), _b.eventHandler), navigating = _b.navigating; return (React.createElement(Anchor, __assign({}, rest, { onClick: eventHandler, href: url, ref: ref }), children(navigating))); }); exports.AsyncLink = AsyncLink; exports.Link = Link; exports.ResponseConsumer = ResponseConsumer; exports.RouterConsumer = RouterConsumer; exports.createRouterComponent = createRouterComponent; exports.useActive = useActive; exports.useConfirm = useConfirm; exports.useNavigating = useNavigating; exports.useNavigationFocus = useNavigationFocus; exports.useNavigationHandler = useNavigationHandler; exports.useResponse = useResponse; exports.useRouter = useRouter; exports.useStatefulNavigationHandler = useStatefulNavigationHandler; exports.useURL = useURL; Object.defineProperty(exports, '__esModule', { value: true }); }));