UNPKG

react-md

Version:

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

1,419 lines (1,356 loc) 1.08 MB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('react-dom'), require('crypto')) : typeof define === 'function' && define.amd ? define(['exports', 'react', 'react-dom', 'crypto'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactMD = {}, global.React, global.ReactDOM, global.crypto)); })(this, (function (exports, require$$1, reactDom, crypto) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1); var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto); var jsxRuntime = {exports: {}}; var reactJsxRuntime_production_min = {}; /* object-assign (c) Sindre Sorhus @license MIT */ var objectAssign; var hasRequiredObjectAssign; function requireObjectAssign () { if (hasRequiredObjectAssign) return objectAssign; hasRequiredObjectAssign = 1; /* eslint-disable no-unused-vars */ var getOwnPropertySymbols = Object.getOwnPropertySymbols; var hasOwnProperty = Object.prototype.hasOwnProperty; var propIsEnumerable = Object.prototype.propertyIsEnumerable; function toObject(val) { if (val === null || val === undefined) { throw new TypeError('Object.assign cannot be called with null or undefined'); } return Object(val); } function shouldUseNative() { try { if (!Object.assign) { return false; } // Detect buggy property enumeration order in older V8 versions. // https://bugs.chromium.org/p/v8/issues/detail?id=4118 var test1 = new String('abc'); // eslint-disable-line no-new-wrappers test1[5] = 'de'; if (Object.getOwnPropertyNames(test1)[0] === '5') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test2 = {}; for (var i = 0; i < 10; i++) { test2['_' + String.fromCharCode(i)] = i; } var order2 = Object.getOwnPropertyNames(test2).map(function (n) { return test2[n]; }); if (order2.join('') !== '0123456789') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test3 = {}; 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { test3[letter] = letter; }); if (Object.keys(Object.assign({}, test3)).join('') !== 'abcdefghijklmnopqrst') { return false; } return true; } catch (err) { // We don't expect any of the above to throw, but better to be safe. return false; } } objectAssign = shouldUseNative() ? Object.assign : function (target, source) { var from; var to = toObject(target); var symbols; for (var s = 1; s < arguments.length; s++) { from = Object(arguments[s]); for (var key in from) { if (hasOwnProperty.call(from, key)) { to[key] = from[key]; } } if (getOwnPropertySymbols) { symbols = getOwnPropertySymbols(from); for (var i = 0; i < symbols.length; i++) { if (propIsEnumerable.call(from, symbols[i])) { to[symbols[i]] = from[symbols[i]]; } } } } return to; }; return objectAssign; } /** @license React v17.0.2 * react-jsx-runtime.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredReactJsxRuntime_production_min; function requireReactJsxRuntime_production_min () { if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min; hasRequiredReactJsxRuntime_production_min = 1; requireObjectAssign();var f=require$$1__default["default"],g=60103;reactJsxRuntime_production_min.Fragment=60107;if("function"===typeof Symbol&&Symbol.for){var h=Symbol.for;g=h("react.element");reactJsxRuntime_production_min.Fragment=h("react.fragment");}var m=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,n=Object.prototype.hasOwnProperty,p={key:!0,ref:!0,__self:!0,__source:!0}; function q(c,a,k){var b,d={},e=null,l=null;void 0!==k&&(e=""+k);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(l=a.ref);for(b in a)n.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return {$$typeof:g,type:c,key:e,ref:l,props:d,_owner:m.current}}reactJsxRuntime_production_min.jsx=q;reactJsxRuntime_production_min.jsxs=q; return reactJsxRuntime_production_min; } (function (module) { { module.exports = requireReactJsxRuntime_production_min(); } } (jsxRuntime)); var classnames = {exports: {}}; /*! Copyright (c) 2018 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ (function (module) { /* 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)) { if (arg.length) { var inner = classNames.apply(null, arg); if (inner) { classes.push(inner); } } } else if (argType === 'object') { if (arg.toString === Object.prototype.toString) { for (var key in arg) { if (hasOwn.call(arg, key) && arg[key]) { classes.push(key); } } } else { classes.push(arg.toString()); } } } return classes.join(' '); } if (module.exports) { classNames.default = classNames; module.exports = classNames; } else { window.classNames = classNames; } }()); } (classnames)); var cn = classnames.exports; /** * 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 * @returns 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; } var __read$1c = (undefined && undefined.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; /** * 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 = __read$1c(require$$1.useState(null), 2), 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 require$$1.useEffect(function () { var nextContainer = getContainer(into, intoId); if (container !== nextContainer) { setContainer(nextContainer); } }, [into, intoId, container]); if (!container) { return null; } return reactDom.createPortal(children, container); } var __assign$3e = (undefined && undefined.__assign) || function () { __assign$3e = 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$3e.apply(this, arguments); }; /** * 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 jsxRuntime.exports.jsx(jsxRuntime.exports.Fragment, { children: children }); } return (jsxRuntime.exports.jsx(Portal, __assign$3e({ into: portalInto, intoId: portalIntoId }, { children: children }))); } /** * 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; } } 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 = "".concat(s, " ").concat(base, "--").concat(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 * @returns a function to call that generates the full class name */ function bem(base) { /** * 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. * @returns the full class name */ return function block(elementOrModifier, modifier) { if (!elementOrModifier) { return base; } if (typeof elementOrModifier !== "string") { return modify(base, elementOrModifier); } return modify("".concat(base, "__").concat(elementOrModifier), modifier); }; } 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 detecting color * contrast ratios and other stuff. * * @param hex - The hex string to convert * @returns an object containing the r, g, b values for the color. */ function hexToRGB(hex) { hex = hex.replace(SHORTHAND_REGEX, function (_m, r, g, b) { return "".concat(r).concat(r).concat(g).concat(g).concat(b).concat(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 __read$1b = (undefined && undefined.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; 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 * @internal */ 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 * @internal */ function getLuminance(color) { var _a = __read$1b(hexToRGB(color), 3), 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. * @returns 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. * @returns 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; } /** * Typeguard that will check if the provided checkable thing is a * MutableRefObject or just an HTMLElement. * * @internal */ var isMutableRefObject = function (thing) { return !!thing && typeof thing.current !== "undefined"; }; /** * Gets the HTMLElement or null from the checkable thing. * * @internal */ var getElement$1 = function (thing) { if (isMutableRefObject(thing)) { return thing.current; } return thing; }; /** * Checks if a container element contains another element as a child while * allowing for nulls or a MutableRefObject of HTMLElement or null. Mostly just * a convenience function that should be used internally. * * @param container - The element to use as a container element. This can be an * HTMLElement, null, or a MutableRefObject of HTMLElement or null. * @param child - The element that might be a child of the container * element. This can be an HTMLElement, null, or a MutableRefObject of * HTMLElement or null. * @returns True if the container contains the child element and both the * container and child are valid HTMLElements (not null). * @internal */ function containsElement(container, child) { container = getElement$1(container); child = getElement$1(child); return !!(container && child && container.contains(child)); } var __assign$3d = (undefined && undefined.__assign) || function () { __assign$3d = 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$3d.apply(this, arguments); }; /** * This is normally used for reusable shareable configs that have multiple * shared options with default values that should be used. This basically works * just like `defaultProps` in react. * * @internal * @param optional - The original object that has the optional/omitted values * @param required - The required default values that should be used to fill the * optional object with * @returns a new object with both the values of the optional and required * objects but use the optional values if they were defined. */ function defaults(optional, required) { var keys = Object.keys(required); return keys.reduce(function (result, key) { if (typeof result[key] === "undefined") { result[key] = required[key]; } return result; }, __assign$3d({}, optional)); } var __assign$3c = (undefined && undefined.__assign) || function () { __assign$3c = 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$3c.apply(this, arguments); }; var __rest$2B = (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 __read$1a = (undefined && undefined.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var context$g = require$$1.createContext({ root: true, dir: "ltr", toggleDir: function () { throw new Error("Tried to toggle the current writing direction without initializing the `Dir` component."); }, }); var Provider$9 = context$g.Provider; /** * Gets the writing direction context which provides access to the current `dir` * and a `toggleDir` function. * * @remarks \@since 2.3.0 */ function useDir() { var _a = require$$1.useContext(context$g); _a.root; var current = __rest$2B(_a, ["root"]); return current; } /** * @remarks \@since 2.3.0 */ var DEFAULT_DIR = function () { var dir = "ltr"; if (typeof document !== "undefined") { var rootDir = document.documentElement.getAttribute("dir"); dir = rootDir === "rtl" ? "rtl" : "ltr"; } return dir; }; /** * The `Dir` component is used to handle the current writing direction within * your app as well as conditionally updating the writing direction for small * sections in your app. When this component is used for the first time near the * root of your React component tree, the current direction will be applied to * the root `<html>` element. Otherwise the current dir will be cloned into the * child element so it can be passed as a prop. * * ```tsx * // html element will be updated to have `dir="ltr"` * ReactDOM.render(<Dir><App /></Dir>, root) * ``` * * ```tsx * // html element will be updated to have `dir="rtl"` while the `<span>` will * // now be `<span dir="ltr">` * ReactDOM.render( * <Dir defaultDir="rtl"> * <Some> * <Other> * <Components> * <Dir defaultDir="ltr"> * <span>Content</span> * </Dir> * </Components> * </Other> * </Some> * </Dir>, * root * ); * ``` * * Note: Since the `dir` is cloned into the child element, you need to make sure * that the child is either a DOM element or the `dir` prop is passed from your * custom component. * * @remarks \@since 2.3.0 */ function Dir(_a) { var children = _a.children, _b = _a.defaultDir, defaultDir = _b === void 0 ? DEFAULT_DIR : _b; var root = require$$1.useContext(context$g).root; var _c = __read$1a(require$$1.useState(defaultDir), 2), dir = _c[0], setDir = _c[1]; require$$1.useEffect(function () { if (!root || typeof document === "undefined") { return; } document.documentElement.setAttribute("dir", dir); return function () { document.documentElement.removeAttribute("dir"); }; }, [dir, root]); var toggleDir = require$$1.useCallback(function () { setDir(function (prevDir) { return (prevDir === "ltr" ? "rtl" : "ltr"); }); }, []); var value = require$$1.useMemo(function () { return ({ root: false, dir: dir, toggleDir: toggleDir }); }, [dir, toggleDir]); var child = require$$1.Children.only(children); if (!root) { child = require$$1.cloneElement(child, { dir: dir }); } return jsxRuntime.exports.jsx(Provider$9, __assign$3c({ value: value }, { children: child })); } /** * 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. * @returns a mutable ref object containing the current cache. */ function useRefCache(cacheable) { var ref = require$$1.useRef(cacheable); require$$1.useEffect(function () { ref.current = cacheable; }); return ref; } 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 list 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. * @returns 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; } /** * * @remarks \@since 5.0.0 Moved the `AddEventListenerOptions` to no longer be * part of an `options` object. */ function useScrollListener(_a) { var once = _a.once, _b = _a.passive, passive = _b === void 0 ? true : _b, signal = _a.signal, capture = _a.capture, _c = _a.enabled, enabled = _c === void 0 ? true : _c, onScroll = _a.onScroll; var scrollHandlerRef = useRefCache(onScroll); require$$1.useEffect(function () { if (!enabled) { return; } var eventHandler = delegateEvent("scroll", window, true, { once: once, passive: passive, signal: signal, capture: capture, }); var scrollHandler = scrollHandlerRef.current; eventHandler.add(scrollHandler); return function () { eventHandler.remove(scrollHandler); }; }, [capture, enabled, once, passive, scrollHandlerRef, signal]); } /** * This is a simple component wrapper for the `useScrollListener` hook. */ function ScrollListener(props) { useScrollListener(props); return null; } /** * Gets the current percentage based on the min, max, and current value. * * @returns the percentage that the `value` is between the `min` and `max` * values. * @internal * @remarks \@since 4.0.1 uses an object for options instead of multiple * arguments. */ function getPercentage(_a) { var min = _a.min, max = _a.max, value = _a.value, _b = _a.validate, validate = _b === void 0 ? true : _b; if (validate) { if (min >= max) { throw new RangeError("A range must have the min value less than the max value"); } if (value > max || value < min) { throw new RangeError("A value must be between the min and max values"); } } var range = max - min; var start = value - min; var percentage = start / range; return Math.max(0, Math.min(Math.abs(percentage), 1)); } /** * The amount of time a user must hover an element before the temporary element * becomes visible. * * @remarks \@since 2.8.0 */ var DEFAULT_HOVER_MODE_VISIBLE_IN_TIME = 1000; /** * The amount of time the user must no longer hover any element attached to the * {@link HoverModeProvider} to disable the hover mode. * * @remarks \@since 2.8.0 */ var DEFAULT_HOVER_MODE_DEACTIVATION_TIME = 1000; /** * The amount of time the user must not hover any element attached to the same * instance of the {@link useHoverMode} hook when the using the sticky mode. * * @remarks \@since 5.0.0 This was named the * `DEFAULT_HOVER_MODE_STICKY_EXIT_TIME` before. */ var DEFAULT_HOVER_MODE_EXIT_TIME = 300; /** * A simple hook that only triggers the callback when a component is unmounted. * This will make sure that the callback function does not have a stale closure * by the time the component unmounts as well. * * @example * Simple Example * ```ts * useOnUnmount(() => { * console.log('Component is unmounted.'); * }); * * const [data, setData] = useState(initialData); * useOnUnmount(() => { * API.saveCurrentData(data); * }); * * // update data * ``` * * @remarks \@since 2.7.1 * @param callback - the function to call when the component unmounts. */ function useOnUnmount(callback) { var ref = require$$1.useRef(callback); require$$1.useEffect(function () { ref.current = callback; }); return require$$1.useEffect(function () { return function () { return ref.current(); }; }, []); } /** @internal */ var noop$c = function () { // do nothing }; /** @internal */ var context$f = require$$1.createContext({ visibleInTime: DEFAULT_HOVER_MODE_VISIBLE_IN_TIME, enableHoverMode: noop$c, disableHoverMode: noop$c, startDisableTimer: noop$c, }); /** * @internal * @remarks \@since 2.8.0 */ var HoverModeContextProvider = context$f.Provider; /** * Gets the {@link HoverModeContext} which allows you implement hover mode * functionality for any component. This is mostly an internal hook since * everything you need will be available in the {@link useHoverMode} hook. * * @internal * @remarks \@since 2.8.0 * @returns The {@link HoverModeContext} */ function useHoverModeContext() { return require$$1.useContext(context$f); } var __assign$3b = (undefined && undefined.__assign) || function () { __assign$3b = 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$3b.apply(this, arguments); }; var __read$19 = (undefined && undefined.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; /** * This component should normally be mounted near the root of your app to enable * hover mode for child components. However, it can also be used at other levels * if hover mode functionality should not carry over between two different parts * of the screen. * * @example * Separating Hover Mode * ```tsx * export default function Example(): ReactElement { * return ( * <> * <HoverModeProvider> * <HeaderActions /> * </HoverModeProvider> * <HoverModeProvider> * <MainContent /> * </HoverModeProvider> * </> * ); * } * ``` * * @remarks \@since 2.8.0 */ function HoverModeProvider(_a) { var children = _a.children, _b = _a.disabled, disabled = _b === void 0 ? false : _b, _c = _a.defaultVisibleInTime, defaultVisibleInTime = _c === void 0 ? DEFAULT_HOVER_MODE_VISIBLE_IN_TIME : _c, _d = _a.deactivateTime, deactivateTime = _d === void 0 ? DEFAULT_HOVER_MODE_DEACTIVATION_TIME : _d; var _e = __read$19(require$$1.useState(defaultVisibleInTime), 2), visibleInTime = _e[0], setVisibleInTime = _e[1]; var timeoutRef = require$$1.useRef(); var enableHoverMode = require$$1.useCallback(function () { if (disabled) { return; } window.clearTimeout(timeoutRef.current); setVisibleInTime(0); }, [disabled]); var disableHoverMode = require$$1.useCallback(function () { window.clearTimeout(timeoutRef.current); setVisibleInTime(defaultVisibleInTime); }, [defaultVisibleInTime]); var startDisableTimer = require$$1.useCallback(function () { window.clearTimeout(timeoutRef.current); timeoutRef.current = window.setTimeout(function () { setVisibleInTime(defaultVisibleInTime); }, deactivateTime); }, [defaultVisibleInTime, deactivateTime]); require$$1.useEffect(function () { if (disabled) { window.clearTimeout(timeoutRef.current); setVisibleInTime(defaultVisibleInTime); } }, [disabled, defaultVisibleInTime]); useOnUnmount(function () { window.clearTimeout(timeoutRef.current); }); var context = require$$1.useMemo(function () { return ({ visibleInTime: visibleInTime, enableHoverMode: enableHoverMode, disableHoverMode: disableHoverMode, startDisableTimer: startDisableTimer, }); }, [disableHoverMode, enableHoverMode, startDisableTimer, visibleInTime]); return (jsxRuntime.exports.jsx(HoverModeContextProvider, __assign$3b({ value: context }, { children: children }))); } /** * This is copy/pasted from react-redux which has some more information about * this and how to fix "invalid" warnings while running tests. * * @see https://github.com/reduxjs/react-redux/blob/4c907c0870c6b9a136dd69be294c17d1dc63c8f5/src/utils/useIsomorphicLayoutEffect.js */ var useIsomorphicLayoutEffect = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined" ? require$$1.useLayoutEffect : require$$1.useEffect; var __read$18 = (undefined && undefined.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; /** * @internal */ var TOUCH_TIMEOUT = 1200; /** * This hook helps determine the current interaction mode by attaching the * required event listeners to the window. The `mode` will always be defaulted * to `mouse` at first since it has the least possibilities of causing errors * with styles since the mouse-only styles are normally just `:hover` effects. * * ## Switching between modes: * * ### While in `mouse` mode: * * - any `keydown` event will switch to `keyboard` mode * - this does have the side effect of meta keys also causing the switch over, * but it feels fine since it helps show the current focus in the document * as well * - any `touchstart` event will switch to `touch` mode * * ### While in `keyboard` mode: * * - any `mousedown` event will switch to `mouse` mode * - it is perfectly okay to move the mouse while in keyboard mode, but still * want to keep the keyboard styles until the user actually starts clicking * - any `touchstart` event will switch to `touch` mode * * ### While in `touch` mode: * * - any `mousemove` event will switch to `mouse` mode, but **only** if there * hasn't been a `contextmenu` event within the last `1.2s` * - you can really only switch back to `mouse` mode if you are using the * devtools to emulate devices OR using a touch-desktop. I don't know how * common this really is though. * - touching the screen will always fire a `mousemove` event (which is why * the `:hover` styles are normally with `rmd-utils-mouse-only`) and even * after the `contextmenu` event. Normally want to go back to `mouse` mode * when the mouse re-enters the `window` * * Note: It's currently impossible to switch from `touch` to `keyboard` * immediately. You'd first need to switch to `mouse` and then to `keyboard`. I * don't really know of any use-cases other than the weird touch-desktop stuff * and I have no experience using them. * * @internal */ function useInteractionMode() { var _a = __read$18(require$$1.useState("mouse"), 2), mode = _a[0], setMode = _a[1]; var lastTouchTime = require$$1.useRef(0); var isTouchContextMenu = require$$1.useRef(false); useIsomorphicLayoutEffect(function () { var enableMouseMode = function () { return setMode("mouse"); }; var enableKeyboardMode = function () { return setMode("keyboard"); }; var handleTouchStart = function () { lastTouchTime.current = Date.now(); isTouchContextMenu.current = false; setMode("touch"); }; var handleMouseMove = function () { if (isTouchContextMenu.current || Date.now() - lastTouchTime.current < TOUCH_TIMEOUT) { isTouchContextMenu.current = false; return; } enableMouseMode(); }; var handleContextMenu = function () { isTouchContextMenu.current = true; }; var className = "rmd-utils--".concat(mode); document.body.classList.add(className); window.addEventListener("touchstart", handleTouchStart, true); if (mode === "mouse") { window.addEventListener("keydown", enableKeyboardMode, true); } else if (mode === "keyboard") { window.addEventListener("mousedown", enableMouseMode, true); } else { window.addEventListener("mousemove", handleMouseMove, true); window.addEventListener("contextmenu", handleContextMenu, true); } return function () { document.body.classList.remove(className); window.removeEventListener("touchstart", handleTouchStart, true); if (mode === "mouse") { window.removeEventListener("keydown", enableKeyboardMode, true); } else if (mode === "keyboard") { window.removeEventListener("mousedown", enableMouseMode, true); } else { window.removeEventListener("mousemove", handleMouseMove, true); window.removeEventListener("contextmenu", handleContextMenu, true); } }; }, [mode]); return mode; } var __assign$3a = (undefined && undefined.__assign) || function () { __assign$3a = 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$3a.apply(this, arguments); }; /** * @internal */ var modeContext = require$$1.createContext("mouse"); /** * @internal */ var parentContext = require$$1.createContext(false); /** * @internal */ var UserInteractionModeProvider = modeContext.Provider; /** * @internal */ var ParentProvider = parentContext.Provider; /** * Returns the current user interaction mode. * * @returns {@link UserInteractionMode} */ function useUserInteractionMode() { return require$$1.useContext(modeContext); } /** * Example: * * ```ts * const isKeyboard = useIsUserInteractionMode("keyboard"); * // do stuff if keyboard only * ``` * * @param mode - The {@link UserInteractionMode} to check against. * @returns `true` if the current user interaction mode matches the provided * mode. */ function useIsUserInteractionMode(mode) { return useUserInteractionMode() === mode; } /** * This component is used to determine how the user is current interacting with * your app as well as modifying the `document.body`'s `className` with the * current mode. This is what allows the `rmd-utils-phone-only`, * `rmd-utils-keyboard-only`, and `rmd-utils-mouse-only` mixins to work. * * @remarks \@since 2.6.0 Renamed from `InteractionModeListener` * @throws When this component has been mounted multiple times in your app. */ function UserInteractionModeListener(_a) { var children = _a.children; var mode = useInteractionMode(); if (require$$1.useContext(parentContext)) { throw new Error("Mounted multiple `UserInteractionModeListener` components."); } return (jsxRuntime.exports.jsx(UserInteractionModeProvider, __assign$3a({ value: mode }, { children: jsxRuntime.exports.jsx(ParentProvider, __assign$3a({ value: true }, { children: children })) }))); } var __read$17 = (undefined && undefined.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; /** * This hook is used to add the hover mode functionality to any component. * * @example * Displaying a Color Preview when hovering a Hex Code * ```tsx * import type { ReactElement } from "react"; * import { CSSTransition } from "@react-md/transition"; * import { useHoverMode } from "@react-md/utils"; * * interface Props { * value: string; * } * * export default function Color({ value }: Props): ReactElement { * const { visible, onMouseEnter, onMouseLeave } = * useHoverMode({ exitVisibilityDelay: 0 }); * * return ( * <> * <span * onMouseEnter={onMouseEnter} * onMouseLeave={onMouseLeave} * style={{ * // pretend styles * }} * > * {value} * </span> * <CSSTransition * transitionIn={visible} * classNames="opacity-change" * timeout={150} * temporary * > * <span * style={{ * backgroundColor: value, * // other styles * }} * /> * </CSSTransition> * </> * ); * } * ``` * * @example * Sticky Usage with a Fixed Dialog * ```tsx * const { * stuck, * active, * visible, * setVisible, * handlers, * hoverHandlers, * } = useHoverMode(); * const buttonRef = useRef<HTMLButtonElement>(null); * * return ( * <> * <Button {...handlers} ref={buttonRef}> * Click Me * </Button> * <FixedDialog * {...hoverHandlers} * aria-labelledby="dialog-title-id" * id="dialog-id" * visible={visible} * onRequestClose={() => setVisible(false)} * fixedTo={buttonRef} * anchor={BELOW_CENTER_ANCHOR} * options={{ preventOverlap: true }} * // this allows the close on outside click"" behavior" to work * overlay={!stuck && active ? false : undefined} * disableScrollLock={active} * > * <YourDialogContent /> * </FixedDialog> * </> * ); * ``` * * @remarks \@since 2.8.0 * @remarks \@since 5.0.0 This hook no longer returns `handlers` or * `stickyHandlers` and does not hide when an element on the page is clicked. * @param options - An optional object of options to use. See * {@link HoverModeOptions} for more details. * @returns either the {@link HoverModeReturnValue} or {@link HoverModeReturnValue} */ function useHoverMode(_a) { var _b = _a === void 0 ? {} : _a, _c = _b.disabled, disabled = _c === void 0 ? false : _c, _d = _b.defaultVisible, defaultVisible = _d === void 0 ? false : _d, _e = _b.exitVisibilityDelay, exitVisibilityDelay = _e === void 0 ? DEFAULT_HOVER_MODE_EXIT_TIME : _e; var mode = useUserInteractionMode(); var isTouch = mode === "touch"; var _f = __read$17(require$$1.useState(defaultVisible), 2), visible = _f[0], setVisible = _f[1]; var _g = __read$17(require$$1.useState(false), 2), stuck = _g[0], setStuck = _g[1]; var timeoutRef = require$$1.useRef(); var _h = useHoverModeContext(), visibleInTime = _h.visibleInTime, enableHoverMode = _h.enableHoverMode, disableHoverMode = _h.disableHoverMode, startDisableTimer = _h.startDisableTimer; var active = visibleInTime === 0; require$$1.useEffect(function () { if (!visible) { setStuck(false); } }, [visible]); useOnUnmount(function () { window.clearTimeout(timeoutRef.current); }); var clearHoverTimeout = require$$1.useCallback(function () { window.clearTimeout(timeoutRef.current); }, []); var onMouseEnter = require$$1.useCallback(function (event) { if (stuck || disabled || isTouch || event.isPropagationStopped()) { return; } clearHoverTimeout(); if (visibleInTime === 0) { enableHoverMode(); setVisible(true); return; } timeoutRef.current = window.setTimeout(function () { enableHoverMode(); setVisible(true); }, visibleInTime); }, [ clearHoverTimeout, disabled, enableHoverMode, isTouch, stuck, visibleInTime, ]); var onMouseLeave = require$$1.useCallback(function (event) { if (stuck || disabled || isTouch || event.isPropagationStopped()) { return; } startDisableTimer(); clearHoverTimeout(); if (exitVisibilityDelay === 0) { setVisible(false); return; } timeoutRef.current = window.setTimeout(function () { setVisible(false); }, exitVisibilityDelay); }, [ clearHoverTimeout, disabled, exitVisibilityDelay,