@mwater/visualization 5.4.3 → 5.4.4

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.
@@ -3,8 +3,6 @@ import { Expr } from "@mwater/expressions";
3
3
  design is an array of quick filters (user-selectable filters).
4
4
  */
5
5
  export interface Quickfilter {
6
- /** Optional id for the quickfilter. If not present, a random id will be generated the first time the quickfilter is modified. */
7
- id?: string;
8
6
  /** filter expression (left hand side only. Usually enum, enumset, text, date, datetime, id[], text[]) */
9
7
  expr: Expr;
10
8
  /** optional label */
@@ -11,11 +11,4 @@ export interface QuickfiltersDesignComponentProps {
11
11
  /** List of possible table ids to use */
12
12
  tables: string[];
13
13
  }
14
- export default class QuickfiltersDesignComponent extends React.Component<QuickfiltersDesignComponentProps> {
15
- handleDesignChange: (design: Quickfilter[]) => void;
16
- isMergeable(design: Quickfilter[], index: number): boolean;
17
- renderQuickfilter: (item: Quickfilter, index: number) => React.JSX.Element;
18
- handleAdd: () => void;
19
- handleRemove: (index: number) => void;
20
- render(): React.JSX.Element;
21
- }
14
+ export default function QuickfiltersDesignComponent(props: QuickfiltersDesignComponentProps): React.JSX.Element;
@@ -26,40 +26,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.default = QuickfiltersDesignComponent;
29
30
  const lodash_1 = __importDefault(require("lodash"));
30
- const react_1 = __importDefault(require("react"));
31
- const uuid_1 = __importDefault(require("uuid"));
31
+ const react_1 = __importStar(require("react"));
32
32
  const immer_1 = require("immer");
33
33
  const expressions_ui_1 = require("@mwater/expressions-ui");
34
34
  const expressions_1 = require("@mwater/expressions");
35
35
  const ui = __importStar(require("@mwater/react-library/lib/bootstrap"));
36
36
  const ListEditorComponent_1 = require("@mwater/react-library/lib/ListEditorComponent");
37
37
  // Displays quick filters and allows their value to be modified
38
- class QuickfiltersDesignComponent extends react_1.default.Component {
39
- handleDesignChange = (design) => {
40
- // Use immer produce to create immutable update
38
+ function QuickfiltersDesignComponent(props) {
39
+ // This counter is used to force a complete remount of the ListEditorComponent when items are reordered
40
+ // This is necessary because the ExprComponent inside the list has internal state that can get corrupted
41
+ // during reordering operations. By remounting, we ensure a clean slate for all expression components.
42
+ const [reorderCounter, setReorderCounter] = (0, react_1.useState)(0);
43
+ const handleDesignChange = (design) => {
41
44
  const newDesign = (0, immer_1.produce)(design, draft => {
42
- // Add UUID if missing
43
- for (let i = 0; i < draft.length; i++) {
44
- if (!draft[i].id) {
45
- draft[i].id = uuid_1.default.v4();
46
- }
47
- }
48
45
  // Update merged flag, clearing if not mergeable
49
46
  for (let i = 0; i < draft.length; i++) {
50
- if (draft[i].merged && !this.isMergeable(draft, i)) {
47
+ if (draft[i].merged && !isMergeable(draft, i)) {
51
48
  draft[i].merged = false;
52
49
  }
53
50
  }
54
51
  });
55
- this.props.onDesignChange(newDesign);
52
+ props.onDesignChange(newDesign);
53
+ };
54
+ const handleReorder = (design) => {
55
+ setReorderCounter(c => c + 1);
56
+ handleDesignChange(design);
56
57
  };
57
58
  // Determine if quickfilter at index is mergeable with previous (same type, id table and enum values)
58
- isMergeable(design, index) {
59
+ const isMergeable = (design, index) => {
59
60
  if (index === 0) {
60
61
  return false;
61
62
  }
62
- const exprUtils = new expressions_1.ExprUtils(this.props.schema);
63
+ const exprUtils = new expressions_1.ExprUtils(props.schema);
63
64
  const type = exprUtils.getExprType(design[index].expr);
64
65
  const prevType = exprUtils.getExprType(design[index - 1].expr);
65
66
  const idTable = exprUtils.getExprIdTable(design[index].expr);
@@ -81,33 +82,30 @@ class QuickfiltersDesignComponent extends react_1.default.Component {
81
82
  return false;
82
83
  }
83
84
  return true;
84
- }
85
- renderQuickfilter = (item, index) => {
86
- return react_1.default.createElement(QuickfilterDesignComponent, { key: index, design: item, schema: this.props.schema, dataSource: this.props.dataSource, tables: this.props.tables, mergeable: this.isMergeable(this.props.design, index), onChange: (newItem) => {
87
- const design = this.props.design.slice();
85
+ };
86
+ const renderQuickfilter = (item, index) => {
87
+ return react_1.default.createElement(QuickfilterDesignComponent, { key: index, design: item, schema: props.schema, dataSource: props.dataSource, tables: props.tables, mergeable: isMergeable(props.design, index), onChange: (newItem) => {
88
+ const design = props.design.slice();
88
89
  design[index] = newItem;
89
- return this.handleDesignChange(design);
90
- }, onRemove: this.handleRemove.bind(null, index) });
90
+ handleDesignChange(design);
91
+ }, onRemove: () => handleRemove(index) });
91
92
  };
92
- handleAdd = () => {
93
+ const handleAdd = () => {
93
94
  // Add blank to end
94
- const design = this.props.design.concat([{ expr: null }]);
95
- this.props.onDesignChange(design);
95
+ const design = props.design.concat([{ expr: null }]);
96
+ props.onDesignChange(design);
96
97
  };
97
- handleRemove = (index) => {
98
- const design = this.props.design.slice();
98
+ const handleRemove = (index) => {
99
+ const design = props.design.slice();
99
100
  design.splice(index, 1);
100
- this.props.onDesignChange(design);
101
+ props.onDesignChange(design);
101
102
  };
102
- render() {
103
- return (react_1.default.createElement("div", null,
104
- react_1.default.createElement(ListEditorComponent_1.ListEditorComponent, { items: this.props.design, onItemsChange: this.handleDesignChange, renderItem: this.renderQuickfilter, getReorderableKey: (item, index) => item.id || index }),
105
- this.props.tables.length > 0 ? (react_1.default.createElement("button", { type: "button", className: "btn btn-sm btn-link", onClick: this.handleAdd },
106
- react_1.default.createElement("span", { className: "fas fa-plus me-1" }),
107
- T `Add Quick Filter`)) : undefined));
108
- }
103
+ return (react_1.default.createElement("div", null,
104
+ react_1.default.createElement(ListEditorComponent_1.ListEditorComponent, { key: reorderCounter, items: props.design, onItemsChange: handleReorder, renderItem: renderQuickfilter, getReorderableKey: (item, index) => index }),
105
+ props.tables.length > 0 ? (react_1.default.createElement("button", { type: "button", className: "btn btn-sm btn-link", onClick: handleAdd },
106
+ react_1.default.createElement("span", { className: "fas fa-plus me-1" }),
107
+ T `Add Quick Filter`)) : undefined));
109
108
  }
110
- exports.default = QuickfiltersDesignComponent;
111
109
  /** Single quickfilter design component */
112
110
  class QuickfilterDesignComponent extends react_1.default.Component {
113
111
  constructor(props) {
@@ -119,10 +117,9 @@ class QuickfilterDesignComponent extends react_1.default.Component {
119
117
  }
120
118
  handleTableChange = (table) => {
121
119
  this.setState({ table });
122
- const design = {
123
- expr: null
124
- };
125
- return this.props.onChange(design);
120
+ this.props.onChange((0, immer_1.produce)(this.props.design, draft => {
121
+ draft.expr = null;
122
+ }));
126
123
  };
127
124
  handleExprChange = (expr) => {
128
125
  this.props.onChange((0, immer_1.produce)(this.props.design, draft => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mwater/visualization",
3
- "version": "5.4.3",
3
+ "version": "5.4.4",
4
4
  "description": "Visualization library",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -4,9 +4,6 @@ import { Expr } from "@mwater/expressions"
4
4
  design is an array of quick filters (user-selectable filters).
5
5
  */
6
6
  export interface Quickfilter {
7
- /** Optional id for the quickfilter. If not present, a random id will be generated the first time the quickfilter is modified. */
8
- id?: string
9
-
10
7
  // `table`: (deprecated) table of filter
11
8
 
12
9
  /** filter expression (left hand side only. Usually enum, enumset, text, date, datetime, id[], text[]) */
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash"
2
- import React from "react"
2
+ import React, { useState } from "react"
3
3
  import uuid from "uuid"
4
4
  import { produce } from "immer"
5
5
  import { ExprComponent } from "@mwater/expressions-ui"
@@ -20,35 +20,37 @@ export interface QuickfiltersDesignComponentProps {
20
20
  }
21
21
 
22
22
  // Displays quick filters and allows their value to be modified
23
- export default class QuickfiltersDesignComponent extends React.Component<QuickfiltersDesignComponentProps> {
24
- handleDesignChange = (design: Quickfilter[]) => {
25
- // Use immer produce to create immutable update
26
- const newDesign = produce(design, draft => {
27
- // Add UUID if missing
28
- for (let i = 0; i < draft.length; i++) {
29
- if (!draft[i].id) {
30
- draft[i].id = uuid.v4()
31
- }
32
- }
23
+ export default function QuickfiltersDesignComponent(props: QuickfiltersDesignComponentProps) {
24
+ // This counter is used to force a complete remount of the ListEditorComponent when items are reordered
25
+ // This is necessary because the ExprComponent inside the list has internal state that can get corrupted
26
+ // during reordering operations. By remounting, we ensure a clean slate for all expression components.
27
+ const [reorderCounter, setReorderCounter] = useState(0)
33
28
 
29
+ const handleDesignChange = (design: Quickfilter[]) => {
30
+ const newDesign = produce(design, draft => {
34
31
  // Update merged flag, clearing if not mergeable
35
32
  for (let i = 0; i < draft.length; i++) {
36
- if (draft[i].merged && !this.isMergeable(draft, i)) {
33
+ if (draft[i].merged && !isMergeable(draft, i)) {
37
34
  draft[i].merged = false
38
35
  }
39
36
  }
40
37
  })
41
38
 
42
- this.props.onDesignChange(newDesign)
39
+ props.onDesignChange(newDesign)
40
+ }
41
+
42
+ const handleReorder = (design: Quickfilter[]) => {
43
+ setReorderCounter(c => c + 1)
44
+ handleDesignChange(design)
43
45
  }
44
46
 
45
47
  // Determine if quickfilter at index is mergeable with previous (same type, id table and enum values)
46
- isMergeable(design: Quickfilter[], index: number) {
48
+ const isMergeable = (design: Quickfilter[], index: number) => {
47
49
  if (index === 0) {
48
50
  return false
49
51
  }
50
52
 
51
- const exprUtils = new ExprUtils(this.props.schema)
53
+ const exprUtils = new ExprUtils(props.schema)
52
54
 
53
55
  const type = exprUtils.getExprType(design[index].expr)
54
56
  const prevType = exprUtils.getExprType(design[index - 1].expr)
@@ -81,53 +83,52 @@ export default class QuickfiltersDesignComponent extends React.Component<Quickfi
81
83
  return true
82
84
  }
83
85
 
84
- renderQuickfilter = (item: Quickfilter, index: number) => {
86
+ const renderQuickfilter = (item: Quickfilter, index: number) => {
85
87
  return <QuickfilterDesignComponent
86
88
  key={index}
87
89
  design={item}
88
- schema={this.props.schema}
89
- dataSource={this.props.dataSource}
90
- tables={this.props.tables}
91
- mergeable={this.isMergeable(this.props.design, index)}
90
+ schema={props.schema}
91
+ dataSource={props.dataSource}
92
+ tables={props.tables}
93
+ mergeable={isMergeable(props.design, index)}
92
94
  onChange={(newItem: Quickfilter) => {
93
- const design = this.props.design.slice()
95
+ const design = props.design.slice()
94
96
  design[index] = newItem
95
- return this.handleDesignChange(design)
97
+ handleDesignChange(design)
96
98
  }}
97
- onRemove={this.handleRemove.bind(null, index)}
99
+ onRemove={() => handleRemove(index)}
98
100
  />
99
101
  }
100
102
 
101
- handleAdd = () => {
103
+ const handleAdd = () => {
102
104
  // Add blank to end
103
- const design = this.props.design.concat([{ expr: null }])
104
- this.props.onDesignChange(design)
105
+ const design = props.design.concat([{ expr: null }])
106
+ props.onDesignChange(design)
105
107
  }
106
108
 
107
- handleRemove = (index: number) => {
108
- const design = this.props.design.slice()
109
+ const handleRemove = (index: number) => {
110
+ const design = props.design.slice()
109
111
  design.splice(index, 1)
110
- this.props.onDesignChange(design)
112
+ props.onDesignChange(design)
111
113
  }
112
114
 
113
- render() {
114
- return (
115
- <div>
116
- <ListEditorComponent
117
- items={this.props.design}
118
- onItemsChange={this.handleDesignChange}
119
- renderItem={this.renderQuickfilter}
120
- getReorderableKey={(item, index) => item.id || index}
121
- />
122
- {this.props.tables.length > 0 ? (
123
- <button type="button" className="btn btn-sm btn-link" onClick={this.handleAdd}>
124
- <span className="fas fa-plus me-1" />
125
- {T`Add Quick Filter`}
126
- </button>
127
- ) : undefined}
128
- </div>
129
- )
130
- }
115
+ return (
116
+ <div>
117
+ <ListEditorComponent
118
+ key={reorderCounter}
119
+ items={props.design}
120
+ onItemsChange={handleReorder}
121
+ renderItem={renderQuickfilter}
122
+ getReorderableKey={(item, index) => index}
123
+ />
124
+ {props.tables.length > 0 ? (
125
+ <button type="button" className="btn btn-sm btn-link" onClick={handleAdd}>
126
+ <span className="fas fa-plus me-1" />
127
+ {T`Add Quick Filter`}
128
+ </button>
129
+ ) : undefined}
130
+ </div>
131
+ )
131
132
  }
132
133
 
133
134
  interface QuickfilterDesignComponentProps {
@@ -162,10 +163,9 @@ class QuickfilterDesignComponent extends React.Component<
162
163
 
163
164
  handleTableChange = (table: string) => {
164
165
  this.setState({ table })
165
- const design = {
166
- expr: null
167
- }
168
- return this.props.onChange(design)
166
+ this.props.onChange(produce(this.props.design, draft => {
167
+ draft.expr = null
168
+ }))
169
169
  }
170
170
 
171
171
  handleExprChange = (expr: Expr) => {