@toolpad/utils
Version:
Shared utilities used by Toolpad packages.
114 lines (108 loc) • 3.15 kB
JavaScript
import * as React from 'react';
import * as ReactIs from 'react-is';
/**
* Like `Array.prototype.join`, but for React nodes.
*/
import { jsx as _jsx } from "react/jsx-runtime";
export function interleave(items, separator) {
const result = [];
for (let i = 0; i < items.length; i += 1) {
if (i > 0) {
if (ReactIs.isElement(separator)) {
result.push(/*#__PURE__*/React.cloneElement(separator, {
key: `separator-${i}`
}));
} else {
result.push(separator);
}
}
const item = items[i];
result.push(item);
}
return /*#__PURE__*/_jsx(React.Fragment, {
children: result
});
}
/**
* Consume a context but throw when used outside of a provider.
*/
export function useNonNullableContext(context, name) {
const maybeContext = React.useContext(context);
if (maybeContext === null || maybeContext === undefined) {
throw new Error(`context "${name}" was used without a Provider`);
}
return maybeContext;
}
/**
* Context that throws when used outside of a provider.
*/
export function createProvidedContext(name) {
const context = /*#__PURE__*/React.createContext(undefined);
const useContext = () => useNonNullableContext(context, name);
return [useContext, context.Provider];
}
export function useAssertedContext(context) {
const value = React.useContext(context);
if (value === undefined) {
throw new Error('context was used without a Provider');
}
return value;
}
/**
* Debugging tool that logs updates to props.
*/
export function useTraceUpdates(prefix, props) {
const prev = React.useRef(props);
React.useEffect(() => {
const changedProps = {};
for (const key of Object.keys(props)) {
if (!Object.is(prev.current[key], props[key])) {
changedProps[key] = props[key];
}
}
if (Object.keys(changedProps).length > 0) {
// eslint-disable-next-line no-console
console.log(`${prefix} changed props:`, changedProps);
}
prev.current = props;
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function getComponentDisplayName(Component) {
if (typeof Component === 'string') {
return Component || 'Unknown';
}
return Component.displayName || Component.name;
}
/**
* Create a shared state to be used across the application. Returns a useState hook that
* is synchronized on the same state between all instances where it is called.
*/
export function createGlobalState(initialState) {
let state = initialState;
const listeners = [];
const subscribe = cb => {
listeners.push(cb);
return () => {
const index = listeners.indexOf(cb);
listeners.splice(index, 1);
};
};
const getState = () => state;
const setState = newState => {
state = typeof newState === 'function' ? newState(state) : newState;
listeners.forEach(cb => cb(state));
};
const useValue = () => React.useSyncExternalStore(subscribe, getState, getState);
const useState = () => {
const value = useValue();
return [value, setState];
};
return {
getState,
setState,
useValue,
useState,
subscribe
};
}