mig-schema-table 3.0.28 → 3.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +1 -0
  2. package/dist/component/SchemaTable/SchemaColumnFilterPopover/FilterFormComponent/index.d.ts +11 -0
  3. package/dist/component/SchemaTable/SchemaColumnFilterPopover/FilterFormComponent/index.js +76 -0
  4. package/dist/component/SchemaTable/SchemaColumnFilterPopover/index.d.ts +15 -0
  5. package/dist/component/SchemaTable/SchemaColumnFilterPopover/index.js +37 -0
  6. package/dist/component/SchemaTable/Td/index.d.ts +22 -0
  7. package/dist/component/SchemaTable/Td/index.js +78 -0
  8. package/dist/component/SchemaTable/Th/index.d.ts +27 -0
  9. package/dist/component/SchemaTable/Th/index.js +78 -0
  10. package/dist/component/SchemaTable/constants.d.ts +2 -0
  11. package/dist/component/SchemaTable/constants.js +2 -0
  12. package/dist/component/SchemaTable/index.d.ts +34 -0
  13. package/dist/component/SchemaTable/index.js +343 -0
  14. package/dist/component/SchemaTable/index.test.d.ts +1 -0
  15. package/dist/component/SchemaTable/index.test.js +17 -0
  16. package/dist/component/inc/constant.d.ts +2 -0
  17. package/dist/component/inc/constant.js +2 -0
  18. package/dist/component/inc/date.d.ts +1 -0
  19. package/dist/component/inc/date.js +3 -0
  20. package/dist/component/inc/schema.d.ts +2 -0
  21. package/dist/component/inc/schema.js +32 -0
  22. package/dist/component/inc/string.d.ts +1 -0
  23. package/dist/component/inc/string.js +17 -0
  24. package/dist/component/index.d.ts +4 -0
  25. package/dist/component/index.js +2 -0
  26. package/dist/component/types.d.ts +21 -0
  27. package/dist/component/types.js +1 -0
  28. package/dist/exampleData.d.ts +29 -0
  29. package/dist/exampleData.js +177 -0
  30. package/dist/index.css +16 -12
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -42,6 +42,7 @@ import { SchemaTable, IColumnConfig } from "mig-schema-table";
42
42
  import "mig-schema-table/dist/index.css";
43
43
  // Add this for default datepicker styling
44
44
  import "react-datepicker/dist/react-datepicker.css";
45
+ // Optionally add bootstrap5 styles
45
46
 
46
47
  const config: { [keyName: string]: IColumnConfig } = {
47
48
  id: {
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { oas31 } from "openapi3-ts";
3
+ import { TColumnFilterValue } from "../../index";
4
+ interface IFilterFormComponentProps {
5
+ onChange: (newValue?: TColumnFilterValue) => void;
6
+ propSchema: oas31.SchemaObject;
7
+ propName: string;
8
+ columnFilterValue: TColumnFilterValue;
9
+ }
10
+ declare const _default: React.MemoExoticComponent<({ onChange, propSchema, propName, columnFilterValue, }: IFilterFormComponentProps) => import("react/jsx-runtime").JSX.Element>;
11
+ export default _default;
@@ -0,0 +1,76 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { uncamel } from "../../../inc/string";
4
+ import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_TIME_FORMAT, } from "../../../inc/constant";
5
+ import DatePicker from "react-datepicker";
6
+ import nl from "date-fns/locale/nl";
7
+ const FilterFormComponent = ({ onChange, propSchema, propName, columnFilterValue, }) => {
8
+ const { type, format, minimum, maximum } = propSchema;
9
+ const value = columnFilterValue;
10
+ switch (type) {
11
+ case "integer":
12
+ return (_jsx("div", Object.assign({ className: "input-group" }, { children: _jsx("input", { className: "form-control", type: "number", value: (value || ""), "data-prop-name": propName, onChange: (e) => {
13
+ onChange(e.currentTarget.value === ""
14
+ ? undefined
15
+ : parseInt(e.currentTarget.value));
16
+ }, placeholder: `Search ${propName}`, min: minimum, max: maximum }) })));
17
+ case "boolean":
18
+ let selectValue = value ? "✓" : "✕";
19
+ if (value === undefined) {
20
+ selectValue = "";
21
+ }
22
+ return (_jsxs("select", Object.assign({ className: "form-select", value: selectValue, "data-prop-name": propName, onChange: (e) => {
23
+ switch (e.currentTarget.value) {
24
+ case "✓":
25
+ onChange(true);
26
+ break;
27
+ case "✕":
28
+ onChange(false);
29
+ break;
30
+ default:
31
+ onChange(undefined);
32
+ }
33
+ } }, { children: [_jsx("option", Object.assign({ value: "" }, { children: "All" }), "all"), ["✓", "✕"].map((optionValue) => (_jsx("option", Object.assign({ value: optionValue }, { children: optionValue }), `column-filter-select-${optionValue}`)))] })));
34
+ // @ts-ignore
35
+ case "string":
36
+ if (propSchema.enum) {
37
+ return (_jsxs("select", Object.assign({ className: "form-select", value: value, "data-prop-name": propName, onChange: (e) => {
38
+ onChange(e.currentTarget.value || undefined);
39
+ } }, { children: [_jsx("option", Object.assign({ value: "" }, { children: "All" }), "all"), propSchema.enum.map((name) => (_jsx("option", Object.assign({ value: name }, { children: uncamel(name) }), `column-filter-select-${name}`)))] })));
40
+ }
41
+ if (format === "date-time" || format === "date") {
42
+ const dateFormat = format === "date" ? DEFAULT_DATE_FORMAT : DEFAULT_DATE_TIME_FORMAT;
43
+ const dateRangeValue = (columnFilterValue || {
44
+ from: undefined,
45
+ to: undefined,
46
+ });
47
+ const startDateChangeHandler = (date) => {
48
+ if (!date && !dateRangeValue.to) {
49
+ onChange(undefined);
50
+ return;
51
+ }
52
+ if (dateRangeValue.to && date && date > dateRangeValue.to) {
53
+ return;
54
+ }
55
+ onChange(Object.assign(Object.assign({}, columnFilterValue), { from: date || undefined }));
56
+ };
57
+ const endDateChangeHandler = (date) => {
58
+ if (!date && !dateRangeValue.from) {
59
+ onChange(undefined);
60
+ return;
61
+ }
62
+ if (dateRangeValue.from && date && date < dateRangeValue.from) {
63
+ return;
64
+ }
65
+ onChange(Object.assign(Object.assign({}, columnFilterValue), { to: date || undefined }));
66
+ };
67
+ return (_jsxs("div", Object.assign({ className: "input-group d-flex" }, { children: [_jsxs("div", Object.assign({ className: "d-flex flex-column m-1" }, { children: [_jsx("label", { children: "Start date-time" }), _jsx(DatePicker, { dateFormat: dateFormat, "data-prop-name": propName, locale: nl, selected: dateRangeValue.from, onChange: startDateChangeHandler, placeholderText: dateFormat, isClearable: true, selectsStart: true, showTimeSelect: format === "date-time", timeIntervals: 15, shouldCloseOnSelect: format === "date" })] })), _jsxs("div", Object.assign({ className: "d-flex flex-column m-1" }, { children: [_jsx("label", { children: "End date-time" }), _jsx(DatePicker, { id: "filter-date", dateFormat: dateFormat, "data-prop-name": propName, locale: nl, selectsEnd: true, selected: dateRangeValue.to, onChange: endDateChangeHandler, placeholderText: dateFormat, isClearable: true, startDate: dateRangeValue.from, endDate: dateRangeValue.to, showTimeSelect: format === "date-time", timeIntervals: 15, shouldCloseOnSelect: format === "date" })] }))] })));
68
+ }
69
+ // falls through
70
+ default:
71
+ return (_jsx("div", Object.assign({ className: "input-group" }, { children: _jsx("input", { type: "text", className: "form-control", placeholder: `Search ${propName}`, "aria-label": `Search ${propName}`, autoFocus: true, value: (value || ""), "data-prop-name": propName, onChange: (e) => {
72
+ onChange(e.currentTarget.value || undefined);
73
+ } }) })));
74
+ }
75
+ };
76
+ export default React.memo(FilterFormComponent);
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { oas31 } from "openapi3-ts";
3
+ import { TColumnFilterValue } from "../index";
4
+ export interface ISchemaColumnFilterPopoverConfig {
5
+ referenceElement: HTMLElement;
6
+ propName: string;
7
+ }
8
+ type TSchemaColumnFilterPopoverProps = ISchemaColumnFilterPopoverConfig & {
9
+ onChange: (newValue?: TColumnFilterValue) => void;
10
+ propSchema: oas31.SchemaObject;
11
+ value: TColumnFilterValue;
12
+ onClose: () => void;
13
+ };
14
+ declare const _default: React.MemoExoticComponent<({ onChange, onClose, propName, propSchema, referenceElement, value, }: TSchemaColumnFilterPopoverProps) => import("react/jsx-runtime").JSX.Element>;
15
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import FilterFormComponent from "./FilterFormComponent";
4
+ import { usePopper } from "react-popper";
5
+ const SchemaColumnFilterPopover = ({ onChange, onClose, propName, propSchema, referenceElement, value, }) => {
6
+ const [popperElement, setPopperElement] = React.useState(null);
7
+ const [arrowElement, setArrowElement] = React.useState(null);
8
+ const { styles, attributes } = usePopper(referenceElement, popperElement, {
9
+ modifiers: [{ name: "arrow", options: { element: arrowElement } }],
10
+ });
11
+ React.useEffect(() => {
12
+ const onWindowClick = (e) => {
13
+ if (!popperElement) {
14
+ return;
15
+ }
16
+ let parent = e.target;
17
+ while (parent && popperElement) {
18
+ if (parent === popperElement) {
19
+ return;
20
+ }
21
+ parent =
22
+ parent.parentNode === window.document ? null : parent.parentNode;
23
+ }
24
+ onClose();
25
+ };
26
+ window.addEventListener("click", onWindowClick, { capture: true });
27
+ return () => {
28
+ window.removeEventListener("click", onWindowClick, { capture: true });
29
+ };
30
+ }, [onClose, popperElement]);
31
+ const classNames = ["popover", "schema-column-filter-popover"];
32
+ if (attributes.popper) {
33
+ classNames.push(`bs-popover-${attributes.popper["data-popper-placement"]}`);
34
+ }
35
+ return (_jsxs("div", Object.assign({ className: classNames.join(" "), role: "tooltip", ref: setPopperElement, style: styles.popper }, attributes.popper, { children: [_jsx("div", { className: "popover-arrow", ref: setArrowElement, style: styles.arrow }), _jsx("div", Object.assign({ className: "popover-body" }, { children: _jsx(FilterFormComponent, { onChange: onChange, propSchema: propSchema, propName: propName, columnFilterValue: value }) }))] })));
36
+ };
37
+ export default React.memo(SchemaColumnFilterPopover);
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { oas31 } from "openapi3-ts";
3
+ import { IColumnConfig, IRenderData } from "../../types";
4
+ interface ITdProps<T> {
5
+ checkedIndexes?: number[];
6
+ columnIndex: number;
7
+ data: T[];
8
+ getRowClassName?: (rowData: T, dataIndex: number) => string;
9
+ getRowSelected?: (rowData: T) => boolean;
10
+ rowHeight: number;
11
+ rowIndex: number;
12
+ onCheckedIndexesChange?: (dataIndex: number[]) => void;
13
+ onRowClick?: (rowData: T, dataIndex: number, event: React.MouseEvent) => void;
14
+ propConfig?: IColumnConfig<any>;
15
+ propName: string;
16
+ propSchema: oas31.SchemaObject;
17
+ sortedRenderData?: IRenderData[];
18
+ style: React.CSSProperties;
19
+ }
20
+ declare function Td<T>({ checkedIndexes, columnIndex, data, getRowClassName, getRowSelected, onCheckedIndexesChange, onRowClick, propConfig, propName, propSchema, rowHeight, rowIndex, sortedRenderData, style, }: ITdProps<T>): import("react/jsx-runtime").JSX.Element | null;
21
+ declare const _default: typeof Td;
22
+ export default _default;
@@ -0,0 +1,78 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { SELECT_ALL_COLUMN_NAME } from "../constants";
4
+ function Td({ checkedIndexes, columnIndex, data, getRowClassName, getRowSelected, onCheckedIndexesChange, onRowClick, propConfig, propName, propSchema, rowHeight, rowIndex, sortedRenderData, style, }) {
5
+ const onTdClick = React.useCallback((e) => {
6
+ if (!sortedRenderData || !onRowClick) {
7
+ return;
8
+ }
9
+ const { rowIndex } = e.currentTarget.dataset;
10
+ if (!rowIndex) {
11
+ return;
12
+ }
13
+ const row = sortedRenderData[parseInt(rowIndex, 10)];
14
+ onRowClick(data[row._index], row._index, e);
15
+ }, [data, onRowClick, sortedRenderData]);
16
+ const row = sortedRenderData ? sortedRenderData[rowIndex] : undefined;
17
+ const tdDivProps = React.useMemo(() => {
18
+ if (!row) {
19
+ return undefined;
20
+ }
21
+ return {
22
+ "data-row-index": rowIndex,
23
+ "data-column-index": columnIndex,
24
+ key: propName,
25
+ style: Object.assign(Object.assign({}, style), { lineHeight: `${rowHeight}px` }),
26
+ onClick: !(propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderCell) ? onTdClick : undefined,
27
+ className: `schema-table__td schema-table__td--${rowIndex % 2 ? "odd" : "even"}${getRowSelected && getRowSelected(data[row._index])
28
+ ? " schema-table__td--selected"
29
+ : ""} ${getRowClassName ? getRowClassName(data[row._index], row._index) : ""}`,
30
+ title: row[propName],
31
+ };
32
+ }, [
33
+ columnIndex,
34
+ data,
35
+ getRowClassName,
36
+ getRowSelected,
37
+ onTdClick,
38
+ propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderCell,
39
+ propName,
40
+ row,
41
+ rowHeight,
42
+ rowIndex,
43
+ style,
44
+ ]);
45
+ if (!row || !tdDivProps) {
46
+ return null;
47
+ }
48
+ if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderCell) {
49
+ return (_jsx("div", Object.assign({}, tdDivProps, { children: propConfig.renderCell(data[row._index], row._index, rowIndex) })));
50
+ }
51
+ if (propName === SELECT_ALL_COLUMN_NAME) {
52
+ const onChecked = () => {
53
+ if (!onCheckedIndexesChange) {
54
+ return;
55
+ }
56
+ onCheckedIndexesChange([row._index]);
57
+ };
58
+ return (_jsx("div", Object.assign({}, tdDivProps, { children: _jsx("div", Object.assign({ style: { textAlign: "center" } }, { children: _jsx("input", { "data-key": "row-checkbox", type: "checkbox", onChange: onChecked, checked: checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.includes(row._index) }) })) })));
59
+ }
60
+ if (!propSchema) {
61
+ return null;
62
+ }
63
+ switch (propSchema.type) {
64
+ case "boolean":
65
+ tdDivProps.className += ` text-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "center"}`;
66
+ break;
67
+ case "number":
68
+ case "integer":
69
+ tdDivProps.className += ` text-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "end"}`;
70
+ break;
71
+ default:
72
+ if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) {
73
+ tdDivProps.className += ` text-${propConfig.align}`;
74
+ }
75
+ }
76
+ return _jsx("div", Object.assign({}, tdDivProps, { children: row[propName] }));
77
+ }
78
+ export default React.memo(Td);
@@ -0,0 +1,27 @@
1
+ import { oas31 } from "openapi3-ts";
2
+ import React, { CSSProperties, Dispatch, SetStateAction } from "react";
3
+ import { IColumnConfig } from "../../types";
4
+ import { ISchemaColumnFilterPopoverConfig } from "../SchemaColumnFilterPopover";
5
+ export declare enum EColumnFilterStatus {
6
+ UNAVAILABLE = "UNAVAILABLE",
7
+ AVAILABLE = "AVAILABLE",
8
+ ACTIVE = "ACTIVE"
9
+ }
10
+ interface IThProps {
11
+ columnFilterStatus: EColumnFilterStatus;
12
+ disableColumnFilter: (propName: string) => void;
13
+ isAllChecked?: boolean;
14
+ isSortable: boolean;
15
+ numberOfSelectedRows?: number;
16
+ onSelectAllIndexesHandler?: (e: React.ChangeEvent<HTMLInputElement>) => void;
17
+ propConfig?: IColumnConfig<any>;
18
+ propName: string;
19
+ schema: oas31.SchemaObject;
20
+ setPopoverConfig?: Dispatch<SetStateAction<ISchemaColumnFilterPopoverConfig | undefined>>;
21
+ setSortAsc: Dispatch<SetStateAction<boolean>>;
22
+ setSortColumn: Dispatch<SetStateAction<string>>;
23
+ sortAsc?: boolean;
24
+ style: CSSProperties;
25
+ }
26
+ declare const _default: ({ isAllChecked, columnFilterStatus, disableColumnFilter, isSortable, numberOfSelectedRows, onSelectAllIndexesHandler, propConfig, propName, schema, setPopoverConfig, setSortAsc, setSortColumn, sortAsc, style, }: IThProps) => import("react/jsx-runtime").JSX.Element;
27
+ export default _default;
@@ -0,0 +1,78 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { uncamel } from "../../inc/string";
4
+ import { SELECT_ALL_COLUMN_NAME } from "../constants";
5
+ export var EColumnFilterStatus;
6
+ (function (EColumnFilterStatus) {
7
+ EColumnFilterStatus["UNAVAILABLE"] = "UNAVAILABLE";
8
+ EColumnFilterStatus["AVAILABLE"] = "AVAILABLE";
9
+ EColumnFilterStatus["ACTIVE"] = "ACTIVE";
10
+ })(EColumnFilterStatus || (EColumnFilterStatus = {}));
11
+ const Th = ({ isAllChecked, columnFilterStatus, disableColumnFilter, isSortable, numberOfSelectedRows, onSelectAllIndexesHandler, propConfig, propName, schema, setPopoverConfig, setSortAsc, setSortColumn, sortAsc, style, }) => {
12
+ const thDivProps = {
13
+ style,
14
+ className: `schema-table__th schema-table__th--column-filter-status-${columnFilterStatus} ${isSortable ? "schema-table__th--sortable" : "schema-table__th--unsortable"}`,
15
+ };
16
+ const onSortButtonClick = React.useCallback(() => {
17
+ if (sortAsc === undefined) {
18
+ setSortColumn(propName);
19
+ setSortAsc(!(propConfig === null || propConfig === void 0 ? void 0 : propConfig.defaultSortDesc));
20
+ return;
21
+ }
22
+ setSortAsc((sortAsc) => !sortAsc);
23
+ }, [
24
+ propConfig === null || propConfig === void 0 ? void 0 : propConfig.defaultSortDesc,
25
+ propName,
26
+ setSortAsc,
27
+ setSortColumn,
28
+ sortAsc,
29
+ ]);
30
+ const onFilterButtonClick = React.useCallback((e) => {
31
+ if (columnFilterStatus === EColumnFilterStatus.ACTIVE &&
32
+ disableColumnFilter) {
33
+ disableColumnFilter(propName);
34
+ return;
35
+ }
36
+ if (!setPopoverConfig) {
37
+ return;
38
+ }
39
+ const referenceElement = e.currentTarget;
40
+ setPopoverConfig((popoverConfig) => {
41
+ if (popoverConfig) {
42
+ return undefined;
43
+ }
44
+ return {
45
+ referenceElement,
46
+ propName,
47
+ };
48
+ });
49
+ }, [columnFilterStatus, disableColumnFilter, propName, setPopoverConfig]);
50
+ if (propName === SELECT_ALL_COLUMN_NAME) {
51
+ return (_jsx("div", Object.assign({}, thDivProps, { children: _jsx("div", Object.assign({ style: {
52
+ width: "100%",
53
+ textAlign: "center",
54
+ }, title: `${numberOfSelectedRows || 0} selected` }, { children: _jsx("input", { type: "checkbox", checked: isAllChecked, onChange: onSelectAllIndexesHandler }) })) })));
55
+ }
56
+ if (!schema) {
57
+ return _jsx("div", Object.assign({}, thDivProps));
58
+ }
59
+ switch (schema.type) {
60
+ case "boolean":
61
+ thDivProps.className += ` text-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "center"} justify-content-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "center"}`;
62
+ break;
63
+ case "integer":
64
+ case "number":
65
+ thDivProps.className += ` text-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "end"} justify-content-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "end"}`;
66
+ break;
67
+ default:
68
+ if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) {
69
+ thDivProps.className += ` text-${propConfig.align}`;
70
+ }
71
+ }
72
+ return (_jsxs("div", Object.assign({}, thDivProps, { children: [isSortable ? (_jsxs("button", Object.assign({ className: "px-0", disabled: (propConfig === null || propConfig === void 0 ? void 0 : propConfig.sortable) === false, onClick: onSortButtonClick }, { children: [(propConfig === null || propConfig === void 0 ? void 0 : propConfig.title) === undefined
73
+ ? uncamel(propName)
74
+ : propConfig === null || propConfig === void 0 ? void 0 : propConfig.title, sortAsc === undefined ? null : sortAsc ? "▲" : "▼"] }))) : (_jsx("div", Object.assign({ style: { lineHeight: "44px" } }, { children: (propConfig === null || propConfig === void 0 ? void 0 : propConfig.title) === undefined
75
+ ? uncamel(propName)
76
+ : propConfig === null || propConfig === void 0 ? void 0 : propConfig.title }))), columnFilterStatus !== EColumnFilterStatus.UNAVAILABLE ? (_jsx("button", Object.assign({ onClick: onFilterButtonClick, "data-bs-toggle": "dropdown" }, { children: _jsx("svg", Object.assign({ viewBox: "0 0 36 36", xmlns: "http://www.w3.org/2000/svg", height: 16, width: 16, style: { display: "block" }, fill: "currentColor" }, { children: _jsx("polygon", { points: "14,30 22,25 22,17 35.999,0 17.988,0 0,0 14,17 " }) })) }))) : null] })));
77
+ };
78
+ export default React.memo(Th);
@@ -0,0 +1,2 @@
1
+ export declare const SELECT_ALL_COLUMN_NAME = "SELECT_ALL_COLUMN_NAME";
2
+ export declare const SELECT_ALL_COLUMN_WIDTH = 40;
@@ -0,0 +1,2 @@
1
+ export const SELECT_ALL_COLUMN_NAME = "SELECT_ALL_COLUMN_NAME";
2
+ export const SELECT_ALL_COLUMN_WIDTH = 40;
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import { oas31 } from "openapi3-ts";
3
+ import { IColumnConfig } from "../types";
4
+ export interface ISchemaTableProps<T> {
5
+ Heading?: any;
6
+ checkedIndexes?: number[];
7
+ config?: {
8
+ [propName: string]: IColumnConfig<T>;
9
+ };
10
+ customElement?: React.ReactNode;
11
+ data: T[];
12
+ defaultSortAsc?: boolean;
13
+ defaultSortColumn?: keyof T;
14
+ getRowClassName?: (rowData: T, dataIndex: number) => string;
15
+ getRowSelected?: (rowData: T) => boolean;
16
+ maxHeight?: number;
17
+ isSearchable?: boolean;
18
+ isColumnFilterable?: boolean;
19
+ isSortable?: boolean;
20
+ onCheckedIndexesChange?: (dataIndex: number[]) => void;
21
+ onRowClick?: (rowData: T, dataIndex: number, event: React.MouseEvent) => void;
22
+ rowHeight?: number;
23
+ schema: oas31.SchemaObject;
24
+ searchPlaceholder?: string;
25
+ style?: React.CSSProperties;
26
+ width: number;
27
+ }
28
+ export type TColumnFilterValue = string | number | boolean | {
29
+ from?: Date;
30
+ to?: Date;
31
+ };
32
+ declare function SchemaTable<T>({ Heading, checkedIndexes, config, customElement, data, defaultSortAsc, defaultSortColumn, getRowClassName, getRowSelected, maxHeight, isSearchable, isColumnFilterable, isSortable, onCheckedIndexesChange, onRowClick, rowHeight, schema, searchPlaceholder, style, width, }: ISchemaTableProps<T>): import("react/jsx-runtime").JSX.Element;
33
+ declare const _default: typeof SchemaTable;
34
+ export default _default;
@@ -0,0 +1,343 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { VariableSizeGrid, VariableSizeList } from "react-window";
4
+ import { localeFormat } from "../inc/date";
5
+ import { uncamel } from "../inc/string";
6
+ import Th, { EColumnFilterStatus } from "./Th";
7
+ import { SELECT_ALL_COLUMN_NAME, SELECT_ALL_COLUMN_WIDTH } from "./constants";
8
+ import Td from "./Td";
9
+ import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_TIME_FORMAT } from "../inc/constant";
10
+ import SchemaColumnFilterPopover from "./SchemaColumnFilterPopover";
11
+ import { sum } from "lodash";
12
+ function SchemaTable({ Heading = VariableSizeList, checkedIndexes, config, customElement, data, defaultSortAsc = false, defaultSortColumn, getRowClassName, getRowSelected, maxHeight, isSearchable, isColumnFilterable, isSortable, onCheckedIndexesChange, onRowClick, rowHeight = 36, schema, searchPlaceholder, style, width, }) {
13
+ const [sortColumn, setSortColumn] = React.useState(defaultSortColumn);
14
+ const [sortAsc, setSortAsc] = React.useState(defaultSortAsc);
15
+ const [searchQuery, setSearchQuery] = React.useState("");
16
+ const [columnFilterMap, setColumnFilterMap] = React.useState({});
17
+ const [popoverConfig, setPopoverConfig] = React.useState();
18
+ const { properties = {} } = schema;
19
+ const columnNames = React.useMemo(() => {
20
+ const columns = Object.keys(properties);
21
+ if (onCheckedIndexesChange) {
22
+ columns.unshift(SELECT_ALL_COLUMN_NAME);
23
+ }
24
+ if (!config) {
25
+ return columns;
26
+ }
27
+ const invisibleColumns = Object.entries(config).reduce((prev, [propName, propConfig]) => {
28
+ if (propConfig.hidden) {
29
+ prev.push(propName);
30
+ }
31
+ return prev;
32
+ }, []);
33
+ return columns
34
+ .filter((key) => !invisibleColumns.includes(key))
35
+ .sort((columnA, columnB) => {
36
+ let orderA = config[columnA] ? config[columnA].order : undefined;
37
+ if (orderA === undefined) {
38
+ orderA = Object.keys(properties).findIndex((propName) => propName === columnA);
39
+ }
40
+ let orderB = config[columnB] ? config[columnB].order : undefined;
41
+ if (orderB === undefined) {
42
+ orderB = Object.keys(properties).findIndex((propName) => propName === columnB);
43
+ }
44
+ if (columnB === SELECT_ALL_COLUMN_NAME ||
45
+ columnA === SELECT_ALL_COLUMN_NAME) {
46
+ return 0;
47
+ }
48
+ if (orderA === -1) {
49
+ return 1;
50
+ }
51
+ if (orderB === -1) {
52
+ return -1;
53
+ }
54
+ return orderA - orderB;
55
+ });
56
+ }, [config, onCheckedIndexesChange, properties]);
57
+ const renderData = React.useMemo(() => data
58
+ ? data.map((object, rowIndex) => columnNames.reduce((prev, propName) => {
59
+ var _a;
60
+ const schema = properties[propName];
61
+ const propConfig = config ? config[propName] : undefined;
62
+ if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderData) {
63
+ prev[propName] = propConfig.renderData(object, rowIndex);
64
+ return prev;
65
+ }
66
+ if (!schema || propName === SELECT_ALL_COLUMN_NAME) {
67
+ prev[propName] = "?";
68
+ return prev;
69
+ }
70
+ const rawValue = object[propName];
71
+ switch (schema.type) {
72
+ case "array":
73
+ prev[propName] =
74
+ ((_a = schema.items) === null || _a === void 0 ? void 0 : _a.type) === "string" &&
75
+ rawValue
76
+ ? rawValue.map(uncamel).join(", ")
77
+ : JSON.stringify(rawValue);
78
+ return prev;
79
+ case "boolean":
80
+ prev[propName] = rawValue ? "✓" : "✕";
81
+ return prev;
82
+ case "integer":
83
+ prev[propName] = [undefined, null].includes(rawValue)
84
+ ? ""
85
+ : `${rawValue}`;
86
+ return prev;
87
+ // @ts-ignore
88
+ case "string":
89
+ if (schema.format === "date" && rawValue) {
90
+ prev[propName] =
91
+ rawValue === "2999-12-31"
92
+ ? "-"
93
+ : localeFormat(new Date(rawValue), (propConfig === null || propConfig === void 0 ? void 0 : propConfig.dateFormat) || DEFAULT_DATE_FORMAT);
94
+ return prev;
95
+ }
96
+ if (schema.format === "date-time" && rawValue) {
97
+ prev[propName] = localeFormat(new Date(rawValue), (propConfig === null || propConfig === void 0 ? void 0 : propConfig.dateFormat) || DEFAULT_DATE_TIME_FORMAT);
98
+ return prev;
99
+ }
100
+ if (schema.enum) {
101
+ prev[propName] = rawValue ? uncamel(rawValue) : "";
102
+ return prev;
103
+ }
104
+ // fallthrough
105
+ default:
106
+ prev[propName] = rawValue ? `${rawValue}` : "";
107
+ return prev;
108
+ }
109
+ }, { _index: rowIndex }))
110
+ : undefined, [columnNames, config, data, properties]);
111
+ const columnCount = columnNames.length;
112
+ const { dynamicWidthColumnCount, fixedWidthColumnsWidth } = React.useMemo(() => {
113
+ let fixedWidthColumnsWidth = 0;
114
+ let dynamicWidthColumnCount = 0;
115
+ columnNames.forEach((propName) => {
116
+ if (propName === SELECT_ALL_COLUMN_NAME) {
117
+ fixedWidthColumnsWidth += SELECT_ALL_COLUMN_WIDTH;
118
+ return;
119
+ }
120
+ const propConfig = config ? config[propName] : undefined;
121
+ if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.width) {
122
+ fixedWidthColumnsWidth += propConfig.width;
123
+ }
124
+ else {
125
+ dynamicWidthColumnCount += 1;
126
+ }
127
+ }, 0);
128
+ return { dynamicWidthColumnCount, fixedWidthColumnsWidth };
129
+ }, [columnNames, config]);
130
+ const columnWidths = React.useMemo(() => {
131
+ const dynamicColumnWidth = Math.floor((width - fixedWidthColumnsWidth) / dynamicWidthColumnCount);
132
+ const result = columnNames.map((propName) => {
133
+ if (propName === SELECT_ALL_COLUMN_NAME) {
134
+ return SELECT_ALL_COLUMN_WIDTH;
135
+ }
136
+ const propConfig = config ? config[propName] : undefined;
137
+ return (propConfig === null || propConfig === void 0 ? void 0 : propConfig.width) || dynamicColumnWidth;
138
+ });
139
+ const totalWidth = sum(result);
140
+ if (totalWidth < width && dynamicWidthColumnCount) {
141
+ // find the last dynamic width column and adjust to overcome rounding differences
142
+ const lastDynamicColumnName = [...columnNames]
143
+ .reverse()
144
+ .find((propName) => !config || !config[propName].width);
145
+ if (lastDynamicColumnName) {
146
+ const lastDynamicColumnIndex = columnNames.indexOf(lastDynamicColumnName);
147
+ result[lastDynamicColumnIndex] += width - totalWidth;
148
+ }
149
+ }
150
+ return result;
151
+ }, [
152
+ columnNames,
153
+ config,
154
+ dynamicWidthColumnCount,
155
+ fixedWidthColumnsWidth,
156
+ width,
157
+ ]);
158
+ const getColumnWidth = React.useCallback((columnIndex) => (columnWidths ? columnWidths[columnIndex] : 1), [columnWidths]);
159
+ const filteredRenderData = React.useMemo(() => {
160
+ if (!renderData || (!isColumnFilterable && !isSearchable)) {
161
+ return renderData;
162
+ }
163
+ return renderData.filter((item) => {
164
+ const lcSearchQuery = searchQuery.toLowerCase();
165
+ if (searchQuery &&
166
+ !columnNames.find((columnName) => `${item[columnName]}`.toLowerCase().includes(lcSearchQuery))) {
167
+ return false;
168
+ }
169
+ let result = true;
170
+ Object.entries(columnFilterMap).forEach(([propName, columnFilterValue]) => {
171
+ if (!result || columnFilterValue === undefined) {
172
+ return;
173
+ }
174
+ const propSchema = properties[propName];
175
+ // @ts-ignore
176
+ const rawValue = data[item._index][propName];
177
+ switch (propSchema.type) {
178
+ case "boolean":
179
+ case "integer":
180
+ result = rawValue === columnFilterValue;
181
+ break;
182
+ // @ts-ignore
183
+ case "string":
184
+ if (typeof columnFilterValue === "object" &&
185
+ (propSchema.format === "date" ||
186
+ propSchema.format === "date-time")) {
187
+ const { from, to } = columnFilterValue;
188
+ const rawDate = rawValue ? new Date(rawValue) : undefined;
189
+ if ((from && (!rawDate || rawDate < from)) ||
190
+ (to && (!rawDate || rawDate > to))) {
191
+ result = false;
192
+ }
193
+ return;
194
+ }
195
+ // fall through
196
+ default:
197
+ // fallback by looking at the render value
198
+ result = `${item[propName]}`
199
+ .toLowerCase()
200
+ .includes(`${columnFilterValue}`.toLowerCase());
201
+ }
202
+ });
203
+ return result;
204
+ });
205
+ }, [
206
+ renderData,
207
+ isColumnFilterable,
208
+ isSearchable,
209
+ searchQuery,
210
+ columnNames,
211
+ columnFilterMap,
212
+ properties,
213
+ data,
214
+ ]);
215
+ // Sort the filtered data
216
+ const sortedRenderData = React.useMemo(() => {
217
+ if (!sortColumn || !filteredRenderData) {
218
+ return filteredRenderData;
219
+ }
220
+ const sortSchema = properties[sortColumn];
221
+ const propConfig = config ? config[sortColumn] : undefined;
222
+ const columnSort = propConfig === null || propConfig === void 0 ? void 0 : propConfig.sort;
223
+ if (columnSort) {
224
+ return filteredRenderData.sort((a, b) => {
225
+ const aData = data[a._index];
226
+ const bData = data[b._index];
227
+ if (!aData) {
228
+ return 1;
229
+ }
230
+ if (!bData) {
231
+ return -1;
232
+ }
233
+ return columnSort(aData, bData, sortAsc);
234
+ });
235
+ }
236
+ return filteredRenderData.sort((a, b) => {
237
+ const sortByValue = (propConfig === null || propConfig === void 0 ? void 0 : propConfig.sortByValue) === undefined
238
+ ? !sortSchema ||
239
+ sortSchema.type === "boolean" ||
240
+ sortSchema.type === "integer" ||
241
+ sortSchema.format === "date" ||
242
+ sortSchema.format === "date-time" ||
243
+ (propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderCell)
244
+ : propConfig.sortByValue;
245
+ const x = sortByValue && data[a._index]
246
+ ? // @ts-ignore
247
+ data[a._index][sortColumn]
248
+ : a[sortColumn].toLowerCase();
249
+ const y = sortByValue && data[b._index]
250
+ ? // @ts-ignore
251
+ data[b._index][sortColumn]
252
+ : b[sortColumn].toLowerCase();
253
+ if (x === y) {
254
+ return 0;
255
+ }
256
+ return (x < y ? 1 : -1) * (sortAsc ? -1 : 1);
257
+ });
258
+ }, [config, data, filteredRenderData, properties, sortAsc, sortColumn]);
259
+ const onSelectAllIndexesHandler = React.useCallback(() => {
260
+ if (!onCheckedIndexesChange || !filteredRenderData) {
261
+ return;
262
+ }
263
+ onCheckedIndexesChange(filteredRenderData.map((el) => el._index));
264
+ }, [onCheckedIndexesChange, filteredRenderData]);
265
+ const isAllRowsChecked = React.useMemo(() => {
266
+ return ((checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length) !== 0 &&
267
+ (filteredRenderData === null || filteredRenderData === void 0 ? void 0 : filteredRenderData.length) === (checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length) &&
268
+ (checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.every((checkedIndex) => filteredRenderData === null || filteredRenderData === void 0 ? void 0 : filteredRenderData.some((el) => el._index === checkedIndex))));
269
+ }, [checkedIndexes, filteredRenderData]);
270
+ const disableColumnFilter = React.useCallback((propName) => {
271
+ const newColumnFilterMap = Object.assign({}, columnFilterMap);
272
+ delete newColumnFilterMap[propName];
273
+ setColumnFilterMap(newColumnFilterMap);
274
+ }, [columnFilterMap]);
275
+ const SchemaTableTh = React.useCallback(({ style, index }) => {
276
+ const propName = columnNames[index];
277
+ const propConfig = config ? config[propName] : undefined;
278
+ let columnFilterStatus = isColumnFilterable && (propConfig === null || propConfig === void 0 ? void 0 : propConfig.isFilterable) !== false
279
+ ? EColumnFilterStatus.AVAILABLE
280
+ : EColumnFilterStatus.UNAVAILABLE;
281
+ if (columnFilterMap[propName] !== undefined) {
282
+ columnFilterStatus = EColumnFilterStatus.ACTIVE;
283
+ }
284
+ return (_jsx(Th, { isAllChecked: isAllRowsChecked, columnFilterStatus: columnFilterStatus, disableColumnFilter: disableColumnFilter, isSortable: !!isSortable, numberOfSelectedRows: checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length, onSelectAllIndexesHandler: onSelectAllIndexesHandler, propConfig: propConfig, propName: propName, schema: properties[propName], setPopoverConfig: setPopoverConfig, setSortAsc: setSortAsc, setSortColumn: setSortColumn, sortAsc: sortColumn === propName ? sortAsc : undefined, style: style }));
285
+ }, [
286
+ checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length,
287
+ columnFilterMap,
288
+ columnNames,
289
+ config,
290
+ disableColumnFilter,
291
+ isAllRowsChecked,
292
+ isColumnFilterable,
293
+ isSortable,
294
+ onSelectAllIndexesHandler,
295
+ properties,
296
+ sortAsc,
297
+ sortColumn,
298
+ ]);
299
+ const SchemaTableTd = React.useCallback(({ columnIndex, rowIndex, style }) => {
300
+ const propName = columnNames[columnIndex];
301
+ return (_jsx(Td, { checkedIndexes: checkedIndexes, columnIndex: columnIndex, data: data, getRowClassName: getRowClassName, getRowSelected: getRowSelected, onCheckedIndexesChange: onCheckedIndexesChange, onRowClick: onRowClick, propConfig: config ? config[propName] : undefined, propName: propName, propSchema: properties[propName], rowHeight: rowHeight, rowIndex: rowIndex, sortedRenderData: sortedRenderData, style: style }));
302
+ }, [
303
+ checkedIndexes,
304
+ columnNames,
305
+ config,
306
+ data,
307
+ getRowClassName,
308
+ getRowSelected,
309
+ onCheckedIndexesChange,
310
+ onRowClick,
311
+ properties,
312
+ rowHeight,
313
+ sortedRenderData,
314
+ ]);
315
+ const onSearchChange = React.useCallback((e) => {
316
+ setSearchQuery(e.currentTarget.value);
317
+ }, []);
318
+ const getRowHeight = React.useCallback(() => rowHeight, [rowHeight]);
319
+ const rowWidth = dynamicWidthColumnCount ? width : fixedWidthColumnsWidth;
320
+ const rowCount = React.useMemo(() => (sortedRenderData ? sortedRenderData.length : 0), [sortedRenderData]);
321
+ const tableBodyHeight = React.useMemo(() => {
322
+ const rowsHeight = rowHeight * rowCount;
323
+ const rowsMaxHeight = maxHeight ? maxHeight - (isSearchable ? 50 : 0) : 0;
324
+ return rowsMaxHeight && rowsMaxHeight < rowsHeight
325
+ ? rowsMaxHeight
326
+ : rowsHeight;
327
+ }, [maxHeight, isSearchable, rowCount, rowHeight]);
328
+ const onPopoverClose = React.useCallback(() => {
329
+ setPopoverConfig(undefined);
330
+ }, []);
331
+ const onSchemaColumnFilterChange = React.useCallback((newColumnFilterValue) => {
332
+ if (!popoverConfig) {
333
+ return;
334
+ }
335
+ if (newColumnFilterValue === undefined) {
336
+ disableColumnFilter(popoverConfig.propName);
337
+ return;
338
+ }
339
+ setColumnFilterMap((columnFilterMap) => (Object.assign(Object.assign({}, columnFilterMap), { [popoverConfig.propName]: newColumnFilterValue })));
340
+ }, [disableColumnFilter, popoverConfig]);
341
+ return (_jsxs("div", Object.assign({ className: `schema-table${onRowClick ? " schema-table--clickable-rows" : ""}`, style: Object.assign(Object.assign({}, style), { width: rowWidth }), role: "table" }, { children: [_jsxs("div", Object.assign({ className: "schema-table__action-container" }, { children: [_jsx("div", Object.assign({ style: { flex: 1 } }, { children: isSearchable ? (_jsx("input", { type: "text", placeholder: searchPlaceholder || "Search...", value: searchQuery, onChange: onSearchChange, autoFocus: true })) : null })), customElement] })), _jsx(Heading, Object.assign({ height: 50, itemCount: columnCount, itemSize: getColumnWidth, layout: "horizontal", width: rowWidth, sortAsc: sortAsc, setSortAsc: setSortAsc, setSortColumn: setSortColumn, sortColumn: sortColumn, sortedRenderData: sortedRenderData, className: "schema-table__th-row" }, { children: SchemaTableTh }), `thead_${rowWidth}_${sortColumn}_${sortAsc}_${searchQuery}`), _jsx(VariableSizeGrid, Object.assign({ className: "schema-table__tbody", height: tableBodyHeight, width: rowWidth, columnWidth: getColumnWidth, rowHeight: getRowHeight, columnCount: columnCount, rowCount: rowCount }, { children: SchemaTableTd }), `tbody_${tableBodyHeight}_${rowWidth}_${sortColumn}_${sortAsc}_${searchQuery}_${columnCount}`), popoverConfig ? (_jsx(SchemaColumnFilterPopover, { referenceElement: popoverConfig.referenceElement, onClose: onPopoverClose, onChange: onSchemaColumnFilterChange, propName: popoverConfig.propName, propSchema: schema.properties[popoverConfig.propName], value: columnFilterMap[popoverConfig.propName] })) : null] })));
342
+ }
343
+ export default React.memo(SchemaTable);
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import "@testing-library/jest-dom";
3
+ import { render, screen } from "@testing-library/react";
4
+ import SchemaTable from "./index";
5
+ import exampleData, { exampleDataSchema } from "../../exampleData";
6
+ it("renders an empty table", () => {
7
+ render(_jsx(SchemaTable, { data: [], schema: {}, width: 1 }));
8
+ const table = screen.getByRole("table");
9
+ expect(table).toBeInTheDocument();
10
+ });
11
+ it("should match the configured width without configuration", () => {
12
+ render(_jsx(SchemaTable, { data: exampleData, schema: exampleDataSchema, width: 1000 }));
13
+ const table = screen.getByRole("table");
14
+ // eslint-disable-next-line testing-library/no-node-access
15
+ const renderedWidth = [...table.querySelectorAll(".schema-table__th")].reduce((prev, node) => prev + parseInt(node.style.width), 0);
16
+ expect(renderedWidth).toBe(1000);
17
+ });
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_DATE_FORMAT = "dd MMM yyyy";
2
+ export declare const DEFAULT_DATE_TIME_FORMAT = "dd MMM yyyy HH:mm";
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_DATE_FORMAT = "dd MMM yyyy";
2
+ export const DEFAULT_DATE_TIME_FORMAT = "dd MMM yyyy HH:mm";
@@ -0,0 +1 @@
1
+ export declare const localeFormat: (date: Date | number, dateFormat: string) => string;
@@ -0,0 +1,3 @@
1
+ import { format } from "date-fns";
2
+ import nl from "date-fns/locale/nl";
3
+ export const localeFormat = (date, dateFormat) => format(date, dateFormat, { locale: nl });
@@ -0,0 +1,2 @@
1
+ import { SchemaObject } from "openapi3-ts/oas31";
2
+ export declare function getEmptyObject<T>(schema: SchemaObject): T;
@@ -0,0 +1,32 @@
1
+ export function getEmptyObject(schema) {
2
+ const { properties = {} } = schema;
3
+ return Object.keys(properties).reduce((prev, propName) => {
4
+ const prop = properties[propName];
5
+ let propValue;
6
+ switch (prop.type) {
7
+ case "string":
8
+ propValue = prop.enum ? prop.enum[0] : prop.default || "";
9
+ break;
10
+ case "array":
11
+ propValue = prop.default || [];
12
+ break;
13
+ case "number":
14
+ case "integer":
15
+ propValue = prop.default || 0;
16
+ break;
17
+ case "boolean":
18
+ propValue = prop.default || false;
19
+ break;
20
+ case "object":
21
+ propValue = prop.default || getEmptyObject(prop);
22
+ break;
23
+ // eslint-disable-next-line no-fallthrough
24
+ default:
25
+ console.log(prop);
26
+ throw new Error("Unsupported property");
27
+ }
28
+ // @ts-ignore
29
+ prev[propName] = propValue;
30
+ return prev;
31
+ }, {});
32
+ }
@@ -0,0 +1 @@
1
+ export declare function uncamel(camel: string): string;
@@ -0,0 +1,17 @@
1
+ import { upperFirst } from "lodash";
2
+ function isUpperCase(letter) {
3
+ return !!letter.match(/[A-Z]/);
4
+ }
5
+ export function uncamel(camel) {
6
+ return upperFirst(camel.replaceAll(/[A-Z]/g, (letter, position) => {
7
+ const prevLetter = camel[position - 1];
8
+ const prevIsUppercase = prevLetter ? isUpperCase(prevLetter) : false;
9
+ const nextLetter = camel[position + 1];
10
+ const nextIsUppercase = nextLetter ? isUpperCase(nextLetter) : false;
11
+ if (position === 0 ||
12
+ (prevIsUppercase && (!nextLetter || nextIsUppercase))) {
13
+ return letter;
14
+ }
15
+ return ` ${nextIsUppercase ? letter : letter.toLowerCase()}`;
16
+ }));
17
+ }
@@ -0,0 +1,4 @@
1
+ import SchemaTable from "./SchemaTable";
2
+ import { IColumnConfig, IRenderData } from "./types";
3
+ export type { IColumnConfig, IRenderData };
4
+ export { SchemaTable };
@@ -0,0 +1,2 @@
1
+ import SchemaTable from "./SchemaTable";
2
+ export { SchemaTable };
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ export interface IColumnConfig<T> {
3
+ align?: "start" | "center" | "end";
4
+ defaultSortDesc?: boolean;
5
+ hidden?: boolean;
6
+ hoverTitle?: string;
7
+ renderCell?: (rowData: T, dataIndex: number, rowIndex: number) => React.ReactElement | null;
8
+ renderData?: (rowData: T, dataIndex: number) => string;
9
+ sort?: (a: T, b: T, sortAsc: boolean) => number;
10
+ sortByValue?: boolean;
11
+ sortable?: boolean;
12
+ title?: string | React.ReactElement;
13
+ width?: number;
14
+ order?: number;
15
+ isFilterable?: boolean;
16
+ dateFormat?: string;
17
+ }
18
+ export interface IRenderData {
19
+ _index: number;
20
+ [key: string]: string | any;
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { oas31 } from "openapi3-ts";
2
+ export interface IExampleData {
3
+ jobId: string;
4
+ /** @enum {string} */
5
+ jobType: "scrape" | "clean";
6
+ topLevelId: number;
7
+ domainId: string;
8
+ hostId: string;
9
+ sourceId: string;
10
+ /** @enum {string} */
11
+ sources: ("fromIndexPage" | "manual")[];
12
+ priority: number;
13
+ /** Format: date-time */
14
+ insertDateTime: string;
15
+ /** Format: date-time */
16
+ scrapeStartDateTime?: string;
17
+ /** Format: date-time */
18
+ scrapeEndDateTime?: string;
19
+ scraperHost?: string;
20
+ scraperContainer?: string;
21
+ errorMessage?: string;
22
+ /** Format: url */
23
+ url?: string;
24
+ retryCount: number;
25
+ isError: boolean;
26
+ }
27
+ export declare const exampleDataSchema: oas31.SchemaObject;
28
+ declare const exampleData: IExampleData[];
29
+ export default exampleData;
@@ -0,0 +1,177 @@
1
+ export const exampleDataSchema = {
2
+ type: "object",
3
+ properties: {
4
+ jobId: {
5
+ type: "string",
6
+ },
7
+ jobType: {
8
+ type: "string",
9
+ enum: ["scrape", "clean"],
10
+ },
11
+ topLevelId: {
12
+ type: "integer",
13
+ },
14
+ domainId: {
15
+ type: "string",
16
+ },
17
+ hostId: {
18
+ type: "string",
19
+ },
20
+ sourceId: {
21
+ type: "string",
22
+ },
23
+ sources: {
24
+ type: "array",
25
+ items: {
26
+ type: "string",
27
+ enum: ["fromIndexPage", "manual"],
28
+ },
29
+ },
30
+ priority: {
31
+ type: "integer",
32
+ minimum: 1,
33
+ maximum: 9,
34
+ },
35
+ insertDateTime: {
36
+ type: "string",
37
+ format: "date-time",
38
+ },
39
+ scrapeStartDateTime: {
40
+ type: "string",
41
+ format: "date-time",
42
+ },
43
+ scrapeEndDateTime: {
44
+ type: "string",
45
+ format: "date-time",
46
+ },
47
+ scraperHost: {
48
+ type: "string",
49
+ },
50
+ scraperContainer: {
51
+ type: "string",
52
+ },
53
+ errorMessage: {
54
+ type: "string",
55
+ },
56
+ url: {
57
+ type: "string",
58
+ format: "url",
59
+ },
60
+ retryCount: {
61
+ type: "integer",
62
+ },
63
+ isError: {
64
+ type: "boolean",
65
+ },
66
+ },
67
+ required: [
68
+ "jobId",
69
+ "jobType",
70
+ "topLevelId",
71
+ "domainId",
72
+ "hostId",
73
+ "sourceId",
74
+ "source",
75
+ "priority",
76
+ "insertDateTime",
77
+ "retryCount",
78
+ ],
79
+ };
80
+ const exampleData = [
81
+ {
82
+ jobId: "Job-1",
83
+ jobType: "clean",
84
+ topLevelId: 1,
85
+ domainId: "1",
86
+ hostId: "1",
87
+ sourceId: "1",
88
+ sources: ["fromIndexPage"],
89
+ priority: 1,
90
+ insertDateTime: "2023-06-01 11:15",
91
+ scrapeStartDateTime: "2023-06-01 01:00",
92
+ scrapeEndDateTime: "2023-06-01 01:10",
93
+ scraperHost: "scraperHost-1",
94
+ scraperContainer: "scraperContainer-1",
95
+ errorMessage: "Failed",
96
+ url: "www.job1.com",
97
+ retryCount: 1,
98
+ isError: false,
99
+ },
100
+ {
101
+ jobId: "Job-2",
102
+ jobType: "scrape",
103
+ topLevelId: 2,
104
+ domainId: "2",
105
+ hostId: "2",
106
+ sourceId: "2",
107
+ sources: ["manual", "fromIndexPage"],
108
+ priority: 2,
109
+ insertDateTime: "2023-06-05 12:20",
110
+ scrapeStartDateTime: "2023-06-01 01:00",
111
+ scrapeEndDateTime: "2023-06-01 01:10",
112
+ scraperHost: "scraperHost-1",
113
+ scraperContainer: "scraperContainer-1",
114
+ errorMessage: "Failed",
115
+ url: "www.asdasd.com",
116
+ retryCount: 2,
117
+ isError: true,
118
+ },
119
+ {
120
+ jobId: "Job-3",
121
+ jobType: "clean",
122
+ topLevelId: 3,
123
+ domainId: "3",
124
+ hostId: "3",
125
+ sourceId: "3",
126
+ sources: ["fromIndexPage"],
127
+ priority: 1,
128
+ insertDateTime: "",
129
+ scrapeStartDateTime: "2023-06-01 01:00",
130
+ scrapeEndDateTime: "2023-06-01 01:10",
131
+ scraperHost: "scraperHost-1",
132
+ scraperContainer: "scraperContainer-1",
133
+ errorMessage: "Failed",
134
+ url: "www.job2.com",
135
+ retryCount: 3,
136
+ isError: false,
137
+ },
138
+ {
139
+ jobId: "Job-4",
140
+ jobType: "clean",
141
+ topLevelId: 3,
142
+ domainId: "3",
143
+ hostId: "3",
144
+ sourceId: "3",
145
+ sources: ["fromIndexPage"],
146
+ priority: 1,
147
+ insertDateTime: "2023-06-01 14:00",
148
+ scrapeStartDateTime: "2023-06-01 01:00",
149
+ scrapeEndDateTime: "2023-06-01 01:10",
150
+ scraperHost: "scraperHost-1",
151
+ scraperContainer: "scraperContainer-1",
152
+ errorMessage: "Failed",
153
+ url: "www.job2.com",
154
+ retryCount: 3,
155
+ isError: false,
156
+ },
157
+ {
158
+ jobId: "Job-5",
159
+ jobType: "clean",
160
+ topLevelId: 3,
161
+ domainId: "3",
162
+ hostId: "3",
163
+ sourceId: "3",
164
+ sources: ["fromIndexPage"],
165
+ priority: 1,
166
+ insertDateTime: "2023-06-05 14:20",
167
+ scrapeStartDateTime: "2023-06-01 01:00",
168
+ scrapeEndDateTime: "2023-06-01 01:10",
169
+ scraperHost: "scraperHost-1",
170
+ scraperContainer: "scraperContainer-1",
171
+ errorMessage: "Failed",
172
+ url: "www.job2.com",
173
+ retryCount: 3,
174
+ isError: false,
175
+ },
176
+ ];
177
+ export default exampleData;
package/dist/index.css CHANGED
@@ -1,7 +1,6 @@
1
- .react-datepicker {
2
- display: flex;
1
+ .schema-table .popover {
2
+ --bs-popover-max-width: 1024px;
3
3
  }
4
-
5
4
  .schema-table__tbody {
6
5
  overflow-x: hidden !important;
7
6
  border-collapse: collapse;
@@ -10,22 +9,27 @@
10
9
  .schema-table__th-row {
11
10
  overflow: hidden !important;
12
11
  }
13
- .schema-table__column-filter {
12
+ .schema-table .schema-column-filter-popover {
14
13
  background-color: #eee;
15
14
  }
16
- .schema-table__column-filter input,
17
- .schema-table__column-filter select {
15
+ .schema-table .schema-column-filter-popover input,
16
+ .schema-table .schema-column-filter-popover select {
18
17
  width: 100%;
19
18
  padding: 3px;
20
19
  margin-top: 3px;
21
20
  border-radius: 5px;
22
21
  }
23
- .schema-table__column-filter input[type=checkbox] {
22
+ .schema-table .schema-column-filter-popover input[type=checkbox] {
24
23
  width: fit-content;
25
24
  margin-top: 10px;
26
25
  margin-left: 30px;
27
26
  }
28
- .schema-table__column-filter .react-datepicker {
27
+ .schema-table .schema-column-filter-popover input[type=number],
28
+ .schema-table .schema-column-filter-popover input[type=text],
29
+ .schema-table .schema-column-filter-popover select {
30
+ min-width: 150px;
31
+ }
32
+ .schema-table .schema-column-filter-popover .react-datepicker {
29
33
  display: flex;
30
34
  }
31
35
  .schema-table__th {
@@ -47,7 +51,7 @@
47
51
  font-style: italic;
48
52
  }
49
53
  .schema-table__th--column-filter-status-ACTIVE svg {
50
- color: red;
54
+ color: var(--bs-red, red);
51
55
  }
52
56
  .schema-table__td {
53
57
  overflow: hidden;
@@ -57,13 +61,13 @@
57
61
  align-items: center;
58
62
  }
59
63
  .schema-table__td--odd {
60
- background-color: #ddd;
64
+ background-color: var(--bs-gray-100, #f8f9fa);
61
65
  }
62
66
  .schema-table__td--even {
63
- background-color: #fff;
67
+ background-color: var(--bs-white, #fff);
64
68
  }
65
69
  .schema-table__td--selected {
66
- background-color: #bbb;
70
+ background-color: var(--bs-gray-300, #dee2e6);
67
71
  }
68
72
  .schema-table__search {
69
73
  margin: 1rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mig-schema-table",
3
- "version": "3.0.28",
3
+ "version": "3.0.29",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist/"