@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
@@ -0,0 +1,61 @@
1
+ import { BaseObserver } from '@journeyapps-labs/common-utils';
2
+
3
+ export enum SortDirection {
4
+ ASC = 'asc',
5
+ DESC = 'desc'
6
+ }
7
+
8
+ export interface SerializedSimpleQuerySort {
9
+ field: string;
10
+ direction: SortDirection;
11
+ }
12
+
13
+ export interface SimpleQuerySortListener {
14
+ changed: () => any;
15
+ removeRequested: () => any;
16
+ }
17
+
18
+ export class SimpleQuerySort extends BaseObserver<SimpleQuerySortListener> {
19
+ constructor(
20
+ public field: string,
21
+ public direction: SortDirection
22
+ ) {
23
+ super();
24
+ }
25
+
26
+ static deserialize(value: SerializedSimpleQuerySort): SimpleQuerySort {
27
+ return new SimpleQuerySort(value.field, value.direction);
28
+ }
29
+
30
+ static create(field: string, direction: SortDirection = SortDirection.ASC): SimpleQuerySort {
31
+ return new SimpleQuerySort(field, direction);
32
+ }
33
+
34
+ serialize(): SerializedSimpleQuerySort {
35
+ return {
36
+ field: this.field,
37
+ direction: this.direction
38
+ };
39
+ }
40
+
41
+ setDirection(direction: SortDirection): boolean {
42
+ if (this.direction === direction) {
43
+ return false;
44
+ }
45
+ this.direction = direction;
46
+ this.iterateListeners((listener) => listener.changed?.());
47
+ return true;
48
+ }
49
+
50
+ toggle(): void {
51
+ if (this.direction === SortDirection.ASC) {
52
+ this.setDirection(SortDirection.DESC);
53
+ return;
54
+ }
55
+ this.remove();
56
+ }
57
+
58
+ remove(): void {
59
+ this.iterateListeners((listener) => listener.removeRequested?.());
60
+ }
61
+ }
@@ -6,6 +6,8 @@ import { IconWidget, styled, TableButtonWidget } from '@journeyapps-labs/reactor
6
6
  import { copyTextToClipboard } from '@journeyapps-labs/lib-reactor-utils';
7
7
  import { Relationship } from '@journeyapps/parser-schema';
8
8
  import { observer } from 'mobx-react';
9
+ import { PeekRelationshipButton } from './PeekRelationshipButton';
10
+ import { EmptyValueWidget } from '../../../widgets/EmptyValueWidget';
9
11
 
10
12
  export interface BelongsToDisplayWidgetProps {
11
13
  relationship: Relationship;
@@ -49,7 +51,7 @@ export const BelongsToDisplayWidget: React.FC<BelongsToDisplayWidgetProps> = obs
49
51
  }
50
52
 
51
53
  if (!props.id) {
52
- return <S.Empty>Not set</S.Empty>;
54
+ return <EmptyValueWidget>Not set</EmptyValueWidget>;
53
55
  }
54
56
 
55
57
  if (!object) {
@@ -59,12 +61,15 @@ export const BelongsToDisplayWidget: React.FC<BelongsToDisplayWidgetProps> = obs
59
61
  return (
60
62
  <S.Container className={props.className}>
61
63
  {object.data.display}
62
- <TableButtonWidget
63
- icon="arrow-right"
64
- action={() => {
65
- props.open(object);
66
- }}
67
- />
64
+ <S.Actions>
65
+ <PeekRelationshipButton object={object} open={props.open} />
66
+ <TableButtonWidget
67
+ icon="arrow-right"
68
+ action={() => {
69
+ props.open(object);
70
+ }}
71
+ />
72
+ </S.Actions>
68
73
  </S.Container>
69
74
  );
70
75
  });
@@ -75,10 +80,6 @@ namespace S {
75
80
  flex-direction: row;
76
81
  `;
77
82
 
78
- export const Empty = styled.div`
79
- opacity: 0.2;
80
- `;
81
-
82
83
  export const Spinner = styled(IconWidget)`
83
84
  opacity: 0.2;
84
85
  `;
@@ -91,4 +92,11 @@ namespace S {
91
92
  justify-content: space-between;
92
93
  flex-grow: 1;
93
94
  `;
95
+
96
+ export const Actions = styled.div`
97
+ display: flex;
98
+ flex-direction: row;
99
+ column-gap: 3px;
100
+ align-items: center;
101
+ `;
94
102
  }
@@ -1,14 +1,9 @@
1
- import { ioc, SmartDateDisplayWidget, styled } from '@journeyapps-labs/reactor-mod';
2
- import * as _ from 'lodash';
1
+ import { ioc, SmartDateDisplayWidget } from '@journeyapps-labs/reactor-mod';
3
2
  import * as React from 'react';
4
3
  import { PageRow } from '../Page';
5
4
  import { TypeEngine } from '../../../forms/TypeEngine';
6
-
7
- namespace S {
8
- export const Empty = styled.div`
9
- opacity: 0.2;
10
- `;
11
- }
5
+ import { EmptyValueWidget } from '../../../widgets/EmptyValueWidget';
6
+ import { StandardModelFields } from '../StandardModelFields';
12
7
 
13
8
  export interface CellDisplayWidgetProps {
14
9
  row: PageRow;
@@ -19,10 +14,10 @@ export interface CellDisplayWidgetProps {
19
14
  export const CellDisplayWidget: React.FC<CellDisplayWidgetProps> = (props) => {
20
15
  const { row, cell, name } = props;
21
16
  if (cell == null) {
22
- return <S.Empty>null</S.Empty>;
17
+ return <EmptyValueWidget />;
23
18
  }
24
19
 
25
- if (name === 'updated_at') {
20
+ if (name === StandardModelFields.UPDATED_AT) {
26
21
  return <SmartDateDisplayWidget date={cell} />;
27
22
  }
28
23
 
@@ -4,33 +4,37 @@ import styled from '@emotion/styled';
4
4
  export interface ColumnDisplayWidgetProps {
5
5
  label: string;
6
6
  className?: any;
7
+ onClick?: () => any;
7
8
  }
8
9
 
9
10
  export const ColumnDisplayWidget: React.FC<ColumnDisplayWidgetProps> = (props) => {
10
- let parts = (props.label || '').split(' ');
11
- if (parts.length >= 5) {
12
- return (
13
- <S.Width className={props.className} length={150}>
14
- {props.label}
15
- </S.Width>
16
- );
17
- }
18
- if (parts.length >= 3) {
19
- return (
20
- <S.Width className={props.className} length={80}>
21
- {props.label}
22
- </S.Width>
23
- );
24
- }
25
- return <S.Span className={props.className}>{props.label}</S.Span>;
11
+ const parts = (props.label || '').split(' ');
12
+ const minWidth = parts.length >= 5 ? 150 : parts.length >= 3 ? 80 : undefined;
13
+ return (
14
+ <S.Label
15
+ className={props.className}
16
+ minWidth={minWidth}
17
+ clickable={!!props.onClick}
18
+ nowrap={!minWidth}
19
+ onClick={props.onClick}
20
+ >
21
+ {props.label}
22
+ </S.Label>
23
+ );
26
24
  };
27
25
 
28
26
  namespace S {
29
- export const Width = styled.div<{ length: number }>`
30
- min-width: ${(p) => p.length}px;
27
+ const clickableStyle = `
28
+ cursor: pointer;
29
+ user-select: none;
30
+ &:hover {
31
+ opacity: 0.9;
32
+ }
31
33
  `;
32
34
 
33
- export const Span = styled.div`
34
- white-space: nowrap;
35
+ export const Label = styled.div<{ minWidth?: number; clickable: boolean; nowrap: boolean }>`
36
+ ${(p) => (p.minWidth ? `min-width: ${p.minWidth}px;` : '')};
37
+ ${(p) => (p.nowrap ? 'white-space: nowrap;' : '')};
38
+ ${(p) => (p.clickable ? clickableStyle : '')};
35
39
  `;
36
40
  }
@@ -0,0 +1,161 @@
1
+ import * as React from 'react';
2
+ import {
3
+ BooleanSetting,
4
+ ComboBoxItem,
5
+ ComboBoxStore2,
6
+ NotificationStore,
7
+ NotificationType,
8
+ PrefsStore,
9
+ SimpleComboBoxDirective,
10
+ SmartDateDisplayWidget,
11
+ TableButtonWidget,
12
+ themed,
13
+ ioc
14
+ } from '@journeyapps-labs/reactor-mod';
15
+ import { copyTextToClipboard } from '@journeyapps-labs/lib-reactor-utils';
16
+ import { SchemaModelObject } from '../../SchemaModelObject';
17
+ import { TypeEngine } from '../../../forms/TypeEngine';
18
+ import { QueryControlPreferences } from '../../../preferences/QueryControlPreferences';
19
+ import { EmptyValueWidget } from '../../../widgets/EmptyValueWidget';
20
+
21
+ export interface PeekRelationshipButtonProps {
22
+ object: SchemaModelObject;
23
+ open: (object: SchemaModelObject) => any;
24
+ }
25
+
26
+ export const PeekRelationshipButton: React.FC<PeekRelationshipButtonProps> = (props) => {
27
+ const typeEngine = ioc.get(TypeEngine);
28
+ const notifications = ioc.get(NotificationStore);
29
+
30
+ const copyFieldValue = async (attribute: any, value: unknown) => {
31
+ try {
32
+ const handler = typeEngine.getHandler(attribute.type);
33
+ let scalar: unknown = value;
34
+ if (handler) {
35
+ const decoded = await handler.decode(value);
36
+ scalar = handler.encodeToScalar ? await handler.encodeToScalar(decoded) : decoded;
37
+ }
38
+ copyTextToClipboard(scalar == null ? 'null' : `${scalar}`);
39
+ notifications.showNotification({
40
+ title: 'Copied',
41
+ description: `${attribute.label || attribute.name} copied to clipboard`,
42
+ type: NotificationType.SUCCESS
43
+ });
44
+ } catch (error) {
45
+ notifications.showNotification({
46
+ title: 'Copy failed',
47
+ description: `Failed to copy ${attribute.label || attribute.name}`,
48
+ type: NotificationType.ERROR
49
+ });
50
+ }
51
+ };
52
+
53
+ const showPeek = async (event: React.MouseEvent<any>) => {
54
+ const hideNullFields = ioc
55
+ .get(PrefsStore)
56
+ .getPreference<BooleanSetting>(QueryControlPreferences.FILTER_NULL_FIELDS_IN_RELATIONSHIP_PEEK).checked;
57
+
58
+ const attributeItems: ComboBoxItem[] = Object.values(props.object.definition.definition.attributes)
59
+ .map((attribute) => {
60
+ const value = props.object.model?.[attribute.name];
61
+ if (hideNullFields && value == null) {
62
+ return null;
63
+ }
64
+ let display: React.ReactNode = null;
65
+ if (value != null) {
66
+ display = typeEngine.getHandler(attribute.type)?.generateDisplay({
67
+ model: props.object,
68
+ value,
69
+ label: attribute.label || attribute.name,
70
+ name: attribute.name,
71
+ type: attribute.type
72
+ });
73
+ }
74
+ return {
75
+ key: `field-${attribute.name}`,
76
+ title: attribute.label || attribute.name,
77
+ right: <S.FieldValue>{display || (value == null ? <EmptyValueWidget /> : `${value}`)}</S.FieldValue>,
78
+ group: 'Fields',
79
+ action: async () => {
80
+ await copyFieldValue(attribute, value);
81
+ }
82
+ } as ComboBoxItem;
83
+ })
84
+ .filter((item) => !!item);
85
+
86
+ const items: ComboBoxItem[] = [
87
+ {
88
+ key: 'meta-id',
89
+ title: 'ID',
90
+ icon: 'copy',
91
+ right: <S.FieldValue>{props.object.id}</S.FieldValue>,
92
+ disabled: true,
93
+ group: 'Object'
94
+ },
95
+ {
96
+ key: 'meta-updated',
97
+ title: 'Updated at',
98
+ icon: 'clock',
99
+ right: props.object.updated_at ? (
100
+ <SmartDateDisplayWidget date={props.object.updated_at} />
101
+ ) : (
102
+ <S.FieldValue>Unknown</S.FieldValue>
103
+ ),
104
+ disabled: true,
105
+ group: 'Object'
106
+ },
107
+ ...attributeItems,
108
+ {
109
+ key: 'open',
110
+ title: 'Open record',
111
+ icon: 'arrow-right',
112
+ group: 'Actions',
113
+ action: async () => {
114
+ props.open(props.object);
115
+ }
116
+ },
117
+ {
118
+ key: 'copy',
119
+ title: 'Copy ID',
120
+ icon: 'copy',
121
+ group: 'Actions',
122
+ action: async () => {
123
+ copyTextToClipboard(props.object.id);
124
+ notifications.showNotification({
125
+ title: 'Copied',
126
+ description: 'Relationship ID copied to clipboard',
127
+ type: NotificationType.SUCCESS
128
+ });
129
+ }
130
+ }
131
+ ];
132
+ const directive = await ioc.get(ComboBoxStore2).show(
133
+ new SimpleComboBoxDirective({
134
+ title: props.object.data.display || props.object.id,
135
+ subtitle: props.object.definition.definition.label || props.object.definition.definition.name,
136
+ event: event as any,
137
+ items
138
+ })
139
+ );
140
+ directive.getSelectedItem();
141
+ };
142
+
143
+ return (
144
+ <TableButtonWidget
145
+ icon="eye"
146
+ tooltip="Peek relationship"
147
+ action={(event) => {
148
+ showPeek(event as any);
149
+ }}
150
+ />
151
+ );
152
+ };
153
+
154
+ namespace S {
155
+ export const FieldValue = themed.div`
156
+ max-width: 360px;
157
+ overflow: hidden;
158
+ text-overflow: ellipsis;
159
+ white-space: nowrap;
160
+ `;
161
+ }
@@ -2,18 +2,24 @@ import * as React from 'react';
2
2
  import styled from '@emotion/styled';
3
3
  import { ObjectType, Type, Variable } from '@journeyapps/db';
4
4
  import { ColumnDisplayWidget } from './ColumnDisplayWidget';
5
- import { SmartFilterWidget } from './SmartFilterWidget';
5
+ import { SmartFilterMetadataWidget, SmartFilterWidget } from './SmartFilterWidget';
6
6
  import { SimpleFilter } from '../filters';
7
+ import { PanelTitleToolbarButtonWidget } from '@journeyapps-labs/reactor-mod';
8
+ import type { SortDirection } from '../query-simple/SimpleQuery';
7
9
 
8
10
  export interface SmartColumnWidgetProps {
9
11
  variable: Variable;
10
12
  type?: ObjectType;
11
13
  filter?: SimpleFilter;
14
+ sortDirection?: SortDirection;
15
+ onToggleSort?: () => Promise<any> | any;
12
16
  filterChanged: (filter: SimpleFilter | null) => any;
13
17
  }
14
18
 
15
19
  export const SmartColumnWidget: React.FC<SmartColumnWidgetProps> = (props) => {
16
- let display = <ColumnDisplayWidget label={props.variable.label || props.variable.name} />;
20
+ const baseLabel = props.variable.label || props.variable.name;
21
+ const displayLabel = props.sortDirection ? `${baseLabel} ${props.sortDirection === 'asc' ? '↑' : '↓'}` : baseLabel;
22
+ let display = <ColumnDisplayWidget label={displayLabel} onClick={props.onToggleSort} />;
17
23
  if (props.type) {
18
24
  display = (
19
25
  <S.TypeGroup>
@@ -24,14 +30,23 @@ export const SmartColumnWidget: React.FC<SmartColumnWidgetProps> = (props) => {
24
30
  }
25
31
  return (
26
32
  <S.Container>
27
- {display}
28
- <SmartFilterWidget filter={props.filter} variable={props.variable} filterChanged={props.filterChanged} />
33
+ <S.TopRow>
34
+ {display}
35
+ <SmartFilterWidget filter={props.filter} variable={props.variable} filterChanged={props.filterChanged} />
36
+ </S.TopRow>
29
37
  </S.Container>
30
38
  );
31
39
  };
32
40
 
33
41
  namespace S {
34
42
  export const Container = styled.div`
43
+ display: inline-flex;
44
+ flex-direction: column;
45
+ align-items: flex-start;
46
+ row-gap: 3px;
47
+ `;
48
+
49
+ export const TopRow = styled.div`
35
50
  display: flex;
36
51
  flex-direction: row;
37
52
  align-items: center;
@@ -43,4 +58,11 @@ namespace S {
43
58
  `;
44
59
 
45
60
  export const TypeGroup = styled.div``;
61
+
62
+ export const FilterMetaRow = styled.div`
63
+ display: flex;
64
+ align-items: center;
65
+ column-gap: 2px;
66
+ justify-content: flex-start;
67
+ `;
46
68
  }
@@ -1,8 +1,16 @@
1
1
  import * as React from 'react';
2
- import { SingleChoiceIntegerType, SingleChoiceType, TextType, Variable } from '@journeyapps/db';
3
- import { ComboBoxStore, DialogStore, ioc, PanelButtonWidget } from '@journeyapps-labs/reactor-mod';
4
- import * as _ from 'lodash';
5
- import { Condition, SimpleFilter } from '../filters';
2
+ import { Variable } from '@journeyapps/db';
3
+ import {
4
+ DualIconWidget,
5
+ IconWidget,
6
+ MetadataWidget,
7
+ ioc,
8
+ setupTooltipProps,
9
+ styled,
10
+ TooltipPosition
11
+ } from '@journeyapps-labs/reactor-mod';
12
+ import { TypeEngine } from '../../../forms/TypeEngine';
13
+ import { SimpleFilter, StatementMatch } from '../filters';
6
14
 
7
15
  export interface SmartFilterWidgetProps {
8
16
  variable: Variable;
@@ -10,71 +18,113 @@ export interface SmartFilterWidgetProps {
10
18
  filterChanged: (filter: SimpleFilter | null) => any;
11
19
  }
12
20
 
13
- export const SmartFilterWidget: React.FC<SmartFilterWidgetProps> = (props) => {
14
- if (props.variable.type instanceof SingleChoiceIntegerType || props.variable.type instanceof SingleChoiceType) {
15
- return (
16
- <PanelButtonWidget
17
- {...{
18
- icon: 'filter',
19
- highlight: !!props.filter,
20
- action: async (position) => {
21
- let results = await ioc.get(ComboBoxStore).showMultiSelectComboBox(
22
- _.map(props.variable.type.options, (option) => {
23
- return {
24
- title: `${option.value}`,
25
- key: `${option.value}`,
26
- checked: !!props.filter?.statements?.find((s) => s.arg === `${option.value}`)
27
- };
28
- }),
29
- position
30
- );
31
- if (results.length > 0) {
32
- props.filterChanged(
33
- new SimpleFilter(
34
- props.variable,
35
- results.map((r) => {
36
- return {
37
- arg: r.key,
38
- condition: Condition.EQUALS
39
- };
40
- })
41
- )
42
- );
43
- } else {
44
- props.filterChanged(null);
45
- }
46
- }
47
- }}
48
- />
49
- );
21
+ export interface SmartFilterMetadataWidgetProps {
22
+ variable: Variable;
23
+ filter?: SimpleFilter;
24
+ className?: any;
25
+ }
26
+
27
+ const getFilterSummary = (filter?: SimpleFilter): string => {
28
+ const metadata = filter?.getMetadata() || [];
29
+ if (metadata.length === 0) {
30
+ return 'No filter applied';
50
31
  }
51
- if (props.variable.type instanceof TextType) {
52
- return (
53
- <PanelButtonWidget
54
- {...{
55
- icon: 'filter',
56
- highlight: !!props.filter,
57
- action: async () => {
58
- let value = await ioc.get(DialogStore).showInputDialog({
59
- title: `${props.variable.label}`,
60
- initialValue: props.filter?.statements[0]?.arg
61
- });
62
- if (value) {
63
- props.filterChanged(
64
- new SimpleFilter(props.variable, [
65
- {
66
- arg: value,
67
- condition: Condition.EQUALS
68
- }
69
- ])
70
- );
71
- } else {
72
- props.filterChanged(null);
73
- }
74
- }
75
- }}
76
- />
77
- );
32
+ const match = filter?.match === StatementMatch.ALL ? 'AND' : 'OR';
33
+ const joined = metadata.map((entry) => `${entry.label} ${entry.value}`).join(` ${match} `);
34
+ return `Filtered by: ${joined}`.trim();
35
+ };
36
+
37
+ const getFilterTooltip = (filter?: SimpleFilter): string => {
38
+ if (!filter || !filter.statements || filter.statements.length === 0) {
39
+ return 'Click to add a filter';
40
+ }
41
+ return `${getFilterSummary(filter)}. Click to edit filter`;
42
+ };
43
+
44
+ export const SmartFilterMetadataWidget: React.FC<SmartFilterMetadataWidgetProps> = (props) => {
45
+ const metadata = props.filter?.getMetadata() || [];
46
+ if (metadata.length === 0) {
47
+ return null;
78
48
  }
79
- return null;
49
+ const matchLabel = props.filter?.match === StatementMatch.ALL ? 'AND' : 'OR';
50
+ return (
51
+ <S.MetaList className={props.className}>
52
+ {metadata.map((entry, index) => {
53
+ return (
54
+ <React.Fragment key={`${entry.label}-${entry.value}-${index}`}>
55
+ {index > 0 ? <S.MetaJoiner>{matchLabel}</S.MetaJoiner> : null}
56
+ <MetadataWidget {...entry} active={entry.active ?? true} />
57
+ </React.Fragment>
58
+ );
59
+ })}
60
+ </S.MetaList>
61
+ );
80
62
  };
63
+
64
+ export const SmartFilterWidget: React.FC<SmartFilterWidgetProps> = (props) => {
65
+ const isActive = (props.filter?.statements?.length || 0) > 0;
66
+ const handler = ioc.get(TypeEngine).getHandler(props.variable.type);
67
+ if (!handler?.setupFilter) {
68
+ return null;
69
+ }
70
+ return (
71
+ <S.FilterButton
72
+ active={isActive}
73
+ {...setupTooltipProps({
74
+ tooltip: getFilterTooltip(props.filter),
75
+ tooltipPos: TooltipPosition.BOTTOM
76
+ })}
77
+ onClick={async (event) => {
78
+ const filter = await handler.setupFilter?.({
79
+ variable: props.variable,
80
+ filter: props.filter,
81
+ position: event.nativeEvent
82
+ });
83
+ if (filter == null) {
84
+ return;
85
+ }
86
+ props.filterChanged(filter);
87
+ }}
88
+ >
89
+ {isActive ? <DualIconWidget icon1="filter" icon2="check" /> : <IconWidget icon="filter" />}
90
+ </S.FilterButton>
91
+ );
92
+ };
93
+
94
+ namespace S {
95
+ export const MetaList = styled.div`
96
+ display: flex;
97
+ flex-wrap: wrap;
98
+ align-items: center;
99
+ gap: 4px;
100
+ `;
101
+
102
+ export const MetaJoiner = styled.div`
103
+ font-size: 10px;
104
+ font-weight: 700;
105
+ letter-spacing: 0.04em;
106
+ color: ${(p) => p.theme.text.secondary};
107
+ padding: 0 2px;
108
+ `;
109
+
110
+ export const FilterButton = styled.button<{ active: boolean }>`
111
+ position: relative;
112
+ width: 24px;
113
+ height: 24px;
114
+ min-width: 24px;
115
+ border-radius: 6px;
116
+ border: 1px solid ${(p) => (p.active ? p.theme.guide.accent : p.theme.button.border)};
117
+ background: ${(p) => (p.active ? p.theme.buttonPrimary.background : p.theme.button.background)};
118
+ color: ${(p) => (p.active ? p.theme.buttonPrimary.color : p.theme.button.color)};
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ cursor: pointer;
123
+ padding: 0;
124
+ line-height: 1;
125
+
126
+ &:hover {
127
+ color: ${(p) => (p.active ? p.theme.buttonPrimary.colorHover : p.theme.button.colorHover)};
128
+ }
129
+ `;
130
+ }