New source found from dndbeyond.com
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
||||
const defaultGenerator = componentName => componentName;
|
||||
const createClassNameGenerator = () => {
|
||||
let generate = defaultGenerator;
|
||||
return {
|
||||
configure(generator) {
|
||||
generate = generator;
|
||||
},
|
||||
generate(componentName) {
|
||||
return generate(componentName);
|
||||
},
|
||||
reset() {
|
||||
generate = defaultGenerator;
|
||||
}
|
||||
};
|
||||
};
|
||||
const ClassNameGenerator = createClassNameGenerator();
|
||||
export default ClassNameGenerator;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import _extends from "@babel/runtime/helpers/esm/extends";
|
||||
import isHostComponent from '../isHostComponent';
|
||||
|
||||
/**
|
||||
* Type of the ownerState based on the type of an element it applies to.
|
||||
* This resolves to the provided OwnerState for React components and `undefined` for host components.
|
||||
* Falls back to `OwnerState | undefined` when the exact type can't be determined in development time.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Appends the ownerState object to the props, merging with the existing one if necessary.
|
||||
*
|
||||
* @param elementType Type of the element that owns the `existingProps`. If the element is a DOM node or undefined, `ownerState` is not applied.
|
||||
* @param otherProps Props of the element.
|
||||
* @param ownerState
|
||||
*/
|
||||
function appendOwnerState(elementType, otherProps, ownerState) {
|
||||
if (elementType === undefined || isHostComponent(elementType)) {
|
||||
return otherProps;
|
||||
}
|
||||
return _extends({}, otherProps, {
|
||||
ownerState: _extends({}, otherProps.ownerState, ownerState)
|
||||
});
|
||||
}
|
||||
export default appendOwnerState;
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import _formatMuiErrorMessage from "@mui/utils/formatMuiErrorMessage";
|
||||
// It should to be noted that this function isn't equivalent to `text-transform: capitalize`.
|
||||
//
|
||||
// A strict capitalization should uppercase the first letter of each word in the sentence.
|
||||
// We only handle the first word.
|
||||
export default function capitalize(string) {
|
||||
if (typeof string !== 'string') {
|
||||
throw new Error(process.env.NODE_ENV !== "production" ? `MUI: \`capitalize(string)\` expects a string argument.` : _formatMuiErrorMessage(7));
|
||||
}
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
function clamp(val, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
|
||||
return Math.max(min, Math.min(val, max));
|
||||
}
|
||||
export default clamp;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
export default function composeClasses(slots, getUtilityClass, classes = undefined) {
|
||||
const output = {};
|
||||
Object.keys(slots).forEach(
|
||||
// `Object.keys(slots)` can't be wider than `T` because we infer `T` from `slots`.
|
||||
// @ts-expect-error https://github.com/microsoft/TypeScript/pull/12253#issuecomment-263132208
|
||||
slot => {
|
||||
output[slot] = slots[slot].reduce((acc, key) => {
|
||||
if (key) {
|
||||
const utilityClass = getUtilityClass(key);
|
||||
if (utilityClass !== '') {
|
||||
acc.push(utilityClass);
|
||||
}
|
||||
if (classes && classes[key]) {
|
||||
acc.push(classes[key]);
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, []).join(' ');
|
||||
});
|
||||
return output;
|
||||
}
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Safe chained function.
|
||||
*
|
||||
* Will only create a new function if needed,
|
||||
* otherwise will pass back existing functions or null.
|
||||
*/
|
||||
export default function createChainedFunction(...funcs) {
|
||||
return funcs.reduce((acc, func) => {
|
||||
if (func == null) {
|
||||
return acc;
|
||||
}
|
||||
return function chainedFunction(...args) {
|
||||
acc.apply(this, args);
|
||||
func.apply(this, args);
|
||||
};
|
||||
}, () => {});
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Corresponds to 10 frames at 60 Hz.
|
||||
// A few bytes payload overhead when lodash/debounce is ~3 kB and debounce ~300 B.
|
||||
export default function debounce(func, wait = 166) {
|
||||
let timeout;
|
||||
function debounced(...args) {
|
||||
const later = () => {
|
||||
// @ts-ignore
|
||||
func.apply(this, args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
}
|
||||
debounced.clear = () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
return debounced;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import _extends from "@babel/runtime/helpers/esm/extends";
|
||||
import * as React from 'react';
|
||||
|
||||
// https://github.com/sindresorhus/is-plain-obj/blob/main/index.js
|
||||
export function isPlainObject(item) {
|
||||
if (typeof item !== 'object' || item === null) {
|
||||
return false;
|
||||
}
|
||||
const prototype = Object.getPrototypeOf(item);
|
||||
return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in item) && !(Symbol.iterator in item);
|
||||
}
|
||||
function deepClone(source) {
|
||||
if ( /*#__PURE__*/React.isValidElement(source) || !isPlainObject(source)) {
|
||||
return source;
|
||||
}
|
||||
const output = {};
|
||||
Object.keys(source).forEach(key => {
|
||||
output[key] = deepClone(source[key]);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
export default function deepmerge(target, source, options = {
|
||||
clone: true
|
||||
}) {
|
||||
const output = options.clone ? _extends({}, target) : target;
|
||||
if (isPlainObject(target) && isPlainObject(source)) {
|
||||
Object.keys(source).forEach(key => {
|
||||
if ( /*#__PURE__*/React.isValidElement(source[key])) {
|
||||
output[key] = source[key];
|
||||
} else if (isPlainObject(source[key]) &&
|
||||
// Avoid prototype pollution
|
||||
Object.prototype.hasOwnProperty.call(target, key) && isPlainObject(target[key])) {
|
||||
// Since `output` is a clone of `target` and we have narrowed `target` in this block we can cast to the same type.
|
||||
output[key] = deepmerge(target[key], source[key], options);
|
||||
} else if (options.clone) {
|
||||
output[key] = isPlainObject(source[key]) ? deepClone(source[key]) : source[key];
|
||||
} else {
|
||||
output[key] = source[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
return output;
|
||||
}
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Extracts event handlers from a given object.
|
||||
* A prop is considered an event handler if it is a function and its name starts with `on`.
|
||||
*
|
||||
* @param object An object to extract event handlers from.
|
||||
* @param excludeKeys An array of keys to exclude from the returned object.
|
||||
*/
|
||||
function extractEventHandlers(object, excludeKeys = []) {
|
||||
if (object === undefined) {
|
||||
return {};
|
||||
}
|
||||
const result = {};
|
||||
Object.keys(object).filter(prop => prop.match(/^on[A-Z]/) && typeof object[prop] === 'function' && !excludeKeys.includes(prop)).forEach(prop => {
|
||||
result[prop] = object[prop];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
export default extractEventHandlers;
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* WARNING: Don't import this directly.
|
||||
* Use `MuiError` from `@mui/internal-babel-macros/MuiError.macro` instead.
|
||||
* @param {number} code
|
||||
*/
|
||||
export default function formatMuiErrorMessage(code) {
|
||||
// Apply babel-plugin-transform-template-literals in loose mode
|
||||
// loose mode is safe if we're concatenating primitives
|
||||
// see https://babeljs.io/docs/en/babel-plugin-transform-template-literals#loose
|
||||
/* eslint-disable prefer-template */
|
||||
let url = 'https://mui.com/production-error/?code=' + code;
|
||||
for (let i = 1; i < arguments.length; i += 1) {
|
||||
// rest params over-transpile for this case
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
url += '&args[]=' + encodeURIComponent(arguments[i]);
|
||||
}
|
||||
return 'Minified MUI error #' + code + '; visit ' + url + ' for the full message.';
|
||||
/* eslint-enable prefer-template */
|
||||
}
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
import ClassNameGenerator from '../ClassNameGenerator';
|
||||
export const globalStateClasses = {
|
||||
active: 'active',
|
||||
checked: 'checked',
|
||||
completed: 'completed',
|
||||
disabled: 'disabled',
|
||||
error: 'error',
|
||||
expanded: 'expanded',
|
||||
focused: 'focused',
|
||||
focusVisible: 'focusVisible',
|
||||
open: 'open',
|
||||
readOnly: 'readOnly',
|
||||
required: 'required',
|
||||
selected: 'selected'
|
||||
};
|
||||
export default function generateUtilityClass(componentName, slot, globalStatePrefix = 'Mui') {
|
||||
const globalStateClass = globalStateClasses[slot];
|
||||
return globalStateClass ? `${globalStatePrefix}-${globalStateClass}` : `${ClassNameGenerator.generate(componentName)}-${slot}`;
|
||||
}
|
||||
export function isGlobalState(slot) {
|
||||
return globalStateClasses[slot] !== undefined;
|
||||
}
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import generateUtilityClass from '../generateUtilityClass';
|
||||
export default function generateUtilityClasses(componentName, slots, globalStatePrefix = 'Mui') {
|
||||
const result = {};
|
||||
slots.forEach(slot => {
|
||||
result[slot] = generateUtilityClass(componentName, slot, globalStatePrefix);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { ForwardRef, Memo } from 'react-is';
|
||||
|
||||
// Simplified polyfill for IE11 support
|
||||
// https://github.com/JamesMGreene/Function.name/blob/58b314d4a983110c3682f1228f845d39ccca1817/Function.name.js#L3
|
||||
const fnNameMatchRegex = /^\s*function(?:\s|\s*\/\*.*\*\/\s*)+([^(\s/]*)\s*/;
|
||||
export function getFunctionName(fn) {
|
||||
const match = `${fn}`.match(fnNameMatchRegex);
|
||||
const name = match && match[1];
|
||||
return name || '';
|
||||
}
|
||||
function getFunctionComponentName(Component, fallback = '') {
|
||||
return Component.displayName || Component.name || getFunctionName(Component) || fallback;
|
||||
}
|
||||
function getWrappedName(outerType, innerType, wrapperName) {
|
||||
const functionName = getFunctionComponentName(innerType);
|
||||
return outerType.displayName || (functionName !== '' ? `${wrapperName}(${functionName})` : wrapperName);
|
||||
}
|
||||
|
||||
/**
|
||||
* cherry-pick from
|
||||
* https://github.com/facebook/react/blob/769b1f270e1251d9dbdce0fcbd9e92e502d059b8/packages/shared/getComponentName.js
|
||||
* originally forked from recompose/getDisplayName with added IE11 support
|
||||
*/
|
||||
export default function getDisplayName(Component) {
|
||||
if (Component == null) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof Component === 'string') {
|
||||
return Component;
|
||||
}
|
||||
if (typeof Component === 'function') {
|
||||
return getFunctionComponentName(Component, 'Component');
|
||||
}
|
||||
|
||||
// TypeScript can't have components as objects but they exist in the form of `memo` or `Suspense`
|
||||
if (typeof Component === 'object') {
|
||||
switch (Component.$$typeof) {
|
||||
case ForwardRef:
|
||||
return getWrappedName(Component, Component.render, 'ForwardRef');
|
||||
case Memo:
|
||||
return getWrappedName(Component, Component.type, 'memo');
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* Returns the ref of a React element handling differences between React 19 and older versions.
|
||||
* It will throw runtime error if the element is not a valid React element.
|
||||
*
|
||||
* @param element React.ReactElement
|
||||
* @returns React.Ref<any> | null
|
||||
*/
|
||||
export default function getReactElementRef(element) {
|
||||
// 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in older versions
|
||||
if (parseInt(React.version, 10) >= 19) {
|
||||
var _element$props;
|
||||
return (element == null || (_element$props = element.props) == null ? void 0 : _element$props.ref) || null;
|
||||
}
|
||||
// @ts-expect-error element.ref is not included in the ReactElement type
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/70189
|
||||
return (element == null ? void 0 : element.ref) || null;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// A change of the browser zoom change the scrollbar size.
|
||||
// Credit https://github.com/twbs/bootstrap/blob/488fd8afc535ca3a6ad4dc581f5e89217b6a36ac/js/src/util/scrollbar.js#L14-L18
|
||||
export default function getScrollbarSize(doc) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
|
||||
const documentWidth = doc.documentElement.clientWidth;
|
||||
return Math.abs(window.innerWidth - documentWidth);
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Determines if a given element is a DOM element name (i.e. not a React component).
|
||||
*/
|
||||
function isHostComponent(element) {
|
||||
return typeof element === 'string';
|
||||
}
|
||||
export default isHostComponent;
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import _extends from "@babel/runtime/helpers/esm/extends";
|
||||
import clsx from 'clsx';
|
||||
import extractEventHandlers from '../extractEventHandlers';
|
||||
import omitEventHandlers from '../omitEventHandlers';
|
||||
/**
|
||||
* Merges the slot component internal props (usually coming from a hook)
|
||||
* with the externally provided ones.
|
||||
*
|
||||
* The merge order is (the latter overrides the former):
|
||||
* 1. The internal props (specified as a getter function to work with get*Props hook result)
|
||||
* 2. Additional props (specified internally on a Base UI component)
|
||||
* 3. External props specified on the owner component. These should only be used on a root slot.
|
||||
* 4. External props specified in the `slotProps.*` prop.
|
||||
* 5. The `className` prop - combined from all the above.
|
||||
* @param parameters
|
||||
* @returns
|
||||
*/
|
||||
function mergeSlotProps(parameters) {
|
||||
const {
|
||||
getSlotProps,
|
||||
additionalProps,
|
||||
externalSlotProps,
|
||||
externalForwardedProps,
|
||||
className
|
||||
} = parameters;
|
||||
if (!getSlotProps) {
|
||||
// The simpler case - getSlotProps is not defined, so no internal event handlers are defined,
|
||||
// so we can simply merge all the props without having to worry about extracting event handlers.
|
||||
const joinedClasses = clsx(additionalProps == null ? void 0 : additionalProps.className, className, externalForwardedProps == null ? void 0 : externalForwardedProps.className, externalSlotProps == null ? void 0 : externalSlotProps.className);
|
||||
const mergedStyle = _extends({}, additionalProps == null ? void 0 : additionalProps.style, externalForwardedProps == null ? void 0 : externalForwardedProps.style, externalSlotProps == null ? void 0 : externalSlotProps.style);
|
||||
const props = _extends({}, additionalProps, externalForwardedProps, externalSlotProps);
|
||||
if (joinedClasses.length > 0) {
|
||||
props.className = joinedClasses;
|
||||
}
|
||||
if (Object.keys(mergedStyle).length > 0) {
|
||||
props.style = mergedStyle;
|
||||
}
|
||||
return {
|
||||
props,
|
||||
internalRef: undefined
|
||||
};
|
||||
}
|
||||
|
||||
// In this case, getSlotProps is responsible for calling the external event handlers.
|
||||
// We don't need to include them in the merged props because of this.
|
||||
|
||||
const eventHandlers = extractEventHandlers(_extends({}, externalForwardedProps, externalSlotProps));
|
||||
const componentsPropsWithoutEventHandlers = omitEventHandlers(externalSlotProps);
|
||||
const otherPropsWithoutEventHandlers = omitEventHandlers(externalForwardedProps);
|
||||
const internalSlotProps = getSlotProps(eventHandlers);
|
||||
|
||||
// The order of classes is important here.
|
||||
// Emotion (that we use in libraries consuming Base UI) depends on this order
|
||||
// to properly override style. It requires the most important classes to be last
|
||||
// (see https://github.com/mui/material-ui/pull/33205) for the related discussion.
|
||||
const joinedClasses = clsx(internalSlotProps == null ? void 0 : internalSlotProps.className, additionalProps == null ? void 0 : additionalProps.className, className, externalForwardedProps == null ? void 0 : externalForwardedProps.className, externalSlotProps == null ? void 0 : externalSlotProps.className);
|
||||
const mergedStyle = _extends({}, internalSlotProps == null ? void 0 : internalSlotProps.style, additionalProps == null ? void 0 : additionalProps.style, externalForwardedProps == null ? void 0 : externalForwardedProps.style, externalSlotProps == null ? void 0 : externalSlotProps.style);
|
||||
const props = _extends({}, internalSlotProps, additionalProps, otherPropsWithoutEventHandlers, componentsPropsWithoutEventHandlers);
|
||||
if (joinedClasses.length > 0) {
|
||||
props.className = joinedClasses;
|
||||
}
|
||||
if (Object.keys(mergedStyle).length > 0) {
|
||||
props.style = mergedStyle;
|
||||
}
|
||||
return {
|
||||
props,
|
||||
internalRef: internalSlotProps.ref
|
||||
};
|
||||
}
|
||||
export default mergeSlotProps;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Removes event handlers from the given object.
|
||||
* A field is considered an event handler if it is a function with a name beginning with `on`.
|
||||
*
|
||||
* @param object Object to remove event handlers from.
|
||||
* @returns Object with event handlers removed.
|
||||
*/
|
||||
function omitEventHandlers(object) {
|
||||
if (object === undefined) {
|
||||
return {};
|
||||
}
|
||||
const result = {};
|
||||
Object.keys(object).filter(prop => !(prop.match(/^on[A-Z]/) && typeof object[prop] === 'function')).forEach(prop => {
|
||||
result[prop] = object[prop];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
export default omitEventHandlers;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export default function ownerDocument(node) {
|
||||
return node && node.ownerDocument || document;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import ownerDocument from '../ownerDocument';
|
||||
export default function ownerWindow(node) {
|
||||
const doc = ownerDocument(node);
|
||||
return doc.defaultView || window;
|
||||
}
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* If `componentProps` is a function, calls it with the provided `ownerState`.
|
||||
* Otherwise, just returns `componentProps`.
|
||||
*/
|
||||
function resolveComponentProps(componentProps, ownerState, slotState) {
|
||||
if (typeof componentProps === 'function') {
|
||||
return componentProps(ownerState, slotState);
|
||||
}
|
||||
return componentProps;
|
||||
}
|
||||
export default resolveComponentProps;
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import _extends from "@babel/runtime/helpers/esm/extends";
|
||||
/**
|
||||
* Add keys, values of `defaultProps` that does not exist in `props`
|
||||
* @param {object} defaultProps
|
||||
* @param {object} props
|
||||
* @returns {object} resolved props
|
||||
*/
|
||||
export default function resolveProps(defaultProps, props) {
|
||||
const output = _extends({}, props);
|
||||
Object.keys(defaultProps).forEach(propName => {
|
||||
if (propName.toString().match(/^(components|slots)$/)) {
|
||||
output[propName] = _extends({}, defaultProps[propName], output[propName]);
|
||||
} else if (propName.toString().match(/^(componentsProps|slotProps)$/)) {
|
||||
const defaultSlotProps = defaultProps[propName] || {};
|
||||
const slotProps = props[propName];
|
||||
output[propName] = {};
|
||||
if (!slotProps || !Object.keys(slotProps)) {
|
||||
// Reduce the iteration if the slot props is empty
|
||||
output[propName] = defaultSlotProps;
|
||||
} else if (!defaultSlotProps || !Object.keys(defaultSlotProps)) {
|
||||
// Reduce the iteration if the default slot props is empty
|
||||
output[propName] = slotProps;
|
||||
} else {
|
||||
output[propName] = _extends({}, slotProps);
|
||||
Object.keys(defaultSlotProps).forEach(slotPropName => {
|
||||
output[propName][slotPropName] = resolveProps(defaultSlotProps[slotPropName], slotProps[slotPropName]);
|
||||
});
|
||||
}
|
||||
} else if (output[propName] === undefined) {
|
||||
output[propName] = defaultProps[propName];
|
||||
}
|
||||
});
|
||||
return output;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* TODO v5: consider making it private
|
||||
*
|
||||
* passes {value} to {ref}
|
||||
*
|
||||
* WARNING: Be sure to only call this inside a callback that is passed as a ref.
|
||||
* Otherwise, make sure to cleanup the previous {ref} if it changes. See
|
||||
* https://github.com/mui/material-ui/issues/13539
|
||||
*
|
||||
* Useful if you want to expose the ref of an inner component to the public API
|
||||
* while still using it inside the component.
|
||||
* @param ref A ref callback or ref object. If anything falsy, this is a no-op.
|
||||
*/
|
||||
export default function setRef(ref, value) {
|
||||
if (typeof ref === 'function') {
|
||||
ref(value);
|
||||
} else if (ref) {
|
||||
ref.current = value;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* A version of `React.useLayoutEffect` that does not show a warning when server-side rendering.
|
||||
* This is useful for effects that are only needed for client-side rendering but not for SSR.
|
||||
*
|
||||
* Before you use this hook, make sure to read https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
|
||||
* and confirm it doesn't apply to your use-case.
|
||||
*/
|
||||
const useEnhancedEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
||||
export default useEnhancedEffect;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import useEnhancedEffect from '../useEnhancedEffect';
|
||||
|
||||
/**
|
||||
* Inspired by https://github.com/facebook/react/issues/14099#issuecomment-440013892
|
||||
* See RFC in https://github.com/reactjs/rfcs/pull/220
|
||||
*/
|
||||
|
||||
function useEventCallback(fn) {
|
||||
const ref = React.useRef(fn);
|
||||
useEnhancedEffect(() => {
|
||||
ref.current = fn;
|
||||
});
|
||||
return React.useRef((...args) =>
|
||||
// @ts-expect-error hide `this`
|
||||
(0, ref.current)(...args)).current;
|
||||
}
|
||||
export default useEventCallback;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import setRef from '../setRef';
|
||||
export default function useForkRef(...refs) {
|
||||
/**
|
||||
* This will create a new function if the refs passed to this hook change and are all defined.
|
||||
* This means react will call the old forkRef with `null` and the new forkRef
|
||||
* with the ref. Cleanup naturally emerges from this behavior.
|
||||
*/
|
||||
return React.useMemo(() => {
|
||||
if (refs.every(ref => ref == null)) {
|
||||
return null;
|
||||
}
|
||||
return instance => {
|
||||
refs.forEach(ref => {
|
||||
setRef(ref, instance);
|
||||
});
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, refs);
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
let globalId = 0;
|
||||
function useGlobalId(idOverride) {
|
||||
const [defaultId, setDefaultId] = React.useState(idOverride);
|
||||
const id = idOverride || defaultId;
|
||||
React.useEffect(() => {
|
||||
if (defaultId == null) {
|
||||
// Fallback to this default id when possible.
|
||||
// Use the incrementing value for client-side rendering only.
|
||||
// We can't use it server-side.
|
||||
// If you want to use random values please consider the Birthday Problem: https://en.wikipedia.org/wiki/Birthday_problem
|
||||
globalId += 1;
|
||||
setDefaultId(`mui-${globalId}`);
|
||||
}
|
||||
}, [defaultId]);
|
||||
return id;
|
||||
}
|
||||
|
||||
// downstream bundlers may remove unnecessary concatenation, but won't remove toString call -- Workaround for https://github.com/webpack/webpack/issues/14814
|
||||
const maybeReactUseId = React['useId'.toString()];
|
||||
/**
|
||||
*
|
||||
* @example <div id={useId()} />
|
||||
* @param idOverride
|
||||
* @returns {string}
|
||||
*/
|
||||
export default function useId(idOverride) {
|
||||
if (maybeReactUseId !== undefined) {
|
||||
const reactId = maybeReactUseId();
|
||||
return idOverride != null ? idOverride : reactId;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks -- `React.useId` is invariant at runtime.
|
||||
return useGlobalId(idOverride);
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
'use client';
|
||||
|
||||
// based on https://github.com/WICG/focus-visible/blob/v4.1.5/src/focus-visible.js
|
||||
import * as React from 'react';
|
||||
import { Timeout } from '../useTimeout/useTimeout';
|
||||
let hadKeyboardEvent = true;
|
||||
let hadFocusVisibleRecently = false;
|
||||
const hadFocusVisibleRecentlyTimeout = new Timeout();
|
||||
const inputTypesWhitelist = {
|
||||
text: true,
|
||||
search: true,
|
||||
url: true,
|
||||
tel: true,
|
||||
email: true,
|
||||
password: true,
|
||||
number: true,
|
||||
date: true,
|
||||
month: true,
|
||||
week: true,
|
||||
time: true,
|
||||
datetime: true,
|
||||
'datetime-local': true
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes whether the given element should automatically trigger the
|
||||
* `focus-visible` class being added, i.e. whether it should always match
|
||||
* `:focus-visible` when focused.
|
||||
* @param {Element} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function focusTriggersKeyboardModality(node) {
|
||||
const {
|
||||
type,
|
||||
tagName
|
||||
} = node;
|
||||
if (tagName === 'INPUT' && inputTypesWhitelist[type] && !node.readOnly) {
|
||||
return true;
|
||||
}
|
||||
if (tagName === 'TEXTAREA' && !node.readOnly) {
|
||||
return true;
|
||||
}
|
||||
if (node.isContentEditable) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep track of our keyboard modality state with `hadKeyboardEvent`.
|
||||
* If the most recent user interaction was via the keyboard;
|
||||
* and the key press did not include a meta, alt/option, or control key;
|
||||
* then the modality is keyboard. Otherwise, the modality is not keyboard.
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
function handleKeyDown(event) {
|
||||
if (event.metaKey || event.altKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
hadKeyboardEvent = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If at any point a user clicks with a pointing device, ensure that we change
|
||||
* the modality away from keyboard.
|
||||
* This avoids the situation where a user presses a key on an already focused
|
||||
* element, and then clicks on a different element, focusing it with a
|
||||
* pointing device, while we still think we're in keyboard modality.
|
||||
*/
|
||||
function handlePointerDown() {
|
||||
hadKeyboardEvent = false;
|
||||
}
|
||||
function handleVisibilityChange() {
|
||||
if (this.visibilityState === 'hidden') {
|
||||
// If the tab becomes active again, the browser will handle calling focus
|
||||
// on the element (Safari actually calls it twice).
|
||||
// If this tab change caused a blur on an element with focus-visible,
|
||||
// re-apply the class when the user switches back to the tab.
|
||||
if (hadFocusVisibleRecently) {
|
||||
hadKeyboardEvent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
function prepare(doc) {
|
||||
doc.addEventListener('keydown', handleKeyDown, true);
|
||||
doc.addEventListener('mousedown', handlePointerDown, true);
|
||||
doc.addEventListener('pointerdown', handlePointerDown, true);
|
||||
doc.addEventListener('touchstart', handlePointerDown, true);
|
||||
doc.addEventListener('visibilitychange', handleVisibilityChange, true);
|
||||
}
|
||||
export function teardown(doc) {
|
||||
doc.removeEventListener('keydown', handleKeyDown, true);
|
||||
doc.removeEventListener('mousedown', handlePointerDown, true);
|
||||
doc.removeEventListener('pointerdown', handlePointerDown, true);
|
||||
doc.removeEventListener('touchstart', handlePointerDown, true);
|
||||
doc.removeEventListener('visibilitychange', handleVisibilityChange, true);
|
||||
}
|
||||
function isFocusVisible(event) {
|
||||
const {
|
||||
target
|
||||
} = event;
|
||||
try {
|
||||
return target.matches(':focus-visible');
|
||||
} catch (error) {
|
||||
// Browsers not implementing :focus-visible will throw a SyntaxError.
|
||||
// We use our own heuristic for those browsers.
|
||||
// Rethrow might be better if it's not the expected error but do we really
|
||||
// want to crash if focus-visible malfunctioned?
|
||||
}
|
||||
|
||||
// No need for validFocusTarget check. The user does that by attaching it to
|
||||
// focusable events only.
|
||||
return hadKeyboardEvent || focusTriggersKeyboardModality(target);
|
||||
}
|
||||
export default function useIsFocusVisible() {
|
||||
const ref = React.useCallback(node => {
|
||||
if (node != null) {
|
||||
prepare(node.ownerDocument);
|
||||
}
|
||||
}, []);
|
||||
const isFocusVisibleRef = React.useRef(false);
|
||||
|
||||
/**
|
||||
* Should be called if a blur event is fired
|
||||
*/
|
||||
function handleBlurVisible() {
|
||||
// checking against potential state variable does not suffice if we focus and blur synchronously.
|
||||
// React wouldn't have time to trigger a re-render so `focusVisible` would be stale.
|
||||
// Ideally we would adjust `isFocusVisible(event)` to look at `relatedTarget` for blur events.
|
||||
// This doesn't work in IE11 due to https://github.com/facebook/react/issues/3751
|
||||
// TODO: check again if React releases their internal changes to focus event handling (https://github.com/facebook/react/pull/19186).
|
||||
if (isFocusVisibleRef.current) {
|
||||
// To detect a tab/window switch, we look for a blur event followed
|
||||
// rapidly by a visibility change.
|
||||
// If we don't see a visibility change within 100ms, it's probably a
|
||||
// regular focus change.
|
||||
hadFocusVisibleRecently = true;
|
||||
hadFocusVisibleRecentlyTimeout.start(100, () => {
|
||||
hadFocusVisibleRecently = false;
|
||||
});
|
||||
isFocusVisibleRef.current = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called if a blur event is fired
|
||||
*/
|
||||
function handleFocusVisible(event) {
|
||||
if (isFocusVisible(event)) {
|
||||
isFocusVisibleRef.current = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
isFocusVisibleRef,
|
||||
onFocus: handleFocusVisible,
|
||||
onBlur: handleBlurVisible,
|
||||
ref
|
||||
};
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
const UNINITIALIZED = {};
|
||||
|
||||
/**
|
||||
* A React.useRef() that is initialized lazily with a function. Note that it accepts an optional
|
||||
* initialization argument, so the initialization function doesn't need to be an inline closure.
|
||||
*
|
||||
* @usage
|
||||
* const ref = useLazyRef(sortColumns, columns)
|
||||
*/
|
||||
export default function useLazyRef(init, initArg) {
|
||||
const ref = React.useRef(UNINITIALIZED);
|
||||
if (ref.current === UNINITIALIZED) {
|
||||
ref.current = init(initArg);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
const EMPTY = [];
|
||||
|
||||
/**
|
||||
* A React.useEffect equivalent that runs once, when the component is mounted.
|
||||
*/
|
||||
export default function useOnMount(fn) {
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
React.useEffect(fn, EMPTY);
|
||||
/* eslint-enable react-hooks/exhaustive-deps */
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
'use client';
|
||||
|
||||
import _extends from "@babel/runtime/helpers/esm/extends";
|
||||
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
||||
const _excluded = ["elementType", "externalSlotProps", "ownerState", "skipResolvingSlotProps"];
|
||||
import useForkRef from '../useForkRef';
|
||||
import appendOwnerState from '../appendOwnerState';
|
||||
import mergeSlotProps from '../mergeSlotProps';
|
||||
import resolveComponentProps from '../resolveComponentProps';
|
||||
/**
|
||||
* @ignore - do not document.
|
||||
* Builds the props to be passed into the slot of an unstyled component.
|
||||
* It merges the internal props of the component with the ones supplied by the user, allowing to customize the behavior.
|
||||
* If the slot component is not a host component, it also merges in the `ownerState`.
|
||||
*
|
||||
* @param parameters.getSlotProps - A function that returns the props to be passed to the slot component.
|
||||
*/
|
||||
function useSlotProps(parameters) {
|
||||
var _parameters$additiona;
|
||||
const {
|
||||
elementType,
|
||||
externalSlotProps,
|
||||
ownerState,
|
||||
skipResolvingSlotProps = false
|
||||
} = parameters,
|
||||
rest = _objectWithoutPropertiesLoose(parameters, _excluded);
|
||||
const resolvedComponentsProps = skipResolvingSlotProps ? {} : resolveComponentProps(externalSlotProps, ownerState);
|
||||
const {
|
||||
props: mergedProps,
|
||||
internalRef
|
||||
} = mergeSlotProps(_extends({}, rest, {
|
||||
externalSlotProps: resolvedComponentsProps
|
||||
}));
|
||||
const ref = useForkRef(internalRef, resolvedComponentsProps == null ? void 0 : resolvedComponentsProps.ref, (_parameters$additiona = parameters.additionalProps) == null ? void 0 : _parameters$additiona.ref);
|
||||
const props = appendOwnerState(elementType, _extends({}, mergedProps, {
|
||||
ref
|
||||
}), ownerState);
|
||||
return props;
|
||||
}
|
||||
export default useSlotProps;
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import useLazyRef from '../useLazyRef/useLazyRef';
|
||||
import useOnMount from '../useOnMount/useOnMount';
|
||||
export class Timeout {
|
||||
constructor() {
|
||||
this.currentId = null;
|
||||
this.clear = () => {
|
||||
if (this.currentId !== null) {
|
||||
clearTimeout(this.currentId);
|
||||
this.currentId = null;
|
||||
}
|
||||
};
|
||||
this.disposeEffect = () => {
|
||||
return this.clear;
|
||||
};
|
||||
}
|
||||
static create() {
|
||||
return new Timeout();
|
||||
}
|
||||
/**
|
||||
* Executes `fn` after `delay`, clearing any previously scheduled call.
|
||||
*/
|
||||
start(delay, fn) {
|
||||
this.clear();
|
||||
this.currentId = setTimeout(() => {
|
||||
this.currentId = null;
|
||||
fn();
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
export default function useTimeout() {
|
||||
const timeout = useLazyRef(Timeout.create).current;
|
||||
useOnMount(timeout.disposeEffect);
|
||||
return timeout;
|
||||
}
|
||||
Reference in New Issue
Block a user