@journeyapps-labs/reactor-mod-data-browser 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/@types/actions/connections/SetConnectionColorAction.d.ts +11 -0
  3. package/dist/@types/actions/saved-queries/OpenSavedQueryAction.d.ts +10 -0
  4. package/dist/@types/actions/saved-queries/RemoveSavedQueryAction.d.ts +9 -0
  5. package/dist/@types/core/AbstractConnection.d.ts +2 -0
  6. package/dist/@types/core/SchemaModelDefinition.d.ts +5 -0
  7. package/dist/@types/core/connection-colors.d.ts +10 -0
  8. package/dist/@types/core/query/StandardModelFields.d.ts +5 -0
  9. package/dist/@types/core/query/filters.d.ts +46 -5
  10. package/dist/@types/core/query/query-simple/SimplePage.d.ts +2 -0
  11. package/dist/@types/core/query/query-simple/SimpleQuery.d.ts +12 -3
  12. package/dist/@types/core/query/query-simple/SimpleQueryColumns.d.ts +12 -0
  13. package/dist/@types/core/query/query-simple/SimpleQueryFilterState.d.ts +37 -0
  14. package/dist/@types/core/query/query-simple/SimpleQueryPlanner.d.ts +4 -0
  15. package/dist/@types/core/query/query-simple/SimpleQuerySortState.d.ts +23 -0
  16. package/dist/@types/core/query/query-simple/SimpleQueryTypes.d.ts +24 -0
  17. package/dist/@types/core/query/widgets/ColumnDisplayWidget.d.ts +1 -0
  18. package/dist/@types/core/query/widgets/PeekRelationshipButton.d.ts +7 -0
  19. package/dist/@types/core/query/widgets/SmartColumnWidget.d.ts +3 -0
  20. package/dist/@types/core/query/widgets/SmartFilterWidget.d.ts +6 -0
  21. package/dist/@types/entities/ConnectionEntityDefinition.d.ts +2 -0
  22. package/dist/@types/entities/SavedQueryEntityDefinition.d.ts +8 -0
  23. package/dist/@types/entities.d.ts +2 -1
  24. package/dist/@types/forms/APIConnectionForm.d.ts +1 -0
  25. package/dist/@types/forms/TypeEngine.d.ts +3 -21
  26. package/dist/@types/forms/types/attachment-handler.d.ts +2 -0
  27. package/dist/@types/forms/types/boolean-handler.d.ts +2 -0
  28. package/dist/@types/forms/types/date-handler.d.ts +2 -0
  29. package/dist/@types/forms/types/filters/ClearableFilterFormDialogDirective.d.ts +10 -0
  30. package/dist/@types/forms/types/filters/ConditionalFilterForm.d.ts +23 -0
  31. package/dist/@types/forms/types/image-handler.d.ts +2 -0
  32. package/dist/@types/forms/types/location-handler.d.ts +2 -0
  33. package/dist/@types/forms/types/multiple-choice-handler.d.ts +2 -0
  34. package/dist/@types/forms/types/multiple-choice-integer-handler.d.ts +2 -0
  35. package/dist/@types/forms/types/number-handler.d.ts +2 -0
  36. package/dist/@types/forms/types/shared/type-handler.d.ts +37 -0
  37. package/dist/@types/forms/types/shared/ui.d.ts +768 -0
  38. package/dist/@types/forms/types/single-choice-handler.d.ts +2 -0
  39. package/dist/@types/forms/types/single-choice-integer-handler.d.ts +2 -0
  40. package/dist/@types/forms/types/text-handler.d.ts +3 -0
  41. package/dist/@types/index.d.ts +1 -0
  42. package/dist/@types/panels/_shared/SharedConnectionPanelFactory.d.ts +19 -0
  43. package/dist/@types/panels/_shared/SharedModelPanelFactory.d.ts +5 -2
  44. package/dist/@types/panels/query/QueryPanelFactory.d.ts +6 -2
  45. package/dist/@types/panels/query/TableControlsWidget.d.ts +2 -0
  46. package/dist/@types/panels/query/table-controls/ChangesControlsWidget.d.ts +8 -0
  47. package/dist/@types/panels/query/table-controls/FilterControlsWidget.d.ts +7 -0
  48. package/dist/@types/panels/query/table-controls/PageControlsWidget.d.ts +9 -0
  49. package/dist/@types/panels/query/table-controls/QueryControlsWidget.d.ts +11 -0
  50. package/dist/@types/panels/query/table-controls/SortChipWidget.d.ts +10 -0
  51. package/dist/@types/panels/query/table-controls/SortControlsWidget.d.ts +7 -0
  52. package/dist/@types/preferences/QueryControlPreferences.d.ts +7 -0
  53. package/dist/@types/stores/SavedQueryStore.d.ts +34 -0
  54. package/dist/@types/widgets/EmptyValueWidget.d.ts +7 -0
  55. package/dist/DataBrowserModule.js +21 -7
  56. package/dist/DataBrowserModule.js.map +1 -1
  57. package/dist/actions/connections/AddConnectionAction.js +2 -2
  58. package/dist/actions/connections/AddConnectionAction.js.map +1 -1
  59. package/dist/actions/connections/RemoveConnectionAction.js +2 -2
  60. package/dist/actions/connections/RemoveConnectionAction.js.map +1 -1
  61. package/dist/actions/connections/SetConnectionColorAction.js +63 -0
  62. package/dist/actions/connections/SetConnectionColorAction.js.map +1 -0
  63. package/dist/actions/saved-queries/OpenSavedQueryAction.js +58 -0
  64. package/dist/actions/saved-queries/OpenSavedQueryAction.js.map +1 -0
  65. package/dist/actions/saved-queries/RemoveSavedQueryAction.js +43 -0
  66. package/dist/actions/saved-queries/RemoveSavedQueryAction.js.map +1 -0
  67. package/dist/actions/schema-definitions/CreateModelAction.js +2 -2
  68. package/dist/actions/schema-definitions/CreateModelAction.js.map +1 -1
  69. package/dist/actions/schema-definitions/QuerySchemaModelAction.js +2 -2
  70. package/dist/actions/schema-definitions/QuerySchemaModelAction.js.map +1 -1
  71. package/dist/actions/schema-model/EditSchemaModelAction.js +2 -2
  72. package/dist/actions/schema-model/EditSchemaModelAction.js.map +1 -1
  73. package/dist/actions/schema-model/ViewSchemaModelAsJsonAction.js +2 -2
  74. package/dist/actions/schema-model/ViewSchemaModelAsJsonAction.js.map +1 -1
  75. package/dist/core/AbstractConnection.js +116 -90
  76. package/dist/core/AbstractConnection.js.map +1 -1
  77. package/dist/core/SchemaModelDefinition.js +14 -0
  78. package/dist/core/SchemaModelDefinition.js.map +1 -1
  79. package/dist/core/connection-colors.js +36 -0
  80. package/dist/core/connection-colors.js.map +1 -0
  81. package/dist/core/query/StandardModelFields.js +10 -0
  82. package/dist/core/query/StandardModelFields.js.map +1 -0
  83. package/dist/core/query/filters.js +86 -4
  84. package/dist/core/query/filters.js.map +1 -1
  85. package/dist/core/query/query-simple/SimplePage.js +2 -4
  86. package/dist/core/query/query-simple/SimplePage.js.map +1 -1
  87. package/dist/core/query/query-simple/SimpleQuery.js +64 -68
  88. package/dist/core/query/query-simple/SimpleQuery.js.map +1 -1
  89. package/dist/core/query/query-simple/SimpleQueryColumns.js +88 -0
  90. package/dist/core/query/query-simple/SimpleQueryColumns.js.map +1 -0
  91. package/dist/core/query/query-simple/SimpleQueryFilterState.js +136 -0
  92. package/dist/core/query/query-simple/SimpleQueryFilterState.js.map +1 -0
  93. package/dist/core/query/query-simple/SimpleQueryPlanner.js +14 -0
  94. package/dist/core/query/query-simple/SimpleQueryPlanner.js.map +1 -0
  95. package/dist/core/query/query-simple/SimpleQuerySortState.js +140 -0
  96. package/dist/core/query/query-simple/SimpleQuerySortState.js.map +1 -0
  97. package/dist/core/query/query-simple/SimpleQueryTypes.js +44 -0
  98. package/dist/core/query/query-simple/SimpleQueryTypes.js.map +1 -0
  99. package/dist/core/query/widgets/BelongsToDisplayWidget.js +14 -7
  100. package/dist/core/query/widgets/BelongsToDisplayWidget.js.map +1 -1
  101. package/dist/core/query/widgets/CellDisplayWidget.js +5 -9
  102. package/dist/core/query/widgets/CellDisplayWidget.js.map +1 -1
  103. package/dist/core/query/widgets/ColumnDisplayWidget.js +13 -12
  104. package/dist/core/query/widgets/ColumnDisplayWidget.js.map +1 -1
  105. package/dist/core/query/widgets/PeekRelationshipButton.js +128 -0
  106. package/dist/core/query/widgets/PeekRelationshipButton.js.map +1 -0
  107. package/dist/core/query/widgets/SmartColumnWidget.js +18 -3
  108. package/dist/core/query/widgets/SmartColumnWidget.js.map +1 -1
  109. package/dist/core/query/widgets/SmartFilterWidget.js +88 -51
  110. package/dist/core/query/widgets/SmartFilterWidget.js.map +1 -1
  111. package/dist/entities/ConnectionEntityDefinition.js +32 -7
  112. package/dist/entities/ConnectionEntityDefinition.js.map +1 -1
  113. package/dist/entities/SavedQueryEntityDefinition.js +68 -0
  114. package/dist/entities/SavedQueryEntityDefinition.js.map +1 -0
  115. package/dist/entities/SchemaModelDefinitionEntityDefinition.js +3 -1
  116. package/dist/entities/SchemaModelDefinitionEntityDefinition.js.map +1 -1
  117. package/dist/entities.js +1 -0
  118. package/dist/entities.js.map +1 -1
  119. package/dist/forms/APIConnectionForm.js +11 -2
  120. package/dist/forms/APIConnectionForm.js.map +1 -1
  121. package/dist/forms/TypeEngine.js +30 -306
  122. package/dist/forms/TypeEngine.js.map +1 -1
  123. package/dist/forms/types/attachment-handler.js +29 -0
  124. package/dist/forms/types/attachment-handler.js.map +1 -0
  125. package/dist/forms/types/boolean-handler.js +22 -0
  126. package/dist/forms/types/boolean-handler.js.map +1 -0
  127. package/dist/forms/types/date-handler.js +97 -0
  128. package/dist/forms/types/date-handler.js.map +1 -0
  129. package/dist/forms/types/filters/ClearableFilterFormDialogDirective.js +25 -0
  130. package/dist/forms/types/filters/ClearableFilterFormDialogDirective.js.map +1 -0
  131. package/dist/forms/types/filters/ConditionalFilterForm.js +87 -0
  132. package/dist/forms/types/filters/ConditionalFilterForm.js.map +1 -0
  133. package/dist/forms/types/image-handler.js +82 -0
  134. package/dist/forms/types/image-handler.js.map +1 -0
  135. package/dist/forms/types/location-handler.js +49 -0
  136. package/dist/forms/types/location-handler.js.map +1 -0
  137. package/dist/forms/types/multiple-choice-handler.js +37 -0
  138. package/dist/forms/types/multiple-choice-handler.js.map +1 -0
  139. package/dist/forms/types/multiple-choice-integer-handler.js +37 -0
  140. package/dist/forms/types/multiple-choice-integer-handler.js.map +1 -0
  141. package/dist/forms/types/number-handler.js +79 -0
  142. package/dist/forms/types/number-handler.js.map +1 -0
  143. package/dist/forms/types/shared/type-handler.js +2 -0
  144. package/dist/forms/types/shared/type-handler.js.map +1 -0
  145. package/dist/forms/types/shared/ui.js +33 -0
  146. package/dist/forms/types/shared/ui.js.map +1 -0
  147. package/dist/forms/types/single-choice-handler.js +41 -0
  148. package/dist/forms/types/single-choice-handler.js.map +1 -0
  149. package/dist/forms/types/single-choice-integer-handler.js +41 -0
  150. package/dist/forms/types/single-choice-integer-handler.js.map +1 -0
  151. package/dist/forms/types/text-handler.js +170 -0
  152. package/dist/forms/types/text-handler.js.map +1 -0
  153. package/dist/index.js +1 -0
  154. package/dist/index.js.map +1 -1
  155. package/dist/panels/_shared/SharedConnectionPanelFactory.js +48 -0
  156. package/dist/panels/_shared/SharedConnectionPanelFactory.js.map +1 -0
  157. package/dist/panels/_shared/SharedModelPanelFactory.js +7 -2
  158. package/dist/panels/_shared/SharedModelPanelFactory.js.map +1 -1
  159. package/dist/panels/query/PageResultsWidget.js +28 -11
  160. package/dist/panels/query/PageResultsWidget.js.map +1 -1
  161. package/dist/panels/query/QueryPanelFactory.js +17 -2
  162. package/dist/panels/query/QueryPanelFactory.js.map +1 -1
  163. package/dist/panels/query/QueryPanelWidget.js +55 -9
  164. package/dist/panels/query/QueryPanelWidget.js.map +1 -1
  165. package/dist/panels/query/TableControlsWidget.js +29 -67
  166. package/dist/panels/query/TableControlsWidget.js.map +1 -1
  167. package/dist/panels/query/table-controls/ChangesControlsWidget.js +36 -0
  168. package/dist/panels/query/table-controls/ChangesControlsWidget.js.map +1 -0
  169. package/dist/panels/query/table-controls/FilterControlsWidget.js +106 -0
  170. package/dist/panels/query/table-controls/FilterControlsWidget.js.map +1 -0
  171. package/dist/panels/query/table-controls/PageControlsWidget.js +65 -0
  172. package/dist/panels/query/table-controls/PageControlsWidget.js.map +1 -0
  173. package/dist/panels/query/table-controls/QueryControlsWidget.js +85 -0
  174. package/dist/panels/query/table-controls/QueryControlsWidget.js.map +1 -0
  175. package/dist/panels/query/table-controls/SortChipWidget.js +75 -0
  176. package/dist/panels/query/table-controls/SortChipWidget.js.map +1 -0
  177. package/dist/panels/query/table-controls/SortControlsWidget.js +65 -0
  178. package/dist/panels/query/table-controls/SortControlsWidget.js.map +1 -0
  179. package/dist/preferences/QueryControlPreferences.js +28 -0
  180. package/dist/preferences/QueryControlPreferences.js.map +1 -0
  181. package/dist/stores/ConnectionStore.js +2 -0
  182. package/dist/stores/ConnectionStore.js.map +1 -1
  183. package/dist/stores/SavedQueryStore.js +131 -0
  184. package/dist/stores/SavedQueryStore.js.map +1 -0
  185. package/dist/tsconfig.tsbuildinfo +1 -1
  186. package/dist/widgets/EmptyValueWidget.js +15 -0
  187. package/dist/widgets/EmptyValueWidget.js.map +1 -0
  188. package/dist-module/bundle.js +181 -51
  189. package/dist-module/bundle.js.map +1 -1
  190. package/package.json +13 -13
  191. package/src/DataBrowserModule.ts +21 -7
  192. package/src/actions/connections/AddConnectionAction.tsx +2 -2
  193. package/src/actions/connections/RemoveConnectionAction.tsx +2 -2
  194. package/src/actions/connections/SetConnectionColorAction.ts +52 -0
  195. package/src/actions/saved-queries/OpenSavedQueryAction.ts +43 -0
  196. package/src/actions/saved-queries/RemoveSavedQueryAction.ts +27 -0
  197. package/src/actions/schema-definitions/CreateModelAction.ts +9 -2
  198. package/src/actions/schema-definitions/QuerySchemaModelAction.ts +9 -2
  199. package/src/actions/schema-model/EditSchemaModelAction.ts +9 -2
  200. package/src/actions/schema-model/ViewSchemaModelAsJsonAction.ts +9 -2
  201. package/src/core/AbstractConnection.ts +7 -1
  202. package/src/core/SchemaModelDefinition.ts +16 -0
  203. package/src/core/connection-colors.ts +49 -0
  204. package/src/core/query/StandardModelFields.ts +9 -0
  205. package/src/core/query/filters.ts +121 -6
  206. package/src/core/query/query-simple/SimplePage.ts +4 -5
  207. package/src/core/query/query-simple/SimpleQuery.tsx +83 -86
  208. package/src/core/query/query-simple/SimpleQueryColumns.tsx +126 -0
  209. package/src/core/query/query-simple/SimpleQueryFilterState.ts +160 -0
  210. package/src/core/query/query-simple/SimpleQueryPlanner.ts +18 -0
  211. package/src/core/query/query-simple/SimpleQuerySortState.ts +133 -0
  212. package/src/core/query/query-simple/SimpleQueryTypes.ts +61 -0
  213. package/src/core/query/widgets/BelongsToDisplayWidget.tsx +19 -11
  214. package/src/core/query/widgets/CellDisplayWidget.tsx +5 -10
  215. package/src/core/query/widgets/ColumnDisplayWidget.tsx +24 -20
  216. package/src/core/query/widgets/PeekRelationshipButton.tsx +161 -0
  217. package/src/core/query/widgets/SmartColumnWidget.tsx +26 -4
  218. package/src/core/query/widgets/SmartFilterWidget.tsx +119 -69
  219. package/src/entities/ConnectionEntityDefinition.tsx +33 -4
  220. package/src/entities/SavedQueryEntityDefinition.ts +72 -0
  221. package/src/entities/SchemaModelDefinitionEntityDefinition.ts +5 -2
  222. package/src/entities.ts +2 -1
  223. package/src/forms/APIConnectionForm.tsx +15 -2
  224. package/src/forms/TypeEngine.tsx +35 -421
  225. package/src/forms/types/attachment-handler.tsx +35 -0
  226. package/src/forms/types/boolean-handler.tsx +28 -0
  227. package/src/forms/types/date-handler.tsx +125 -0
  228. package/src/forms/types/filters/ClearableFilterFormDialogDirective.ts +32 -0
  229. package/src/forms/types/filters/ConditionalFilterForm.tsx +109 -0
  230. package/src/forms/types/image-handler.tsx +90 -0
  231. package/src/forms/types/location-handler.tsx +53 -0
  232. package/src/forms/types/multiple-choice-handler.tsx +37 -0
  233. package/src/forms/types/multiple-choice-integer-handler.tsx +37 -0
  234. package/src/forms/types/number-handler.tsx +100 -0
  235. package/src/forms/types/shared/type-handler.ts +36 -0
  236. package/src/forms/types/shared/ui.tsx +40 -0
  237. package/src/forms/types/single-choice-handler.tsx +47 -0
  238. package/src/forms/types/single-choice-integer-handler.tsx +47 -0
  239. package/src/forms/types/text-handler.tsx +247 -0
  240. package/src/index.ts +1 -0
  241. package/src/panels/_shared/SharedConnectionPanelFactory.tsx +55 -0
  242. package/src/panels/_shared/SharedModelPanelFactory.tsx +8 -2
  243. package/src/panels/query/PageResultsWidget.tsx +40 -28
  244. package/src/panels/query/QueryPanelFactory.tsx +19 -2
  245. package/src/panels/query/QueryPanelWidget.tsx +64 -9
  246. package/src/panels/query/TableControlsWidget.tsx +42 -120
  247. package/src/panels/query/table-controls/ChangesControlsWidget.tsx +72 -0
  248. package/src/panels/query/table-controls/FilterControlsWidget.tsx +145 -0
  249. package/src/panels/query/table-controls/PageControlsWidget.tsx +97 -0
  250. package/src/panels/query/table-controls/QueryControlsWidget.tsx +127 -0
  251. package/src/panels/query/table-controls/SortChipWidget.tsx +119 -0
  252. package/src/panels/query/table-controls/SortControlsWidget.tsx +95 -0
  253. package/src/preferences/QueryControlPreferences.ts +34 -0
  254. package/src/stores/ConnectionStore.ts +2 -0
  255. package/src/stores/SavedQueryStore.ts +121 -0
  256. package/src/widgets/EmptyValueWidget.tsx +20 -0
@@ -1,19 +1,20 @@
1
- import { inject, TableColumn } from '@journeyapps-labs/reactor-mod';
1
+ import { inject, ioc, TableColumn } from '@journeyapps-labs/reactor-mod';
2
2
  import { ConnectionStore } from '../../../stores/ConnectionStore';
3
- import { Promise, Variable } from '@journeyapps/db';
4
- import { Page, PageRow } from '../Page';
5
- import { SchemaModelDefinition } from '../../SchemaModelDefinition';
3
+ import { Promise } from '@journeyapps/db';
6
4
  import * as _ from 'lodash';
7
- import * as React from 'react';
5
+ import { Page } from '../Page';
6
+ import { SchemaModelDefinition } from '../../SchemaModelDefinition';
8
7
  import { action, observable } from 'mobx';
9
- import { CellDisplayWidget } from '../widgets/CellDisplayWidget';
10
- import { SmartColumnWidget } from '../widgets/SmartColumnWidget';
11
- import { SimpleFilter } from '../filters';
12
- import { SmartCellDisplayWidget } from '../widgets/SmartCellDisplayWidget';
13
8
  import { SchemaModelObject } from '../../SchemaModelObject';
14
9
  import { SimplePage } from './SimplePage';
15
10
  import { AbstractQueryEncoded, AbstractSerializableQuery } from '../AbstractSerializableQuery';
16
- import { SmartBelongsToDisplayWidget } from '../widgets/SmartBelongsToDisplayWidget';
11
+ import { SerializedSimpleFilter } from '../filters';
12
+ import { TypeEngine } from '../../../forms/TypeEngine';
13
+ import { SerializedSimpleQuerySort, SimpleQuerySort, SortDirection } from './SimpleQueryTypes';
14
+ import { applyFiltersAndSorts } from './SimpleQueryPlanner';
15
+ import { buildSimpleQueryColumns } from './SimpleQueryColumns';
16
+ import { SimpleQuerySortState } from './SimpleQuerySortState';
17
+ import { SimpleQueryFilterState } from './SimpleQueryFilterState';
17
18
 
18
19
  export interface SimpleQueryOptions {
19
20
  definition?: SchemaModelDefinition;
@@ -23,8 +24,12 @@ export interface SimpleQueryOptions {
23
24
  export interface SimpleQueryEncoded extends AbstractQueryEncoded {
24
25
  limit: number;
25
26
  definition: string;
27
+ filters?: SerializedSimpleFilter[];
28
+ sorts?: SerializedSimpleQuerySort[];
26
29
  }
27
30
 
31
+ export { SortDirection, SimpleQuerySort, type SerializedSimpleQuerySort };
32
+
28
33
  export class SimpleQuery extends AbstractSerializableQuery<SimpleQueryEncoded> {
29
34
  @inject(ConnectionStore)
30
35
  accessor connStore: ConnectionStore;
@@ -35,34 +40,70 @@ export class SimpleQuery extends AbstractSerializableQuery<SimpleQueryEncoded> {
35
40
  @observable
36
41
  accessor _pages: Page[];
37
42
 
38
- simple_filters: Map<Variable, SimpleFilter>;
43
+ readonly sortState: SimpleQuerySortState;
44
+ readonly filterState: SimpleQueryFilterState;
45
+ private suspendStateLoad: boolean;
46
+ private readonly scheduleLoad: () => void;
39
47
 
40
48
  constructor(public options: SimpleQueryOptions = {}) {
41
49
  super('simple', options.definition?.connection);
42
50
  this._totalPages = 0;
43
51
  this._pages = [];
44
- this.simple_filters = new Map();
52
+ this.sortState = new SimpleQuerySortState(options.definition);
53
+ this.filterState = new SimpleQueryFilterState({
54
+ definition: options.definition,
55
+ typeEngine: ioc.get(TypeEngine)
56
+ });
57
+ this.suspendStateLoad = false;
58
+ this.scheduleLoad = _.debounce(
59
+ () => {
60
+ if (this.suspendStateLoad) {
61
+ return;
62
+ }
63
+ this.load();
64
+ },
65
+ 10,
66
+ { trailing: true, leading: false }
67
+ );
68
+ const onStateChange = () => {
69
+ if (this.suspendStateLoad) {
70
+ return;
71
+ }
72
+ this.scheduleLoad();
73
+ };
74
+ this.sortState.registerListener({
75
+ changed: () => {
76
+ onStateChange();
77
+ }
78
+ });
79
+ this.filterState.registerListener({
80
+ changed: () => {
81
+ onStateChange();
82
+ }
83
+ });
45
84
  }
46
85
 
47
86
  @action async load() {
48
87
  this._pages = [];
49
- let collection = await this.options.definition.getCollection();
50
- let query = collection.all();
51
- this.simple_filters.forEach((f) => {
52
- query = f.augment(query);
53
- });
54
- let results = await (collection.adapter as any).doApiQuery(query);
88
+ const collection = await this.options.definition.getCollection();
89
+ const query = applyFiltersAndSorts(
90
+ collection.all(),
91
+ Array.from(this.filterState.simpleFilters.values()),
92
+ this.sortState.sorts
93
+ );
94
+ const results = await (collection.adapter as any).doApiQuery(query);
55
95
  this._totalPages = Math.ceil(results.total / this.options.limit);
56
96
  }
57
97
 
58
98
  getPage(number: number): Page {
59
99
  if (!this._pages[number]) {
60
- let page = new SimplePage({
100
+ const page = new SimplePage({
61
101
  offset: number * this.options.limit,
62
102
  limit: this.options.limit,
63
103
  definition: this.options.definition,
64
104
  index: number,
65
- filters: Array.from(this.simple_filters.values())
105
+ filters: Array.from(this.filterState.simpleFilters.values()),
106
+ sorts: this.sortState.sorts
66
107
  });
67
108
  page.load();
68
109
  this._pages[number] = page;
@@ -78,76 +119,35 @@ export class SimpleQuery extends AbstractSerializableQuery<SimpleQueryEncoded> {
78
119
  return {
79
120
  ...super.serialize(),
80
121
  definition: this.options.definition.definition.name,
81
- limit: this.options.limit
122
+ limit: this.options.limit,
123
+ filters: this.filterState.getSerializedFilters(),
124
+ sorts: this.sortState.getSerializedSorts()
82
125
  };
83
126
  }
84
127
 
85
128
  async deserialize(connectionStore: ConnectionStore, data: SimpleQueryEncoded): Promise<void> {
86
129
  await super.deserialize(connectionStore, data);
87
- this.options.limit = data.limit;
88
- this.options.definition = await this.connection.waitForSchemaModelDefinitionByName(data.definition);
130
+ this.suspendStateLoad = true;
131
+ try {
132
+ this.options.limit = data.limit;
133
+ const definition = await this.connection.waitForSchemaModelDefinitionByName(data.definition);
134
+ this.options.definition = definition;
135
+ this.sortState.setDefinition(definition);
136
+ this.filterState.setDefinition(definition);
137
+ this.filterState.hydrateFilters(data.filters || []);
138
+ this.sortState.hydrateSorts(data.sorts || []);
139
+ } finally {
140
+ this.suspendStateLoad = false;
141
+ }
89
142
  }
90
143
 
91
144
  getColumns(): TableColumn[] {
92
- return [
93
- {
94
- key: 'id',
95
- display: 'ID',
96
- noWrap: true,
97
- shrink: true
98
- },
99
- {
100
- key: 'updated_at',
101
- display: 'Updated at',
102
- noWrap: true,
103
- shrink: true,
104
- accessor: (cell, row: PageRow) => {
105
- return <CellDisplayWidget name="updated_at" cell={row.model.updated_at} row={row} />;
106
- }
107
- },
108
- ..._.map(this.options.definition.definition.belongsToIdVars, (a) => {
109
- return {
110
- key: a.name,
111
- display: (
112
- <SmartColumnWidget
113
- variable={this.options.definition.definition.belongsToVars[a.relationship]}
114
- type={this.options.definition.definition.belongsTo[a.relationship].foreignType}
115
- filterChanged={(filter) => {}}
116
- />
117
- ),
118
- noWrap: true,
119
- shrink: true,
120
- accessor: (cell, row: PageRow) => {
121
- return <SmartBelongsToDisplayWidget variable_id={a} row={row} connection={this.connection} />;
122
- }
123
- } as TableColumn;
124
- }),
125
- ..._.map(this.options.definition.definition.attributes, (a) => {
126
- return {
127
- key: a.name,
128
- display: (
129
- <SmartColumnWidget
130
- variable={a}
131
- filter={this.simple_filters.get(a)}
132
- filterChanged={(filter) => {
133
- if (!filter) {
134
- this.simple_filters.delete(a);
135
- this.load();
136
- } else {
137
- this.simple_filters.set(a, filter);
138
- this.load();
139
- }
140
- }}
141
- />
142
- ),
143
- noWrap: true,
144
- shrink: true,
145
- accessor: (cell, row: PageRow) => {
146
- return <SmartCellDisplayWidget name={a.name} row={row} />;
147
- }
148
- } as TableColumn;
149
- })
150
- ];
145
+ return buildSimpleQueryColumns({
146
+ definition: this.options.definition,
147
+ connection: this.connection,
148
+ sortState: this.sortState,
149
+ filterState: this.filterState
150
+ });
151
151
  }
152
152
 
153
153
  getSimpleName(): string {
@@ -155,9 +155,6 @@ export class SimpleQuery extends AbstractSerializableQuery<SimpleQueryEncoded> {
155
155
  }
156
156
 
157
157
  getDirtyObjects(): SchemaModelObject[] {
158
- return _.flatMap(
159
- this._pages.filter((p) => !!p),
160
- (page) => page.getDirtyObjects()
161
- );
158
+ return this._pages.flatMap((page) => page?.getDirtyObjects() || []);
162
159
  }
163
160
  }
@@ -0,0 +1,126 @@
1
+ import * as React from 'react';
2
+ import { TableColumn } from '@journeyapps-labs/reactor-mod';
3
+ import * as _ from 'lodash';
4
+ import { PageRow } from '../Page';
5
+ import { CellDisplayWidget } from '../widgets/CellDisplayWidget';
6
+ import { SmartColumnWidget } from '../widgets/SmartColumnWidget';
7
+ import { SmartCellDisplayWidget } from '../widgets/SmartCellDisplayWidget';
8
+ import { SmartBelongsToDisplayWidget } from '../widgets/SmartBelongsToDisplayWidget';
9
+ import { ColumnDisplayWidget } from '../widgets/ColumnDisplayWidget';
10
+ import { SchemaModelDefinition } from '../../SchemaModelDefinition';
11
+ import { AbstractConnection } from '../../AbstractConnection';
12
+ import { SimpleQuerySort, SortDirection } from './SimpleQueryTypes';
13
+ import { SimpleQuerySortState } from './SimpleQuerySortState';
14
+ import { SimpleQueryFilterState } from './SimpleQueryFilterState';
15
+ import { STANDARD_MODEL_FIELD_LABELS, StandardModelFields } from '../StandardModelFields';
16
+
17
+ export interface BuildSimpleQueryColumnsOptions {
18
+ definition: SchemaModelDefinition;
19
+ connection: AbstractConnection;
20
+ sortState: SimpleQuerySortState;
21
+ filterState: SimpleQueryFilterState;
22
+ }
23
+
24
+ export const buildSimpleQueryColumns = (options: BuildSimpleQueryColumnsOptions): TableColumn[] => {
25
+ const getSortLabel = (field: string, label: string) => {
26
+ const direction = options.sortState.getSort(field)?.direction;
27
+ if (!direction) {
28
+ return label;
29
+ }
30
+ return `${label} ${direction === SortDirection.ASC ? '↑' : '↓'}`;
31
+ };
32
+
33
+ return [
34
+ {
35
+ key: StandardModelFields.ID,
36
+ display: (
37
+ <ColumnDisplayWidget
38
+ label={getSortLabel(StandardModelFields.ID, STANDARD_MODEL_FIELD_LABELS[StandardModelFields.ID])}
39
+ onClick={async () => {
40
+ const sort = options.sortState.getSort(StandardModelFields.ID);
41
+ if (!sort) {
42
+ options.sortState.addSort(SimpleQuerySort.create(StandardModelFields.ID));
43
+ return;
44
+ }
45
+ sort.toggle();
46
+ }}
47
+ />
48
+ ),
49
+ noWrap: true,
50
+ shrink: true
51
+ },
52
+ {
53
+ key: StandardModelFields.UPDATED_AT,
54
+ display: (
55
+ <ColumnDisplayWidget
56
+ label={getSortLabel(
57
+ StandardModelFields.UPDATED_AT,
58
+ STANDARD_MODEL_FIELD_LABELS[StandardModelFields.UPDATED_AT]
59
+ )}
60
+ onClick={async () => {
61
+ const sort = options.sortState.getSort(StandardModelFields.UPDATED_AT);
62
+ if (!sort) {
63
+ options.sortState.addSort(SimpleQuerySort.create(StandardModelFields.UPDATED_AT));
64
+ return;
65
+ }
66
+ sort.toggle();
67
+ }}
68
+ />
69
+ ),
70
+ noWrap: true,
71
+ shrink: true,
72
+ accessor: (cell, row: PageRow) => {
73
+ return <CellDisplayWidget name={StandardModelFields.UPDATED_AT} cell={row.model.updated_at} row={row} />;
74
+ }
75
+ },
76
+ ..._.map(options.definition.definition.belongsToIdVars, (a) => {
77
+ return {
78
+ key: a.name,
79
+ display: (
80
+ <SmartColumnWidget
81
+ variable={options.definition.definition.belongsToVars[a.relationship]}
82
+ type={options.definition.definition.belongsTo[a.relationship].foreignType}
83
+ filterChanged={(filter) => {}}
84
+ />
85
+ ),
86
+ noWrap: true,
87
+ shrink: true,
88
+ accessor: (cell, row: PageRow) => {
89
+ return <SmartBelongsToDisplayWidget variable_id={a} row={row} connection={options.connection} />;
90
+ }
91
+ } as TableColumn;
92
+ }),
93
+ ..._.map(options.definition.definition.attributes, (a) => {
94
+ return {
95
+ key: a.name,
96
+ display: (
97
+ <SmartColumnWidget
98
+ variable={a}
99
+ filter={options.filterState.getFilter(a.name)}
100
+ sortDirection={options.sortState.getSort(a.name)?.direction}
101
+ onToggleSort={async () => {
102
+ const sort = options.sortState.getSort(a.name);
103
+ if (!sort) {
104
+ options.sortState.addSort(SimpleQuerySort.create(a.name));
105
+ return;
106
+ }
107
+ sort.toggle();
108
+ }}
109
+ filterChanged={async (filter) => {
110
+ if (!filter) {
111
+ options.filterState.getFilter(a.name)?.delete();
112
+ return;
113
+ }
114
+ options.filterState.setFilter(a.name, filter);
115
+ }}
116
+ />
117
+ ),
118
+ noWrap: true,
119
+ shrink: true,
120
+ accessor: (cell, row: PageRow) => {
121
+ return <SmartCellDisplayWidget name={a.name} row={row} />;
122
+ }
123
+ } as TableColumn;
124
+ })
125
+ ];
126
+ };
@@ -0,0 +1,160 @@
1
+ import { Variable } from '@journeyapps/db';
2
+ import * as _ from 'lodash';
3
+ import { BaseObserver } from '@journeyapps-labs/common-utils';
4
+ import { SchemaModelDefinition } from '../../SchemaModelDefinition';
5
+ import { SerializedSimpleFilter, SimpleFilter } from '../filters';
6
+ import { TypeEngine } from '../../../forms/TypeEngine';
7
+
8
+ export interface SimpleQueryFilterStateListener {
9
+ changed: () => any;
10
+ }
11
+
12
+ export interface SimpleQueryFilterStateOptions {
13
+ definition?: SchemaModelDefinition;
14
+ typeEngine: TypeEngine;
15
+ }
16
+
17
+ export interface ActiveFilterEntry {
18
+ variable: Variable;
19
+ filter: SimpleFilter;
20
+ }
21
+
22
+ export class SimpleQueryFilterState extends BaseObserver<SimpleQueryFilterStateListener> {
23
+ readonly simpleFilters: Map<Variable, SimpleFilter>;
24
+ accessor definition: SchemaModelDefinition | undefined;
25
+ readonly typeEngine: TypeEngine;
26
+
27
+ constructor(options: SimpleQueryFilterStateOptions) {
28
+ super();
29
+ this.simpleFilters = new Map();
30
+ this.definition = options.definition;
31
+ this.typeEngine = options.typeEngine;
32
+ }
33
+
34
+ get filters(): SimpleFilter[] {
35
+ return Array.from(this.simpleFilters.values());
36
+ }
37
+
38
+ clear() {
39
+ this.filters.forEach((filter) => filter.delete());
40
+ }
41
+
42
+ setDefinition(definition: SchemaModelDefinition | undefined) {
43
+ const definitionChanged = this.definition?.definition?.name !== definition?.definition?.name;
44
+ this.definition = definition;
45
+ const hadFilters = this.simpleFilters.size > 0;
46
+ if (definitionChanged) {
47
+ this.clear();
48
+ }
49
+ if (definitionChanged && !hadFilters) {
50
+ this.iterateListeners((cb) => cb.changed?.());
51
+ }
52
+ return definitionChanged || hadFilters;
53
+ }
54
+
55
+ getFilterableFields(): { key: string; label: string }[] {
56
+ if (!this.definition?.definition) {
57
+ return [];
58
+ }
59
+ return this.definition.getFilterableFields(this.typeEngine);
60
+ }
61
+
62
+ getActiveFilters(): ActiveFilterEntry[] {
63
+ return Array.from(this.simpleFilters.entries()).map(([variable, filter]) => {
64
+ return {
65
+ variable,
66
+ filter
67
+ };
68
+ });
69
+ }
70
+
71
+ getFilter(field: string): SimpleFilter | undefined {
72
+ const variable = this.resolveAttribute(field);
73
+ return variable ? this.simpleFilters.get(variable) : undefined;
74
+ }
75
+
76
+ getSerializedFilters() {
77
+ return this.filters.map((filter) => filter.serialize());
78
+ }
79
+
80
+ hydrateFilters(filters: SerializedSimpleFilter[]) {
81
+ this.clear();
82
+ const definition = this.definition;
83
+ if (!definition?.definition) {
84
+ return;
85
+ }
86
+ (filters || []).forEach((filter) => {
87
+ if (!SimpleFilter.canDeserialize(filter)) {
88
+ return;
89
+ }
90
+ const variable = _.find(_.values(definition.definition.attributes), (attribute) => {
91
+ return attribute.name === filter.variable;
92
+ });
93
+ if (!variable) {
94
+ return;
95
+ }
96
+ this.addFilter(variable, SimpleFilter.deserialize(variable, filter));
97
+ });
98
+ }
99
+
100
+ setFilter(field: string, filter: SimpleFilter) {
101
+ const variable = this.resolveAttribute(field);
102
+ if (!variable) {
103
+ return false;
104
+ }
105
+ return this.addFilter(variable, filter);
106
+ }
107
+
108
+ async setupFilterForField(field: string, position?: MouseEvent): Promise<boolean> {
109
+ const variable = this.resolveAttribute(field);
110
+ if (!variable) {
111
+ return false;
112
+ }
113
+ const handler = this.typeEngine.getHandler(variable.type);
114
+ if (!handler?.setupFilter) {
115
+ return false;
116
+ }
117
+ const existing = this.simpleFilters.get(variable);
118
+ const nextFilter = await handler.setupFilter({
119
+ variable,
120
+ filter: existing,
121
+ position
122
+ });
123
+ if (!nextFilter) {
124
+ return false;
125
+ }
126
+ return this.addFilter(variable, nextFilter);
127
+ }
128
+
129
+ private addFilter(variable: Variable, filter: SimpleFilter): boolean {
130
+ const existing = this.simpleFilters.get(variable);
131
+ if (existing && existing !== filter) {
132
+ existing.delete();
133
+ }
134
+ if (existing === filter) {
135
+ this.iterateListeners((cb) => cb.changed?.());
136
+ return true;
137
+ }
138
+ let unsubscribe = filter.registerListener({
139
+ removeRequested: () => {
140
+ unsubscribe();
141
+ if (this.simpleFilters.get(variable) === filter) {
142
+ this.simpleFilters.delete(variable);
143
+ this.iterateListeners((cb) => cb.changed?.());
144
+ }
145
+ }
146
+ });
147
+ this.simpleFilters.set(variable, filter);
148
+ this.iterateListeners((cb) => cb.changed?.());
149
+ return true;
150
+ }
151
+
152
+ private resolveAttribute(field: string): Variable | undefined {
153
+ if (!this.definition?.definition) {
154
+ return undefined;
155
+ }
156
+ return _.find(_.values(this.definition.definition.attributes), (attribute) => {
157
+ return attribute.name === field;
158
+ });
159
+ }
160
+ }
@@ -0,0 +1,18 @@
1
+ import { Query } from '@journeyapps/db';
2
+ import { AbstractFilter } from '../filters';
3
+ import { SimpleQuerySort, SortDirection } from './SimpleQueryTypes';
4
+
5
+ export const applyFiltersAndSorts = (query: Query, filters: AbstractFilter[], sorts: SimpleQuerySort[] = []): Query => {
6
+ let next = query;
7
+ (filters || []).forEach((filter) => {
8
+ next = filter.augment(next);
9
+ });
10
+ if (sorts.length > 0) {
11
+ next = next.orderBy(
12
+ ...sorts.map((sort) => {
13
+ return sort.direction === SortDirection.DESC ? `-${sort.field}` : sort.field;
14
+ })
15
+ );
16
+ }
17
+ return next;
18
+ };
@@ -0,0 +1,133 @@
1
+ import { observable } from 'mobx';
2
+ import * as _ from 'lodash';
3
+ import { BaseObserver } from '@journeyapps-labs/common-utils';
4
+ import { SchemaModelDefinition } from '../../SchemaModelDefinition';
5
+ import { SerializedSimpleQuerySort, SimpleQuerySort, SortDirection } from './SimpleQueryTypes';
6
+ import { STANDARD_MODEL_FIELD_LABELS, StandardModelFields } from '../StandardModelFields';
7
+
8
+ export interface SimpleQuerySortStateListener {
9
+ changed: () => any;
10
+ }
11
+
12
+ export class SimpleQuerySortState extends BaseObserver<SimpleQuerySortStateListener> {
13
+ @observable
14
+ accessor sorts: SimpleQuerySort[];
15
+
16
+ accessor definition: SchemaModelDefinition | undefined;
17
+
18
+ constructor(definition?: SchemaModelDefinition) {
19
+ super();
20
+ this.sorts = [];
21
+ this.definition = definition;
22
+ }
23
+
24
+ setDefinition(definition: SchemaModelDefinition | undefined) {
25
+ const changed = this.definition?.definition?.name !== definition?.definition?.name;
26
+ this.definition = definition;
27
+ if (changed) {
28
+ this.iterateListeners((cb) => cb.changed?.());
29
+ }
30
+ return changed;
31
+ }
32
+
33
+ getSort(field: string): SimpleQuerySort | undefined {
34
+ return this.sorts.find((sort) => sort.field === field);
35
+ }
36
+
37
+ addSort(sort: SimpleQuerySort): boolean {
38
+ if (!sort?.field) {
39
+ return false;
40
+ }
41
+ const existing = this.getSort(sort.field);
42
+ if (existing) {
43
+ return false;
44
+ }
45
+ let unsubscribe = sort.registerListener({
46
+ changed: () => {
47
+ this.iterateListeners((cb) => cb.changed?.());
48
+ },
49
+ removeRequested: () => {
50
+ unsubscribe();
51
+ this.sorts = this.sorts.filter((entry) => entry !== sort);
52
+ this.iterateListeners((cb) => cb.changed?.());
53
+ }
54
+ });
55
+ this.sorts = [...this.sorts, sort];
56
+ this.iterateListeners((cb) => cb.changed?.());
57
+ return true;
58
+ }
59
+
60
+ moveSortBefore(sourceField: string, targetField: string): boolean {
61
+ if (!sourceField || !targetField || sourceField === targetField) {
62
+ return false;
63
+ }
64
+ const sourceIndex = this.sorts.findIndex((sort) => sort.field === sourceField);
65
+ const targetIndex = this.sorts.findIndex((sort) => sort.field === targetField);
66
+ if (sourceIndex < 0 || targetIndex < 0) {
67
+ return false;
68
+ }
69
+ const next = [...this.sorts];
70
+ const [source] = next.splice(sourceIndex, 1);
71
+ const nextTargetIndex = next.findIndex((sort) => sort.field === targetField);
72
+ const insertIndex = sourceIndex < targetIndex ? nextTargetIndex + 1 : nextTargetIndex;
73
+ next.splice(insertIndex, 0, source);
74
+ this.sorts = next;
75
+ this.iterateListeners((cb) => cb.changed?.());
76
+ return true;
77
+ }
78
+
79
+ setSorts(sorts: SimpleQuerySort[]) {
80
+ const next = this.normalizeSorts(sorts);
81
+ [...this.sorts].forEach((sort) => sort.remove());
82
+ this.sorts = [];
83
+ next.forEach((sort) => this.addSort(sort));
84
+ return true;
85
+ }
86
+
87
+ hydrateSorts(serializedSorts: SerializedSimpleQuerySort[] = []) {
88
+ const next = (serializedSorts || []).map((sort) => SimpleQuerySort.deserialize(sort));
89
+ return this.setSorts(next);
90
+ }
91
+
92
+ getSerializedSorts(): SerializedSimpleQuerySort[] {
93
+ return this.sorts.map((sort) => sort.serialize());
94
+ }
95
+
96
+ getSortableFields(): { key: string; label: string }[] {
97
+ const definition = this.definition;
98
+ if (!definition?.definition) {
99
+ return [];
100
+ }
101
+ const dynamic = _.map(definition.definition.attributes, (attribute) => {
102
+ return {
103
+ key: attribute.name,
104
+ label: attribute.label || attribute.name
105
+ };
106
+ });
107
+ return [
108
+ { key: StandardModelFields.ID, label: STANDARD_MODEL_FIELD_LABELS[StandardModelFields.ID] },
109
+ {
110
+ key: StandardModelFields.UPDATED_AT,
111
+ label: STANDARD_MODEL_FIELD_LABELS[StandardModelFields.UPDATED_AT]
112
+ },
113
+ ...dynamic
114
+ ];
115
+ }
116
+
117
+ private normalizeSorts(sorts: SimpleQuerySort[]): SimpleQuerySort[] {
118
+ const seen = new Set<string>();
119
+ return (sorts || []).filter((sort) => {
120
+ if (!sort?.field) {
121
+ return false;
122
+ }
123
+ if (!(sort.direction === SortDirection.ASC || sort.direction === SortDirection.DESC)) {
124
+ return false;
125
+ }
126
+ if (seen.has(sort.field)) {
127
+ return false;
128
+ }
129
+ seen.add(sort.field);
130
+ return true;
131
+ });
132
+ }
133
+ }