tweak-tools
Version:
Tweak your React projects until awesomeness
318 lines (317 loc) • 11.1 kB
TypeScript
/// <reference types="react" />
/**
* Types exposed through the public API
*/
import type { VectorSettings } from '../components/Vector/vector-types';
import { StoreType, Data, DataInput } from './internal';
import type { BeautifyUnionType, UnionToIntersection } from './utils';
export declare type RenderFn = (get: (key: string) => any) => boolean;
/**
* Utility types that joins a value with its settings
*/
export declare type InputWithSettings<V extends unknown, Settings = {}, K extends string = 'value'> = {
[key in K]: V;
} & {
type?: TweakInputs;
} & Settings;
/**
* Either the raw value, either the value with its settings
* In other words => value || { value, ...settings }
*/
export declare type MergedInputWithSettings<V, Settings = {}, K extends string = 'value'> = V | InputWithSettings<V, Settings, K>;
/**
* Special Inputs
*/
export declare enum SpecialInputs {
BUTTON = "BUTTON",
BUTTON_GROUP = "BUTTON_GROUP",
MONITOR = "MONITOR",
FOLDER = "FOLDER"
}
export declare enum TweakInputs {
SELECT = "SELECT",
IMAGE = "IMAGE",
NUMBER = "NUMBER",
COLOR = "COLOR",
STRING = "STRING",
BOOLEAN = "BOOLEAN",
INTERVAL = "INTERVAL",
VECTOR3D = "VECTOR3D",
VECTOR2D = "VECTOR2D"
}
export declare type ButtonSettings = {
disabled?: boolean;
};
export declare type ButtonInput = {
type: SpecialInputs.BUTTON;
onClick: (get: (path: string) => any) => void;
settings: ButtonSettings;
};
export declare type ButtonGroupOpts = {
[title: string]: (get: (path: string) => any) => void;
};
export declare type ButtonGroupInputOpts = ButtonGroupOpts | {
label?: string | JSX.Element | null;
opts: ButtonGroupOpts;
};
export declare type ButtonGroupInput = {
type: SpecialInputs.BUTTON_GROUP;
opts: ButtonGroupInputOpts;
};
export declare type MonitorSettings = {
graph?: boolean;
interval?: number;
};
export declare type MonitorInput = {
type: SpecialInputs.MONITOR;
objectOrFn: React.MutableRefObject<any> | Function;
settings: MonitorSettings;
};
export declare type SpecialInput = MonitorInput | ButtonInput | ButtonGroupInput;
export declare type FolderSettings = {
collapsed?: boolean;
render?: RenderFn;
color?: string;
/** works similar to css order property */
order?: number;
};
export declare type NumberSettings = {
min?: number;
max?: number;
step?: number;
};
export declare type VectorObj = Record<string, number>;
export declare type Vector2dArray = [number, number];
export declare type Vector2d = Vector2dArray | VectorObj;
export declare type Vector2dSettings = VectorSettings<Vector2d, 'x' | 'y'> & {
joystick?: boolean | 'invertY';
lock?: boolean;
};
export declare type Vector2dInput = MergedInputWithSettings<Vector2d, Vector2dSettings>;
export declare type Vector3dArray = [number, number, number];
export declare type Vector3d = Vector3dArray | VectorObj;
export declare type Vector3dSettings = VectorSettings<Vector3d, 'x' | 'y' | 'z'> & {
lock?: boolean;
};
export declare type Vector3dInput = MergedInputWithSettings<Vector3d, Vector3dSettings>;
export declare type IntervalInput = {
value: [number, number];
min: number;
max: number;
};
export declare type ImageInput = {
image: undefined | string;
};
declare type SelectInput = {
options: any[] | Record<string, any>;
value?: any;
};
declare type SelectWithValueInput<T, K> = {
options: T[] | Record<string, T>;
value: K;
};
declare type SelectWithoutValueInput<T> = {
options: T[] | Record<string, T>;
};
declare type ColorRgbaInput = {
r: number;
g: number;
b: number;
a?: number;
};
declare type ColorHslaInput = {
h: number;
s: number;
l: number;
a?: number;
};
declare type ColorHsvaInput = {
h: number;
s: number;
v: number;
a?: number;
};
export declare type ColorVectorInput = ColorRgbaInput | ColorHslaInput | ColorHsvaInput;
declare type BooleanInput = boolean;
declare type StringSettings = {
rows?: boolean | number;
editable?: boolean;
};
declare type StringInput = InputWithSettings<string, StringSettings>;
export declare type FolderInput<Schema> = {
type: SpecialInputs.FOLDER;
schema: Schema;
settings: FolderSettings;
};
export declare type CustomInput<Value> = {
type: string;
__customInput: Value;
};
declare type SchemaItem = InputWithSettings<number, NumberSettings> | InputWithSettings<boolean> | InputWithSettings<string> | IntervalInput | ColorVectorInput | Vector2dInput | Vector3dInput | ImageInput | SelectInput | BooleanInput | StringInput | CustomInput<unknown>;
declare type GenericSchemaItemOptions = {
render?: RenderFn;
label?: string | JSX.Element;
hint?: string;
order?: number;
};
declare type OnHandlerContext = DataInput & {
get(path: string): any;
};
declare type OnChangeHandlerContext = OnHandlerContext & {
/**
* Whether the onChange handler is invoked initially.
*/
initial: boolean;
};
export declare type OnChangeHandler = (value: any, path: string, context: OnChangeHandlerContext) => void;
declare type TransientOnChangeSchemaItemOptions = {
onChange: OnChangeHandler;
transient?: true;
};
declare type NonTransientOnChangeSchemaItemOptions = {
onChange: OnChangeHandler;
transient: false;
};
declare type NoOnChangeSchemaItemOptions = {
onChange?: undefined;
transient?: undefined;
};
declare type OnChangeSchemaItemOptions = TransientOnChangeSchemaItemOptions | NonTransientOnChangeSchemaItemOptions | NoOnChangeSchemaItemOptions;
export declare type InputOptions = GenericSchemaItemOptions & OnChangeSchemaItemOptions & {
optional?: boolean;
disabled?: boolean;
onEditStart?: (value: any, path: string, context: OnHandlerContext) => void;
onEditEnd?: (value: any, path: string, context: OnHandlerContext) => void;
};
declare type SchemaItemWithOptions = number | boolean | string | (SchemaItem & InputOptions) | (SpecialInput & GenericSchemaItemOptions) | FolderInput<unknown>;
export declare type Schema = Record<string, SchemaItemWithOptions>;
/**
* Dummy type used internally to flag non compatible input types.
* @internal
*/
declare type NotAPrimitiveType = {
____: 'NotAPrimitiveType';
};
declare type PrimitiveToValue<P> = P extends CustomInput<infer CustomValue> ? BeautifyUnionType<CustomValue> : P extends ImageInput ? string | undefined : P extends SelectWithValueInput<infer SelectValue, infer Options> ? SelectValue | Options : P extends SelectWithoutValueInput<infer Options> ? Options : P extends IntervalInput ? [number, number] : P extends {
value: infer Value;
} ? PrimitiveToValue<Value> : P extends VectorObj ? P : P extends Vector3dArray ? [number, number, number] : P extends Vector2dArray ? [number, number] : P extends number ? number : P extends string ? string : P extends boolean ? boolean : NotAPrimitiveType;
export declare type SchemaToValues<Schema, IncludeTransient extends boolean = false> = BeautifyUnionType<UnionToIntersection<Tree<IncludeTransient, Schema>>>;
declare type EndLeaf = {
___leaf: 'leaf';
};
declare type Join<Leaf1, Leaf1Key extends keyof Leaf1, Leaf2> = EndLeaf extends Leaf2 ? {
[i in Leaf1Key]: Leaf1[Leaf1Key];
} : Leaf2;
declare type Tree<IncludeTransient extends boolean, Leaf, LeafKey extends string | number | symbol = ''> = {
0: Leaf extends {
schema: infer Schema;
} ? {
[Key in keyof Schema]: Join<Schema, Key, Schema[Key]>;
} : never;
1: never;
2: {
[Key in LeafKey]: Leaf extends {
optional: true;
} | {
disabled: true;
} ? PrimitiveToValue<Leaf> | undefined : PrimitiveToValue<Leaf>;
};
3: {
[Key in keyof Leaf]: Join<Leaf, Key, Tree<IncludeTransient, Leaf[Key], Key>>;
}[keyof Leaf];
4: EndLeaf;
}[LeafKey extends '' ? 3 : Leaf extends FolderInput<unknown> ? 0 : Leaf extends SpecialInput ? 1 : PrimitiveToValue<Leaf> extends NotAPrimitiveType ? Leaf extends object ? 3 : 4 : Leaf extends TransientOnChangeSchemaItemOptions ? IncludeTransient extends true ? 2 : 1 : 2];
/**
* If P is '' then T is the whole schema and we shouldn't run any type check
* on the schema, to the risk that { a: 1, b: 2 } is recognized as Vector
* instead of a two number inputs.
*/
/**
* Interface to build a plugin.
*
* @public
*/
export interface Plugin<Input, Value = Input, InternalSettings = {}> {
/**
* The component that shows the input value;
*/
component: React.ComponentType;
/**
* Normalizes the input into a { value, settings } object.
*
* @example
* Let's consider a color with an inverted settings option that computes the negative
* of that color. The plugin could look something like:
* ```ts
* myColorPlugin({ color: '#fff', inverted: true })
* ```
*
* In that case, your normalize funciton would be something like:
* ```ts
* function normalize({ color, inverted }) {
* return { value: color, settings: { inverted }}
* }
* ```
*/
normalize?: (input: Input, path: string, data: Data) => {
value: Value;
settings?: InternalSettings;
};
/**
* Sanitizes the user value before registering it to the store. For
* example, the Number plugin would santize "3.00" into 3. If the provided
* value isn't formatted properly, the sanitize function should throw.
*/
sanitize?: (value: any, settings: InternalSettings, prevValue: any, path: string, store: StoreType) => Value;
/**
* Formats the value into the value that will be displayed by the component.
* If the input value of the Number plugin, then format will add proper
* padding and show "3.00".
* (Prop name in useInputContext context hook is `displayedValue`)
*/
format?: (value: any, settings: InternalSettings) => any;
}
export declare type InputContextProps = {
id: string;
label: string | JSX.Element;
hint?: string;
path: string;
key: string;
optional: boolean;
disabled: boolean;
disable: (flag: boolean) => void;
storeId: string;
value: unknown;
displayValue: unknown;
onChange: React.Dispatch<any>;
emitOnEditStart: () => void;
emitOnEditEnd: () => void;
onUpdate: (v: any | ((v: any) => any)) => void;
settings: unknown;
setSettings: (v: any) => void;
};
/**
* Interface consumed by the useInputContext hook so that its returned values
* are properly typed.
*
* @example
* ```ts
* useInputContext<TweakInputProps<boolean>>()
* ```
* @public
*/
export interface TweakInputProps<V, InternalSettings = {}, DisplayValue = V> {
path?: string;
id?: string;
hint?: string;
disabled?: boolean;
displayValue: DisplayValue;
value: V;
onChange: React.Dispatch<any>;
emitOnEditStart: () => void;
emitOnEditEnd: () => void;
onUpdate: (v: any | ((v: any) => any)) => void;
settings: InternalSettings;
setSettings: (v: Partial<InternalSettings>) => void;
}
export {};