react-md
Version:
This is the full react-md library bundled together for convenience.
1,419 lines (1,356 loc) • 1.08 MB
JavaScript
(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,