@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,35 @@
1
+ import { Attachment, AttachmentType } from '@journeyapps/db';
2
+ import { FileInput, TableButtonWidget } from '@journeyapps-labs/reactor-mod';
3
+ import * as React from 'react';
4
+ import { TypeHandler } from './shared/type-handler';
5
+
6
+ export const attachmentHandler: TypeHandler = {
7
+ matches: (type) => type instanceof AttachmentType,
8
+ encode: async (value: File) => {
9
+ return Attachment.create({
10
+ data: await value.arrayBuffer(),
11
+ filename: value.name
12
+ });
13
+ },
14
+ decode: async (value: Attachment) => {
15
+ return new File([await value.toArrayBuffer()], value.id);
16
+ },
17
+ encodeToScalar: async (value: File) => value?.name || '',
18
+ decodeFromScalar: async (value) => new File([], value == null ? '' : `${value}`),
19
+ generateField: ({ label, name }) => {
20
+ return new FileInput({
21
+ name,
22
+ label
23
+ });
24
+ },
25
+ generateDisplay: ({ value }) => {
26
+ return (
27
+ <TableButtonWidget
28
+ icon="download"
29
+ action={() => {
30
+ window.open(value.url(), '_blank');
31
+ }}
32
+ />
33
+ );
34
+ }
35
+ };
@@ -0,0 +1,28 @@
1
+ import { BooleanType } from '@journeyapps/db';
2
+ import { BooleanInput, CheckboxWidget } from '@journeyapps-labs/reactor-mod';
3
+ import * as React from 'react';
4
+ import { TypeHandler } from './shared/type-handler';
5
+
6
+ export const booleanHandler: TypeHandler = {
7
+ matches: (type) => type instanceof BooleanType,
8
+ encode: async (value: boolean) => value,
9
+ decode: async (value: boolean) => value,
10
+ encodeToScalar: async (value: boolean) => value,
11
+ decodeFromScalar: async (value) => value === true || value === 'true',
12
+ generateField: ({ label, name }) => {
13
+ return new BooleanInput({
14
+ name,
15
+ label
16
+ });
17
+ },
18
+ generateDisplay: ({ value, name, model }) => {
19
+ return (
20
+ <CheckboxWidget
21
+ checked={value}
22
+ onChange={(checked) => {
23
+ model.set(name, checked);
24
+ }}
25
+ />
26
+ );
27
+ }
28
+ };
@@ -0,0 +1,125 @@
1
+ import { DatetimeType, DateType, Day, Variable } from '@journeyapps/db';
2
+ import {
3
+ DateInput,
4
+ DateTimePickerType,
5
+ DialogStore2,
6
+ FormInput,
7
+ SmartDateDisplayWidget,
8
+ ioc
9
+ } from '@journeyapps-labs/reactor-mod';
10
+ import * as React from 'react';
11
+ import { TypeHandler } from './shared/type-handler';
12
+ import { Condition, SimpleFilter, Statement, StatementMatch } from '../../core/query/filters';
13
+ import {
14
+ ConditionalFilterForm,
15
+ ConditionalFilterFormOptions,
16
+ ConditionalFilterFormValue,
17
+ ConditionalStatementValue
18
+ } from './filters/ConditionalFilterForm';
19
+ import { ClearableFilterFormDialogDirective } from './filters/ClearableFilterFormDialogDirective';
20
+
21
+ interface DateFilterFormValue extends ConditionalFilterFormValue<Date> {
22
+ statements: ConditionalStatementValue<Date>[];
23
+ }
24
+
25
+ interface DateFilterFormOptions extends ConditionalFilterFormOptions<Date> {
26
+ pickerType: DateTimePickerType;
27
+ statements?: ConditionalStatementValue<Date>[];
28
+ }
29
+
30
+ class DateFilterForm extends ConditionalFilterForm<Date> {
31
+ constructor(protected options2: DateFilterFormOptions) {
32
+ super(options2);
33
+ }
34
+
35
+ static fromFilter(filter: SimpleFilter | undefined, pickerType: DateTimePickerType): DateFilterFormOptions {
36
+ return {
37
+ pickerType,
38
+ match: filter?.match || StatementMatch.ALL,
39
+ statements: (filter?.statements || [])
40
+ .map((statement) => ({
41
+ condition: statement.condition || Condition.EQUALS,
42
+ value: toDateValue(statement.arg)
43
+ }))
44
+ .filter((statement) => !!statement.value)
45
+ };
46
+ }
47
+
48
+ toFilter(variable: Variable): SimpleFilter | null {
49
+ const formValue = this.value() as DateFilterFormValue;
50
+ const statements = (formValue.statements || [])
51
+ .map((statement) => ({
52
+ condition: statement?.condition || Condition.EQUALS,
53
+ arg: toDateValue(statement?.value)
54
+ }))
55
+ .filter((statement) => !!statement.arg)
56
+ .map((statement) => new Statement(statement.condition, new Day(statement.arg)));
57
+ if (statements.length === 0) {
58
+ return null;
59
+ }
60
+ return new SimpleFilter(variable, statements, formValue.match || StatementMatch.ALL);
61
+ }
62
+
63
+ protected generateValueInput(): FormInput {
64
+ return new DateInput({
65
+ name: 'value',
66
+ label: 'Value',
67
+ required: true,
68
+ type: this.options2.pickerType
69
+ });
70
+ }
71
+ }
72
+
73
+ const toDateValue = (value: unknown): Date | undefined => {
74
+ if (!value) {
75
+ return undefined;
76
+ }
77
+ if (value instanceof Day) {
78
+ return value.toDate();
79
+ }
80
+ if (value instanceof Date) {
81
+ return value;
82
+ }
83
+ const parsed = new Date(`${value}`);
84
+ return Number.isNaN(parsed.getTime()) ? undefined : parsed;
85
+ };
86
+
87
+ export const dateHandler: TypeHandler = {
88
+ matches: (type) => type instanceof DatetimeType || type instanceof DateType,
89
+ encode: async (value: Date) => new Day(value),
90
+ decode: async (value: Day | Date) => toDateValue(value),
91
+ encodeToScalar: async (value: Date) => value?.toISOString() || null,
92
+ decodeFromScalar: async (value) => toDateValue(value),
93
+ generateField: ({ label, name, type }) => {
94
+ return new DateInput({
95
+ name,
96
+ label,
97
+ type: type instanceof DatetimeType ? DateTimePickerType.DATETIME : DateTimePickerType.DATE
98
+ });
99
+ },
100
+ generateDisplay: ({ value }) => {
101
+ if (value instanceof Day) {
102
+ return <SmartDateDisplayWidget date={value.toDate()} />;
103
+ }
104
+ return <SmartDateDisplayWidget date={value} />;
105
+ },
106
+ setupFilter: async ({ variable, filter }) => {
107
+ if (!(variable.type instanceof DatetimeType || variable.type instanceof DateType)) {
108
+ return null;
109
+ }
110
+ const pickerType = variable.type instanceof DatetimeType ? DateTimePickerType.DATETIME : DateTimePickerType.DATE;
111
+ const form = new DateFilterForm(DateFilterForm.fromFilter(filter, pickerType));
112
+ const result = await ioc.get(DialogStore2).showDialog(
113
+ new ClearableFilterFormDialogDirective({
114
+ title: `Filter ${variable.label || variable.name}`,
115
+ form,
116
+ filter,
117
+ handler: async () => {}
118
+ })
119
+ );
120
+ if (!result) {
121
+ return null;
122
+ }
123
+ return form.toFilter(variable);
124
+ }
125
+ };
@@ -0,0 +1,32 @@
1
+ import { Btn, FormDialogDirective, FormDialogDirectiveOptions, FormModel } from '@journeyapps-labs/reactor-mod';
2
+ import { AbstractFilter } from '../../../core/query/filters';
3
+
4
+ export interface ClearableFilterFormDialogDirectiveOptions<
5
+ T extends FormModel = FormModel
6
+ > extends FormDialogDirectiveOptions<T> {
7
+ filter?: AbstractFilter;
8
+ }
9
+
10
+ export class ClearableFilterFormDialogDirective<T extends FormModel = FormModel> extends FormDialogDirective<T> {
11
+ constructor(private options3: ClearableFilterFormDialogDirectiveOptions<T>) {
12
+ super(options3);
13
+ }
14
+
15
+ getButtons(): Btn[] {
16
+ const baseButtons = super.getButtons();
17
+ if (!this.options3.filter) {
18
+ return baseButtons;
19
+ }
20
+ return [
21
+ {
22
+ label: 'Clear filter',
23
+ icon: 'trash',
24
+ action: () => {
25
+ this.options3.filter.delete();
26
+ this.dispose(true);
27
+ }
28
+ },
29
+ ...baseButtons
30
+ ];
31
+ }
32
+ }
@@ -0,0 +1,109 @@
1
+ import { ArrayInput, FormInput, FormModel, GroupInput, SelectInput } from '@journeyapps-labs/reactor-mod';
2
+ import { Condition, StatementMatch } from '../../../core/query/filters';
3
+
4
+ export interface ConditionalStatementValue<T = any> {
5
+ condition: Condition;
6
+ value: T;
7
+ }
8
+
9
+ export interface ConditionalFilterFormValue<T = any> {
10
+ match: StatementMatch;
11
+ statements: ConditionalStatementValue<T>[];
12
+ }
13
+
14
+ export interface ConditionalFilterFormOptions<T = any> {
15
+ match?: StatementMatch;
16
+ statements?: ConditionalStatementValue<T>[];
17
+ }
18
+
19
+ export abstract class ConditionalFilterForm<T = any> extends FormModel<ConditionalFilterFormValue<T>> {
20
+ constructor(protected options2?: ConditionalFilterFormOptions<T>) {
21
+ super();
22
+ const matchInput = this.addInput(
23
+ new SelectInput({
24
+ name: 'match',
25
+ label: 'Match',
26
+ visible: false,
27
+ value: options2?.match || StatementMatch.ALL,
28
+ options: {
29
+ [StatementMatch.ALL]: 'Match all conditions (AND)',
30
+ [StatementMatch.ANY]: 'Match any condition (OR)'
31
+ }
32
+ })
33
+ );
34
+ const statementsInput = this.addInput(
35
+ new ArrayInput<ConditionalStatementValue<T>>({
36
+ name: 'statements',
37
+ label: 'Conditions',
38
+ value: this.getInitialStatements(),
39
+ generate: () => {
40
+ return new GroupInput<ConditionalStatementValue<T>>({
41
+ name: 'statement',
42
+ label: 'Condition',
43
+ layout: {
44
+ horizontal: true,
45
+ border: false
46
+ },
47
+ inputs: [
48
+ new SelectInput({
49
+ name: 'condition',
50
+ label: 'Operator',
51
+ value: this.getDefaultCondition(),
52
+ options: this.getConditionOptions()
53
+ }),
54
+ this.generateValueInput()
55
+ ]
56
+ });
57
+ }
58
+ })
59
+ );
60
+ const syncMatchVisibility = () => {
61
+ const count = statementsInput.value?.length || 0;
62
+ matchInput.update({
63
+ visible: count > 1
64
+ });
65
+ };
66
+ syncMatchVisibility();
67
+ statementsInput.registerListener({
68
+ valueChanged: () => {
69
+ syncMatchVisibility();
70
+ }
71
+ });
72
+ }
73
+
74
+ private getInitialStatements(): ConditionalStatementValue<T>[] {
75
+ if (this.options2?.statements && this.options2.statements.length > 0) {
76
+ return this.options2.statements.map((statement) => ({
77
+ condition: statement.condition || this.getDefaultCondition(),
78
+ value: statement.value
79
+ }));
80
+ }
81
+ return [
82
+ {
83
+ condition: this.getDefaultCondition(),
84
+ value: this.getDefaultValue()
85
+ }
86
+ ];
87
+ }
88
+
89
+ protected getDefaultCondition(): Condition {
90
+ return Condition.EQUALS;
91
+ }
92
+
93
+ protected getConditionOptions(): Record<string, string> {
94
+ return {
95
+ [Condition.GREATER_THAN]: '>',
96
+ [Condition.GREATER_THAN_OR_EQUAL]: '>=',
97
+ [Condition.EQUALS]: '=',
98
+ [Condition.LESS_THAN]: '<',
99
+ [Condition.LESS_THAN_OR_EQUAL]: '<=',
100
+ [Condition.NOT_EQUALS]: '!='
101
+ };
102
+ }
103
+
104
+ protected getDefaultValue(): T {
105
+ return null;
106
+ }
107
+
108
+ protected abstract generateValueInput(): FormInput;
109
+ }
@@ -0,0 +1,90 @@
1
+ import { Attachment, PhotoType, SignatureType } from '@journeyapps/db';
2
+ import { ImageInput, ImageMedia, styled } from '@journeyapps-labs/reactor-mod';
3
+ import { TypeHandler, TypeHandlerContext } from './shared/type-handler';
4
+ import { TypeUI } from './shared/ui';
5
+ import * as React from 'react';
6
+
7
+ export const imageHandler = (context: TypeHandlerContext): TypeHandler => {
8
+ const toRawBase64 = (value: string): string => {
9
+ const marker = 'base64,';
10
+ const index = value.indexOf(marker);
11
+ if (index === -1) {
12
+ return value;
13
+ }
14
+ return value.substring(index + marker.length);
15
+ };
16
+
17
+ const decode = async (value: Attachment) => {
18
+ if (context.mediaCache.has(value.id)) {
19
+ return context.mediaCache.get(value.id);
20
+ }
21
+ const media = context.mediaEngine.getMediaTypeForPath('.jpg').generateMedia({
22
+ content: await value.toArrayBuffer(),
23
+ name: value.id,
24
+ uid: value.id
25
+ });
26
+
27
+ context.mediaCache.set(value.id, media);
28
+ return media;
29
+ };
30
+
31
+ return {
32
+ matches: (type) => type instanceof SignatureType || type instanceof PhotoType,
33
+ encode: async (value: ImageMedia) => {
34
+ return Attachment.create({
35
+ data: await value.toArrayBuffer()
36
+ });
37
+ },
38
+ decode,
39
+ encodeToScalar: async (value: ImageMedia) => {
40
+ if (!value) {
41
+ return null;
42
+ }
43
+ const base64 = await value.toBase64();
44
+ return toRawBase64(base64);
45
+ },
46
+ decodeFromScalar: async (value) => {
47
+ if (typeof value !== 'string' || value.trim() === '') {
48
+ return null;
49
+ }
50
+ return context.mediaEngine.getMediaTypeForPath('.jpg').generateMedia({
51
+ content: toRawBase64(value),
52
+ name: 'imported-image',
53
+ uid: 'imported-image'
54
+ });
55
+ },
56
+ generateField: ({ label, name }) => {
57
+ return new ImageInput({
58
+ name,
59
+ label
60
+ });
61
+ },
62
+ generateDisplay: ({ value, type }) => {
63
+ if (value.uploaded()) {
64
+ return (
65
+ <S.Preview
66
+ onClick={() => {
67
+ decode(value).then((media: ImageMedia) => {
68
+ if (media instanceof ImageMedia) {
69
+ media.open();
70
+ } else {
71
+ window.open(value.url(), '_blank');
72
+ }
73
+ });
74
+ }}
75
+ src={value.urls['thumbnail']}
76
+ />
77
+ );
78
+ }
79
+ return <TypeUI.Empty>Not uploaded</TypeUI.Empty>;
80
+ }
81
+ };
82
+ };
83
+
84
+ namespace S {
85
+ export const Preview = styled.img`
86
+ max-height: 40px;
87
+ max-width: 40px;
88
+ cursor: pointer;
89
+ `;
90
+ }
@@ -0,0 +1,53 @@
1
+ import { Location, LocationType } from '@journeyapps/db';
2
+ import { MetadataWidget, styled } from '@journeyapps-labs/reactor-mod';
3
+ import * as React from 'react';
4
+ import { LocationInput } from '../inputs/LocationInput';
5
+ import { TypeHandler } from './shared/type-handler';
6
+
7
+ export const locationHandler: TypeHandler = {
8
+ matches: (type) => type instanceof LocationType,
9
+ encode: async (value: Location) => value,
10
+ decode: async (value: Location) => value,
11
+ encodeToScalar: async (value: Location) => {
12
+ if (!value) {
13
+ return null;
14
+ }
15
+ return `${value.latitude},${value.longitude}`;
16
+ },
17
+ decodeFromScalar: async (value) => {
18
+ if (typeof value !== 'string') {
19
+ return null;
20
+ }
21
+ const [latitude, longitude] = value.split(',').map((v) => Number(v.trim()));
22
+ if (Number.isNaN(latitude) || Number.isNaN(longitude)) {
23
+ return null;
24
+ }
25
+ return new Location({
26
+ latitude,
27
+ longitude,
28
+ timestamp: new Date()
29
+ });
30
+ },
31
+ generateField: ({ label, name }) => {
32
+ return new LocationInput({
33
+ name,
34
+ label
35
+ });
36
+ },
37
+ generateDisplay: ({ value }) => {
38
+ return (
39
+ <S.Container>
40
+ <MetadataWidget label={'Lat'} value={`${value.latitude}`} />
41
+ <MetadataWidget label={'Long'} value={`${value.longitude}`} />
42
+ </S.Container>
43
+ );
44
+ }
45
+ };
46
+
47
+ namespace S {
48
+ export const Container = styled.div`
49
+ display: flex;
50
+ flex-wrap: wrap;
51
+ row-gap: 2px;
52
+ `;
53
+ }
@@ -0,0 +1,37 @@
1
+ import { MultipleChoiceType } from '@journeyapps/db';
2
+ import { MultiSelectInput } from '@journeyapps-labs/reactor-mod';
3
+ import * as _ from 'lodash';
4
+ import { TypeHandler, TypeHandlerContext } from './shared/type-handler';
5
+
6
+ export const multipleChoiceHandler = (context: TypeHandlerContext): TypeHandler => {
7
+ return {
8
+ matches: (type) => type instanceof MultipleChoiceType,
9
+ encode: async (value: string[]) => value,
10
+ decode: async (value: string[]) => value,
11
+ encodeToScalar: async (value: string[]) => JSON.stringify(value || []),
12
+ decodeFromScalar: async (value) => {
13
+ if (typeof value !== 'string') {
14
+ return [];
15
+ }
16
+ try {
17
+ const parsed = JSON.parse(value);
18
+ return Array.isArray(parsed) ? parsed.map((v) => `${v}`) : [];
19
+ } catch (error) {
20
+ return value
21
+ .split(',')
22
+ .map((v) => v.trim())
23
+ .filter((v) => v !== '');
24
+ }
25
+ },
26
+ generateField: ({ label, name, type }) => {
27
+ return new MultiSelectInput({
28
+ name,
29
+ label,
30
+ options: _.mapValues(type.options, (o) => `${o.value}`)
31
+ });
32
+ },
33
+ generateDisplay: ({ value }) => {
34
+ return context.displayArray(value);
35
+ }
36
+ };
37
+ };
@@ -0,0 +1,37 @@
1
+ import { MultipleChoiceIntegerType } from '@journeyapps/db';
2
+ import { MultiSelectInput } from '@journeyapps-labs/reactor-mod';
3
+ import * as _ from 'lodash';
4
+ import { TypeHandler, TypeHandlerContext } from './shared/type-handler';
5
+
6
+ export const multipleChoiceIntegerHandler = (context: TypeHandlerContext): TypeHandler => {
7
+ return {
8
+ matches: (type) => type instanceof MultipleChoiceIntegerType,
9
+ encode: async (value: string[]) => value.map((v) => parseInt(v)),
10
+ decode: async (value: number[]) => value.map((v) => `${v}`),
11
+ encodeToScalar: async (value: string[]) => JSON.stringify(value || []),
12
+ decodeFromScalar: async (value) => {
13
+ if (typeof value !== 'string') {
14
+ return [];
15
+ }
16
+ try {
17
+ const parsed = JSON.parse(value);
18
+ return Array.isArray(parsed) ? parsed.map((v) => `${v}`) : [];
19
+ } catch (error) {
20
+ return value
21
+ .split(',')
22
+ .map((v) => v.trim())
23
+ .filter((v) => v !== '');
24
+ }
25
+ },
26
+ generateField: ({ label, name, type }) => {
27
+ return new MultiSelectInput({
28
+ name,
29
+ label,
30
+ options: _.mapValues(type.options, (o) => `${o.value}`)
31
+ });
32
+ },
33
+ generateDisplay: ({ value }) => {
34
+ return context.displayArray(value);
35
+ }
36
+ };
37
+ };
@@ -0,0 +1,100 @@
1
+ import { NumberType, Variable } from '@journeyapps/db';
2
+ import { DialogStore2, FormInput, NumberInput, ioc } from '@journeyapps-labs/reactor-mod';
3
+ import { TypeHandler } from './shared/type-handler';
4
+ import { Condition, SimpleFilter, Statement, StatementMatch } from '../../core/query/filters';
5
+ import {
6
+ ConditionalFilterForm,
7
+ ConditionalFilterFormOptions,
8
+ ConditionalFilterFormValue,
9
+ ConditionalStatementValue
10
+ } from './filters/ConditionalFilterForm';
11
+ import { ClearableFilterFormDialogDirective } from './filters/ClearableFilterFormDialogDirective';
12
+
13
+ interface NumberFilterFormValue extends ConditionalFilterFormValue<number> {
14
+ statements: ConditionalStatementValue<number>[];
15
+ }
16
+
17
+ interface NumberFilterFormOptions extends ConditionalFilterFormOptions<number> {
18
+ statements?: ConditionalStatementValue<number>[];
19
+ }
20
+
21
+ const toNumericValue = (value: unknown): number | undefined => {
22
+ if (value == null) {
23
+ return undefined;
24
+ }
25
+ const parsed = Number(value);
26
+ return Number.isNaN(parsed) ? undefined : parsed;
27
+ };
28
+
29
+ class NumberFilterForm extends ConditionalFilterForm<number> {
30
+ constructor(options?: NumberFilterFormOptions) {
31
+ super(options);
32
+ }
33
+
34
+ static fromFilter(filter?: SimpleFilter): NumberFilterFormOptions {
35
+ return {
36
+ match: filter?.match || StatementMatch.ALL,
37
+ statements: (filter?.statements || [])
38
+ .map((statement) => ({
39
+ condition: statement.condition || Condition.EQUALS,
40
+ value: toNumericValue(statement.arg)
41
+ }))
42
+ .filter((statement) => statement.value != null)
43
+ };
44
+ }
45
+
46
+ toFilter(variable: Variable): SimpleFilter | null {
47
+ const formValue = this.value() as NumberFilterFormValue;
48
+ const statements = (formValue.statements || [])
49
+ .map((statement) => ({
50
+ condition: statement?.condition || Condition.EQUALS,
51
+ arg: toNumericValue(statement?.value)
52
+ }))
53
+ .filter((statement) => statement.arg != null)
54
+ .map((statement) => new Statement(statement.condition, statement.arg));
55
+ if (statements.length === 0) {
56
+ return null;
57
+ }
58
+ return new SimpleFilter(variable, statements, formValue.match || StatementMatch.ALL);
59
+ }
60
+
61
+ protected generateValueInput(): FormInput {
62
+ return new NumberInput({
63
+ name: 'value',
64
+ label: 'Value',
65
+ required: true
66
+ });
67
+ }
68
+ }
69
+
70
+ export const numberHandler: TypeHandler = {
71
+ matches: (type) => type instanceof NumberType,
72
+ encode: async (value: number) => value,
73
+ decode: async (value: number) => value,
74
+ encodeToScalar: async (value: number) => value,
75
+ decodeFromScalar: async (value) => toNumericValue(value) || 0,
76
+ generateField: ({ label, name }) => {
77
+ return new NumberInput({
78
+ name,
79
+ label
80
+ });
81
+ },
82
+ generateDisplay: ({ value }) => {
83
+ return `${value}`;
84
+ },
85
+ setupFilter: async ({ variable, filter }) => {
86
+ const form = new NumberFilterForm(NumberFilterForm.fromFilter(filter));
87
+ const result = await ioc.get(DialogStore2).showDialog(
88
+ new ClearableFilterFormDialogDirective({
89
+ title: `Filter ${variable.label || variable.name}`,
90
+ form,
91
+ filter,
92
+ handler: async () => {}
93
+ })
94
+ );
95
+ if (!result) {
96
+ return null;
97
+ }
98
+ return form.toFilter(variable);
99
+ }
100
+ };
@@ -0,0 +1,36 @@
1
+ import { Type, Variable } from '@journeyapps/db';
2
+ import { FormInput } from '@journeyapps-labs/reactor-mod';
3
+ import { JSX } from 'react';
4
+ import { SchemaModelObject } from '../../../core/SchemaModelObject';
5
+ import { AbstractMedia, MediaEngine, WorkspaceStore } from '@journeyapps-labs/reactor-mod';
6
+ import { SimpleFilter } from '../../../core/query/filters';
7
+
8
+ export type ScalarValue = string | number | boolean | null;
9
+
10
+ export interface TypeHandler<T extends Type = Type, ENCODED = any, DECODED = any> {
11
+ matches: (type: Type) => boolean;
12
+ generateField: (event: { label: string; name: string; type: T }) => FormInput;
13
+ generateDisplay: (event: {
14
+ label: string;
15
+ name: string;
16
+ type: T;
17
+ value: ENCODED;
18
+ model: SchemaModelObject;
19
+ }) => JSX.Element | string;
20
+ decode: (value: ENCODED) => Promise<DECODED>;
21
+ encode: (value: DECODED) => Promise<ENCODED>;
22
+ encodeToScalar?: (value: DECODED) => Promise<ScalarValue>;
23
+ decodeFromScalar?: (value: ScalarValue) => Promise<DECODED>;
24
+ setupFilter?: (event: {
25
+ variable: Variable;
26
+ filter?: SimpleFilter;
27
+ position?: MouseEvent;
28
+ }) => Promise<SimpleFilter | null>;
29
+ }
30
+
31
+ export interface TypeHandlerContext {
32
+ mediaEngine: MediaEngine;
33
+ workspaceStore: WorkspaceStore;
34
+ mediaCache: Map<string, AbstractMedia>;
35
+ displayArray: (value: any[]) => JSX.Element | string;
36
+ }