@mwater/visualization 5.4.2 → 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.
@@ -2,7 +2,6 @@ import React, { ReactNode } from "react";
2
2
  import { DataSource, Schema } from "@mwater/expressions";
3
3
  import UndoStack from "../UndoStack";
4
4
  import { DashboardViewComponentHandle } from "./DashboardViewComponent";
5
- import QuickfiltersComponent from "../quickfilter/QuickfiltersComponent";
6
5
  import SettingsModalComponent from "./SettingsModalComponent";
7
6
  import { DashboardDesign } from "./DashboardDesign";
8
7
  import DashboardDataSource from "./DashboardDataSource";
@@ -78,25 +77,12 @@ export default class DashboardComponent extends React.Component<DashboardCompone
78
77
  getCompiledFilters(): JsonQLFilter[];
79
78
  /** Translate function to use for display. Do not use when editing. */
80
79
  translate: (input: string) => string;
81
- renderEditingSwitch(): React.DetailedReactHTMLElement<{
82
- key: string;
83
- className: string;
84
- onClick: () => void;
85
- }, HTMLElement>;
86
- renderStyle(): React.DetailedReactHTMLElement<{
87
- type: string;
88
- key: string;
89
- className: string;
90
- onClick: () => void;
91
- }, HTMLElement>;
92
- renderActionLinks(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
93
- renderTitleBar(): React.DetailedReactHTMLElement<{
94
- style: {
95
- height: number;
96
- padding: number;
97
- };
98
- }, HTMLElement>;
99
- renderQuickfilter(): React.CElement<import("../quickfilter/QuickfiltersComponent").QuickfiltersComponentProps, QuickfiltersComponent>;
80
+ renderEditingSwitch(): React.JSX.Element;
81
+ renderStyle(): React.JSX.Element;
82
+ renderActionLinks(): React.JSX.Element;
83
+ renderTitleBar(): React.JSX.Element;
84
+ renderQuickfilter(): React.JSX.Element;
85
+ renderFloatingShowQuickfiltersButton(): React.JSX.Element | null;
100
86
  refDashboardView: (el: any) => void;
101
87
  render(): React.JSX.Element;
102
88
  }
@@ -28,7 +28,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const lodash_1 = __importDefault(require("lodash"));
30
30
  const react_1 = __importDefault(require("react"));
31
- const R = react_1.default.createElement;
32
31
  const UndoStack_1 = __importDefault(require("../UndoStack"));
33
32
  const DashboardUtils = __importStar(require("./DashboardUtils"));
34
33
  const DashboardViewComponent_1 = __importDefault(require("./DashboardViewComponent"));
@@ -162,66 +161,98 @@ class DashboardComponent extends react_1.default.Component {
162
161
  return this.props.design.translations?.[displayLocale]?.[input] ?? input;
163
162
  };
164
163
  renderEditingSwitch() {
165
- return R("a", {
166
- key: "edit",
167
- className: `btn btn-primary btn-sm ${this.state.editing ? "active" : ""}`,
168
- onClick: this.handleToggleEditing
169
- }, R("span", { className: "fas fa-pencil-alt" }), " ", this.state.editing ? T `Editing` : T `Edit`);
164
+ return (react_1.default.createElement("a", { key: "edit", className: `btn btn-primary btn-sm ${this.state.editing ? "active" : ""}`, onClick: this.handleToggleEditing },
165
+ react_1.default.createElement("span", { className: "fas fa-pencil-alt" }),
166
+ " ",
167
+ this.state.editing ? T `Editing` : T `Edit`));
170
168
  }
171
169
  renderStyle() {
172
- return R("button", { type: "button", key: "style", className: "btn btn-link btn-sm", onClick: this.handleOpenLayoutOptions }, R("span", { className: "fa fa-mobile" }), R("span", { className: "hide-600px" }, " ", T `Layout`));
170
+ return (react_1.default.createElement("button", { type: "button", key: "style", className: "btn btn-link btn-sm", onClick: this.handleOpenLayoutOptions },
171
+ react_1.default.createElement("span", { className: "fa fa-mobile" }),
172
+ react_1.default.createElement("span", { className: "hide-600px" },
173
+ " ",
174
+ T `Layout`)));
173
175
  }
174
176
  renderActionLinks() {
175
- return R("div", null, this.state.editing
176
- ? [
177
- R("a", {
178
- key: "undo",
179
- className: `btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`,
180
- onClick: this.handleUndo
181
- }, R("span", { className: "fas fa-caret-left" }), R("span", { className: "hide-600px" }, " ", T `Undo`)),
182
- " ",
183
- R("a", {
184
- key: "redo",
185
- className: `btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`,
186
- onClick: this.handleRedo
187
- }, R("span", { className: "fas fa-caret-right" }), R("span", { className: "hide-600px" }, " ", T `Redo`))
188
- ]
189
- : undefined, !this.state.editing && this.props.design.otherLocales && this.props.design.otherLocales.length > 0
190
- ? R("div", { key: "translations", className: "dropdown d-inline-block" }, R("a", {
191
- className: "btn btn-link btn-sm dropdown-toggle",
192
- "data-bs-toggle": "dropdown"
193
- }, R("span", { className: "fal fa-globe" }), " ", this.state.locale), R("ul", { className: "dropdown-menu dropdown-menu-end" }, [this.props.design.locale || "en", ...this.props.design.otherLocales].map(locale => R("li", { key: locale }, R("a", {
194
- className: "dropdown-item",
195
- onClick: () => this.setState({ locale: locale })
196
- }, __1.languages.find(l => l.code === locale)?.name || locale)))))
197
- : undefined, R("a", { key: "print", className: "btn btn-link btn-sm", onClick: this.handlePrint }, R("span", { className: "fas fa-print" }), R("span", { className: "hide-600px" }, " ", T `Print`)), R("a", { key: "refresh", className: "btn btn-link btn-sm", onClick: this.handleRefreshData }, R("span", { className: "fas fa-sync" }), R("span", { className: "hide-600px" }, " ", T `Refresh`)), this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
198
- ? R("a", { key: "showQuickfilters", className: "btn btn-link btn-sm", onClick: this.handleShowQuickfilters }, R("span", { className: "fa fa-filter" }), R("span", { className: "hide-600px" }, " ", T `Show Quickfilters`))
199
- : undefined,
200
- // R 'a', key: "export", className: "btn btn-link btn-sm", onClick: @handleSaveDesignFile,
201
- // R('span', className: "glyphicon glyphicon-download-alt")
202
- // " Export"
203
- this.state.editing
204
- ? R("a", { key: "settings", className: "btn btn-link btn-sm", onClick: this.handleSettings }, R("span", { className: "fas fa-cog" }), R("span", { className: "hide-600px" }, " ", T `Settings`))
205
- : undefined, this.state.editing ? this.renderStyle() : undefined, this.props.extraTitleButtonsElem, this.props.onDesignChange != null ? this.renderEditingSwitch() : undefined);
177
+ return (react_1.default.createElement("div", null,
178
+ this.state.editing
179
+ ? [
180
+ react_1.default.createElement("a", { key: "undo", className: `btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`, onClick: this.handleUndo },
181
+ react_1.default.createElement("span", { className: "fas fa-caret-left" }),
182
+ react_1.default.createElement("span", { className: "hide-600px" },
183
+ " ",
184
+ T `Undo`)),
185
+ " ",
186
+ react_1.default.createElement("a", { key: "redo", className: `btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`, onClick: this.handleRedo },
187
+ react_1.default.createElement("span", { className: "fas fa-caret-right" }),
188
+ react_1.default.createElement("span", { className: "hide-600px" },
189
+ " ",
190
+ T `Redo`))
191
+ ]
192
+ : undefined,
193
+ !this.state.editing && this.props.design.otherLocales && this.props.design.otherLocales.length > 0
194
+ ? react_1.default.createElement("div", { key: "translations", className: "dropdown d-inline-block" },
195
+ react_1.default.createElement("a", { className: "btn btn-link btn-sm dropdown-toggle", "data-bs-toggle": "dropdown" },
196
+ react_1.default.createElement("span", { className: "fal fa-globe" }),
197
+ " ",
198
+ this.state.locale),
199
+ react_1.default.createElement("ul", { className: "dropdown-menu dropdown-menu-end" }, [this.props.design.locale || "en", ...this.props.design.otherLocales].map(locale => react_1.default.createElement("li", { key: locale },
200
+ react_1.default.createElement("a", { className: "dropdown-item", onClick: () => this.setState({ locale: locale }) }, __1.languages.find(l => l.code === locale)?.name || locale)))))
201
+ : undefined,
202
+ react_1.default.createElement("a", { key: "print", className: "btn btn-link btn-sm", onClick: this.handlePrint },
203
+ react_1.default.createElement("span", { className: "fas fa-print" }),
204
+ react_1.default.createElement("span", { className: "hide-600px" },
205
+ " ",
206
+ T `Print`)),
207
+ react_1.default.createElement("a", { key: "refresh", className: "btn btn-link btn-sm", onClick: this.handleRefreshData },
208
+ react_1.default.createElement("span", { className: "fas fa-sync" }),
209
+ react_1.default.createElement("span", { className: "hide-600px" },
210
+ " ",
211
+ T `Refresh`)),
212
+ this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
213
+ ? react_1.default.createElement("a", { key: "showQuickfilters", className: "btn btn-link btn-sm", onClick: this.handleShowQuickfilters },
214
+ react_1.default.createElement("span", { className: "fa fa-filter" }),
215
+ react_1.default.createElement("span", { className: "hide-600px" },
216
+ " ",
217
+ T `Show Quickfilters`))
218
+ : undefined,
219
+ this.state.editing
220
+ ? react_1.default.createElement("a", { key: "settings", className: "btn btn-link btn-sm", onClick: this.handleSettings },
221
+ react_1.default.createElement("span", { className: "fas fa-cog" }),
222
+ react_1.default.createElement("span", { className: "hide-600px" },
223
+ " ",
224
+ T `Settings`))
225
+ : undefined,
226
+ this.state.editing ? this.renderStyle() : undefined,
227
+ this.props.extraTitleButtonsElem,
228
+ this.props.onDesignChange != null ? this.renderEditingSwitch() : undefined));
206
229
  }
207
230
  renderTitleBar() {
208
- return R("div", { style: { height: 40, padding: 4 } }, R("div", { style: { float: "right" } }, this.renderActionLinks()), this.props.titleElem);
231
+ return (react_1.default.createElement("div", { style: { height: 40, padding: 4 } },
232
+ react_1.default.createElement("div", { style: { float: "right" } }, this.renderActionLinks()),
233
+ this.props.titleElem));
209
234
  }
210
235
  renderQuickfilter() {
211
- return R(QuickfiltersComponent_1.default, {
212
- design: this.props.design.quickfilters || [],
213
- schema: this.props.schema,
214
- dataSource: this.props.dataSource,
215
- quickfiltersDataSource: this.props.dashboardDataSource.getQuickfiltersDataSource(),
216
- values: this.state.quickfiltersValues || undefined,
217
- onValuesChange: (values) => this.setState({ quickfiltersValues: values }),
218
- locks: this.props.quickfilterLocks,
219
- filters: this.getCompiledFilters(),
220
- hideTopBorder: this.props.hideTitleBar,
236
+ return react_1.default.createElement(QuickfiltersComponent_1.default, { design: this.props.design.quickfilters || [], schema: this.props.schema, dataSource: this.props.dataSource, quickfiltersDataSource: this.props.dashboardDataSource.getQuickfiltersDataSource(), values: this.state.quickfiltersValues || undefined, onValuesChange: (values) => this.setState({ quickfiltersValues: values }), locks: this.props.quickfilterLocks, filters: this.getCompiledFilters(), hideTopBorder: this.props.hideTitleBar,
221
237
  // Don't hide if title bar is hidden as it can't be shown again
222
- onHide: () => !this.props.hideTitleBar ? this.setState({ hideQuickfilters: true }) : undefined,
223
- translate: this.translate
224
- });
238
+ onHide: () => this.setState({ hideQuickfilters: true }), translate: this.translate });
239
+ }
240
+ renderFloatingShowQuickfiltersButton() {
241
+ // Only show if:
242
+ // 1. Quick filters exist
243
+ // 2. Quick filters are hidden
244
+ // 3. Title bar is hidden (since otherwise button is in title bar)
245
+ if (!this.props.design.quickfilters?.length || !this.state.hideQuickfilters || !this.props.hideTitleBar) {
246
+ return null;
247
+ }
248
+ return (react_1.default.createElement("div", { style: {
249
+ position: "absolute",
250
+ top: 5,
251
+ right: 20,
252
+ zIndex: 1000
253
+ } },
254
+ react_1.default.createElement("button", { className: "btn btn-link btn-sm", onClick: () => this.setState({ hideQuickfilters: false }) },
255
+ react_1.default.createElement("i", { className: "fa fa-angle-double-down" }))));
225
256
  }
226
257
  refDashboardView = (el) => {
227
258
  this.dashboardView = el;
@@ -231,32 +262,8 @@ class DashboardComponent extends react_1.default.Component {
231
262
  // Compile quickfilters
232
263
  filters = filters.concat(new QuickfilterCompiler_1.default(this.props.schema).compile(this.props.design.quickfilters || [], this.state.quickfiltersValues, this.props.quickfilterLocks));
233
264
  const displayLocale = this.state.editing ? this.props.design.locale || "en" : this.state.locale;
234
- const dashboardView = R(DashboardViewComponent_1.default, {
235
- schema: this.props.schema,
236
- dataSource: this.props.dataSource,
237
- dashboardDataSource: this.props.dashboardDataSource,
238
- ref: this.refDashboardView,
239
- design: this.props.design,
240
- onDesignChange: this.state.editing ? this.props.onDesignChange : undefined,
241
- filters,
242
- onRowClick: this.props.onRowClick,
243
- namedStrings: this.props.namedStrings,
244
- hideScopes: this.state.hideQuickfilters,
245
- refreshKey: this.state.refreshKey,
246
- locale: displayLocale
247
- });
248
- const readonlyDashboardView = R(DashboardViewComponent_1.default, {
249
- schema: this.props.schema,
250
- dataSource: this.props.dataSource,
251
- dashboardDataSource: this.props.dashboardDataSource,
252
- ref: this.refDashboardView,
253
- design: this.props.design,
254
- filters,
255
- onRowClick: this.props.onRowClick,
256
- namedStrings: this.props.namedStrings,
257
- hideScopes: this.state.hideQuickfilters,
258
- locale: displayLocale
259
- });
265
+ const dashboardView = react_1.default.createElement(DashboardViewComponent_1.default, { schema: this.props.schema, dataSource: this.props.dataSource, dashboardDataSource: this.props.dashboardDataSource, ref: this.refDashboardView, design: this.props.design, onDesignChange: this.state.editing ? this.props.onDesignChange : undefined, filters: filters, onRowClick: this.props.onRowClick, namedStrings: this.props.namedStrings, hideScopes: this.state.hideQuickfilters, refreshKey: this.state.refreshKey, locale: displayLocale });
266
+ const readonlyDashboardView = react_1.default.createElement(DashboardViewComponent_1.default, { schema: this.props.schema, dataSource: this.props.dataSource, dashboardDataSource: this.props.dashboardDataSource, ref: this.refDashboardView, design: this.props.design, filters: filters, onRowClick: this.props.onRowClick, namedStrings: this.props.namedStrings, hideScopes: this.state.hideQuickfilters, locale: displayLocale });
260
267
  // Pass active tables down to table select components so they can present a shorter list
261
268
  return react_1.default.createElement(expressions_ui_1.ActiveTablesContext.Provider, { value: DashboardUtils.getFilterableTables(this.props.design, this.props.schema) },
262
269
  react_1.default.createElement(expressions_ui_1.LocaleContext.Provider, { value: displayLocale },
@@ -268,6 +275,7 @@ class DashboardComponent extends react_1.default.Component {
268
275
  !this.props.hideTitleBar ? this.renderTitleBar() : undefined,
269
276
  react_1.default.createElement("div", null, !this.state.hideQuickfilters ? this.renderQuickfilter() : undefined),
270
277
  dashboardView,
278
+ this.renderFloatingShowQuickfiltersButton(),
271
279
  this.props.onDesignChange != null && (react_1.default.createElement(SettingsModalComponent_1.default, { onDesignChange: this.handleDesignChange, schema: this.props.schema, dataSource: this.props.dataSource, ref: (c) => {
272
280
  this.settings = c;
273
281
  } })),
package/lib/languages.js CHANGED
@@ -816,7 +816,12 @@ exports.languages = [
816
816
  {
817
817
  "code": "tl",
818
818
  "name": "Tagalog",
819
- "en": "Tagalog / Filipino"
819
+ "en": "Tagalog"
820
+ },
821
+ {
822
+ "code": "fil",
823
+ "name": "Filipino",
824
+ "en": "Filipino"
820
825
  },
821
826
  {
822
827
  "code": "tn",
@@ -11,36 +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: any) => void;
16
- isMergeable(design: any, index: any): boolean;
17
- renderQuickfilter: (item: any, index: any) => React.CElement<any, QuickfilterDesignComponent>;
18
- handleAdd: () => void;
19
- handleRemove: (index: any) => void;
20
- render(): React.DetailedReactHTMLElement<React.HTMLAttributes<HTMLElement>, HTMLElement>;
21
- }
22
- interface QuickfilterDesignComponentProps {
23
- /** Design of a single quickfilters. See README.md */
24
- design: Quickfilter;
25
- onChange: (design: Quickfilter) => void;
26
- onRemove: () => void;
27
- /** True if can be merged */
28
- mergeable?: boolean;
29
- schema: Schema;
30
- dataSource: DataSource;
31
- tables: string[];
32
- }
33
- interface QuickfilterDesignComponentState {
34
- table: any;
35
- }
36
- /** Single quickfilter design component */
37
- declare class QuickfilterDesignComponent extends React.Component<QuickfilterDesignComponentProps, QuickfilterDesignComponentState> {
38
- constructor(props: any);
39
- handleTableChange: (table: any) => void;
40
- handleExprChange: (expr: any) => void;
41
- handleLabelChange: (ev: any) => void;
42
- handleMergedChange: (merged: any) => void;
43
- handleMultiChange: (multi: any) => void;
44
- render(): React.JSX.Element;
45
- }
46
- export {};
14
+ export default function QuickfiltersDesignComponent(props: QuickfiltersDesignComponentProps): React.JSX.Element;
@@ -26,32 +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 R = react_1.default.createElement;
32
- const update_object_1 = __importDefault(require("update-object"));
31
+ const react_1 = __importStar(require("react"));
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
- design = design.slice();
41
- // Update merged, clearing if not mergeable
42
- for (let index = 0, end = design.length, asc = 0 <= end; asc ? index < end : index > end; asc ? index++ : index--) {
43
- if (design[index].merged && !this.isMergeable(design, index)) {
44
- design[index] = lodash_1.default.extend({}, design[index], { merged: false });
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) => {
44
+ const newDesign = (0, immer_1.produce)(design, draft => {
45
+ // Update merged flag, clearing if not mergeable
46
+ for (let i = 0; i < draft.length; i++) {
47
+ if (draft[i].merged && !isMergeable(draft, i)) {
48
+ draft[i].merged = false;
49
+ }
45
50
  }
46
- }
47
- return this.props.onDesignChange(design);
51
+ });
52
+ props.onDesignChange(newDesign);
53
+ };
54
+ const handleReorder = (design) => {
55
+ setReorderCounter(c => c + 1);
56
+ handleDesignChange(design);
48
57
  };
49
58
  // Determine if quickfilter at index is mergeable with previous (same type, id table and enum values)
50
- isMergeable(design, index) {
59
+ const isMergeable = (design, index) => {
51
60
  if (index === 0) {
52
61
  return false;
53
62
  }
54
- const exprUtils = new expressions_1.ExprUtils(this.props.schema);
63
+ const exprUtils = new expressions_1.ExprUtils(props.schema);
55
64
  const type = exprUtils.getExprType(design[index].expr);
56
65
  const prevType = exprUtils.getExprType(design[index - 1].expr);
57
66
  const idTable = exprUtils.getExprIdTable(design[index].expr);
@@ -73,40 +82,30 @@ class QuickfiltersDesignComponent extends react_1.default.Component {
73
82
  return false;
74
83
  }
75
84
  return true;
76
- }
77
- renderQuickfilter = (item, index) => {
78
- return R(QuickfilterDesignComponent, {
79
- key: index,
80
- design: item,
81
- schema: this.props.schema,
82
- dataSource: this.props.dataSource,
83
- tables: this.props.tables,
84
- mergeable: this.isMergeable(this.props.design, index),
85
- onChange: (newItem) => {
86
- 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();
87
89
  design[index] = newItem;
88
- return this.handleDesignChange(design);
89
- },
90
- onRemove: this.handleRemove.bind(null, index)
91
- });
90
+ handleDesignChange(design);
91
+ }, onRemove: () => handleRemove(index) });
92
92
  };
93
- handleAdd = () => {
93
+ const handleAdd = () => {
94
94
  // Add blank to end
95
- const design = this.props.design.concat([{ expr: null }]);
96
- return this.props.onDesignChange(design);
95
+ const design = props.design.concat([{ expr: null }]);
96
+ props.onDesignChange(design);
97
97
  };
98
- handleRemove = (index) => {
99
- const design = this.props.design.slice();
98
+ const handleRemove = (index) => {
99
+ const design = props.design.slice();
100
100
  design.splice(index, 1);
101
- return this.props.onDesignChange(design);
101
+ props.onDesignChange(design);
102
102
  };
103
- render() {
104
- return R("div", null, react_1.default.createElement(ListEditorComponent_1.ListEditorComponent, { items: this.props.design, onItemsChange: this.handleDesignChange, renderItem: this.renderQuickfilter, getReorderableKey: (item, index) => index }), this.props.tables.length > 0
105
- ? R("button", { type: "button", className: "btn btn-sm btn-link", onClick: this.handleAdd }, R("span", { className: "fas fa-plus me-1" }), T `Add Quick Filter`)
106
- : undefined);
107
- }
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));
108
108
  }
109
- exports.default = QuickfiltersDesignComponent;
110
109
  /** Single quickfilter design component */
111
110
  class QuickfilterDesignComponent extends react_1.default.Component {
112
111
  constructor(props) {
@@ -118,22 +117,29 @@ class QuickfilterDesignComponent extends react_1.default.Component {
118
117
  }
119
118
  handleTableChange = (table) => {
120
119
  this.setState({ table });
121
- const design = {
122
- expr: null
123
- };
124
- return this.props.onChange(design);
120
+ this.props.onChange((0, immer_1.produce)(this.props.design, draft => {
121
+ draft.expr = null;
122
+ }));
125
123
  };
126
124
  handleExprChange = (expr) => {
127
- return this.props.onChange((0, update_object_1.default)(this.props.design, { expr: { $set: expr } }));
125
+ this.props.onChange((0, immer_1.produce)(this.props.design, draft => {
126
+ draft.expr = expr;
127
+ }));
128
128
  };
129
129
  handleLabelChange = (ev) => {
130
- return this.props.onChange((0, update_object_1.default)(this.props.design, { label: { $set: ev.target.value } }));
130
+ this.props.onChange((0, immer_1.produce)(this.props.design, draft => {
131
+ draft.label = ev.target.value;
132
+ }));
131
133
  };
132
134
  handleMergedChange = (merged) => {
133
- return this.props.onChange((0, update_object_1.default)(this.props.design, { merged: { $set: merged } }));
135
+ this.props.onChange((0, immer_1.produce)(this.props.design, draft => {
136
+ draft.merged = merged;
137
+ }));
134
138
  };
135
139
  handleMultiChange = (multi) => {
136
- return this.props.onChange((0, update_object_1.default)(this.props.design, { multi: { $set: multi } }));
140
+ this.props.onChange((0, immer_1.produce)(this.props.design, draft => {
141
+ draft.multi = multi;
142
+ }));
137
143
  };
138
144
  render() {
139
145
  // Determine type of expression
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mwater/visualization",
3
- "version": "5.4.2",
3
+ "version": "5.4.4",
4
4
  "description": "Visualization library",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -1,7 +1,5 @@
1
1
  import _ from "lodash"
2
2
  import React, { ReactNode } from "react"
3
- const R = React.createElement
4
-
5
3
  import { DataSource, ExprCompiler, Schema } from "@mwater/expressions"
6
4
  import UndoStack from "../UndoStack"
7
5
  import * as DashboardUtils from "./DashboardUtils"
@@ -233,153 +231,154 @@ export default class DashboardComponent extends React.Component<DashboardCompone
233
231
  }
234
232
 
235
233
  renderEditingSwitch() {
236
- return R(
237
- "a",
238
- {
239
- key: "edit",
240
- className: `btn btn-primary btn-sm ${this.state.editing ? "active" : ""}`,
241
- onClick: this.handleToggleEditing
242
- },
243
- R("span", { className: "fas fa-pencil-alt" }),
244
- " ",
245
- this.state.editing ? T`Editing` : T`Edit`
234
+ return (
235
+ <a
236
+ key="edit"
237
+ className={`btn btn-primary btn-sm ${this.state.editing ? "active" : ""}`}
238
+ onClick={this.handleToggleEditing}
239
+ >
240
+ <span className="fas fa-pencil-alt"/>
241
+ {" "}
242
+ {this.state.editing ? T`Editing` : T`Edit`}
243
+ </a>
246
244
  )
247
245
  }
248
246
 
249
247
  renderStyle() {
250
- return R(
251
- "button",
252
- { type: "button", key: "style", className: "btn btn-link btn-sm", onClick: this.handleOpenLayoutOptions },
253
- R("span", { className: "fa fa-mobile" }),
254
- R("span", { className: "hide-600px" }, " ", T`Layout`)
248
+ return (
249
+ <button type="button" key="style" className="btn btn-link btn-sm" onClick={this.handleOpenLayoutOptions}>
250
+ <span className="fa fa-mobile"/>
251
+ <span className="hide-600px"> {T`Layout`}</span>
252
+ </button>
255
253
  )
256
254
  }
257
255
 
258
256
  renderActionLinks() {
259
- return R(
260
- "div",
261
- null,
262
- this.state.editing
263
- ? [
264
- R(
265
- "a",
266
- {
267
- key: "undo",
268
- className: `btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`,
269
- onClick: this.handleUndo
270
- },
271
- R("span", { className: "fas fa-caret-left" }),
272
- R("span", { className: "hide-600px" }, " ", T`Undo`)
273
- ),
274
- " ",
275
- R(
276
- "a",
277
- {
278
- key: "redo",
279
- className: `btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`,
280
- onClick: this.handleRedo
281
- },
282
- R("span", { className: "fas fa-caret-right" }),
283
- R("span", { className: "hide-600px" }, " ", T`Redo`)
284
- )
285
- ]
286
- : undefined,
287
- !this.state.editing && this.props.design.otherLocales && this.props.design.otherLocales.length > 0
288
- ? R(
289
- "div",
290
- { key: "translations", className: "dropdown d-inline-block" },
291
- R(
292
- "a",
293
- {
294
- className: "btn btn-link btn-sm dropdown-toggle",
295
- "data-bs-toggle": "dropdown"
296
- },
297
- R("span", { className: "fal fa-globe" }),
257
+ return (
258
+ <div>
259
+ {this.state.editing
260
+ ? [
261
+ <a
262
+ key="undo"
263
+ className={`btn btn-link btn-sm ${!this.state.undoStack.canUndo() ? "disabled" : ""}`}
264
+ onClick={this.handleUndo}
265
+ >
266
+ <span className="fas fa-caret-left"/>
267
+ <span className="hide-600px"> {T`Undo`}</span>
268
+ </a>,
298
269
  " ",
299
- this.state.locale
300
- ),
301
- R(
302
- "ul",
303
- { className: "dropdown-menu dropdown-menu-end" },
304
- [this.props.design.locale || "en", ...this.props.design.otherLocales].map(locale =>
305
- R(
306
- "li",
307
- { key: locale },
308
- R(
309
- "a",
310
- {
311
- className: "dropdown-item",
312
- onClick: () => this.setState({ locale: locale })
313
- },
314
- languages.find(l => l.code === locale)?.name || locale
315
- )
316
- )
317
- )
318
- )
319
- )
320
- : undefined,
321
- R(
322
- "a",
323
- { key: "print", className: "btn btn-link btn-sm", onClick: this.handlePrint },
324
- R("span", { className: "fas fa-print" }),
325
- R("span", { className: "hide-600px" }, " ", T`Print`)
326
- ),
327
- R(
328
- "a",
329
- { key: "refresh", className: "btn btn-link btn-sm", onClick: this.handleRefreshData },
330
- R("span", { className: "fas fa-sync" }),
331
- R("span", { className: "hide-600px" }, " ", T`Refresh`)
332
- ),
333
- this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
334
- ? R(
335
- "a",
336
- { key: "showQuickfilters", className: "btn btn-link btn-sm", onClick: this.handleShowQuickfilters },
337
- R("span", { className: "fa fa-filter" }),
338
- R("span", { className: "hide-600px" }, " ", T`Show Quickfilters`)
339
- )
340
- : undefined,
341
-
342
- // R 'a', key: "export", className: "btn btn-link btn-sm", onClick: @handleSaveDesignFile,
343
- // R('span', className: "glyphicon glyphicon-download-alt")
344
- // " Export"
345
- this.state.editing
346
- ? R(
347
- "a",
348
- { key: "settings", className: "btn btn-link btn-sm", onClick: this.handleSettings },
349
- R("span", { className: "fas fa-cog" }),
350
- R("span", { className: "hide-600px" }, " ", T`Settings`)
351
- )
352
- : undefined,
353
- this.state.editing ? this.renderStyle() : undefined,
354
- this.props.extraTitleButtonsElem,
355
- this.props.onDesignChange != null ? this.renderEditingSwitch() : undefined
270
+ <a
271
+ key="redo"
272
+ className={`btn btn-link btn-sm ${!this.state.undoStack.canRedo() ? "disabled" : ""}`}
273
+ onClick={this.handleRedo}
274
+ >
275
+ <span className="fas fa-caret-right"/>
276
+ <span className="hide-600px"> {T`Redo`}</span>
277
+ </a>
278
+ ]
279
+ : undefined}
280
+ {!this.state.editing && this.props.design.otherLocales && this.props.design.otherLocales.length > 0
281
+ ? <div key="translations" className="dropdown d-inline-block">
282
+ <a
283
+ className="btn btn-link btn-sm dropdown-toggle"
284
+ data-bs-toggle="dropdown"
285
+ >
286
+ <span className="fal fa-globe"/>
287
+ {" "}
288
+ {this.state.locale}
289
+ </a>
290
+ <ul className="dropdown-menu dropdown-menu-end">
291
+ {[this.props.design.locale || "en", ...this.props.design.otherLocales].map(locale =>
292
+ <li key={locale}>
293
+ <a
294
+ className="dropdown-item"
295
+ onClick={() => this.setState({ locale: locale })}
296
+ >
297
+ {languages.find(l => l.code === locale)?.name || locale}
298
+ </a>
299
+ </li>
300
+ )}
301
+ </ul>
302
+ </div>
303
+ : undefined}
304
+ <a key="print" className="btn btn-link btn-sm" onClick={this.handlePrint}>
305
+ <span className="fas fa-print"/>
306
+ <span className="hide-600px"> {T`Print`}</span>
307
+ </a>
308
+ <a key="refresh" className="btn btn-link btn-sm" onClick={this.handleRefreshData}>
309
+ <span className="fas fa-sync"/>
310
+ <span className="hide-600px"> {T`Refresh`}</span>
311
+ </a>
312
+ {this.state.hideQuickfilters && this.props.design.quickfilters && this.props.design.quickfilters.length > 0
313
+ ? <a key="showQuickfilters" className="btn btn-link btn-sm" onClick={this.handleShowQuickfilters}>
314
+ <span className="fa fa-filter"/>
315
+ <span className="hide-600px"> {T`Show Quickfilters`}</span>
316
+ </a>
317
+ : undefined}
318
+
319
+ {this.state.editing
320
+ ? <a key="settings" className="btn btn-link btn-sm" onClick={this.handleSettings}>
321
+ <span className="fas fa-cog"/>
322
+ <span className="hide-600px"> {T`Settings`}</span>
323
+ </a>
324
+ : undefined}
325
+ {this.state.editing ? this.renderStyle() : undefined}
326
+ {this.props.extraTitleButtonsElem}
327
+ {this.props.onDesignChange != null ? this.renderEditingSwitch() : undefined}
328
+ </div>
356
329
  )
357
330
  }
358
331
 
359
332
  renderTitleBar() {
360
- return R(
361
- "div",
362
- { style: { height: 40, padding: 4 } },
363
- R("div", { style: { float: "right" } }, this.renderActionLinks()),
364
- this.props.titleElem
333
+ return (
334
+ <div style={{ height: 40, padding: 4 }}>
335
+ <div style={{ float: "right" }}>{this.renderActionLinks()}</div>
336
+ {this.props.titleElem}
337
+ </div>
365
338
  )
366
339
  }
367
340
 
368
341
  renderQuickfilter() {
369
- return R(QuickfiltersComponent, {
370
- design: this.props.design.quickfilters || [],
371
- schema: this.props.schema,
372
- dataSource: this.props.dataSource,
373
- quickfiltersDataSource: this.props.dashboardDataSource.getQuickfiltersDataSource(),
374
- values: this.state.quickfiltersValues || undefined,
375
- onValuesChange: (values: any) => this.setState({ quickfiltersValues: values }),
376
- locks: this.props.quickfilterLocks,
377
- filters: this.getCompiledFilters(),
378
- hideTopBorder: this.props.hideTitleBar,
342
+ return <QuickfiltersComponent
343
+ design={this.props.design.quickfilters || []}
344
+ schema={this.props.schema}
345
+ dataSource={this.props.dataSource}
346
+ quickfiltersDataSource={this.props.dashboardDataSource.getQuickfiltersDataSource()}
347
+ values={this.state.quickfiltersValues || undefined}
348
+ onValuesChange={(values: any) => this.setState({ quickfiltersValues: values })}
349
+ locks={this.props.quickfilterLocks}
350
+ filters={this.getCompiledFilters()}
351
+ hideTopBorder={this.props.hideTitleBar}
379
352
  // Don't hide if title bar is hidden as it can't be shown again
380
- onHide: () => !this.props.hideTitleBar ? this.setState({ hideQuickfilters: true }) : undefined,
381
- translate: this.translate
382
- })
353
+ onHide={() => this.setState({ hideQuickfilters: true })}
354
+ translate={this.translate}
355
+ />
356
+ }
357
+
358
+ renderFloatingShowQuickfiltersButton() {
359
+ // Only show if:
360
+ // 1. Quick filters exist
361
+ // 2. Quick filters are hidden
362
+ // 3. Title bar is hidden (since otherwise button is in title bar)
363
+ if (!this.props.design.quickfilters?.length || !this.state.hideQuickfilters || !this.props.hideTitleBar) {
364
+ return null
365
+ }
366
+
367
+ return (
368
+ <div style={{
369
+ position: "absolute",
370
+ top: 5,
371
+ right: 20,
372
+ zIndex: 1000
373
+ }}>
374
+ <button
375
+ className="btn btn-link btn-sm"
376
+ onClick={() => this.setState({ hideQuickfilters: false })}
377
+ >
378
+ <i className="fa fa-angle-double-down"/>
379
+ </button>
380
+ </div>
381
+ )
383
382
  }
384
383
 
385
384
  refDashboardView = (el: any) => {
@@ -400,34 +399,33 @@ export default class DashboardComponent extends React.Component<DashboardCompone
400
399
 
401
400
  const displayLocale = this.state.editing ? this.props.design.locale || "en" : this.state.locale
402
401
 
403
- const dashboardView = R(DashboardViewComponent, {
404
- schema: this.props.schema,
405
- dataSource: this.props.dataSource,
406
- dashboardDataSource: this.props.dashboardDataSource,
407
- ref: this.refDashboardView,
408
- design: this.props.design,
409
- onDesignChange: this.state.editing ? this.props.onDesignChange : undefined,
410
- filters,
411
- onRowClick: this.props.onRowClick,
412
- namedStrings: this.props.namedStrings,
413
- hideScopes: this.state.hideQuickfilters,
414
- refreshKey: this.state.refreshKey,
415
- locale: displayLocale
416
- })
417
-
418
- const readonlyDashboardView = R(DashboardViewComponent, {
419
- schema: this.props.schema,
420
- dataSource: this.props.dataSource,
421
- dashboardDataSource: this.props.dashboardDataSource,
422
- ref: this.refDashboardView,
423
- design: this.props.design,
424
- filters,
425
- onRowClick: this.props.onRowClick,
426
- namedStrings: this.props.namedStrings,
427
- hideScopes: this.state.hideQuickfilters,
428
- locale: displayLocale
429
- })
430
-
402
+ const dashboardView = <DashboardViewComponent
403
+ schema={this.props.schema}
404
+ dataSource={this.props.dataSource}
405
+ dashboardDataSource={this.props.dashboardDataSource}
406
+ ref={this.refDashboardView}
407
+ design={this.props.design}
408
+ onDesignChange={this.state.editing ? this.props.onDesignChange : undefined}
409
+ filters={filters}
410
+ onRowClick={this.props.onRowClick}
411
+ namedStrings={this.props.namedStrings}
412
+ hideScopes={this.state.hideQuickfilters}
413
+ refreshKey={this.state.refreshKey}
414
+ locale={displayLocale}
415
+ />
416
+
417
+ const readonlyDashboardView = <DashboardViewComponent
418
+ schema={this.props.schema}
419
+ dataSource={this.props.dataSource}
420
+ dashboardDataSource={this.props.dashboardDataSource}
421
+ ref={this.refDashboardView}
422
+ design={this.props.design}
423
+ filters={filters}
424
+ onRowClick={this.props.onRowClick}
425
+ namedStrings={this.props.namedStrings}
426
+ hideScopes={this.state.hideQuickfilters}
427
+ locale={displayLocale}
428
+ />
431
429
 
432
430
  // Pass active tables down to table select components so they can present a shorter list
433
431
  return <ActiveTablesContext.Provider
@@ -441,6 +439,7 @@ export default class DashboardComponent extends React.Component<DashboardCompone
441
439
  {!this.props.hideTitleBar ? this.renderTitleBar() : undefined}
442
440
  <div>{!this.state.hideQuickfilters ? this.renderQuickfilter() : undefined}</div>
443
441
  {dashboardView}
442
+ {this.renderFloatingShowQuickfiltersButton()}
444
443
  {this.props.onDesignChange != null && (
445
444
  <SettingsModalComponent
446
445
  onDesignChange={this.handleDesignChange}
package/src/languages.ts CHANGED
@@ -821,7 +821,12 @@ export const languages: {
821
821
  {
822
822
  "code": "tl",
823
823
  "name": "Tagalog",
824
- "en": "Tagalog / Filipino"
824
+ "en": "Tagalog"
825
+ },
826
+ {
827
+ "code": "fil",
828
+ "name": "Filipino",
829
+ "en": "Filipino"
825
830
  },
826
831
  {
827
832
  "code": "tn",
@@ -1,9 +1,9 @@
1
1
  import _ from "lodash"
2
- import React from "react"
3
- const R = React.createElement
4
- import update from "update-object"
2
+ import React, { useState } from "react"
3
+ import uuid from "uuid"
4
+ import { produce } from "immer"
5
5
  import { ExprComponent } from "@mwater/expressions-ui"
6
- import { DataSource, ExprUtils, Schema } from "@mwater/expressions"
6
+ import { DataSource, Expr, ExprUtils, FieldExpr, Schema } from "@mwater/expressions"
7
7
  import * as ui from "@mwater/react-library/lib/bootstrap"
8
8
  import { ListEditorComponent } from "@mwater/react-library/lib/ListEditorComponent"
9
9
  import { Quickfilter } from "./Quickfilter"
@@ -20,27 +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: any) => {
25
- design = design.slice()
26
-
27
- // Update merged, clearing if not mergeable
28
- for (let index = 0, end = design.length, asc = 0 <= end; asc ? index < end : index > end; asc ? index++ : index--) {
29
- if (design[index].merged && !this.isMergeable(design, index)) {
30
- design[index] = _.extend({}, design[index], { merged: false })
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)
28
+
29
+ const handleDesignChange = (design: Quickfilter[]) => {
30
+ const newDesign = produce(design, draft => {
31
+ // Update merged flag, clearing if not mergeable
32
+ for (let i = 0; i < draft.length; i++) {
33
+ if (draft[i].merged && !isMergeable(draft, i)) {
34
+ draft[i].merged = false
35
+ }
31
36
  }
32
- }
37
+ })
38
+
39
+ props.onDesignChange(newDesign)
40
+ }
33
41
 
34
- return this.props.onDesignChange(design)
42
+ const handleReorder = (design: Quickfilter[]) => {
43
+ setReorderCounter(c => c + 1)
44
+ handleDesignChange(design)
35
45
  }
36
46
 
37
47
  // Determine if quickfilter at index is mergeable with previous (same type, id table and enum values)
38
- isMergeable(design: any, index: any) {
48
+ const isMergeable = (design: Quickfilter[], index: number) => {
39
49
  if (index === 0) {
40
50
  return false
41
51
  }
42
52
 
43
- const exprUtils = new ExprUtils(this.props.schema)
53
+ const exprUtils = new ExprUtils(props.schema)
44
54
 
45
55
  const type = exprUtils.getExprType(design[index].expr)
46
56
  const prevType = exprUtils.getExprType(design[index - 1].expr)
@@ -73,56 +83,52 @@ export default class QuickfiltersDesignComponent extends React.Component<Quickfi
73
83
  return true
74
84
  }
75
85
 
76
- renderQuickfilter = (item: any, index: any) => {
77
- return R(QuickfilterDesignComponent, {
78
- key: index,
79
- design: item,
80
- schema: this.props.schema,
81
- dataSource: this.props.dataSource,
82
- tables: this.props.tables,
83
- mergeable: this.isMergeable(this.props.design, index),
84
- onChange: (newItem: any) => {
85
- const design = this.props.design.slice()
86
+ const renderQuickfilter = (item: Quickfilter, index: number) => {
87
+ return <QuickfilterDesignComponent
88
+ key={index}
89
+ design={item}
90
+ schema={props.schema}
91
+ dataSource={props.dataSource}
92
+ tables={props.tables}
93
+ mergeable={isMergeable(props.design, index)}
94
+ onChange={(newItem: Quickfilter) => {
95
+ const design = props.design.slice()
86
96
  design[index] = newItem
87
- return this.handleDesignChange(design)
88
- },
89
-
90
- onRemove: this.handleRemove.bind(null, index)
91
- })
97
+ handleDesignChange(design)
98
+ }}
99
+ onRemove={() => handleRemove(index)}
100
+ />
92
101
  }
93
102
 
94
- handleAdd = () => {
103
+ const handleAdd = () => {
95
104
  // Add blank to end
96
- const design = this.props.design.concat([{ expr: null }])
97
- return this.props.onDesignChange(design)
105
+ const design = props.design.concat([{ expr: null }])
106
+ props.onDesignChange(design)
98
107
  }
99
108
 
100
- handleRemove = (index: any) => {
101
- const design = this.props.design.slice()
109
+ const handleRemove = (index: number) => {
110
+ const design = props.design.slice()
102
111
  design.splice(index, 1)
103
- return this.props.onDesignChange(design)
112
+ props.onDesignChange(design)
104
113
  }
105
114
 
106
- render() {
107
- return R(
108
- "div",
109
- null,
115
+ return (
116
+ <div>
110
117
  <ListEditorComponent
111
- items={this.props.design}
112
- onItemsChange={this.handleDesignChange}
113
- renderItem={this.renderQuickfilter}
118
+ key={reorderCounter}
119
+ items={props.design}
120
+ onItemsChange={handleReorder}
121
+ renderItem={renderQuickfilter}
114
122
  getReorderableKey={(item, index) => index}
115
- />,
116
- this.props.tables.length > 0
117
- ? R(
118
- "button",
119
- { type: "button", className: "btn btn-sm btn-link", onClick: this.handleAdd },
120
- R("span", { className: "fas fa-plus me-1" }),
121
- T`Add Quick Filter`
122
- )
123
- : undefined
124
- )
125
- }
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
+ )
126
132
  }
127
133
 
128
134
  interface QuickfilterDesignComponentProps {
@@ -138,7 +144,7 @@ interface QuickfilterDesignComponentProps {
138
144
  }
139
145
 
140
146
  interface QuickfilterDesignComponentState {
141
- table: any
147
+ table: string
142
148
  }
143
149
 
144
150
  /** Single quickfilter design component */
@@ -146,35 +152,46 @@ class QuickfilterDesignComponent extends React.Component<
146
152
  QuickfilterDesignComponentProps,
147
153
  QuickfilterDesignComponentState
148
154
  > {
149
- constructor(props: any) {
155
+ constructor(props: QuickfilterDesignComponentProps) {
150
156
  super(props)
151
157
 
152
158
  // Store table to allow selecting table first, then expression
153
159
  this.state = {
154
- table: props.design.expr?.table || props.tables[0]
160
+ table: (props.design.expr as FieldExpr)?.table || props.tables[0]
155
161
  }
156
162
  }
157
163
 
158
- handleTableChange = (table: any) => {
164
+ handleTableChange = (table: string) => {
159
165
  this.setState({ table })
160
- const design = {
161
- expr: null
162
- }
163
- return this.props.onChange(design)
166
+ this.props.onChange(produce(this.props.design, draft => {
167
+ draft.expr = null
168
+ }))
164
169
  }
165
170
 
166
- handleExprChange = (expr: any) => {
167
- return this.props.onChange(update(this.props.design, { expr: { $set: expr } }))
171
+ handleExprChange = (expr: Expr) => {
172
+ this.props.onChange(produce(this.props.design, draft => {
173
+ draft.expr = expr
174
+ }))
168
175
  }
176
+
169
177
  handleLabelChange = (ev: any) => {
170
- return this.props.onChange(update(this.props.design, { label: { $set: ev.target.value } }))
178
+ this.props.onChange(produce(this.props.design, draft => {
179
+ draft.label = ev.target.value
180
+ }))
171
181
  }
172
- handleMergedChange = (merged: any) => {
173
- return this.props.onChange(update(this.props.design, { merged: { $set: merged } }))
182
+
183
+ handleMergedChange = (merged: boolean) => {
184
+ this.props.onChange(produce(this.props.design, draft => {
185
+ draft.merged = merged
186
+ }))
174
187
  }
175
- handleMultiChange = (multi: any) => {
176
- return this.props.onChange(update(this.props.design, { multi: { $set: multi } }))
188
+
189
+ handleMultiChange = (multi: boolean) => {
190
+ this.props.onChange(produce(this.props.design, draft => {
191
+ draft.multi = multi
192
+ }))
177
193
  }
194
+
178
195
  render() {
179
196
  // Determine type of expression
180
197
  const exprType = new ExprUtils(this.props.schema).getExprType(this.props.design.expr)