mig-schema-table 3.0.24 → 3.0.26

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 CHANGED
@@ -3,78 +3,88 @@
3
3
  This component will render fields dynamically based on openApi schema JSON. Styling can be done using Bootstrap 5 compatible classes
4
4
 
5
5
  # Install
6
+
6
7
  ```
7
8
  npm install mig-schema-table
8
9
  ```
9
10
 
10
11
  ## Usage
12
+
11
13
  #### Schema Example:
14
+
12
15
  ```ts
13
- const userSchema ={
14
- "properties": {
15
- "id":{
16
- "type":"string",
17
- "readOnly": true
18
- },
19
- "name": {
20
- "type": "string",
21
- "minLength": 3
16
+ const userSchema = {
17
+ properties: {
18
+ id: {
19
+ type: "string",
20
+ readOnly: true,
21
+ },
22
+ name: {
23
+ type: "string",
24
+ minLength: 3,
22
25
  },
23
- "dob": {
24
- "type": "string",
25
- "format": "date"
26
+ dob: {
27
+ type: "string",
28
+ format: "date",
29
+ },
30
+ address: {
31
+ type: "string",
32
+ maxLength: 250,
26
33
  },
27
- "address": {
28
- "type": "string",
29
- "maxLength": 250
30
- }
31
34
  },
32
- "required":["name"]
33
- }
35
+ required: ["name"],
36
+ };
34
37
  ```
38
+
35
39
  ```typescript jsx
36
- import React from 'react';
37
- import { SchemaTable, IColumnConfig } from "mig-schema-table";
38
- import "mig-schema-table/dist/index.css";
40
+ import React from "react";
41
+ import { SchemaTable, IColumnConfig } from "mig-schema-table";
42
+ import "mig-schema-table/dist/index.css";
43
+ // Add this for default datepicker styling
44
+ import "react-datepicker/dist/react-datepicker.css";
45
+
46
+ const config: { [keyName: string]: IColumnConfig } = {
47
+ id: {
48
+ hidden: true,
49
+ },
50
+ dob: {
51
+ title: "Date of Birth",
52
+ },
53
+ };
39
54
 
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
- }
55
+ const Table = () => {
56
+ const [users, setUsers] = useState();
57
+
58
+ return (
59
+ <SchemaTableComponent
60
+ data={users || []}
61
+ schema={userSchema}
62
+ width={window.innerWidth}
63
+ height={window.innerHeight - 150}
64
+ config={config}
65
+ />
66
+ );
67
+ };
60
68
  ```
61
69
 
62
70
  ## 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 |
71
+
72
+ | Prop | Type | Description |
73
+ | ------------ | ---------- | ----------------------------------------------------------------------- |
74
+ | schema | `object` | schemaObject to be rendered as a set of fields(example openapi schema). |
75
+ | config | `object` | custom UI config {[keyName: string]: IColumnConfig;}. |
76
+ | data | `array` | data props will be rendered from api |
77
+ | onRowClick | `function` | it will be navigate to detail of row data |
78
+ | width | `number` | this props will be calculated width of table |
79
+ | height | `number` | this props will be calculated height of table |
80
+ | tableTitle | `string` | custom title for table your own |
81
+ | isSearchable | `boolean` | if this props is `true` then the search filed will shown |
82
+ | isSortable | `boolean` | if this props is `true` then the table to be able to shorting the data |
74
83
 
75
84
  ## Config
85
+
76
86
  #### you can import the type of config from the IFieldConfig.
87
+
77
88
  ```ts
78
- const config: { [keyName: string]: IColumnConfig } = {}
89
+ const config: { [keyName: string]: IColumnConfig } = {};
79
90
  ```
80
-
@@ -1,21 +1,17 @@
1
- import { Dispatch, SetStateAction } from "react";
2
1
  import { IColumnConfig } from "../../types";
3
2
  import { oas31 } from "openapi3-ts";
4
3
  import "react-datepicker/dist/react-datepicker.css";
5
- import { tColumnSearchValue } from "../index";
6
4
  interface IColumnFilterRowProps {
7
5
  columnNames: string[];
8
6
  getWidth: (index: number) => number;
9
7
  config?: {
10
8
  [propName: string]: IColumnConfig<any>;
11
9
  };
12
- columnSearchHandler: (propName: string, value: tColumnSearchValue) => void;
10
+ columnSearchHandler: (propName: string, value: string | number | boolean) => void;
13
11
  value: {
14
- [propName: string]: tColumnSearchValue;
12
+ [propName: string]: string | number | boolean;
15
13
  };
16
14
  schema: oas31.SchemaObject;
17
- columnFilterDropdown?: string;
18
- setColumnFilterDropdown?: Dispatch<SetStateAction<string | undefined>>;
19
15
  }
20
- export default function ColumnFilterRow({ columnNames, getWidth, config, value, columnSearchHandler, schema, columnFilterDropdown, setColumnFilterDropdown, }: IColumnFilterRowProps): import("react/jsx-runtime").JSX.Element;
16
+ export default function ColumnFilterRow({ columnNames, getWidth, config, value, columnSearchHandler, schema, }: IColumnFilterRowProps): import("react/jsx-runtime").JSX.Element;
21
17
  export {};
@@ -4,15 +4,15 @@ import { SELECT_ALL_COLUMN_NAME } from "../constants";
4
4
  import DatePicker from "react-datepicker";
5
5
  import nl from "date-fns/locale/nl";
6
6
  import "react-datepicker/dist/react-datepicker.css";
7
+ import { localeFormat } from "../../inc/date";
7
8
  import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_TIME_FORMAT, } from "../../inc/constant";
8
- import { getUnixTimeStamp } from "../../inc/date";
9
- export default function ColumnFilterRow({ columnNames, getWidth, config, value, columnSearchHandler, schema, columnFilterDropdown, setColumnFilterDropdown, }) {
9
+ export default function ColumnFilterRow({ columnNames, getWidth, config, value, columnSearchHandler, schema, }) {
10
10
  const { properties = {} } = schema;
11
11
  const getSelectComponent = React.useCallback((propSchema, propName, value, inputChangeHandler) => {
12
- const enumItems = propSchema.type === "boolean" ? ["", ""] : propSchema.enum;
12
+ const enumItems = propSchema.type === "boolean" ? ["true", "false"] : propSchema.enum;
13
13
  return (_jsxs("select", Object.assign({ value: value, "data-prop-name": propName, onChange: inputChangeHandler }, { children: [_jsx("option", Object.assign({ value: "" }, { children: "All" }), "all"), enumItems.map((name) => (_jsx("option", Object.assign({ value: name }, { children: name }), `column-filter-select-${name}`)))] })));
14
14
  }, []);
15
- const field = React.useCallback((propSchema, propName, propValue) => {
15
+ const field = React.useCallback((propSchema, propName, propValue, propConfig) => {
16
16
  const { type, format, minimum, maximum } = propSchema;
17
17
  const inputChangeHandler = (event) => {
18
18
  columnSearchHandler(propName, event.target.value);
@@ -24,69 +24,24 @@ export default function ColumnFilterRow({ columnNames, getWidth, config, value,
24
24
  return getSelectComponent(propSchema, propName, strValue, inputChangeHandler);
25
25
  }
26
26
  if (format === "date-time" || format === "date") {
27
- const dateFormat = format === "date"
28
- ? DEFAULT_DATE_FORMAT
29
- : DEFAULT_DATE_TIME_FORMAT;
30
- const dateRangeValue = propValue;
31
- const startDate = (dateRangeValue === null || dateRangeValue === void 0 ? void 0 : dateRangeValue.from)
32
- ? new Date(dateRangeValue.from)
33
- : null;
34
- const endDate = (dateRangeValue === null || dateRangeValue === void 0 ? void 0 : dateRangeValue.to)
35
- ? new Date(dateRangeValue.to)
36
- : null;
37
- const startDateChangeHandler = (date) => {
38
- if (endDate && date && date > endDate) {
39
- return;
40
- }
41
- columnSearchHandler(propName, Object.assign(Object.assign({}, dateRangeValue), { from: `${date || ""}`, to: `${date && endDate ? dateRangeValue.to : ""}` }));
27
+ const dateFormat = (propConfig === null || propConfig === void 0 ? void 0 : propConfig.dateFormat) ||
28
+ (format === "date"
29
+ ? DEFAULT_DATE_FORMAT
30
+ : DEFAULT_DATE_TIME_FORMAT);
31
+ const dateChangeHandler = (date) => {
32
+ columnSearchHandler(propName, date ? localeFormat(date, dateFormat) : "");
42
33
  };
43
- const endDateChangeHandler = (date) => {
44
- if (startDate &&
45
- date &&
46
- getUnixTimeStamp(`${date}`) < getUnixTimeStamp(`${startDate}`)) {
47
- return;
48
- }
49
- columnSearchHandler(propName, Object.assign(Object.assign({}, dateRangeValue), { to: `${date || ""}` }));
50
- };
51
- return (_jsx("div", Object.assign({ "data-bs-toggle": "dropdown-menu-item" }, { children: _jsxs("div", Object.assign({ className: "d-flex p-2" }, { children: [_jsxs("div", Object.assign({ className: "d-flex flex-column m-1" }, { children: [_jsx("label", { children: "Start date-time" }), _jsx(DatePicker, { id: "filter-date", dateFormat: dateFormat, "data-prop-name": propName, locale: nl, selected: startDate, onChange: startDateChangeHandler, placeholderText: dateFormat, isClearable: true, selectsStart: true, startDate: startDate, endDate: endDate, 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: endDate, onChange: endDateChangeHandler, placeholderText: dateFormat, isClearable: true, startDate: startDate, endDate: endDate, showTimeSelect: format === "date-time", timeIntervals: 15, shouldCloseOnSelect: format === "date" })] }))] })) })));
34
+ return (_jsx(DatePicker, { id: "filter-date", dateFormat: dateFormat, "data-prop-name": propName, showTimeSelect: format === "date-time", locale: nl, selected: propValue ? new Date(propValue) : null, onChange: dateChangeHandler, value: propValue || undefined, placeholderText: dateFormat, isClearable: true }));
52
35
  }
53
- return (_jsx("div", Object.assign({ className: "p-1" }, { children: _jsx("input", { value: strValue, "data-prop-name": propName, onChange: inputChangeHandler, placeholder: `Search ${propName}` }) })));
36
+ return (_jsx("input", { value: strValue, "data-prop-name": propName, onChange: inputChangeHandler, placeholder: `Search ${propName}` }));
54
37
  case "integer":
55
- return (_jsx("div", Object.assign({ className: "d-flex p-1" }, { children: _jsx("input", { type: "number", value: strValue, "data-prop-name": propName, onChange: inputChangeHandler, placeholder: `Search ${propName}`, min: minimum, max: maximum }) })));
38
+ return (_jsx("input", { type: "number", value: strValue, "data-prop-name": propName, onChange: inputChangeHandler, placeholder: `Search ${propName}`, min: minimum, max: maximum }));
56
39
  case "boolean":
57
40
  return getSelectComponent(propSchema, propName, strValue, inputChangeHandler);
58
41
  default:
59
42
  return _jsx(_Fragment, {});
60
43
  }
61
44
  }, [columnSearchHandler, getSelectComponent]);
62
- const removeDropdown = React.useCallback((e) => {
63
- var _a;
64
- if (!columnFilterDropdown || ((_a = e.target.dataset) === null || _a === void 0 ? void 0 : _a.bsToggle) === "dropdown") {
65
- return;
66
- }
67
- let columnFilterEl = null;
68
- let parentNode = e.target;
69
- while (parentNode && parentNode !== window.document) {
70
- if (typeof parentNode.className === "string" &&
71
- parentNode.className.includes("schema-table__column-filter")) {
72
- columnFilterEl = parentNode;
73
- break;
74
- }
75
- parentNode = parentNode.parentNode;
76
- }
77
- if (!columnFilterEl) {
78
- setColumnFilterDropdown && setColumnFilterDropdown(undefined);
79
- }
80
- }, [columnFilterDropdown, setColumnFilterDropdown]);
81
- React.useEffect(() => {
82
- if (!columnFilterDropdown) {
83
- return;
84
- }
85
- window.addEventListener("click", removeDropdown, { capture: true });
86
- return () => {
87
- window.removeEventListener("click", removeDropdown, { capture: true });
88
- };
89
- }, [columnFilterDropdown, removeDropdown]);
90
45
  const SchemaColumnFilter = React.useCallback((index) => {
91
46
  const propName = columnNames[index];
92
47
  const propSchema = properties[propName];
@@ -95,14 +50,8 @@ export default function ColumnFilterRow({ columnNames, getWidth, config, value,
95
50
  if (propName === SELECT_ALL_COLUMN_NAME || !(propConfig === null || propConfig === void 0 ? void 0 : propConfig.isFilterable)) {
96
51
  return _jsx("div", { className: "schema-table__th" });
97
52
  }
98
- return (_jsx("ul", Object.assign({ className: `column-filter-dropdown ${columnFilterDropdown && columnFilterDropdown === propName
99
- ? "column-filter-dropdown--show"
100
- : ""}`, style: {
101
- position: "fixed",
102
- marginTop: -10,
103
- minHeight: 100,
104
- } }, { children: _jsx("div", Object.assign({ id: "filter-dropdown-item", style: { margin: 10 } }, { children: field(propSchema, propName, propValue) })) })));
105
- }, [columnFilterDropdown, columnNames, config, field, properties, value]);
53
+ return (_jsx("div", Object.assign({ className: "schema-table__th" }, { children: field(propSchema, propName, propValue, propConfig) }), `filter-col-${propName}`));
54
+ }, [columnNames, config, field, properties, value]);
106
55
  return (_jsx("div", Object.assign({ className: "schema-table__th-row", style: { display: "flex", flex: "row" } }, { children: columnNames.map((columnName, index) => {
107
56
  return (_jsx("div", Object.assign({ className: "schema-table__column-filter", style: {
108
57
  width: getWidth(index),
@@ -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: "d-flex p-1" }, { children: _jsx("input", { 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({ 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({ 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 (_jsx("div", Object.assign({ "data-bs-toggle": "dropdown-menu-item" }, { children: _jsxs("div", Object.assign({ className: "d-flex p-2" }, { 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: "p-1" }, { children: _jsx("input", { autoFocus: true, value: (value || ""), "data-prop-name": propName, onChange: (e) => {
72
+ onChange(e.currentTarget.value || undefined);
73
+ }, placeholder: `Search ${propName}` }) })));
74
+ }
75
+ };
76
+ export default React.memo(FilterFormComponent);
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { oas31 } from "openapi3-ts";
3
+ import { TColumnFilterValue } from "../index";
4
+ export interface ISchemaColumnFilterPopoverConfig {
5
+ anchorPoint: {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ anchorPosition?: "bottomLeft" | "bottomRight" | "topLeft" | "topRight";
10
+ propName: string;
11
+ }
12
+ type TSchemaColumnFilterPopoverProps = ISchemaColumnFilterPopoverConfig & {
13
+ onChange: (newValue?: TColumnFilterValue) => void;
14
+ propSchema: oas31.SchemaObject;
15
+ value: TColumnFilterValue;
16
+ onClose: () => void;
17
+ };
18
+ declare const _default: React.MemoExoticComponent<(props: TSchemaColumnFilterPopoverProps) => import("react/jsx-runtime").JSX.Element>;
19
+ export default _default;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import FilterFormComponent from "./FilterFormComponent";
4
+ const SchemaColumnFilterPopover = (props) => {
5
+ const { anchorPoint, onClose, onChange, propName, propSchema, value } = props;
6
+ const [popoverEl, setPopoverEl] = React.useState();
7
+ const rect = React.useMemo(() => {
8
+ return popoverEl === null || popoverEl === void 0 ? void 0 : popoverEl.getBoundingClientRect();
9
+ }, [popoverEl]);
10
+ const ref = React.useCallback((el) => {
11
+ if (el) {
12
+ setPopoverEl(el);
13
+ }
14
+ }, []);
15
+ const style = React.useMemo(() => {
16
+ const result = {};
17
+ let anchorPosition = props.anchorPosition;
18
+ if (!anchorPosition) {
19
+ const anchorPositionVertical = anchorPoint.y > window.innerHeight / 2 ? "bottom" : "top";
20
+ const anchorPositionHorizontal = anchorPoint.x > window.innerWidth / 2 ? "Right" : "Left";
21
+ anchorPosition = `${anchorPositionVertical}${anchorPositionHorizontal}`;
22
+ }
23
+ switch (anchorPosition) {
24
+ case "bottomLeft":
25
+ result.left = (anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.x) || 0;
26
+ result.top = rect ? ((anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.y) || 0) - rect.height : 0;
27
+ break;
28
+ case "topLeft":
29
+ result.left = (anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.x) || 0;
30
+ result.top = (anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.y) || 0;
31
+ break;
32
+ case "bottomRight":
33
+ result.left = rect ? ((anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.x) || 0) - rect.width : 0;
34
+ result.top = rect ? ((anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.y) || 0) - rect.height : 0;
35
+ break;
36
+ case "topRight":
37
+ default:
38
+ result.left = rect ? ((anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.x) || 0) - rect.width : 0;
39
+ result.top = anchorPoint === null || anchorPoint === void 0 ? void 0 : anchorPoint.y;
40
+ break;
41
+ }
42
+ result.maxHeight = window.innerHeight - result.top - 10;
43
+ return result;
44
+ }, [anchorPoint.x, anchorPoint.y, props.anchorPosition, rect]);
45
+ const lightboxRef = React.useRef(null);
46
+ const onLightboxClick = React.useCallback((e) => {
47
+ if (e.target === lightboxRef.current) {
48
+ onClose();
49
+ }
50
+ }, [onClose]);
51
+ return (_jsx("div", Object.assign({ className: `lightbox lightbox--transparent schema-column-filter-popover schema-column-filter-popover__${propSchema.type}${propSchema.format ? `_${propSchema.format}` : ""}`, onClick: onLightboxClick, onContextMenu: onClose, ref: lightboxRef }, { children: _jsx("div", Object.assign({ className: "popover", ref: ref, style: style }, { children: _jsx(FilterFormComponent, { onChange: onChange, propSchema: propSchema, propName: propName, columnFilterValue: value }) })) })));
52
+ };
53
+ export default React.memo(SchemaColumnFilterPopover);
@@ -1,21 +1,27 @@
1
1
  import { oas31 } from "openapi3-ts";
2
2
  import React, { CSSProperties, Dispatch, SetStateAction } from "react";
3
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
+ }
4
10
  interface IThProps {
5
- columnFilterDropdown?: string;
11
+ columnFilterStatus: EColumnFilterStatus;
12
+ disableColumnFilter: (propName: string) => void;
6
13
  isAllChecked?: boolean;
7
- isColumnSearchable?: boolean;
8
14
  isSortable: boolean;
9
- name: string;
10
15
  numberOfSelectedRows?: number;
11
16
  onSelectAllIndexesHandler?: (e: React.ChangeEvent<HTMLInputElement>) => void;
12
17
  propConfig?: IColumnConfig<any>;
18
+ propName: string;
13
19
  schema: oas31.SchemaObject;
14
- setColumnFilterDropdown?: Dispatch<SetStateAction<string | undefined>>;
20
+ setPopoverConfig?: Dispatch<SetStateAction<ISchemaColumnFilterPopoverConfig | undefined>>;
15
21
  setSortAsc: Dispatch<SetStateAction<boolean>>;
16
22
  setSortColumn: Dispatch<SetStateAction<string>>;
17
23
  sortAsc?: boolean;
18
24
  style: CSSProperties;
19
25
  }
20
- declare const _default: ({ columnFilterDropdown, isAllChecked, isColumnSearchable, isSortable, name, numberOfSelectedRows, onSelectAllIndexesHandler, propConfig, schema, setColumnFilterDropdown, setSortAsc, setSortColumn, sortAsc, style, }: IThProps) => import("react/jsx-runtime").JSX.Element;
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;
21
27
  export default _default;
@@ -2,30 +2,60 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { uncamel } from "../../inc/string";
4
4
  import { SELECT_ALL_COLUMN_NAME } from "../constants";
5
- const Th = ({ columnFilterDropdown, isAllChecked, isColumnSearchable, isSortable, name, numberOfSelectedRows, onSelectAllIndexesHandler, propConfig, schema, setColumnFilterDropdown, setSortAsc, setSortColumn, sortAsc, style, }) => {
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, }) => {
6
12
  const thDivProps = {
7
13
  style,
8
- className: `schema-table__th ${isSortable ? "schema-table__th--sortable" : "schema-table__th--unsortable"}`,
14
+ className: `schema-table__th schema-table__th--column-filter-status-${columnFilterStatus} ${isSortable ? "schema-table__th--sortable" : "schema-table__th--unsortable"}`,
9
15
  };
10
16
  const onSortButtonClick = React.useCallback(() => {
11
17
  if (sortAsc === undefined) {
12
- setSortColumn(name);
18
+ setSortColumn(propName);
13
19
  setSortAsc(!(propConfig === null || propConfig === void 0 ? void 0 : propConfig.defaultSortDesc));
14
20
  return;
15
21
  }
16
22
  setSortAsc((sortAsc) => !sortAsc);
17
- }, [propConfig === null || propConfig === void 0 ? void 0 : propConfig.defaultSortDesc, name, setSortAsc, setSortColumn, sortAsc]);
18
- const onFilterButtonClick = React.useCallback(() => {
19
- if (!setColumnFilterDropdown) {
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);
20
34
  return;
21
35
  }
22
- if (columnFilterDropdown && columnFilterDropdown === name) {
23
- setColumnFilterDropdown(undefined);
36
+ if (!setPopoverConfig) {
24
37
  return;
25
38
  }
26
- setColumnFilterDropdown(name);
27
- }, [columnFilterDropdown, name, setColumnFilterDropdown]);
28
- if (name === SELECT_ALL_COLUMN_NAME) {
39
+ const rect = e.currentTarget.getBoundingClientRect();
40
+ setPopoverConfig((popoverConfig) => {
41
+ if (popoverConfig) {
42
+ return undefined;
43
+ }
44
+ return {
45
+ anchorPoint: rect
46
+ ? {
47
+ x: Math.round(rect.x + rect.width),
48
+ y: Math.round(rect.y + rect.height),
49
+ }
50
+ : {
51
+ x: e.clientX,
52
+ y: e.clientY,
53
+ },
54
+ propName,
55
+ };
56
+ });
57
+ }, [columnFilterStatus, disableColumnFilter, propName, setPopoverConfig]);
58
+ if (propName === SELECT_ALL_COLUMN_NAME) {
29
59
  return (_jsx("div", Object.assign({}, thDivProps, { children: _jsx("div", Object.assign({ style: {
30
60
  width: "100%",
31
61
  textAlign: "center",
@@ -47,6 +77,10 @@ const Th = ({ columnFilterDropdown, isAllChecked, isColumnSearchable, isSortable
47
77
  thDivProps.className += ` text-${propConfig.align}`;
48
78
  }
49
79
  }
50
- 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 ? uncamel(name) : 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 ? uncamel(name) : propConfig === null || propConfig === void 0 ? void 0 : propConfig.title }))), (propConfig === null || propConfig === void 0 ? void 0 : propConfig.isFilterable) && isColumnSearchable ? (_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" }, "data-bs-toggle": "dropdown" }, { children: _jsx("polygon", { "data-bs-toggle": "dropdown", fill: "#231F20", points: "14,30 22,25 22,17 35.999,0 17.988,0 0,0 14,17 " }) })) }))) : null] })));
80
+ 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
81
+ ? uncamel(propName)
82
+ : 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
83
+ ? uncamel(propName)
84
+ : 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] })));
51
85
  };
52
86
  export default React.memo(Th);
@@ -15,7 +15,7 @@ export interface ISchemaTableProps<T> {
15
15
  getRowSelected?: (rowData: T) => boolean;
16
16
  maxHeight?: number;
17
17
  isSearchable?: boolean;
18
- isColumnSearchable?: boolean;
18
+ isColumnFilterable?: boolean;
19
19
  isSortable?: boolean;
20
20
  onCheckedIndexesChange?: (dataIndex: number[]) => void;
21
21
  onRowClick?: (rowData: T, dataIndex: number, event: React.MouseEvent) => void;
@@ -25,8 +25,10 @@ export interface ISchemaTableProps<T> {
25
25
  style?: React.CSSProperties;
26
26
  width: number;
27
27
  }
28
- export type tColumnSearchValue = string | number | boolean | {
29
- from: string;
30
- to: string;
28
+ export type TColumnFilterValue = string | number | boolean | {
29
+ from?: Date;
30
+ to?: Date;
31
31
  };
32
- export default function SchemaTable<T>({ Heading, checkedIndexes, config, customElement, data, defaultSortAsc, defaultSortColumn, getRowClassName, getRowSelected, maxHeight, isSearchable, isColumnSearchable, isSortable, onCheckedIndexesChange, onRowClick, rowHeight, schema, searchPlaceholder, style, width, }: ISchemaTableProps<T>): import("react/jsx-runtime").JSX.Element;
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;
@@ -1,19 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
- import { VariableSizeList, VariableSizeGrid } from "react-window";
4
- import { localeFormat, getUnixTimeStamp } from "../inc/date";
3
+ import { VariableSizeGrid, VariableSizeList } from "react-window";
4
+ import { localeFormat } from "../inc/date";
5
5
  import { uncamel } from "../inc/string";
6
- import Th from "./Th";
6
+ import Th, { EColumnFilterStatus } from "./Th";
7
7
  import { SELECT_ALL_COLUMN_NAME, SELECT_ALL_COLUMN_WIDTH } from "./constants";
8
8
  import Td from "./Td";
9
- import ColumnFilterRow from "./ColumnFilterRow";
10
9
  import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_TIME_FORMAT } from "../inc/constant";
11
- export default function SchemaTable({ Heading = VariableSizeList, checkedIndexes, config, customElement, data, defaultSortAsc = false, defaultSortColumn, getRowClassName, getRowSelected, maxHeight, isSearchable, isColumnSearchable, isSortable, onCheckedIndexesChange, onRowClick, rowHeight = 36, schema, searchPlaceholder, style, width, }) {
10
+ import SchemaColumnFilterPopover from "./SchemaColumnFilterPopover";
11
+ 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, }) {
12
12
  const [sortColumn, setSortColumn] = React.useState(defaultSortColumn);
13
13
  const [sortAsc, setSortAsc] = React.useState(defaultSortAsc);
14
14
  const [searchQuery, setSearchQuery] = React.useState("");
15
- const [columnSearchObj, setColumnSearchObj] = React.useState({});
16
- const [columnFilterDropdown, setColumnFilterDropdown] = React.useState();
15
+ const [columnFilterMap, setColumnFilterMap] = React.useState({});
16
+ const [popoverConfig, setPopoverConfig] = React.useState();
17
17
  const { properties = {} } = schema;
18
18
  const columnNames = React.useMemo(() => {
19
19
  const columns = Object.keys(properties);
@@ -143,65 +143,60 @@ export default function SchemaTable({ Heading = VariableSizeList, checkedIndexes
143
143
  ]);
144
144
  const getColumnWidth = React.useCallback((columnIndex) => (columnWidths ? columnWidths[columnIndex] : 1), [columnWidths]);
145
145
  const filteredRenderData = React.useMemo(() => {
146
- if (!renderData || (!isColumnSearchable && !isSearchable)) {
146
+ if (!renderData || (!isColumnFilterable && !isSearchable)) {
147
147
  return renderData;
148
148
  }
149
149
  return renderData.filter((item) => {
150
- const globalSearchFilterResult = searchQuery
151
- ? !!columnNames.find((columnName) =>
150
+ const lcSearchQuery = searchQuery.toLowerCase();
151
+ if (searchQuery &&
152
+ !columnNames.find((columnName) => `${item[columnName]}`.toLowerCase().includes(lcSearchQuery))) {
153
+ return false;
154
+ }
155
+ let result = true;
156
+ Object.entries(columnFilterMap).forEach(([propName, columnFilterValue]) => {
157
+ if (!result || columnFilterValue === undefined) {
158
+ return;
159
+ }
160
+ const propSchema = properties[propName];
152
161
  // @ts-ignore
153
- `${item[columnName]}`
154
- .toLowerCase()
155
- .includes(searchQuery.toLowerCase()))
156
- : true;
157
- const rowColumnValidation = isColumnSearchable
158
- ? Object.keys(columnSearchObj).reduce((previousValue, propName) => {
159
- const propSchema = properties[propName];
160
- const columnSearchValue = columnSearchObj[propName];
161
- const rawValue = `${item[propName]}`;
162
- if (typeof columnSearchValue === "object" &&
163
- (propSchema.format === "date" ||
164
- propSchema.format === "date-time")) {
165
- const dateFormat = propSchema.format === "date"
166
- ? DEFAULT_DATE_FORMAT
167
- : DEFAULT_DATE_TIME_FORMAT;
168
- const { from, to } = columnSearchValue;
169
- if (!from) {
170
- return previousValue;
162
+ const rawValue = data[item._index][propName];
163
+ switch (propSchema.type) {
164
+ case "boolean":
165
+ case "integer":
166
+ result = rawValue === columnFilterValue;
167
+ break;
168
+ // @ts-ignore
169
+ case "string":
170
+ if (typeof columnFilterValue === "object" &&
171
+ (propSchema.format === "date" ||
172
+ propSchema.format === "date-time")) {
173
+ const { from, to } = columnFilterValue;
174
+ const rawDate = rawValue ? new Date(rawValue) : undefined;
175
+ if ((from && (!rawDate || rawDate < from)) ||
176
+ (to && (!rawDate || rawDate > to))) {
177
+ result = false;
178
+ }
179
+ return;
171
180
  }
172
- const startDate = from
173
- ? getUnixTimeStamp(from, dateFormat)
174
- : undefined;
175
- const endDate = to
176
- ? getUnixTimeStamp(to, dateFormat)
177
- : undefined;
178
- const currentDate = rawValue
179
- ? getUnixTimeStamp(rawValue, dateFormat)
180
- : undefined;
181
- if (!startDate || !currentDate) {
182
- return previousValue;
183
- }
184
- previousValue.push(startDate && endDate
185
- ? currentDate >= startDate && currentDate <= endDate
186
- : currentDate >= startDate);
187
- return previousValue;
188
- }
189
- previousValue.push(rawValue
190
- .toLowerCase()
191
- .includes(`${columnSearchValue}`.toLowerCase()));
192
- return previousValue;
193
- }, [])
194
- : [true];
195
- return globalSearchFilterResult && rowColumnValidation.every((el) => el);
181
+ // fall through
182
+ default:
183
+ // fallback by looking at the render value
184
+ result = `${item[propName]}`
185
+ .toLowerCase()
186
+ .includes(`${columnFilterValue}`.toLowerCase());
187
+ }
188
+ });
189
+ return result;
196
190
  });
197
191
  }, [
198
- columnNames,
199
- columnSearchObj,
200
- isColumnSearchable,
201
- isSearchable,
202
- properties,
203
192
  renderData,
193
+ isColumnFilterable,
194
+ isSearchable,
204
195
  searchQuery,
196
+ columnNames,
197
+ columnFilterMap,
198
+ properties,
199
+ data,
205
200
  ]);
206
201
  // Sort the filtered data
207
202
  const sortedRenderData = React.useMemo(() => {
@@ -258,21 +253,33 @@ export default function SchemaTable({ Heading = VariableSizeList, checkedIndexes
258
253
  (filteredRenderData === null || filteredRenderData === void 0 ? void 0 : filteredRenderData.length) === (checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length) &&
259
254
  (checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.every((checkedIndex) => filteredRenderData === null || filteredRenderData === void 0 ? void 0 : filteredRenderData.some((el) => el._index === checkedIndex))));
260
255
  }, [checkedIndexes, filteredRenderData]);
256
+ const disableColumnFilter = React.useCallback((propName) => {
257
+ const newColumnFilterMap = Object.assign({}, columnFilterMap);
258
+ delete newColumnFilterMap[propName];
259
+ setColumnFilterMap(newColumnFilterMap);
260
+ }, [columnFilterMap]);
261
261
  const SchemaTableTh = React.useCallback(({ style, index }) => {
262
262
  const propName = columnNames[index];
263
- return (_jsx(Th, { columnFilterDropdown: columnFilterDropdown, isAllChecked: isAllRowsChecked, isColumnSearchable: isColumnSearchable, isSortable: !!isSortable, name: propName, numberOfSelectedRows: checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length, onSelectAllIndexesHandler: onSelectAllIndexesHandler, propConfig: config ? config[propName] : undefined, schema: properties[propName], setColumnFilterDropdown: setColumnFilterDropdown, setSortAsc: setSortAsc, setSortColumn: setSortColumn, sortAsc: sortColumn === propName ? sortAsc : undefined, style: style }));
263
+ let columnFilterStatus = isColumnFilterable
264
+ ? EColumnFilterStatus.AVAILABLE
265
+ : EColumnFilterStatus.UNAVAILABLE;
266
+ if (columnFilterMap[propName] !== undefined) {
267
+ columnFilterStatus = EColumnFilterStatus.ACTIVE;
268
+ }
269
+ return (_jsx(Th, { isAllChecked: isAllRowsChecked, columnFilterStatus: columnFilterStatus, disableColumnFilter: disableColumnFilter, isSortable: !!isSortable, numberOfSelectedRows: checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length, onSelectAllIndexesHandler: onSelectAllIndexesHandler, propConfig: config ? config[propName] : undefined, propName: propName, schema: properties[propName], setPopoverConfig: setPopoverConfig, setSortAsc: setSortAsc, setSortColumn: setSortColumn, sortAsc: sortColumn === propName ? sortAsc : undefined, style: style }));
264
270
  }, [
271
+ checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length,
272
+ columnFilterMap,
265
273
  columnNames,
266
274
  config,
275
+ disableColumnFilter,
276
+ isAllRowsChecked,
277
+ isColumnFilterable,
267
278
  isSortable,
279
+ onSelectAllIndexesHandler,
268
280
  properties,
269
- sortColumn,
270
281
  sortAsc,
271
- onSelectAllIndexesHandler,
272
- isAllRowsChecked,
273
- checkedIndexes === null || checkedIndexes === void 0 ? void 0 : checkedIndexes.length,
274
- columnFilterDropdown,
275
- isColumnSearchable,
282
+ sortColumn,
276
283
  ]);
277
284
  const SchemaTableTd = React.useCallback(({ columnIndex, rowIndex, style }) => {
278
285
  const propName = columnNames[columnIndex];
@@ -290,9 +297,6 @@ export default function SchemaTable({ Heading = VariableSizeList, checkedIndexes
290
297
  rowHeight,
291
298
  sortedRenderData,
292
299
  ]);
293
- const columnSearchHandler = React.useCallback((propName, value) => {
294
- setColumnSearchObj((columnSearch) => (Object.assign(Object.assign({}, columnSearch), { [propName]: value })));
295
- }, []);
296
300
  const onSearchChange = React.useCallback((e) => {
297
301
  setSearchQuery(e.currentTarget.value);
298
302
  }, []);
@@ -306,5 +310,19 @@ export default function SchemaTable({ Heading = VariableSizeList, checkedIndexes
306
310
  ? rowsMaxHeight
307
311
  : rowsHeight;
308
312
  }, [maxHeight, isSearchable, rowCount, rowHeight]);
309
- return (_jsxs("div", Object.assign({ className: `schema-table${onRowClick ? " schema-table--clickable-rows" : ""}`, style: Object.assign(Object.assign({}, style), { width: rowWidth }) }, { 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}`), isColumnSearchable ? (_jsx(ColumnFilterRow, { schema: schema, columnNames: columnNames, config: config, value: columnSearchObj, columnSearchHandler: columnSearchHandler, getWidth: getColumnWidth, columnFilterDropdown: columnFilterDropdown, setColumnFilterDropdown: setColumnFilterDropdown })) : null, _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}`)] })));
313
+ const onPopoverClose = React.useCallback(() => {
314
+ setPopoverConfig(undefined);
315
+ }, []);
316
+ const onSchemaColumnFilterChange = React.useCallback((newColumnFilterValue) => {
317
+ if (!popoverConfig) {
318
+ return;
319
+ }
320
+ if (newColumnFilterValue === undefined) {
321
+ disableColumnFilter(popoverConfig.propName);
322
+ return;
323
+ }
324
+ setColumnFilterMap((columnFilterMap) => (Object.assign(Object.assign({}, columnFilterMap), { [popoverConfig.propName]: newColumnFilterValue })));
325
+ }, [disableColumnFilter, popoverConfig]);
326
+ 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, { anchorPoint: popoverConfig.anchorPoint, anchorPosition: popoverConfig.anchorPosition, onClose: onPopoverClose, onChange: onSchemaColumnFilterChange, propName: popoverConfig.propName, propSchema: schema.properties[popoverConfig.propName], value: columnFilterMap[popoverConfig.propName] })) : null] })));
310
327
  }
328
+ export default React.memo(SchemaTable);
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,18 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import "@testing-library/jest-dom";
12
+ import { render, screen } from "@testing-library/react";
13
+ import SchemaTable from "./index";
14
+ it("renders an empty table", () => __awaiter(void 0, void 0, void 0, function* () {
15
+ render(_jsx(SchemaTable, { data: [], schema: {}, width: 1 }));
16
+ const table = yield screen.findByRole("table");
17
+ expect(table).toBeInTheDocument();
18
+ }));
@@ -1,2 +1 @@
1
1
  export declare const localeFormat: (date: Date | number, dateFormat: string) => string;
2
- export declare const getUnixTimeStamp: (date: string, dateFormat?: string) => number;
package/dist/inc/date.js CHANGED
@@ -1,7 +1,3 @@
1
1
  import { format } from "date-fns";
2
2
  import nl from "date-fns/locale/nl";
3
3
  export const localeFormat = (date, dateFormat) => format(date, dateFormat, { locale: nl });
4
- export const getUnixTimeStamp = (date, dateFormat) => {
5
- return Math.floor(new Date(format(new Date(date), dateFormat || "MM/dd/yyyy")).getTime() /
6
- 1000);
7
- };
package/dist/index.css CHANGED
@@ -1,3 +1,7 @@
1
+ .react-datepicker {
2
+ display: flex;
3
+ }
4
+
1
5
  .schema-table {
2
6
  overflow: hidden;
3
7
  }
@@ -87,6 +91,12 @@
87
91
  font-weight: bold;
88
92
  display: flex;
89
93
  }
94
+ .schema-table__th--column-filter-status-ACTIVE button {
95
+ font-style: italic;
96
+ }
97
+ .schema-table__th--column-filter-status-ACTIVE svg {
98
+ color: red;
99
+ }
90
100
  .schema-table__td {
91
101
  overflow: hidden;
92
102
  white-space: nowrap;
@@ -126,3 +136,32 @@
126
136
  border-radius: 8px;
127
137
  padding: 4px 10px;
128
138
  }
139
+ .schema-table .schema-column-filter-popover__string_date-time .popover {
140
+ width: 500px;
141
+ max-width: initial;
142
+ }
143
+ .schema-table .lightbox {
144
+ position: fixed;
145
+ top: 0;
146
+ bottom: 0;
147
+ right: 0;
148
+ left: 0;
149
+ background-color: rgba(0, 0, 0, 0.5);
150
+ z-index: 1;
151
+ }
152
+ .schema-table .lightbox--transparent {
153
+ background-color: transparent;
154
+ }
155
+ .schema-table .popover {
156
+ position: fixed;
157
+ border-radius: 2px;
158
+ background: #ddd;
159
+ box-shadow: 0 2px 10px #999999;
160
+ z-index: 1;
161
+ padding: 10px;
162
+ min-width: 150px;
163
+ }
164
+ .schema-table .popover select,
165
+ .schema-table .popover input {
166
+ width: 100%;
167
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mig-schema-table",
3
- "version": "3.0.24",
3
+ "version": "3.0.26",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist/"
@@ -23,9 +23,10 @@
23
23
  "react-window": "^1.8.9"
24
24
  },
25
25
  "scripts": {
26
+ "build": "tsc -p tsconfig.json; sass --no-source-map src/component/index.scss dist/index.css",
27
+ "prepublishOnly": "npm run build",
26
28
  "start": "react-scripts start",
27
- "build": "tsc -p tsconfig.json; sass --no-source-map src/component/SchemaTable/index.scss dist/index.css",
28
- "prepublishOnly": "npm run build"
29
+ "test": "react-scripts test"
29
30
  },
30
31
  "eslintConfig": {
31
32
  "extends": [
@@ -49,6 +50,8 @@
49
50
  "@types/date-fns": "^2.6.0",
50
51
  "@types/lodash": "^4.14.194",
51
52
  "@types/react-window": "^1.8.5",
53
+ "@testing-library/jest-dom": "^5.16.5",
54
+ "@testing-library/react": "^13.4.0",
52
55
  "axios": "^1.4.0",
53
56
  "prettier": "^2.8.8",
54
57
  "react-router-dom": "^6.11.2",