mig-schema-table 1.0.0 → 1.0.2
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.
- package/README.md +80 -92
- package/build/component/SchemaTable/Th/index.d.ts +21 -0
- package/build/component/SchemaTable/Th/index.js +44 -0
- package/build/component/SchemaTable/index.d.ts +27 -0
- package/build/component/SchemaTable/index.js +265 -0
- package/build/inc/date.d.ts +1 -0
- package/build/inc/date.js +3 -0
- package/build/inc/schema.d.ts +2 -0
- package/build/inc/schema.js +32 -0
- package/build/inc/string.d.ts +1 -0
- package/build/inc/string.js +4 -0
- package/build/index.css +132 -0
- package/build/index.css.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +2 -0
- package/build/reportWebVitals.d.ts +3 -0
- package/build/reportWebVitals.js +12 -0
- package/build/setupTests.d.ts +1 -0
- package/build/setupTests.js +5 -0
- package/build/types/type.d.ts +20 -0
- package/build/types/type.js +1 -0
- package/package.json +53 -12
package/README.md
CHANGED
|
@@ -1,92 +1,80 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
##
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
|
82
|
-
|
|
83
|
-
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
|
84
|
-
|
|
85
|
-
## Authors and acknowledgment
|
|
86
|
-
Show your appreciation to those who have contributed to the project.
|
|
87
|
-
|
|
88
|
-
## License
|
|
89
|
-
For open source projects, say how it is licensed.
|
|
90
|
-
|
|
91
|
-
## Project status
|
|
92
|
-
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
|
1
|
+
# schema-table-component
|
|
2
|
+
|
|
3
|
+
This component will render fields dynamically based on openApi schema JSON.
|
|
4
|
+
|
|
5
|
+
# Install
|
|
6
|
+
```
|
|
7
|
+
npm install mig-schema-table
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
#### Schema Example:
|
|
12
|
+
```ts
|
|
13
|
+
const userSchema ={
|
|
14
|
+
"properties": {
|
|
15
|
+
"id":{
|
|
16
|
+
"type":"string",
|
|
17
|
+
"readOnly": true
|
|
18
|
+
},
|
|
19
|
+
"name": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 3
|
|
22
|
+
},
|
|
23
|
+
"dob": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"format": "date"
|
|
26
|
+
},
|
|
27
|
+
"address": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"maxLength": 250
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"required":["name"]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
```typescript jsx
|
|
36
|
+
import React from 'react';
|
|
37
|
+
import { SchemaTableComponent, IColumnConfig } from "mig-schema-table";
|
|
38
|
+
import "mig-schema-table/index.css";
|
|
39
|
+
|
|
40
|
+
const config:{[keyName: string]: IColumnConfig} ={
|
|
41
|
+
"id":{
|
|
42
|
+
hidden:true
|
|
43
|
+
},
|
|
44
|
+
"dob":{
|
|
45
|
+
title:"Date of Birth"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const Table=()=>{
|
|
50
|
+
const [users, setUsers]= useState();
|
|
51
|
+
|
|
52
|
+
return <SchemaTableComponent
|
|
53
|
+
data={users || []}
|
|
54
|
+
schema={userSchema}
|
|
55
|
+
width={window.innerWidth}
|
|
56
|
+
height={window.innerHeight - 150}
|
|
57
|
+
config={config}
|
|
58
|
+
/>
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Component Props
|
|
63
|
+
Prop | Type | Description |
|
|
64
|
+
--- |----------------|-------------------------------------------------------------------------|
|
|
65
|
+
schema | ```object``` | schemaObject to be rendered as a set of fields(example openapi schema). |
|
|
66
|
+
config | ```object``` | custom UI config {[keyName: string]: IColumnConfig;}. |
|
|
67
|
+
data | ```array``` | data props will be rendered from api |
|
|
68
|
+
onRowClick | ```function``` | it will be navigate to detail of row data |
|
|
69
|
+
width | ```number``` | this props will be calculated width of table |
|
|
70
|
+
height | ```number``` | this props will be calculated height of table |
|
|
71
|
+
tableTitle | ```string``` | custom title for table your own |
|
|
72
|
+
isSearchable | ```boolean``` | if this props is ```true``` then the search filed will shown |
|
|
73
|
+
isSortable | ```boolean``` | if this props is ```true``` then the table to be able to shorting the data |
|
|
74
|
+
|
|
75
|
+
## Config
|
|
76
|
+
#### you can import the type of config from the IFieldConfig.
|
|
77
|
+
```ts
|
|
78
|
+
const config: { [keyName: string]: IColumnConfig } = {}
|
|
79
|
+
```
|
|
80
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { oas31 } from "openapi3-ts";
|
|
2
|
+
import { CSSProperties, Dispatch, SetStateAction } from "react";
|
|
3
|
+
import { IColumnConfig } from "../../../types/type";
|
|
4
|
+
interface IThProps {
|
|
5
|
+
columnFilters?: {
|
|
6
|
+
[prop: string]: any;
|
|
7
|
+
};
|
|
8
|
+
config?: IColumnConfig<any>;
|
|
9
|
+
isSortable: boolean;
|
|
10
|
+
name: string;
|
|
11
|
+
schema: oas31.SchemaObject;
|
|
12
|
+
setColumnFilters?: Dispatch<SetStateAction<{
|
|
13
|
+
[prop: string]: any;
|
|
14
|
+
} | undefined>>;
|
|
15
|
+
setSortAsc: Dispatch<SetStateAction<boolean>>;
|
|
16
|
+
setSortColumn: Dispatch<SetStateAction<string>>;
|
|
17
|
+
sortAsc?: boolean;
|
|
18
|
+
style: CSSProperties;
|
|
19
|
+
}
|
|
20
|
+
declare const _default: ({ columnFilters, config, isSortable, name, schema, setColumnFilters, setSortAsc, setSortColumn, sortAsc, style, }: IThProps) => import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { camelTextToTitleText } from "../../../inc/string";
|
|
4
|
+
const Th = ({ columnFilters, config, isSortable, name, schema, setColumnFilters, setSortAsc, setSortColumn, sortAsc, style, }) => {
|
|
5
|
+
const thDivProps = {
|
|
6
|
+
style,
|
|
7
|
+
className: `schema-table__th ${isSortable ? "schema-table__th--sortable" : "schema-table__th--unsortable"}`,
|
|
8
|
+
};
|
|
9
|
+
const onFilterButtonClick = React.useCallback((e) => {
|
|
10
|
+
if (!setColumnFilters) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (columnFilters) {
|
|
14
|
+
setColumnFilters(undefined);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}, [columnFilters, setColumnFilters]);
|
|
18
|
+
const onSortButtonClick = React.useCallback(() => {
|
|
19
|
+
if (sortAsc === undefined) {
|
|
20
|
+
setSortColumn(name);
|
|
21
|
+
setSortAsc(!(config === null || config === void 0 ? void 0 : config.defaultSortDesc));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
setSortAsc((sortAsc) => !sortAsc);
|
|
25
|
+
}, [config === null || config === void 0 ? void 0 : config.defaultSortDesc, name, setSortAsc, setSortColumn, sortAsc]);
|
|
26
|
+
if (!schema) {
|
|
27
|
+
return _jsx("div", Object.assign({}, thDivProps));
|
|
28
|
+
}
|
|
29
|
+
switch (schema.type) {
|
|
30
|
+
case "boolean":
|
|
31
|
+
thDivProps.className += ` text-${(config === null || config === void 0 ? void 0 : config.align) || "center"}`;
|
|
32
|
+
break;
|
|
33
|
+
case "integer":
|
|
34
|
+
case "number":
|
|
35
|
+
thDivProps.className += ` text-${(config === null || config === void 0 ? void 0 : config.align) || "end"}`;
|
|
36
|
+
break;
|
|
37
|
+
case "string":
|
|
38
|
+
if (schema.format && ["date", "date-time"].indexOf(schema.format) >= 0) {
|
|
39
|
+
thDivProps.className += ` text-${(config === null || config === void 0 ? void 0 : config.align) || "end"}`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return (_jsxs("div", Object.assign({}, thDivProps, { children: [isSortable ? (_jsxs("button", Object.assign({ className: "px-0", disabled: (config === null || config === void 0 ? void 0 : config.sortable) === false, onClick: onSortButtonClick }, { children: [(config === null || config === void 0 ? void 0 : config.title) || camelTextToTitleText(name), sortAsc === undefined ? null : sortAsc ? "▲" : "▼"] }))) : (_jsx("div", Object.assign({ style: { lineHeight: "44px" } }, { children: (config === null || config === void 0 ? void 0 : config.title) || camelTextToTitleText(name) }))), (config === null || config === void 0 ? void 0 : config.isFilterable) ? (_jsx("button", Object.assign({ onClick: onFilterButtonClick }, { children: _jsx("svg", Object.assign({ viewBox: "0 0 36 36", xmlns: "http://www.w3.org/2000/svg", height: 16, width: 16, style: { display: "block" } }, { children: _jsx("polygon", { fill: "#231F20", points: "14,30 22,25 22,17 35.999,0 17.988,0 0,0 14,17 " }) })) }))) : null] })));
|
|
43
|
+
};
|
|
44
|
+
export default React.memo(Th);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { oas31 } from "openapi3-ts";
|
|
3
|
+
import { IColumnConfig, IRenderData } from "../../types/type";
|
|
4
|
+
import "../../index.scss";
|
|
5
|
+
export interface ISchemaTableComponentProps<T> {
|
|
6
|
+
config?: {
|
|
7
|
+
[propName: string]: IColumnConfig<T>;
|
|
8
|
+
};
|
|
9
|
+
data: T[];
|
|
10
|
+
defaultSortColumn?: keyof T;
|
|
11
|
+
defaultSortAsc?: boolean;
|
|
12
|
+
getRowSelected?: (rowData: T) => boolean;
|
|
13
|
+
Heading?: any;
|
|
14
|
+
isSearchable?: boolean;
|
|
15
|
+
isSortable?: boolean;
|
|
16
|
+
onRowClick?: (rowData: T, rowIndex: number, event: React.MouseEvent) => void;
|
|
17
|
+
getRowClassName?: (rowData: T, filteredSortedRows: IRenderData[]) => string;
|
|
18
|
+
rowHeight?: number;
|
|
19
|
+
schema: oas31.SchemaObject;
|
|
20
|
+
style?: React.CSSProperties;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
customElement?: React.ReactNode;
|
|
24
|
+
tableTitle?: string;
|
|
25
|
+
searchPlaceholder?: string;
|
|
26
|
+
}
|
|
27
|
+
export default function SchemaTableComponent<T>(props: ISchemaTableComponentProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { VariableSizeList, VariableSizeGrid } from "react-window";
|
|
4
|
+
import { localeFormat } from "../../inc/date";
|
|
5
|
+
import { camelTextToTitleText } from "../../inc/string";
|
|
6
|
+
import Th from "./Th";
|
|
7
|
+
import "../../index.scss";
|
|
8
|
+
export default function SchemaTableComponent(props) {
|
|
9
|
+
const { config, data, defaultSortColumn, defaultSortAsc = false, Heading = VariableSizeList, isSearchable, isSortable, onRowClick, getRowClassName, getRowSelected, rowHeight = 36, schema, style, customElement, tableTitle, searchPlaceholder } = props;
|
|
10
|
+
const [sortColumn, setSortColumn] = React.useState(defaultSortColumn);
|
|
11
|
+
const [sortAsc, setSortAsc] = React.useState(defaultSortAsc);
|
|
12
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
13
|
+
const [columnFilters, setColumnFilters] = React.useState();
|
|
14
|
+
const { properties = {} } = schema;
|
|
15
|
+
const columnNames = React.useMemo(() => {
|
|
16
|
+
const columns = Object.keys(properties);
|
|
17
|
+
if (!config) {
|
|
18
|
+
return columns;
|
|
19
|
+
}
|
|
20
|
+
const invisibleColumns = Object.entries(config).reduce((prev, [propName, propConfig]) => {
|
|
21
|
+
if (propConfig.hidden) {
|
|
22
|
+
prev.push(propName);
|
|
23
|
+
}
|
|
24
|
+
return prev;
|
|
25
|
+
}, []);
|
|
26
|
+
return columns
|
|
27
|
+
.filter((key) => !invisibleColumns.includes(key))
|
|
28
|
+
.sort((columnA, columnB) => {
|
|
29
|
+
let orderA = config[columnA] ? config[columnA].order : undefined;
|
|
30
|
+
if (orderA === undefined) {
|
|
31
|
+
orderA = Object.keys(properties).findIndex((propName) => propName === columnA);
|
|
32
|
+
}
|
|
33
|
+
let orderB = config[columnB] ? config[columnB].order : undefined;
|
|
34
|
+
if (orderB === undefined) {
|
|
35
|
+
orderB = Object.keys(properties).findIndex((propName) => propName === columnB);
|
|
36
|
+
}
|
|
37
|
+
if (orderA === -1) {
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
if (orderB === -1) {
|
|
41
|
+
return -1;
|
|
42
|
+
}
|
|
43
|
+
return orderA - orderB;
|
|
44
|
+
});
|
|
45
|
+
}, [config, properties]);
|
|
46
|
+
const renderData = React.useMemo(() => data
|
|
47
|
+
? data.map((object, rowIndex) => columnNames.reduce((prev, propName) => {
|
|
48
|
+
const schema = properties[propName];
|
|
49
|
+
const propConfig = config ? config[propName] : undefined;
|
|
50
|
+
if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderData) {
|
|
51
|
+
prev[propName] = propConfig.renderData(object, rowIndex);
|
|
52
|
+
return prev;
|
|
53
|
+
}
|
|
54
|
+
if (!schema) {
|
|
55
|
+
prev[propName] = "?";
|
|
56
|
+
return prev;
|
|
57
|
+
}
|
|
58
|
+
const rawValue = object[propName];
|
|
59
|
+
switch (schema.type) {
|
|
60
|
+
case "array":
|
|
61
|
+
prev[propName] = JSON.stringify(rawValue);
|
|
62
|
+
return prev;
|
|
63
|
+
case "boolean":
|
|
64
|
+
prev[propName] = rawValue ? "✓" : "✕";
|
|
65
|
+
return prev;
|
|
66
|
+
case "integer":
|
|
67
|
+
prev[propName] = [undefined, null].includes(rawValue)
|
|
68
|
+
? ""
|
|
69
|
+
: `${rawValue}`;
|
|
70
|
+
return prev;
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
case "string":
|
|
73
|
+
if (schema.format === "date" && rawValue) {
|
|
74
|
+
prev[propName] =
|
|
75
|
+
rawValue === "2999-12-31"
|
|
76
|
+
? "-"
|
|
77
|
+
: localeFormat(new Date(rawValue), "dd MMM yyyy");
|
|
78
|
+
return prev;
|
|
79
|
+
}
|
|
80
|
+
if (schema.format === "date-time" && rawValue) {
|
|
81
|
+
prev[propName] = localeFormat(new Date(rawValue), "dd MMM yyyy hh:mm");
|
|
82
|
+
return prev;
|
|
83
|
+
}
|
|
84
|
+
if (schema.enum) {
|
|
85
|
+
prev[propName] = camelTextToTitleText(rawValue);
|
|
86
|
+
return prev;
|
|
87
|
+
}
|
|
88
|
+
// fallthrough
|
|
89
|
+
default:
|
|
90
|
+
prev[propName] = rawValue ? `${rawValue}` : "";
|
|
91
|
+
return prev;
|
|
92
|
+
}
|
|
93
|
+
}, { _index: rowIndex }))
|
|
94
|
+
: undefined, [columnNames, config, data, properties]);
|
|
95
|
+
const gridWidth = props.width;
|
|
96
|
+
const columnCount = columnNames.length;
|
|
97
|
+
const { columnWidths, dynamicWidthColumnCount, fixedWidthColumnsWidth } = React.useMemo(() => {
|
|
98
|
+
let fixedWidthColumnsWidth = 0;
|
|
99
|
+
let dynamicWidthColumnCount = 0;
|
|
100
|
+
columnNames.forEach((propName) => {
|
|
101
|
+
const propConfig = config ? config[propName] : undefined;
|
|
102
|
+
if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.width) {
|
|
103
|
+
fixedWidthColumnsWidth += propConfig.width;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
dynamicWidthColumnCount += 1;
|
|
107
|
+
}
|
|
108
|
+
}, 0);
|
|
109
|
+
const dynamicColumnWidth = Math.floor((gridWidth - 16 - fixedWidthColumnsWidth) / dynamicWidthColumnCount);
|
|
110
|
+
const columnWidths = columnNames.map((propName) => {
|
|
111
|
+
const propConfig = config ? config[propName] : undefined;
|
|
112
|
+
return (propConfig === null || propConfig === void 0 ? void 0 : propConfig.width) || dynamicColumnWidth;
|
|
113
|
+
});
|
|
114
|
+
return { columnWidths, dynamicWidthColumnCount, fixedWidthColumnsWidth };
|
|
115
|
+
}, [columnNames, config, gridWidth]);
|
|
116
|
+
const getColumnWidth = React.useCallback((columnIndex) => columnWidths[columnIndex], [columnWidths]);
|
|
117
|
+
const SchemaTableTh = React.useCallback(({ style, index }) => {
|
|
118
|
+
const propName = columnNames[index];
|
|
119
|
+
return (_jsx(Th, { columnFilters: columnFilters, config: config ? config[propName] : undefined, isSortable: !!isSortable, name: propName, schema: properties[propName], setColumnFilters: setColumnFilters, setSortColumn: setSortColumn, setSortAsc: setSortAsc, sortAsc: sortColumn === propName ? sortAsc : undefined, style: style }));
|
|
120
|
+
}, [
|
|
121
|
+
columnFilters,
|
|
122
|
+
columnNames,
|
|
123
|
+
config,
|
|
124
|
+
isSortable,
|
|
125
|
+
properties,
|
|
126
|
+
sortAsc,
|
|
127
|
+
sortColumn,
|
|
128
|
+
]);
|
|
129
|
+
const filteredRenderData = React.useMemo(() => {
|
|
130
|
+
let result = renderData;
|
|
131
|
+
if (!result) {
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
if (searchQuery) {
|
|
135
|
+
const lcQuery = searchQuery.toLowerCase();
|
|
136
|
+
result = result.filter((item) => !!columnNames.find((columnName) =>
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
`${item[columnName]}`.toLowerCase().includes(lcQuery)));
|
|
139
|
+
}
|
|
140
|
+
if (!columnFilters) {
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
const columnFilterEntries = Object.entries(columnFilters);
|
|
144
|
+
return result.filter((item) => columnFilterEntries.find(([columnName, columnFilterValue]) =>
|
|
145
|
+
// @ts-ignore
|
|
146
|
+
data[item._index][columnName] === columnFilterValue));
|
|
147
|
+
}, [columnFilters, columnNames, data, renderData, searchQuery]);
|
|
148
|
+
// Sort the filtered data
|
|
149
|
+
const sortedRenderData = React.useMemo(() => {
|
|
150
|
+
if (!sortColumn || !filteredRenderData) {
|
|
151
|
+
return filteredRenderData;
|
|
152
|
+
}
|
|
153
|
+
const sortSchema = properties[sortColumn];
|
|
154
|
+
const propConfig = config ? config[sortColumn] : undefined;
|
|
155
|
+
const columnSort = propConfig === null || propConfig === void 0 ? void 0 : propConfig.sort;
|
|
156
|
+
if (columnSort) {
|
|
157
|
+
return filteredRenderData.sort((a, b) => {
|
|
158
|
+
const aData = data[a._index];
|
|
159
|
+
const bData = data[b._index];
|
|
160
|
+
if (!aData) {
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
if (!bData) {
|
|
164
|
+
return -1;
|
|
165
|
+
}
|
|
166
|
+
return columnSort(aData, bData, sortAsc);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return filteredRenderData.sort((a, b) => {
|
|
170
|
+
const sortByValue = (propConfig === null || propConfig === void 0 ? void 0 : propConfig.sortByValue) === undefined
|
|
171
|
+
? !sortSchema ||
|
|
172
|
+
sortSchema.type === "boolean" ||
|
|
173
|
+
sortSchema.type === "integer" ||
|
|
174
|
+
sortSchema.format === "date" ||
|
|
175
|
+
sortSchema.format === "date-time" ||
|
|
176
|
+
(propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderCell)
|
|
177
|
+
: propConfig.sortByValue;
|
|
178
|
+
const x = sortByValue && data[a._index]
|
|
179
|
+
? // @ts-ignore
|
|
180
|
+
data[a._index][sortColumn]
|
|
181
|
+
: a[sortColumn].toLowerCase();
|
|
182
|
+
const y = sortByValue && data[b._index]
|
|
183
|
+
? // @ts-ignore
|
|
184
|
+
data[b._index][sortColumn]
|
|
185
|
+
: b[sortColumn].toLowerCase();
|
|
186
|
+
if (x === y) {
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
return (x < y ? 1 : -1) * (sortAsc ? -1 : 1);
|
|
190
|
+
});
|
|
191
|
+
}, [config, data, filteredRenderData, properties, sortAsc, sortColumn]);
|
|
192
|
+
const onTdClick = React.useCallback((e) => {
|
|
193
|
+
if (!sortedRenderData || !onRowClick) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const { rowIndex } = e.currentTarget.dataset;
|
|
197
|
+
if (!rowIndex) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const row = sortedRenderData[parseInt(rowIndex, 10)];
|
|
201
|
+
onRowClick(data[row._index], row._index, e);
|
|
202
|
+
}, [data, onRowClick, sortedRenderData]);
|
|
203
|
+
const Td = React.useCallback(({ columnIndex, rowIndex, style }) => {
|
|
204
|
+
if (!sortedRenderData) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
const propName = columnNames[columnIndex];
|
|
208
|
+
const propConfig = config ? config[propName] : undefined;
|
|
209
|
+
const row = sortedRenderData[rowIndex];
|
|
210
|
+
const schema = properties[propName];
|
|
211
|
+
const tdDivProps = {
|
|
212
|
+
"data-row-index": rowIndex,
|
|
213
|
+
"data-column-index": columnIndex,
|
|
214
|
+
key: propName,
|
|
215
|
+
style,
|
|
216
|
+
onClick: !(propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderCell) ? onTdClick : undefined,
|
|
217
|
+
className: `schema-table__td schema-table__td--${rowIndex % 2 ? "odd" : "even"}${row && getRowSelected && getRowSelected(data[row._index])
|
|
218
|
+
? " schema-table__td--selected"
|
|
219
|
+
: ""} ${row && getRowClassName
|
|
220
|
+
? getRowClassName(data[row._index], sortedRenderData)
|
|
221
|
+
: ""}`,
|
|
222
|
+
};
|
|
223
|
+
if (propConfig === null || propConfig === void 0 ? void 0 : propConfig.renderCell) {
|
|
224
|
+
return (_jsx("div", Object.assign({}, tdDivProps, { children: propConfig.renderCell(data[row._index], rowIndex) })));
|
|
225
|
+
}
|
|
226
|
+
if (!schema) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
// @ts-ignore
|
|
230
|
+
switch (schema.type) {
|
|
231
|
+
case "boolean":
|
|
232
|
+
tdDivProps.className += ` text-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "center"}`;
|
|
233
|
+
break;
|
|
234
|
+
case "number":
|
|
235
|
+
case "integer":
|
|
236
|
+
tdDivProps.className += ` text-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "end"}`;
|
|
237
|
+
break;
|
|
238
|
+
case "string":
|
|
239
|
+
// @ts-ignore
|
|
240
|
+
tdDivProps.title = row[propName];
|
|
241
|
+
if (schema.format === "date" || schema.format === "date-time") {
|
|
242
|
+
tdDivProps.className += ` text-${(propConfig === null || propConfig === void 0 ? void 0 : propConfig.align) || "end"}`;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return _jsx("div", Object.assign({}, tdDivProps, { children: row[propName] }));
|
|
246
|
+
}, [
|
|
247
|
+
sortedRenderData,
|
|
248
|
+
columnNames,
|
|
249
|
+
config,
|
|
250
|
+
properties,
|
|
251
|
+
onTdClick,
|
|
252
|
+
getRowSelected,
|
|
253
|
+
data,
|
|
254
|
+
getRowClassName,
|
|
255
|
+
]);
|
|
256
|
+
const onSearchChange = React.useCallback((e) => {
|
|
257
|
+
setSearchQuery(e.currentTarget.value);
|
|
258
|
+
}, []);
|
|
259
|
+
const getRowHeight = React.useCallback(() => rowHeight, [rowHeight]);
|
|
260
|
+
const width = dynamicWidthColumnCount ? gridWidth : fixedWidthColumnsWidth;
|
|
261
|
+
const totalWidth = React.useMemo(() => columnWidths.reduce((a, b) => {
|
|
262
|
+
return a + b;
|
|
263
|
+
}, 0), [columnWidths]);
|
|
264
|
+
return (_jsxs("div", Object.assign({ className: `schema-table${onRowClick ? " schema-table--clickable-rows" : ""}`, style: Object.assign(Object.assign({}, style), { width: dynamicWidthColumnCount ? gridWidth : fixedWidthColumnsWidth }) }, { children: [_jsx("div", Object.assign({ className: "tableTitle" }, { children: tableTitle })), _jsxs("div", Object.assign({ className: "form-action-container" }, { children: [_jsx("div", Object.assign({ className: "flex-1" }, { children: isSearchable ? (_jsx("input", { id: "input-filter", type: "text", placeholder: searchPlaceholder, value: searchQuery, onChange: onSearchChange, autoFocus: true })) : null })), customElement] })), _jsx(Heading, Object.assign({ height: 50, itemCount: columnCount, itemSize: getColumnWidth, layout: "horizontal", width: width, sortAsc: sortAsc, setSortAsc: setSortAsc, setSortColumn: setSortColumn, sortColumn: sortColumn, sortedRenderData: sortedRenderData }, { children: SchemaTableTh }), `thead_${width}_${sortColumn}_${sortAsc}_${searchQuery}`), _jsx(VariableSizeGrid, Object.assign({ className: "schema-table__tbody", height: props.height - (isSearchable ? 50 : 0), width: totalWidth, columnWidth: getColumnWidth, rowHeight: getRowHeight, columnCount: columnCount, rowCount: sortedRenderData ? sortedRenderData.length : 0 }, { children: Td }), `tbody_${width}_${sortColumn}_${sortAsc}_${searchQuery}_${columnCount}`)] })));
|
|
265
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const localeFormat: (date: Date | number, dateFormat: string) => string;
|
|
@@ -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 const camelTextToTitleText: (keyName: string) => string;
|
package/build/index.css
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
table {
|
|
2
|
+
border-collapse: collapse;
|
|
3
|
+
width: 100%;
|
|
4
|
+
cursor: pointer;
|
|
5
|
+
margin-top: 5px;
|
|
6
|
+
}
|
|
7
|
+
table thead {
|
|
8
|
+
width: calc(100% - 1em);
|
|
9
|
+
}
|
|
10
|
+
table thead, table tbody tr {
|
|
11
|
+
display: table;
|
|
12
|
+
width: 100%;
|
|
13
|
+
table-layout: fixed;
|
|
14
|
+
}
|
|
15
|
+
table tbody {
|
|
16
|
+
display: block;
|
|
17
|
+
overflow: auto;
|
|
18
|
+
max-height: 80vh;
|
|
19
|
+
}
|
|
20
|
+
table th {
|
|
21
|
+
background-color: lightgray;
|
|
22
|
+
text-align: left;
|
|
23
|
+
position: sticky;
|
|
24
|
+
top: 0;
|
|
25
|
+
}
|
|
26
|
+
table th .arrow {
|
|
27
|
+
border: solid black;
|
|
28
|
+
border-width: 0 3px 3px 0;
|
|
29
|
+
display: inline-block;
|
|
30
|
+
padding: 3px;
|
|
31
|
+
position: absolute;
|
|
32
|
+
right: 0;
|
|
33
|
+
margin: 6px;
|
|
34
|
+
}
|
|
35
|
+
table th, table td {
|
|
36
|
+
border-right: 1px solid #ddd;
|
|
37
|
+
border-left: 1px solid #ddd;
|
|
38
|
+
padding: 8px;
|
|
39
|
+
}
|
|
40
|
+
table td div {
|
|
41
|
+
text-overflow: ellipsis;
|
|
42
|
+
width: 100%;
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
}
|
|
46
|
+
table tbody tr {
|
|
47
|
+
height: 50px;
|
|
48
|
+
}
|
|
49
|
+
table tbody tr:hover {
|
|
50
|
+
background-color: #858585;
|
|
51
|
+
}
|
|
52
|
+
table tr:nth-child(even) {
|
|
53
|
+
background-color: #f2f2f2;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.ASC {
|
|
57
|
+
transform: rotate(45deg);
|
|
58
|
+
-webkit-transform: rotate(45deg);
|
|
59
|
+
transition: 0.5s;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.DSC {
|
|
63
|
+
transform: rotate(-135deg);
|
|
64
|
+
-webkit-transform: rotate(-135deg);
|
|
65
|
+
transition: 0.5s;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.tableTitle {
|
|
69
|
+
margin-top: 10px;
|
|
70
|
+
margin-bottom: 20px;
|
|
71
|
+
font-size: 25px;
|
|
72
|
+
font-weight: bold;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.schema-table {
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
}
|
|
78
|
+
.schema-table__tbody {
|
|
79
|
+
overflow-x: hidden !important;
|
|
80
|
+
border-collapse: collapse;
|
|
81
|
+
width: 100%;
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
}
|
|
84
|
+
.schema-table__th-row {
|
|
85
|
+
overflow: hidden !important;
|
|
86
|
+
}
|
|
87
|
+
.schema-table__th {
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
background-color: #eee;
|
|
90
|
+
}
|
|
91
|
+
.schema-table__th,
|
|
92
|
+
.schema-table__th button {
|
|
93
|
+
font-weight: bold;
|
|
94
|
+
margin-top: 10px;
|
|
95
|
+
display: flex;
|
|
96
|
+
}
|
|
97
|
+
.schema-table__th--unsortable {
|
|
98
|
+
padding-left: 1rem;
|
|
99
|
+
}
|
|
100
|
+
.schema-table__td {
|
|
101
|
+
overflow: hidden;
|
|
102
|
+
white-space: nowrap;
|
|
103
|
+
text-overflow: ellipsis;
|
|
104
|
+
padding-left: 8px;
|
|
105
|
+
align-items: center;
|
|
106
|
+
line-height: 40px;
|
|
107
|
+
}
|
|
108
|
+
.schema-table__td--odd {
|
|
109
|
+
background-color: #ddd;
|
|
110
|
+
}
|
|
111
|
+
.schema-table__td--even {
|
|
112
|
+
background-color: #fff;
|
|
113
|
+
}
|
|
114
|
+
.schema-table__td--selected {
|
|
115
|
+
background-color: #bbb;
|
|
116
|
+
}
|
|
117
|
+
.schema-table__search {
|
|
118
|
+
margin: 1rem;
|
|
119
|
+
padding-right: 1rem;
|
|
120
|
+
}
|
|
121
|
+
.schema-table__search input {
|
|
122
|
+
width: 100%;
|
|
123
|
+
line-height: 20px;
|
|
124
|
+
}
|
|
125
|
+
.schema-table--clickable-rows .schema-table__td {
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
}
|
|
128
|
+
.schema-table button {
|
|
129
|
+
border: 1px solid transparent;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../src/index.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAIA;EACE;EACA;EACA;EACA;;AAKF;EACE;;AACA;EACE;;AAKN;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAIF;EAgEE;;AA/DA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;AAAA;EAEE;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAEF;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;;AAEF;EACE","file":"index.css"}
|
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const reportWebVitals = (onPerfEntry) => {
|
|
2
|
+
if (onPerfEntry && onPerfEntry instanceof Function) {
|
|
3
|
+
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
|
4
|
+
getCLS(onPerfEntry);
|
|
5
|
+
getFID(onPerfEntry);
|
|
6
|
+
getFCP(onPerfEntry);
|
|
7
|
+
getLCP(onPerfEntry);
|
|
8
|
+
getTTFB(onPerfEntry);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
export default reportWebVitals;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
isFilterable?: boolean;
|
|
8
|
+
renderCell?: (rowData: T, index: number) => React.ReactElement | null;
|
|
9
|
+
renderData?: (rowData: T, index: number) => string;
|
|
10
|
+
sort?: (a: T, b: T, sortAsc: boolean) => number;
|
|
11
|
+
sortByValue?: boolean;
|
|
12
|
+
sortable?: boolean;
|
|
13
|
+
title?: string | React.ReactElement;
|
|
14
|
+
width?: number;
|
|
15
|
+
order?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface IRenderData {
|
|
18
|
+
_index: number;
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,19 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mig-schema-table",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"main": "build/static/js/main.js",
|
|
5
|
+
"files": [
|
|
6
|
+
"build/"
|
|
7
|
+
],
|
|
8
|
+
"private": false,
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@testing-library/jest-dom": "^5.16.5",
|
|
11
|
+
"@testing-library/react": "^13.4.0",
|
|
12
|
+
"@testing-library/user-event": "^13.5.0",
|
|
13
|
+
"@types/jest": "^27.5.2",
|
|
14
|
+
"@types/node": "^16.18.31",
|
|
15
|
+
"@types/react": "^18.2.6",
|
|
16
|
+
"@types/react-dom": "^18.2.4",
|
|
17
|
+
"copyfiles": "^2.4.1",
|
|
18
|
+
"date-fns": "^2.30.0",
|
|
19
|
+
"lodash": "^4.17.21",
|
|
20
|
+
"openapi-typescript": "^6.2.4",
|
|
21
|
+
"openapi3-ts": "^4.1.2",
|
|
22
|
+
"react": "^18.2.0",
|
|
23
|
+
"react-dom": "^18.2.0",
|
|
24
|
+
"react-scripts": "5.0.1",
|
|
25
|
+
"react-window": "^1.8.9",
|
|
26
|
+
"typescript": "^4.9.5",
|
|
27
|
+
"web-vitals": "^2.1.4"
|
|
28
|
+
},
|
|
6
29
|
"scripts": {
|
|
7
|
-
"
|
|
30
|
+
"start": "react-scripts start",
|
|
31
|
+
"build": "tsc; sass src/index.scss build/index.css"
|
|
8
32
|
},
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
33
|
+
"eslintConfig": {
|
|
34
|
+
"extends": [
|
|
35
|
+
"react-app",
|
|
36
|
+
"react-app/jest"
|
|
37
|
+
]
|
|
12
38
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
39
|
+
"browserslist": {
|
|
40
|
+
"production": [
|
|
41
|
+
">0.2%",
|
|
42
|
+
"not dead",
|
|
43
|
+
"not op_mini all"
|
|
44
|
+
],
|
|
45
|
+
"development": [
|
|
46
|
+
"last 1 chrome version",
|
|
47
|
+
"last 1 firefox version",
|
|
48
|
+
"last 1 safari version"
|
|
49
|
+
]
|
|
17
50
|
},
|
|
18
|
-
"
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/date-fns": "^2.6.0",
|
|
53
|
+
"@types/lodash": "^4.14.194",
|
|
54
|
+
"@types/react-window": "^1.8.5",
|
|
55
|
+
"axios": "^1.4.0",
|
|
56
|
+
"react-router-dom": "^6.11.2",
|
|
57
|
+
"sass": "^1.62.1",
|
|
58
|
+
"scss": "^0.2.4"
|
|
59
|
+
}
|
|
19
60
|
}
|