mobx-restful-table
Version:
A Pagination Table & Scroll List component suite for CRUD operation, which is based on MobX RESTful & React.
240 lines (238 loc) • 11.6 kB
TypeScript
import { FC, Component, HTMLAttributes, InputHTMLAttributes, DragEvent, TextareaHTMLAttributes, ReactNode, KeyboardEvent, ChangeEvent, FormEvent, FocusEvent } from "react";
import { BadgeProps, ImageProps, FloatingLabelProps, FormControlProps, FormSelectProps, FormGroupProps, FormProps, TableProps } from "react-bootstrap";
import { Variant } from "react-bootstrap/esm/types";
import { JSX } from "react/jsx-runtime";
import { FormComponent, FormComponentProps, ObservedComponent } from "mobx-react-helper";
import { BaseModel, DataObject, Filter, IDType, ListModel } from "mobx-restful";
import { TranslationModel } from "mobx-i18n";
import { EditorProps } from "react-bootstrap-editor";
import { DebouncedFunc } from "lodash";
export const VariantColors: Variant[];
export const text2color: (raw: string) => Variant;
export interface BadgeItem {
text: string;
link?: string;
}
export interface BadgeBarProps extends BadgeProps {
list: BadgeItem[];
bgResolver?: (text: string) => Variant | string;
onDelete?: (item: BadgeItem, index: number) => any;
}
export const BadgeBar: FC<BadgeBarProps>;
export class ImagePreview extends Component<ImageProps> {
static readonly displayName = "ImagePreview";
accessor downloading: boolean;
accessor loadedPath: string;
accessor viewing: boolean;
componentDidMount(): void;
componentDidUpdate({ src }: Readonly<ImageProps>): void;
load(path: string): Promise<void>;
render(): JSX.Element;
}
export type FilePreviewProps = ImageProps & HTMLAttributes<HTMLAudioElement> & HTMLAttributes<HTMLVideoElement> & HTMLAttributes<HTMLAnchorElement> & {
type?: InputHTMLAttributes<HTMLInputElement>['accept'];
path: string;
};
export const FileTypeMap: {
stream: string;
compressed: string;
msword: string;
document: string;
powerpoint: string;
presentation: string;
excel: string;
sheet: string;
};
export const FilePreview: FC<FilePreviewProps>;
export type FilePickerProps = FormComponentProps<string | File>;
export class FilePicker extends FormComponent<FilePickerProps> {
#private;
static readonly displayName = "FilePicker";
accessor file: File | undefined;
get fileType(): string;
get filePath(): string;
componentDidMount(): void;
componentWillUnmount(): void;
renderInput(): JSX.Element;
render(): JSX.Element;
}
export abstract class FileModel extends BaseModel {
accessor files: string[];
clear(): void;
/**
* Override this method for Network calling,
* then call `super.upload(fileURL)` to update `this.files` array.
*/
upload(file: string | Blob): Promise<string>;
/**
* Override this method for Network calling,
* then call `super.delete(fileURL)` to update `this.files` array.
*/
delete(file: string): Promise<void>;
move(sourceIndex: number, targetIndex: number): void;
}
export interface FileUploaderProps extends FormComponentProps<string[]> {
store: FileModel;
}
export class FileUploader extends FormComponent<FileUploaderProps> {
#private;
static readonly displayName = "FileUploader";
accessor pickIndex: number | undefined;
componentDidMount(): void;
componentWillUnmount(): void;
handleDrop: (index: number) => (event: DragEvent<HTMLElement>) => void;
handleChange: (oldURI?: string) => (file: File) => Promise<void>;
render(): JSX.Element;
}
export interface SelectOption extends Partial<Pick<HTMLOptionElement, 'text' | 'label' | 'disabled'>> {
value: string;
}
export type FormFieldProps = FormControlProps & FormSelectProps & Pick<TextareaHTMLAttributes<HTMLTextAreaElement>, 'rows'> & Pick<FloatingLabelProps, 'label'> & {
options?: SelectOption[];
};
export const FormField: FC<FormFieldProps>;
export interface RangeInputProps extends Omit<FormComponentProps, 'type'> {
icon?: ReactNode | ((itemValue: number) => ReactNode);
}
export class RangeInput extends FormComponent<RangeInputProps> {
static readonly displayName = "RangeInput";
renderItem(index: number): JSX.Element;
render(): JSX.Element;
}
export const TextInputTypes: readonly ["text", "number", "tel", "email", "url"];
export interface BadgeInputProps extends FormComponentProps<string[]> {
type?: (typeof TextInputTypes)[number];
}
export class BadgeInput extends FormComponent<BadgeInputProps> {
static readonly displayName = "BadgeInput";
static match(type: string): type is BadgeInputProps['type'];
handleInput: (event: KeyboardEvent<HTMLInputElement>) => void;
delete(index: number): void;
render(): JSX.Element;
}
export type ArrayFieldProps<T extends DataObject = DataObject> = Pick<HTMLAttributes<HTMLFieldSetElement>, 'className' | 'style'> & FormComponentProps<T[]> & {
renderItem: (item: T, index: number) => ReactNode;
};
export class ArrayField<T extends DataObject = DataObject> extends FormComponent<ArrayFieldProps<T>> {
static displayName: string;
componentDidMount(): void;
add: () => T[];
remove: (index: number) => T[];
handleChange: (index: number) => ({ currentTarget }: ChangeEvent<EventTarget>) => void;
handleUpdate: (index: number) => ({ currentTarget }: ChangeEvent<EventTarget>) => T;
render(): JSX.Element;
}
export interface Field<T extends DataObject> extends Pick<InputHTMLAttributes<HTMLInputElement>, 'type' | 'readOnly' | 'disabled' | 'required' | 'min' | 'minLength' | 'max' | 'maxLength' | 'step' | 'pattern' | 'multiple' | 'accept' | 'placeholder'>, Pick<FormFieldProps, 'options' | 'rows' | 'contentEditable'>, Pick<EditorProps, 'tools'>, Partial<Record<`${'' | 'in'}validMessage`, ReactNode>> {
key?: keyof T;
renderLabel?: ReactNode | ((key: keyof T) => ReactNode);
renderInput?: (data: T, meta: Field<T>) => ReactNode;
uploader?: FileModel;
}
export interface FieldBoxProps<D extends DataObject> extends FormGroupProps, Pick<Field<D>, 'renderLabel' | `${'' | 'in'}validMessage`> {
name: Field<D>['key'];
}
export interface RestFormProps<D extends DataObject, F extends Filter<D> = Filter<D>> extends Pick<FormProps, 'className' | 'style'> {
id?: IDType;
fields: Field<D>[];
store: ListModel<D, F>;
translator: TranslationModel<string, 'submit' | 'cancel'>;
onSubmit?: (data: D) => any;
}
export class RestForm<D extends DataObject, F extends Filter<D> = Filter<D>> extends ObservedComponent<RestFormProps<D, F>> {
static readonly displayName = "RestForm";
static dateValueOf: <D_1 extends DataObject>({ type, step }: Field<D_1>, raw: D_1[keyof D_1]) => string | D_1[keyof D_1];
static FieldBox: <D_1 extends DataObject>({ name, renderLabel, validMessage, invalidMessage, children, ...props }: FieldBoxProps<D_1>) => JSX.Element;
accessor validated: boolean;
componentDidMount(): void;
handleSubmit: (event: FormEvent<HTMLFormElement>) => Promise<void>;
get fields(): Field<D>[];
get readOnly(): boolean;
get customValidation(): boolean;
get fieldReady(): boolean;
renderFile: ({ key, type, required, multiple, accept, uploader, ...meta }: Field<D>) => ({ [key]: paths }: D) => JSX.Element;
renderCheckGroup: ({ key, type, options, ...meta }: Field<D>) => (data: D) => JSX.Element;
renderMultipleInput: ({ key, type, ...meta }: Field<D>) => ({ [key]: value }: D) => JSX.Element;
renderHTMLEditor: ({ key, contentEditable, tools, ...meta }: Field<D>) => ({ [key]: value }: D) => JSX.Element;
renderField: ({ key, type, step, renderLabel, renderInput, validMessage, invalidMessage, ...meta }: Field<D>, props?: Partial<FormFieldProps>) => ({ [key]: value }: D) => JSX.Element;
render(): JSX.Element;
}
export type PageMeta = Pick<ListModel<{}>, 'pageSize' | 'pageIndex'>;
export interface PagerProps extends PageMeta {
pageCount: number;
onChange?: (meta: PageMeta) => any;
}
export const Pager: FC<PagerProps>;
export interface Column<T extends DataObject> extends Omit<Field<T>, 'renderLabel'> {
renderHead?: Field<T>['renderLabel'];
renderBody?: (data: T) => ReactNode;
renderFoot?: ReactNode | ((data: keyof T) => ReactNode);
}
type Translator<T extends DataObject> = RestFormProps<T>['translator'] & TranslationModel<string, 'create' | 'view' | 'edit' | 'delete' | 'total_x_rows' | 'sure_to_delete_x'>;
export interface RestTableProps<T extends DataObject> extends Omit<TableProps, 'onSubmit'>, Omit<RestFormProps<T>, 'id' | 'fields' | 'translator'> {
editable?: boolean;
deletable?: boolean;
columns: Column<T>[];
translator: Translator<T>;
onCheck?: (keys: IDType[]) => any;
}
export class RestTable<T extends DataObject> extends ObservedComponent<RestTableProps<T>> {
static readonly displayName = "RestTable";
componentDidMount(): void;
accessor checkedKeys: IDType[];
toggleCheck(key: IDType): void;
toggleCheckAll: () => void;
get checkColumn(): Column<T>;
get operateColumn(): Column<T>;
get columns(): Column<T>[];
get hasHeader(): boolean;
get hasFooter(): boolean;
get editing(): boolean;
renderCustomBody: ({ key, type, multiple, options, accept, rows, }: Column<T>) => Column<T>["renderBody"];
renderTable(): JSX.Element;
getList: DebouncedFunc<({ pageIndex, pageSize }: any) => void>;
deleteList(keys: IDType[]): Promise<void>;
render(): JSX.Element;
}
export type EdgePosition = 'top' | 'bottom' | 'left' | 'right';
export type TouchHandler = (edge: EdgePosition) => any;
export interface ScrollBoundaryProps extends HTMLAttributes<HTMLDivElement>, Partial<Record<EdgePosition, ReactNode>> {
onTouch: TouchHandler;
}
export const ScrollBoundary: FC<ScrollBoundaryProps>;
export interface ScrollListProps<D extends DataObject, F extends Filter<D> = Filter<D>> extends Omit<ScrollBoundaryProps, 'onTouch'> {
translator: TranslationModel<string, 'load_more' | 'no_more'>;
store: ListModel<D, F>;
filter?: F;
defaultData?: D[];
renderList(allItems: D[]): ReactNode;
}
export class ScrollList<D extends DataObject = DataObject, F extends Filter<D> = Filter<D>> extends Component<ScrollListProps<D, F>> {
static readonly displayName = "ScrollList";
componentDidMount(): Promise<void>;
componentWillUnmount(): void;
loadMore: DebouncedFunc<(edge: EdgePosition) => void>;
render(): JSX.Element;
}
export type OptionData = Record<'label' | 'value', string>;
export type SearchableInputProps<D extends DataObject, F extends Filter<D> = Filter<D>> = Omit<ScrollListProps<D, F>, 'id' | 'defaultValue' | 'onChange' | 'defaultData' | 'renderList'> & FormComponentProps<OptionData[]> & Omit<RestFormProps<D, F>, 'fields'> & {
translator: RestFormProps<D, F>['translator'] & ScrollListProps<D, F>['translator'];
fields?: RestFormProps<D, F>['fields'];
labelKey: keyof D;
valueKey: keyof D;
renderList?: ScrollListProps<D, F>['renderList'];
type?: (typeof TextInputTypes)[number];
multiple?: boolean;
};
export class SearchableInput<D extends DataObject, F extends Filter<D> = Filter<D>> extends FormComponent<SearchableInputProps<D, F>> {
static readonly displayName = "SearchableInput";
accessor filter: F;
accessor listShown: boolean;
search: DebouncedFunc<(value: string) => Promise<void>>;
add: (label: string, value: string) => void;
delete: (index: number) => OptionData[];
handleBlur: ({ target, relatedTarget }: FocusEvent<HTMLElement>) => void;
renderList: ScrollListProps<D, F>['renderList'];
renderOverlay(): JSX.Element;
render(): JSX.Element;
}
//# sourceMappingURL=index.d.ts.map