UNPKG

react-md

Version:

This is the full react-md library bundled together for convenience.

1,322 lines (1,278 loc) 1 MB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('react-dom')) : typeof define === 'function' && define.amd ? define(['exports', 'react', 'react-dom'], factory) : (global = global || self, factory(global.ReactMD = {}, global.React, global.ReactDOM)); }(this, (function (exports, React, ReactDOM) { 'use strict'; var React__default = 'default' in React ? React['default'] : React; var ReactDOM__default = 'default' in ReactDOM ? ReactDOM['default'] : ReactDOM; function createCommonjsModule(fn, basedir, module) { return module = { path: basedir, exports: {}, require: function (path, base) { return commonjsRequire(path, (base === undefined || base === null) ? module.path : base); } }, fn(module, module.exports), module.exports; } function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); } var classnames = createCommonjsModule(function (module) { /*! Copyright (c) 2017 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ /* global define */ (function () { var hasOwn = {}.hasOwnProperty; function classNames () { var classes = []; for (var i = 0; i < arguments.length; i++) { var arg = arguments[i]; if (!arg) continue; var argType = typeof arg; if (argType === 'string' || argType === 'number') { classes.push(arg); } else if (Array.isArray(arg) && arg.length) { var inner = classNames.apply(null, arg); if (inner) { classes.push(inner); } } else if (argType === 'object') { for (var key in arg) { if (hasOwn.call(arg, key) && arg[key]) { classes.push(key); } } } } return classes.join(' '); } if ( module.exports) { classNames.default = classNames; module.exports = classNames; } else { window.classNames = classNames; } }()); }); /** * A utility function to get the current container for the portal. For SSR, the * container will always be `null` since portals don't work server side. * * @param into The element to portal into * @param intoId An id for an element to portal into * @return the portal container element or null */ function getContainer(into, intoId) { if (typeof document === "undefined") { return null; } var container = null; if (typeof into === "undefined" && typeof intoId === "undefined") { container = document.body; } else if (typeof intoId === "string") { container = document.getElementById(intoId); } else if (typeof into === "string") { container = document.querySelector(into); } else if (typeof into === "function") { container = into(); } else if (into) { container = into; } return container; } /** * This component is a simple wrapper for the `createPortal` API from ReactDOM * that will just ensure that `null` is always returned for server side * rendering as well as a "nice" way to choose specific portal targets or just * falling back to the `document.body`. */ function Portal(_a) { var into = _a.into, intoId = _a.intoId, children = _a.children; var _b = React.useState(null), container = _b[0], setContainer = _b[1]; // setting the container via useEffect instead of immediately in the render // just so that it doesn't throw an error immediately if the dom hasn't fully // painted after a SSR React.useEffect(function () { var nextContainer = getContainer(into, intoId); if (container !== nextContainer) { setContainer(nextContainer); } }, [into, intoId, container]); if (!container) { return null; } return ReactDOM.createPortal(children, container); } { try { var PropTypes = require("prop-types"); Portal.propTypes = { into: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, PropTypes.object, ]), intoId: PropTypes.string, children: PropTypes.node, }; } catch (e) { } } /** * This is a very simple component that is used in other places within react-md * to conditionally render the children within a portal or not based on general * portal config props. */ function ConditionalPortal(_a) { var portal = _a.portal, portalInto = _a.portalInto, portalIntoId = _a.portalIntoId, children = _a.children; if (!portal && !portalInto && !portalIntoId) { return children; } return (React__default.createElement(Portal, { into: portalInto, intoId: portalIntoId }, children)); } { try { var PropTypes$1 = require("prop-types"); ConditionalPortal.propTypes = { portal: PropTypes$1.bool, portalInto: PropTypes$1.oneOfType([ PropTypes$1.string, PropTypes$1.func, PropTypes$1.object, ]), portalIntoId: PropTypes$1.string, children: PropTypes$1.node, }; } catch (e) { } } var SHORTHAND_REGEX = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; var VERBOSE_REGEX = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; /** * Converts a hex string into an rgb value. This is useful for deteching color * contrast ratios and other stuff. * * @param hex The hex string to convert * @return an object containing the r, g, b values for the color. */ function hexToRGB(hex) { if ( !SHORTHAND_REGEX.test(hex) && !VERBOSE_REGEX.test(hex)) { throw new TypeError("Invalid color string."); } hex = hex.replace(SHORTHAND_REGEX, function (_m, r, g, b) { return "" + r + r + g + g + b + b; }); var result = hex.match(VERBOSE_REGEX) || []; var r = parseInt(result[1] || "", 16) || 0; var g = parseInt(result[2] || "", 16) || 0; var b = parseInt(result[3] || "", 16) || 0; return [r, g, b]; } var RED_MULTIPLIER = 0.2126; var GREEN_MULTIPLIER = 0.7152; var BLUE_MULTIPLIER = 0.0722; /** * I really couldn't figure out how to name these "magic" numbers since the * formula doesn't really describe it much: * * @see https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests * @private */ function get8BitColor(color) { color /= 255; if (color <= 0.03928) { return color / 12.92; } return Math.pow(((color + 0.055) / 1.055), 2.4); } /** * A number closest to 0 should be closest to black while a number closest to 1 * should be closest to white. * * @see https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests * @private */ function getLuminance(color) { var _a = hexToRGB(color), r = _a[0], g = _a[1], b = _a[2]; var red = get8BitColor(r) * RED_MULTIPLIER; var green = get8BitColor(g) * GREEN_MULTIPLIER; var blue = get8BitColor(b) * BLUE_MULTIPLIER; return red + green + blue; } /** * Gets the contrast ratio between a background color and a foreground color. * * @see https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests * * @param background The background color * @param foreground The foreground color. This is normally the `color` css * value. * @return the contrast ratio between the background and foreground colors. */ function getContrastRatio(background, foreground) { var backgroundLuminance = getLuminance(background) + 0.05; var foregroundLuminance = getLuminance(foreground) + 0.05; return (Math.max(backgroundLuminance, foregroundLuminance) / Math.min(backgroundLuminance, foregroundLuminance)); } /** * The contrast ratio that can be used for large text where large text is * considered 18pt or 14pt bold. */ var LARGE_TEXT_CONTRAST_RATIO = 3; /** * The contrast ratio that can be used for normal text. */ var NORMAL_TEXT_CONTRAST_RATIO = 4.5; /** * The AAA contrast ratio for passing WGAC 2.0 color contrast ratios. */ var AAA_CONTRAST_RATIO = 7; /** * Checks if there is an acceptable contrast ratio between the background and * foreground colors based on the provided compliance level. * * @param background The background color to check against * @param foreground The foreground color to check against * @param compliance The compliance level to use or a custom number as a ratio. * @return true if there is enough contrast between the foreground and * background colors for the provided compliance level. */ function isContrastCompliant(background, foreground, compliance) { if (compliance === void 0) { compliance = "normal"; } var ratio; switch (compliance) { case "large": ratio = LARGE_TEXT_CONTRAST_RATIO; break; case "normal": ratio = NORMAL_TEXT_CONTRAST_RATIO; break; case "AAA": ratio = AAA_CONTRAST_RATIO; break; default: ratio = compliance; } return getContrastRatio(background, foreground) >= ratio; } /* eslint-disable import/no-mutable-exports, getter-return */ var noop = function () { }; var isSupported = false; /** * Checks if the browser supports passive events. This shouldn't really be used * outside of this file, but you can always check again if needed. */ function update() { if (typeof window === "undefined") { return false; } var isPassiveEventsSupported = false; var opts = Object.defineProperty({}, "passive", { get: function () { isPassiveEventsSupported = true; }, }); window.addEventListener("testSupportsPassive", noop, opts); window.removeEventListener("testSupportsPassive", noop, opts); isSupported = isPassiveEventsSupported; return isPassiveEventsSupported; } // invoke immediately update(); var passiveEvents = /*#__PURE__*/Object.freeze({ __proto__: null, update: update, get isSupported () { return isSupported; } }); var delegatedEvents = []; /* eslint-disable @typescript-eslint/explicit-function-return-type */ /** * Creates the delegated event handler that will run all the callbacks once an * event happens. The callbacks' invocation can also be throttled for event * types that trigger rapidly for additional performance. * * The `<K extends keyof WindowEventMap` is a nice thing I found while looking * through the `lib.d.ts` implementation of `addEventListener` that will allow * you to get the "correct" event type when using the `add` and `remove` * functions once you have created this event handler. Otherwise there'd be ts * errors trying to do `MouseEvent` or `KeyboardEvent` in your listeners. */ function createEventHandler(throttle, callbacks) { var running = false; var runCallbacks = function (event) { return function () { for (var i = 0; i < callbacks.length; i += 1) { callbacks[i](event); } running = false; }; }; return function eventHandler(event) { if (!throttle) { runCallbacks(event)(); return; } if (running) { return; } running = true; window.requestAnimationFrame(runCallbacks(event)); }; } /* eslint-enable @typescript-eslint/explicit-function-return-type */ /** * Creates a throttled event handler for the provided event type and event * target. */ function createDelegatedEventHandler(eventType, eventTarget, throttle, options) { if (eventTarget === void 0) { eventTarget = window; } if (throttle === void 0) { throttle = false; } var callbacks = []; var handler = createEventHandler(throttle, callbacks); return { /** * Attempts to add the provided callback to the list of callbacks for the * throttled event. If this is the first callback to be added, the throttled * event will also be started. */ add: function (callback) { if (!callbacks.length) { eventTarget.addEventListener(eventType, handler, options); } if (callbacks.indexOf(callback) === -1) { callbacks.push(callback); } }, /** * Attempts to remove the provided callback from the lsit of callbacks for * the throttled event. If this is the last callback that was removed, the * throttled event will also be stopped. */ remove: function (callback) { var i = callbacks.indexOf(callback); if (i >= 0) { callbacks.splice(i, 1); if (!callbacks.length) { eventTarget.removeEventListener(eventType, handler, options); } } }, }; } /** * Creates a delegated event listener using custom events. Most of this code * comes from the MDN about resize listeners. * * This will return an object for adding or removing event handlers for the * provided `eventType` since only one base throttled event listener will be * created. Each callback that is added will be called with the event each time * the event is triggered. This does mean that you will manually need to remove * your callback like normal or else it can be called when no longer in use. * This also means that it doesn't "hurt" to call this function without * immediately calling the `add` function since the event won't start until * there is at least 1 callback. * * @see https://developer.mozilla.org/en-US/docs/Web/Events/resize#Examples * @param eventType One of the event types that should be used to create a * delegated event for. This should be things like resize, click, scroll, etc. * @param eventTarget The target that should have the delegated event handler * attached to. This is normally the window, but can be any element as needed. * @param throttle Boolean if the event should be throttled or not. Normally * only event types like resize or scroll should be throttled for performance * boosts, but anything can be. * @return The delegated event handler that allows you to add or remove * `EventListener`s to that event. */ function delegateEvent(eventType, eventTarget, throttle, options) { if (eventTarget === void 0) { eventTarget = window; } if (throttle === void 0) { throttle = eventType === "resize" || eventType === "scroll"; } var index = delegatedEvents.findIndex(function (event) { return event.type === eventType && event.target === eventTarget && event.options === options && event.throttle === throttle; }); if (index === -1) { delegatedEvents.push({ type: eventType, target: eventTarget, options: options, throttle: throttle, handler: createDelegatedEventHandler(eventType, eventTarget, throttle, options), }); index = delegatedEvents.length - 1; } return delegatedEvents[index].handler; } var __assign = (undefined && undefined.__assign) || function () { __assign = Object.assign || function(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); }; /** * A helper function for manually setting touch events on elements when they * cannot be directly added with a React event listener. This will attempt to * create a passive event if the browser supports passive events so there is * better scroll performance. */ function setTouchEvent( /** * Boolean if the event should be added or removed. */ add, /** * The element to add the touch event to. */ el, /** * One of the touch types to modify. */ eventType, /** * The touch event callback function to use. */ callback, /** * Boolean if the event should be captured if the browser does not support * passive events. */ capture, /** * Any additional options to provide to the passive event. */ options) { if (capture === void 0) { capture = false; } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore el[(add ? "add" : "remove") + "EventListener"]("touch" + eventType, callback, isSupported ? __assign({ passive: true, capture: capture }, options) : capture); } /** * A simple wrapper for the `setTouchEvent` to just always add events. */ function addTouchEvent( /** * The element to add the touch event to. */ el, /** * One of the touch types to modify. */ eventType, /** * The touch event callback function to use. */ callback, /** * Boolean if the event should be captured if the browser does not support * passive events. */ capture, /** * Any additional options to provide to the passive event. */ options) { if (capture === void 0) { capture = false; } setTouchEvent(true, el, eventType, callback, capture, options); } /** * A simple wrapper for the `setTouchEvent` to just always remove events. * * @param el The element to add the touch event to. * @param eventType One of the touch types to modify. */ function removeTouchEvent(el, eventType, /** * The touch event callback function to use. */ callback, /** * Boolean if the event should be captured if the browser does not support * passive events. */ capture, /** * Any additional options to provide to the passive event. */ options) { if (capture === void 0) { capture = false; } setTouchEvent(false, el, eventType, callback, capture, options); } /** * This hook allows you to provide anything that should be "cached" and puts it * into a ref that'll be updated each render. This is pretty overkill for most * places, but it's really nice when you want to create event handlers that * shouldn't update if the developer used arrow functions to define callbacks. * (A great example is for ref callbacks that *shouldn't* be triggered each * render. But that might just be a programming error instead). * * @param cacheable The cacheable thing that gets updated after each render. * @return a mutable ref object containing the current cache. */ function useRefCache(cacheable) { var ref = React.useRef(cacheable); React.useEffect(function () { ref.current = cacheable; }); return ref; } /** * This hook will create a performant scroll listener by enabling passive events * if it's supported by the browser and delegating the event as needed. */ function useScrollListener(_a) { var _b = _a.enabled, enabled = _b === void 0 ? true : _b, onScroll = _a.onScroll, element = _a.element, _c = _a.options, options = _c === void 0 ? isSupported ? { passive: true } : false : _c; var callback = useRefCache(onScroll); React.useEffect(function () { if (!enabled) { return; } var eventHandler = delegateEvent("scroll", element || window, true, options); var handler = function (event) { return callback.current(event); }; eventHandler.add(handler); return function () { eventHandler.remove(handler); }; // disabled since useRefCache // eslint-disable-next-line react-hooks/exhaustive-deps }, [enabled, element, options]); } /** * This is a simple component wrapper for the `useScrollListener` hook. */ function ScrollListener(props) { useScrollListener(props); return null; } /** * This hook will apply the current mode class name to the `document.body` so * that the specific mode style mixins work as expected. * * @private */ function useModeClassName(mode) { React.useEffect(function () { var className = "rmd-utils--" + mode; document.body.classList.add(className); return function () { document.body.classList.remove(className); }; }, [mode]); } var DEFAULT_TOUCH_TIMEOUT = 1200; /** * This is a small hook that is used to determine if the app is currently being * used by a touch device or not. All this really does is switch between * mousemove and touchstart events to determine which mode you are in. This * also tracks the `contextmenu` appearance since long touches can trigger the * context menu on mobile devices. When the context menu appears after a touch, * the mode will still be considered "touch" instead of swapping to mouse. * * @param touchTimeout This is the amount of time that can occur between a * touchstart and mousemove event but still be considered part of a "touch" user * mode. This should probably be kept at the default value, but if the touch * mode isn't updating as you would expect, you can try increasing or decreasing * this value until it does. * @return true if the app is in touch mode. * @private */ function useTouchDetection(touchTimeout) { if (touchTimeout === void 0) { touchTimeout = DEFAULT_TOUCH_TIMEOUT; } var _a = React.useState(0), lastTouchTime = _a[0], setTouchTime = _a[1]; var touchRef = useRefCache(lastTouchTime); var contextMenuRef = React.useRef(false); var updateTouchTime = React.useCallback(function () { setTouchTime(Date.now()); contextMenuRef.current = false; }, []); var resetTouchTime = React.useCallback(function () { var lastTouchTime = touchRef.current; if (contextMenuRef.current || Date.now() - lastTouchTime < touchTimeout) { contextMenuRef.current = false; return; } setTouchTime(0); // disabled since useRefCache for touchRef // eslint-disable-next-line react-hooks/exhaustive-deps }, [touchTimeout]); React.useEffect(function () { window.addEventListener("touchstart", updateTouchTime, true); return function () { window.removeEventListener("touchstart", updateTouchTime, true); }; }, [updateTouchTime]); React.useEffect(function () { if (lastTouchTime === 0) { contextMenuRef.current = false; return; } var updateContextMenu = function () { contextMenuRef.current = true; }; window.addEventListener("mousemove", resetTouchTime, true); window.addEventListener("contextmenu", updateContextMenu, true); return function () { window.removeEventListener("mousemove", resetTouchTime, true); window.removeEventListener("contextmenu", updateContextMenu, true); }; }, [lastTouchTime, resetTouchTime]); return lastTouchTime !== 0; } /** * This hooks provides an easy way to toggle a boolean flag for React * components. The main use case for this will be toggling the visibility of * something. All the provided actions are guaranteed to never change. * * @param defaultToggled Boolean if the visibility should be enabled first * render. * @return an array containing the toggled state, an enable function, a disable * function, a toggle function, and then a manual set toggle function. */ function useToggle(defaultToggled) { var _a = React.useState(defaultToggled), toggled = _a[0], setToggled = _a[1]; var previous = useRefCache(toggled); var enable = React.useCallback(function () { if (!previous.current) { setToggled(true); } // disabled since useRefCache // eslint-disable-next-line react-hooks/exhaustive-deps }, []); var disable = React.useCallback(function () { if (previous.current) { setToggled(false); } // disabled since useRefCache // eslint-disable-next-line react-hooks/exhaustive-deps }, []); var toggle = React.useCallback(function () { setToggled(function (prevVisible) { return !prevVisible; }); }, []); return [toggled, enable, disable, toggle, setToggled]; } /** * A small hook for checking if the app is currently being interacted with by a * keyboard. * * @return true if the app is in keyboard mode * @private */ function useKeyboardDetection() { var _a = useToggle(false), enabled = _a[0], enable = _a[1], disable = _a[2]; React.useEffect(function () { if (enabled) { return; } window.addEventListener("keydown", enable, true); return function () { window.removeEventListener("keydown", enable, true); }; }, [enabled, enable]); React.useEffect(function () { if (!enabled) { return; } window.addEventListener("mousedown", disable, true); window.addEventListener("touchstart", disable, true); return function () { window.removeEventListener("mousedown", disable, true); window.removeEventListener("touchstart", disable, true); }; }, [enabled, disable]); return enabled; } /** * This hook combines the touch and keyboard detection hooks and returns a * string of the current interaction mode of the app/user. * * @private */ function useModeDetection() { var touch = useTouchDetection(); var keyboard = useKeyboardDetection(); if (touch) { return "touch"; } if (keyboard) { return "keyboard"; } return "mouse"; } var InteractionMode = React.createContext("mouse"); var ParentContext = React.createContext(false); /** * Gets the current interaction mode of the user. */ function useUserInteractionMode() { return React.useContext(InteractionMode); } /** * Checks if the provided user interaction mode matches the current interaction * mode within the app. * * @param mode The mode to check against. * @return true if the mode matches. */ function useIsUserInteractionMode(mode) { return useUserInteractionMode() === mode; } /** * A component that should be mounted once in your app near the top of the tree * to determine the current interaction mode for your app. */ function InteractionModeListener(_a) { var children = _a.children; if (React.useContext(ParentContext)) { throw new Error("Nested `InteractionModeListener` components"); } var mode = useModeDetection(); useModeClassName(mode); return (React__default.createElement(InteractionMode.Provider, { value: mode }, React__default.createElement(ParentContext.Provider, { value: true }, children))); } { try { var PropTypes$2 = require("prop-types"); InteractionModeListener.propTypes = { children: PropTypes$2.node.isRequired, }; } catch (e) { } } function modify(base, modifier) { if (!modifier) { return base; } var hasOwn = Object.prototype.hasOwnProperty; return Object.keys(modifier).reduce(function (s, mod) { if (hasOwn.call(modifier, mod) && modifier[mod]) { s = s + " " + base + "--" + mod; } return s; }, base); } /** * Applies the BEM styled class name to an element. * * @see https://en.bem.info/methodology/css/ * @param base The base class to use * @return a function to call that generates the full class name */ function bem(base) { { if (!base) { throw new Error("bem requires a base block class but none were provided."); } } /** * Creates the full class name from the base block name. This can be called * without any arguments which will just return the base block name (kind of * worthless), or you can provide a child element name and modifiers. * * @param elementOrModifier This is either the child element name or an object * of modifiers to apply. This **must** be a string if the second argument is * provided. * @param modifier Any optional modifiers to apply to the block and optional * element. * @return the full class name */ return function block(elementOrModifier, modifier) { { if (typeof elementOrModifier !== "string" && modifier) { throw new TypeError("bem does not support having two modifier arguments."); } } if (!elementOrModifier) { return base; } if (typeof elementOrModifier !== "string") { return modify(base, elementOrModifier); } return modify(base + "__" + elementOrModifier, modifier); }; } var DEFAULT_DESKTOP_MIN_WIDTH = 1025 / 16 + "em"; var DEFAULT_TABLET_MIN_WIDTH = 768 / 16 + "em"; var DEFAULT_TABLET_MAX_WIDTH = 1024 / 16 + "em"; var DEFAULT_PHONE_MAX_WIDTH = 767 / 16 + "em"; var DEFAULT_DESKTOP_LARGE_MIN_WIDTH = 1280 / 16 + "em"; /** * A helper hook that is used to create a memoized media query tester for * `window.matchMedia`. * * Note: This is a **client side only** hook as it requires the `window` to * attach a resize event listener to. * * @param query The media query to use * @param defaultValue The default value for if this media query matches. When * this is `undefined`, it will default to `false` unless the `window` is * defined and the `checkImmediately` param was not set to `false`. Otherwise, * it will check the media query matches on mount and use that value. * @param disabled Boolean if the media query checking should be disabled. * @param checkImmediately Boolean if the media query should be checked * immediately on mount. When omittied, it will default to checking when the * window is defined. * @return true if the media query is a match. */ function useMediaQuery(query, defaultValue, disabled, checkImmediately) { if (disabled === void 0) { disabled = false; } if (checkImmediately === void 0) { checkImmediately = typeof window !== "undefined"; } var _a = React.useState(function () { if (typeof defaultValue !== "undefined") { return defaultValue; } if (!disabled && checkImmediately && typeof window !== "undefined") { return window.matchMedia(query).matches; } return false; }), matches = _a[0], setMatches = _a[1]; React.useEffect(function () { if (typeof window === "undefined" || disabled) { return; } var mq = window.matchMedia(query); var updater = function (_a) { var matches = _a.matches; return setMatches(matches); }; mq.addListener(updater); if (mq.matches !== matches) { setMatches(mq.matches); } return function () { return mq.removeListener(updater); }; }, [disabled, matches, query]); return matches; } /** * This is a small helper that will create a media query block based on the * provided width value. */ var toWidthPart = function (v, prefix) { var type = typeof v; if (type === "undefined") { return ""; } var value = type === "number" ? v + "px" : v; return "(" + prefix + "-width: " + value + ")"; }; /** * This is a simple hoo that will create a memoized media query string with the * provided min anx max values. * * @param min An optional min value to use * @param max An optional max value to use * @return a boolean if the current media query is a match. */ function useWidthMediaQuery(_a) { var min = _a.min, max = _a.max; var query = React.useMemo(function () { var parts = [toWidthPart(min, "min"), toWidthPart(max, "max")] .filter(Boolean) .join(" and "); return "screen and " + parts; }, [min, max]); return useMediaQuery(query); } /** * An extremely simple "pollyfill" for the `window.screen.orientation` just for * the `type` value that is required for the `useOrientation` hook. */ var getOrientationType = function () { var _a; var screenOrientation = (_a = window.screen.orientation) === null || _a === void 0 ? void 0 : _a.type; if (typeof screenOrientation === "string") { return screenOrientation; } var _b = window.screen, availHeight = _b.availHeight, availWidth = _b.availWidth; return availHeight > availWidth ? "portrait-primary" : "landscape-primary"; }; /** * This media query is used to determine the current orientation of the app * based on the `window.screen.orientation.type`. This will always be * `"landscape-primary"` server side unless a default value is provided. * * @param defaultValue an optional default value to use. When this is omitted, * it will default to `"landscape-primary"` unless the `window` is defined. If * the `window` is defined, it will immediately check the orientation type on * mount. * @return the orientation type value. */ function useOrientation(defaultValue) { var _a = React.useState(function () { if (defaultValue) { return defaultValue; } if (typeof window !== "undefined") { return getOrientationType(); } return "landscape-primary"; }), value = _a[0], setValue = _a[1]; React.useEffect(function () { if (typeof window === "undefined") { return; } var handler = function () { setValue(getOrientationType()); }; window.addEventListener("orientationchange", handler); return function () { return window.removeEventListener("orientationchange", handler); }; }, []); return value; } var DEFAULT_APP_SIZE = { isPhone: false, isTablet: false, isDesktop: true, isLargeDesktop: false, isLandscape: true, }; /** * This hook is used to determine the current application size based on the * provided query sizes. When you want to render your app server side, you will * need to provide a custom `defaultSize` that implements your logic to * determine the type of device requesting a page. Once the app has been * rendered in the DOM, this hook will attach event listeners to automatically * update the app size when the page is resized. * * @private */ function useAppSizeMedia(_a) { var _b = _a === void 0 ? {} : _a, _c = _b.phoneMaxWidth, phoneMaxWidth = _c === void 0 ? DEFAULT_PHONE_MAX_WIDTH : _c, _d = _b.tabletMinWidth, tabletMinWidth = _d === void 0 ? DEFAULT_TABLET_MIN_WIDTH : _d, _e = _b.tabletMaxWidth, tabletMaxWidth = _e === void 0 ? DEFAULT_TABLET_MAX_WIDTH : _e, _f = _b.desktopMinWidth, desktopMinWidth = _f === void 0 ? DEFAULT_DESKTOP_MIN_WIDTH : _f, _g = _b.desktopLargeMinWidth, desktopLargeMinWidth = _g === void 0 ? DEFAULT_DESKTOP_LARGE_MIN_WIDTH : _g, _h = _b.defaultSize, defaultSize = _h === void 0 ? DEFAULT_APP_SIZE : _h; /* eslint-disable react-hooks/rules-of-hooks */ // disabled since this is conditionally applied for SSR if (typeof window === "undefined") { return defaultSize; } var matchesDesktop = useWidthMediaQuery({ min: desktopMinWidth }); var matchesLargeDesktop = useWidthMediaQuery({ min: desktopLargeMinWidth }); var matchesTablet = useWidthMediaQuery({ min: tabletMinWidth, max: tabletMaxWidth, }); var matchesPhone = useWidthMediaQuery({ max: phoneMaxWidth }); var isDesktop = matchesDesktop; var isTablet = !matchesDesktop && matchesTablet; var isPhone = !isTablet && !isDesktop && matchesPhone; var isLandscape = useOrientation().includes("landscape"); var isLargeDesktop = matchesLargeDesktop; var _j = React.useState(defaultSize), appSize = _j[0], setAppSize = _j[1]; React.useEffect(function () { if (appSize.isPhone === isPhone && appSize.isTablet === isTablet && appSize.isDesktop === isDesktop && appSize.isLargeDesktop === isLargeDesktop && appSize.isLandscape === isLandscape) { return; } // for some reason, it's sometimes possible to fail every single matchMedia // value when you are resizing the browser a lot. this is an "invalid" event // so skip it. It normally happens between 760px-768px if (!isPhone && !isTablet && !isDesktop && !isLargeDesktop) { return; } setAppSize({ isPhone: isPhone, isTablet: isTablet, isDesktop: isDesktop, isLargeDesktop: isLargeDesktop, isLandscape: isLandscape }); }, [isPhone, isTablet, isDesktop, isLargeDesktop, isLandscape, appSize]); return appSize; } var __assign$1 = (undefined && undefined.__assign) || function () { __assign$1 = Object.assign || function(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$1.apply(this, arguments); }; var __rest = (undefined && undefined.__rest) || function (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 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; /** * @private */ var AppSizeContext = React.createContext(__assign$1(__assign$1({}, DEFAULT_APP_SIZE), { __initialized: false })); /** * Gets the current app size. * * @return the current AppSize */ function useAppSize() { var _a = React.useContext(AppSizeContext), __initialized = _a.__initialized, context = __rest(_a, ["__initialized"]); if (!__initialized) { throw new Error("Attempted to use the current `AppSizeContext` without mounting the `AppSizeListener` component beforehand."); } return context; } var __assign$2 = (undefined && undefined.__assign) || function () { __assign$2 = Object.assign || function(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$2.apply(this, arguments); }; var __rest$1 = (undefined && undefined.__rest) || function (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 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var block = bem("rmd-grid"); var GridCell = React.forwardRef(function GridCell(_a, ref) { var _b, _c, _d, _e, _f, _g, _h; var style = _a.style, className = _a.className, clone = _a.clone, children = _a.children, propColSpan = _a.colSpan, propColStart = _a.colStart, propColEnd = _a.colEnd, propRowSpan = _a.rowSpan, propRowStart = _a.rowStart, propRowEnd = _a.rowEnd, phone = _a.phone, tablet = _a.tablet, desktop = _a.desktop, largeDesktop = _a.largeDesktop, props = __rest$1(_a, ["style", "className", "clone", "children", "colSpan", "colStart", "colEnd", "rowSpan", "rowStart", "rowEnd", "phone", "tablet", "desktop", "largeDesktop"]); var _j = useAppSize(), isPhone = _j.isPhone, isTablet = _j.isTablet, isDesktop = _j.isDesktop, isLargeDesktop = _j.isLargeDesktop; var colSpan = propColSpan; var colStart = propColStart; var colEnd = propColEnd; var rowSpan = propRowSpan; var rowStart = propRowStart; var rowEnd = propRowEnd; var media = (isPhone && phone) || (isTablet && tablet) || (isDesktop && desktop) || (isLargeDesktop && largeDesktop); if (media) { (_b = media.rowSpan, rowSpan = _b === void 0 ? propRowSpan : _b, _c = media.rowStart, rowStart = _c === void 0 ? propRowStart : _c, _d = media.rowEnd, rowEnd = _d === void 0 ? propRowEnd : _d, _e = media.colSpan, colSpan = _e === void 0 ? propColSpan : _e, _f = media.colStart, colStart = _f === void 0 ? propColStart : _f, _g = media.colEnd, colEnd = _g === void 0 ? propColEnd : _g); } var cellStyle = __assign$2({ gridColumnStart: colStart, gridColumnEnd: colEnd, gridRowStart: rowStart, gridRowEnd: rowSpan ? "span " + rowSpan : rowEnd }, style); var cellClassName = classnames(block("cell", (_h = {}, _h["" + colSpan] = colSpan, _h)), className); if (clone && React.isValidElement(children)) { var child = React.Children.only(children); return React.cloneElement(child, { style: __assign$2(__assign$2({}, child.props.style), cellStyle), className: classnames(cellClassName, child.props.className), }); } return (React__default.createElement("div", __assign$2({}, props, { ref: ref, style: cellStyle, className: cellClassName }), children)); }); { try { var PropTypes$3 = require("prop-types"); var gridCSSProperties = PropTypes$3.shape({ rowSpan: PropTypes$3.number, rowStart: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), rowEnd: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), colSpan: PropTypes$3.number, colStart: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), colEnd: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), }); GridCell.propTypes = { style: PropTypes$3.object, className: PropTypes$3.string, clone: PropTypes$3.bool, rowSpan: PropTypes$3.number, rowStart: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), rowEnd: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), colSpan: PropTypes$3.number, colStart: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), colEnd: PropTypes$3.oneOfType([PropTypes$3.number, PropTypes$3.string]), phone: gridCSSProperties, tablet: gridCSSProperties, desktop: gridCSSProperties, largeDesktop: gridCSSProperties, children: PropTypes$3.node, }; } catch (e) { } } var __assign$3 = (undefined && undefined.__assign) || function () { __assign$3 = Object.assign || function(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$3.apply(this, arguments); }; var __rest$2 = (undefined && undefined.__rest) || function (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 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; /** * This CSS Variable allows you to override the number of columns that should be * displayed in the grid. This is automatically updated with media queries with * the default grid implementation, but is used here to add additional * inline-style overrides. * * @private */ var GRID_COLUMNS_VAR = "--rmd-grid-cols"; /** * This CSS Variable allows you to override the gutter (grid-gap) between each * cell in the grid. * * @private */ var GRID_GUTTER_VAR = "--rmd-grid-gutter"; var block$1 = bem("rmd-grid"); /** * The grid component is generally used for a base layout in your app to provide * nice padding and spacing between each item. * * Note: This component relies on the `AppSizeListener` as a parent component to * work and will throw an error if it does not exist as a parent. */ var Grid = React.forwardRef(function Grid(_a, ref) { var _b; var style = _a.style, className = _a.className, children = _a.children, _c = _a.clone, clone = _c === void 0 ? false : _c, _d = _a.wrapOnly, wrapOnly = _d === void 0 ? false : _d, columns = _a.columns, phoneColumns = _a.phoneColumns, tabletColumns = _a.tabletColumns, desktopColumns = _a.desktopColumns, largeDesktopColumns = _a.largeDesktopColumns, padding = _a.padding, gutter = _a.gutter, minCellWidth = _a.minCellWidth, props = __rest$2(_a, ["style", "className", "children", "clone", "wrapOnly", "columns", "phoneColumns", "tabletColumns", "desktopColumns", "largeDesktopColumns", "padding", "gutter", "minCellWidth"]); var _e = useAppSize(), isPhone = _e.isPhone, isTablet = _e.isTablet, isDesktop = _e.isDesktop, isLargeDesktop = _e.isLargeDesktop; var mergedStyle = __assign$3(__assign$3({ padding: (padding !== 0 && padding) || undefined, gridTemplateColumns: minCellWidth ? "repeat(auto-fill, minmax(" + minCellWidth + ", 1fr))" : undefined }, style), (_b = {}, _b[GRID_COLUMNS_VAR] = (isPhone && phoneColumns) || (isTablet && tabletColumns) || (isLargeDesktop && largeDesktopColumns) || (isDesktop && desktopColumns) || columns, _b[GRID_GUTTER_VAR] = gutter, _b)); var content = children; if (clone || wrapOnly) { content = React.Children.map(children, function (child) { return child && React__default.createElement(GridCell, { clone: clone }, child); }); } return (React__default.createElement("div", __assign$3({}, props, { ref: ref, style: mergedStyle, className: classnames(block$1({ "no-padding": padding === 0 }), className) }), content)); }); { try { var PropTypes$4 = require("prop-types"); Grid.propTypes = { style: PropTypes$4.object, className: PropTypes$4.string, clone: PropTypes$4.bool, wrapOnly: PropTypes$4.bool, columns: PropTypes$4.number, phoneColumns: PropTypes$4.number, tabletColumns: PropTypes$4.number, desktopColumns: PropTypes$4.number, largeDesktopColumns: PropTypes$4.number, padding: PropTypes$4.oneOfType([PropTypes$4.number, PropTypes$4.string]), gutter: PropTypes$4.string, children: PropTypes$4.node, minCellWidth: PropTypes$4.string, }; } catch (e) { } } /** * A small utility function that allows me to apply a passed in ref along with * my own custom ref logic. * * @param instance The DOM Node instance * @param ref The prop ref */ function applyRef(instance, ref) { if (!ref) { return; } if (typeof ref === "function") { ref(instance); } else if (typeof ref === "object") { ref.current = instance; } } /** * A collection of shims that provide minimal functionality of the ES6 collections. * * These implementations are not meant to be used outside of the ResizeObserver * modules as they cover only a limited range of use cases. */ /* eslint-disable require-jsdoc, valid-jsdoc */ var MapShim = (function () { if (typeof Map !== 'undefined') { return Map; } /** * Returns index in provided array that matches the specified key. * * @param {Array<Array>} arr * @param {*} key * @returns {number} */ function getIndex(arr, key) { var result = -1; arr.some(function (entry, index) { if (entry[0] === key) { result = index; return true; } return false; }); return result; } return /** @class */ (function () { function class_1() { this.__entries__ = []; } Object.defineProperty(class_1.prototype, "size", { /** * @returns {boolean} */ get: function () { return this.__entries__.length; }, enumerable: true, configurable: true }); /** * @param {*} key * @returns {*} */ class_1.prototype.get = function (key) { var index = getIndex(this.__entries__, key); var entry = this.__entries__[index]; return entry && entry[1]; }; /** * @param {*} key * @param {*} value * @returns {void} */ class_1.prototype.set = function (key, value) { var index = getIndex(this.__entries__, key); if (~index) { this.__entries__[index][1] = value; } else { this.__entries__.push([key, value]); } }; /** * @param {*} key