chonky
Version:
A File Browser component for React
78 lines (66 loc) • 2.79 kB
text/typescript
// Used in React hooks to indicate empty deps are intentional.
import { MaybePromise, Nullable, WritableProps } from 'tsdef';
import { FileAction, FileActionEffect } from '../types/action.types';
import { Logger } from './logger';
// Used in contexts that need to provide some default value for a function.
// eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars
export const NOOP_FUNCTION = (...args: any[]) => {
Logger.warn(
`The "NOOP_FUNCTION" from the constants module was called. ` +
`This can indicate a bug in one of the components. Supplied args:`,
args
);
};
export const isPromise = <T>(value: MaybePromise<T> | any): value is Promise<T> => {
if (typeof value !== 'object' || !value) return false;
const then = (value as Promise<T>).then;
return then && typeof then === 'function';
};
export const defineFileAction = <Action extends FileAction>(
action: Action,
effect?: FileActionEffect<FileAction>
): WritableProps<Action> => {
if (action.__payloadType !== undefined && (action.hotkeys || action.button)) {
const errorMessage =
`Invalid definition was provided for file action "${action.id}". Actions ` +
`that specify hotkeys or buttons cannot define a payload type. If ` +
`your application requires this functionality, define two actions ` +
`and chain them using effects.`;
Logger.error(errorMessage);
throw new Error(errorMessage);
}
action.effect = effect;
return action;
};
/**
* Recursively check the current element and the parent elements, going bottom-up.
* Returns the first element to match the predicate, otherwise returns null if such
* element is not found.
*/
export const findElementAmongAncestors = (
maybeElement: HTMLElement | any,
predicate: (maybeElement: HTMLElement | any) => boolean
): Nullable<HTMLElement> => {
if (!maybeElement) return maybeElement;
if (predicate(maybeElement)) return maybeElement;
if (maybeElement.parentElement) {
return findElementAmongAncestors(maybeElement.parentElement, predicate);
}
return null;
};
export const elementIsInsideButton = (buttonCandidate: HTMLElement | any): boolean => {
return !!findElementAmongAncestors(
buttonCandidate,
(element: any) => element.tagName && element.tagName.toLowerCase() === 'button'
);
};
export const getValueOrFallback = <T extends any>(
value: T | undefined,
fallback: T,
desiredType?: 'boolean' | 'string' | 'number'
): NonNullable<T> => {
if (desiredType) {
return (typeof value === desiredType ? value : fallback) as NonNullable<T>;
}
return (value !== undefined ? value : fallback) as NonNullable<T>;
};