@journeyapps-labs/reactor-mod-data-browser 3.3.0 → 3.4.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 (104) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/@types/actions/schema-model/ViewHasManyAction.d.ts +12 -0
  3. package/dist/@types/core/SchemaModelDefinition.d.ts +12 -5
  4. package/dist/@types/core/query/StandardModelFields.d.ts +4 -0
  5. package/dist/@types/core/query/query-simple/SimpleQueryFilterState.d.ts +4 -6
  6. package/dist/@types/core/query/widgets/BelongsToDisplayWidget.d.ts +1 -0
  7. package/dist/@types/core/query/widgets/ColumnDisplayWidget.d.ts +1 -0
  8. package/dist/@types/core/query/widgets/IDCellDisplayWidget.d.ts +5 -0
  9. package/dist/@types/core/query/widgets/PeekRelationshipButton.d.ts +1 -0
  10. package/dist/@types/core/query/widgets/SmartBelongsToDisplayWidget.d.ts +2 -0
  11. package/dist/@types/core/query/widgets/SmartColumnWidget.d.ts +7 -2
  12. package/dist/@types/core/query/widgets/SmartFilterWidget.d.ts +6 -0
  13. package/dist/@types/entities/SchemaModelObjectEntityDefinition.d.ts +2 -1
  14. package/dist/@types/forms/types/belongs-to-filter.d.ts +10 -0
  15. package/dist/@types/forms/types/shared/type-handler.d.ts +1 -0
  16. package/dist/DataBrowserModule.js +2 -0
  17. package/dist/DataBrowserModule.js.map +1 -1
  18. package/dist/actions/schema-model/ViewHasManyAction.js +112 -0
  19. package/dist/actions/schema-model/ViewHasManyAction.js.map +1 -0
  20. package/dist/core/SchemaModelDefinition.js +70 -12
  21. package/dist/core/SchemaModelDefinition.js.map +1 -1
  22. package/dist/core/query/StandardModelFields.js +3 -0
  23. package/dist/core/query/StandardModelFields.js.map +1 -1
  24. package/dist/core/query/query-simple/SimpleQueryColumns.js +36 -11
  25. package/dist/core/query/query-simple/SimpleQueryColumns.js.map +1 -1
  26. package/dist/core/query/query-simple/SimpleQueryFilterState.js +35 -7
  27. package/dist/core/query/query-simple/SimpleQueryFilterState.js.map +1 -1
  28. package/dist/core/query/query-simple/SimpleQuerySortState.js +0 -1
  29. package/dist/core/query/query-simple/SimpleQuerySortState.js.map +1 -1
  30. package/dist/core/query/widgets/BelongsToDisplayWidget.js +1 -1
  31. package/dist/core/query/widgets/BelongsToDisplayWidget.js.map +1 -1
  32. package/dist/core/query/widgets/ColumnDisplayWidget.js +13 -2
  33. package/dist/core/query/widgets/ColumnDisplayWidget.js.map +1 -1
  34. package/dist/core/query/widgets/IDCellDisplayWidget.js +33 -0
  35. package/dist/core/query/widgets/IDCellDisplayWidget.js.map +1 -0
  36. package/dist/core/query/widgets/PeekRelationshipButton.js +23 -16
  37. package/dist/core/query/widgets/PeekRelationshipButton.js.map +1 -1
  38. package/dist/core/query/widgets/SmartBelongsToDisplayWidget.js +1 -1
  39. package/dist/core/query/widgets/SmartBelongsToDisplayWidget.js.map +1 -1
  40. package/dist/core/query/widgets/SmartColumnWidget.js +3 -13
  41. package/dist/core/query/widgets/SmartColumnWidget.js.map +1 -1
  42. package/dist/core/query/widgets/SmartFilterWidget.js +10 -7
  43. package/dist/core/query/widgets/SmartFilterWidget.js.map +1 -1
  44. package/dist/entities/SchemaModelObjectEntityDefinition.js +15 -14
  45. package/dist/entities/SchemaModelObjectEntityDefinition.js.map +1 -1
  46. package/dist/forms/types/attachment-handler.js +1 -0
  47. package/dist/forms/types/attachment-handler.js.map +1 -1
  48. package/dist/forms/types/belongs-to-filter.js +57 -0
  49. package/dist/forms/types/belongs-to-filter.js.map +1 -0
  50. package/dist/forms/types/boolean-handler.js +34 -1
  51. package/dist/forms/types/boolean-handler.js.map +1 -1
  52. package/dist/forms/types/date-handler.js +1 -0
  53. package/dist/forms/types/date-handler.js.map +1 -1
  54. package/dist/forms/types/image-handler.js +1 -0
  55. package/dist/forms/types/image-handler.js.map +1 -1
  56. package/dist/forms/types/location-handler.js +1 -0
  57. package/dist/forms/types/location-handler.js.map +1 -1
  58. package/dist/forms/types/multiple-choice-handler.js +1 -0
  59. package/dist/forms/types/multiple-choice-handler.js.map +1 -1
  60. package/dist/forms/types/multiple-choice-integer-handler.js +1 -0
  61. package/dist/forms/types/multiple-choice-integer-handler.js.map +1 -1
  62. package/dist/forms/types/number-handler.js +3 -2
  63. package/dist/forms/types/number-handler.js.map +1 -1
  64. package/dist/forms/types/single-choice-handler.js +1 -0
  65. package/dist/forms/types/single-choice-handler.js.map +1 -1
  66. package/dist/forms/types/single-choice-integer-handler.js +1 -0
  67. package/dist/forms/types/single-choice-integer-handler.js.map +1 -1
  68. package/dist/forms/types/text-handler.js +1 -0
  69. package/dist/forms/types/text-handler.js.map +1 -1
  70. package/dist/panels/query/table-controls/FilterControlsWidget.js +1 -0
  71. package/dist/panels/query/table-controls/FilterControlsWidget.js.map +1 -1
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/dist-module/bundle.js +51 -35
  74. package/dist-module/bundle.js.map +1 -1
  75. package/package.json +1 -1
  76. package/src/DataBrowserModule.ts +2 -0
  77. package/src/actions/schema-model/ViewHasManyAction.ts +114 -0
  78. package/src/core/SchemaModelDefinition.ts +90 -14
  79. package/src/core/query/StandardModelFields.ts +8 -0
  80. package/src/core/query/query-simple/SimpleQueryColumns.tsx +49 -13
  81. package/src/core/query/query-simple/SimpleQueryFilterState.ts +42 -9
  82. package/src/core/query/query-simple/SimpleQuerySortState.ts +0 -1
  83. package/src/core/query/widgets/BelongsToDisplayWidget.tsx +2 -1
  84. package/src/core/query/widgets/ColumnDisplayWidget.tsx +17 -4
  85. package/src/core/query/widgets/IDCellDisplayWidget.tsx +46 -0
  86. package/src/core/query/widgets/PeekRelationshipButton.tsx +23 -16
  87. package/src/core/query/widgets/SmartBelongsToDisplayWidget.tsx +3 -0
  88. package/src/core/query/widgets/SmartColumnWidget.tsx +23 -20
  89. package/src/core/query/widgets/SmartFilterWidget.tsx +14 -5
  90. package/src/entities/SchemaModelObjectEntityDefinition.ts +17 -15
  91. package/src/forms/types/attachment-handler.tsx +1 -0
  92. package/src/forms/types/belongs-to-filter.tsx +84 -0
  93. package/src/forms/types/boolean-handler.tsx +46 -2
  94. package/src/forms/types/date-handler.tsx +1 -0
  95. package/src/forms/types/image-handler.tsx +1 -0
  96. package/src/forms/types/location-handler.tsx +1 -0
  97. package/src/forms/types/multiple-choice-handler.tsx +1 -0
  98. package/src/forms/types/multiple-choice-integer-handler.tsx +1 -0
  99. package/src/forms/types/number-handler.tsx +3 -2
  100. package/src/forms/types/shared/type-handler.ts +1 -0
  101. package/src/forms/types/single-choice-handler.tsx +1 -0
  102. package/src/forms/types/single-choice-integer-handler.tsx +1 -0
  103. package/src/forms/types/text-handler.tsx +1 -0
  104. package/src/panels/query/table-controls/FilterControlsWidget.tsx +1 -0
@@ -21,6 +21,7 @@ import { EmptyValueWidget } from '../../../widgets/EmptyValueWidget';
21
21
  export interface PeekRelationshipButtonProps {
22
22
  object: SchemaModelObject;
23
23
  open: (object: SchemaModelObject) => any;
24
+ filterBelongsTo?: (object: SchemaModelObject) => any;
24
25
  }
25
26
 
26
27
  export const PeekRelationshipButton: React.FC<PeekRelationshipButtonProps> = (props) => {
@@ -89,8 +90,15 @@ export const PeekRelationshipButton: React.FC<PeekRelationshipButtonProps> = (pr
89
90
  title: 'ID',
90
91
  icon: 'copy',
91
92
  right: <S.FieldValue>{props.object.id}</S.FieldValue>,
92
- disabled: true,
93
- group: 'Object'
93
+ group: 'Object',
94
+ action: async () => {
95
+ copyTextToClipboard(props.object.id);
96
+ notifications.showNotification({
97
+ title: 'Copied',
98
+ description: 'Relationship ID copied to clipboard',
99
+ type: NotificationType.SUCCESS
100
+ });
101
+ }
94
102
  },
95
103
  {
96
104
  key: 'meta-updated',
@@ -114,20 +122,19 @@ export const PeekRelationshipButton: React.FC<PeekRelationshipButtonProps> = (pr
114
122
  props.open(props.object);
115
123
  }
116
124
  },
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
- }
125
+ ...(props.filterBelongsTo
126
+ ? [
127
+ {
128
+ key: 'filter-belongs-to',
129
+ title: 'Filter belongs to',
130
+ icon: 'filter',
131
+ group: 'Actions',
132
+ action: async () => {
133
+ await props.filterBelongsTo?.(props.object);
134
+ }
135
+ } as ComboBoxItem
136
+ ]
137
+ : [])
131
138
  ];
132
139
  const directive = await ioc.get(ComboBoxStore2).show(
133
140
  new SimpleComboBoxDirective({
@@ -4,6 +4,7 @@ import { BelongsToDisplayWidget } from './BelongsToDisplayWidget';
4
4
  import { PageRow } from '../Page';
5
5
  import { ActionSource, styled } from '@journeyapps-labs/reactor-mod';
6
6
  import { AbstractConnection } from '../../AbstractConnection';
7
+ import { SchemaModelObject } from '../../SchemaModelObject';
7
8
  import { Variable } from '@journeyapps/db';
8
9
  import { observer } from 'mobx-react';
9
10
 
@@ -11,6 +12,7 @@ export interface SmartBelongsToDisplayWidgetProps {
11
12
  row: PageRow;
12
13
  connection: AbstractConnection;
13
14
  variable_id: Variable;
15
+ filterBelongsTo?: (object: SchemaModelObject) => any;
14
16
  }
15
17
 
16
18
  export const SmartBelongsToDisplayWidget: React.FC<SmartBelongsToDisplayWidgetProps> = observer((props) => {
@@ -35,6 +37,7 @@ export const SmartBelongsToDisplayWidget: React.FC<SmartBelongsToDisplayWidgetPr
35
37
  relationship={row.model.definition.definition.belongsTo[variable_id.relationship]}
36
38
  connection={connection}
37
39
  id={value}
40
+ filterBelongsTo={props.filterBelongsTo}
38
41
  />
39
42
  );
40
43
  });
@@ -1,17 +1,22 @@
1
1
  import * as React from 'react';
2
2
  import styled from '@emotion/styled';
3
- import { ObjectType, Variable } from '@journeyapps/db';
3
+ import { Variable } from '@journeyapps/db';
4
4
  import { ColumnDisplayWidget } from './ColumnDisplayWidget';
5
- import { SmartFilterWidget } from './SmartFilterWidget';
5
+ import { SmartFilterWidget, SmartTypeEngineFilterWidget } from './SmartFilterWidget';
6
6
  import { SimpleFilter } from '../filters';
7
7
  import { SortDirection } from '../query-simple/SimpleQuery';
8
8
 
9
9
  export interface SmartColumnWidgetProps {
10
10
  variable: Variable;
11
- type?: ObjectType;
11
+ typeLabel?: string;
12
12
  filter?: SimpleFilter;
13
13
  sortDirection?: SortDirection;
14
14
  onToggleSort?: () => Promise<any> | any;
15
+ setupFilter?: (event: {
16
+ variable: Variable;
17
+ filter?: SimpleFilter;
18
+ position?: MouseEvent;
19
+ }) => Promise<SimpleFilter | null>;
15
20
  filterChanged: (filter: SimpleFilter | null) => any;
16
21
  }
17
22
 
@@ -20,20 +25,24 @@ export const SmartColumnWidget: React.FC<SmartColumnWidgetProps> = (props) => {
20
25
  const displayLabel = props.sortDirection
21
26
  ? `${baseLabel} ${props.sortDirection === SortDirection.ASC ? '↓' : '↑'}`
22
27
  : baseLabel;
23
- let display = <ColumnDisplayWidget label={displayLabel} onClick={props.onToggleSort} />;
24
- if (props.type) {
25
- display = (
26
- <S.TypeGroup>
27
- {display}
28
- <S.Type label={props.type.label} />
29
- </S.TypeGroup>
30
- );
31
- }
32
28
  return (
33
29
  <S.Container>
34
30
  <S.TopRow>
35
- {display}
36
- <SmartFilterWidget filter={props.filter} variable={props.variable} filterChanged={props.filterChanged} />
31
+ <ColumnDisplayWidget label={displayLabel} secondaryLabel={props.typeLabel} onClick={props.onToggleSort} />
32
+ {props.setupFilter ? (
33
+ <SmartFilterWidget
34
+ filter={props.filter}
35
+ variable={props.variable}
36
+ setupFilter={props.setupFilter}
37
+ filterChanged={props.filterChanged}
38
+ />
39
+ ) : (
40
+ <SmartTypeEngineFilterWidget
41
+ filter={props.filter}
42
+ variable={props.variable}
43
+ filterChanged={props.filterChanged}
44
+ />
45
+ )}
37
46
  </S.TopRow>
38
47
  </S.Container>
39
48
  );
@@ -54,12 +63,6 @@ namespace S {
54
63
  column-gap: 5px;
55
64
  `;
56
65
 
57
- export const Type = styled(ColumnDisplayWidget)`
58
- opacity: 0.5;
59
- `;
60
-
61
- export const TypeGroup = styled.div``;
62
-
63
66
  export const FilterMetaRow = styled.div`
64
67
  display: flex;
65
68
  align-items: center;
@@ -15,6 +15,11 @@ import { SimpleFilter, StatementMatch } from '../filters';
15
15
  export interface SmartFilterWidgetProps {
16
16
  variable: Variable;
17
17
  filter?: SimpleFilter;
18
+ setupFilter: (event: {
19
+ variable: Variable;
20
+ filter?: SimpleFilter;
21
+ position?: MouseEvent;
22
+ }) => Promise<SimpleFilter | null>;
18
23
  filterChanged: (filter: SimpleFilter | null) => any;
19
24
  }
20
25
 
@@ -63,10 +68,6 @@ export const SmartFilterMetadataWidget: React.FC<SmartFilterMetadataWidgetProps>
63
68
 
64
69
  export const SmartFilterWidget: React.FC<SmartFilterWidgetProps> = (props) => {
65
70
  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
71
  return (
71
72
  <S.FilterButton
72
73
  active={isActive}
@@ -75,7 +76,7 @@ export const SmartFilterWidget: React.FC<SmartFilterWidgetProps> = (props) => {
75
76
  tooltipPos: TooltipPosition.BOTTOM
76
77
  })}
77
78
  onClick={async (event) => {
78
- const filter = await handler.setupFilter?.({
79
+ const filter = await props.setupFilter({
79
80
  variable: props.variable,
80
81
  filter: props.filter,
81
82
  position: event.nativeEvent
@@ -91,6 +92,14 @@ export const SmartFilterWidget: React.FC<SmartFilterWidgetProps> = (props) => {
91
92
  );
92
93
  };
93
94
 
95
+ export const SmartTypeEngineFilterWidget: React.FC<Omit<SmartFilterWidgetProps, 'setupFilter'>> = (props) => {
96
+ const setupFilter = ioc.get(TypeEngine).getHandler(props.variable.type)?.setupFilter;
97
+ if (!setupFilter) {
98
+ return null;
99
+ }
100
+ return <SmartFilterWidget {...props} setupFilter={setupFilter} />;
101
+ };
102
+
94
103
  namespace S {
95
104
  export const MetaList = styled.div`
96
105
  display: flex;
@@ -1,4 +1,5 @@
1
1
  import {
2
+ Action,
2
3
  EntityDefinition,
3
4
  EntityDescriberComponent,
4
5
  inject,
@@ -10,6 +11,8 @@ import { DataBrowserEntities } from '../entities';
10
11
  import { ConnectionStore } from '../stores/ConnectionStore';
11
12
  import { SchemaModelObject } from '../core/SchemaModelObject';
12
13
  import { SchemaModelDefinition } from '../core/SchemaModelDefinition';
14
+ import { validate as validateUUID } from 'uuid';
15
+ import { ViewHasManyAction } from '../actions/schema-model/ViewHasManyAction';
13
16
 
14
17
  export interface SchemaModelObjectEntityDefinitionEncoded {
15
18
  connection_id: string;
@@ -42,21 +45,6 @@ export class SchemaModelObjectEntityDefinition extends EntityDefinition<SchemaMo
42
45
  })
43
46
  );
44
47
 
45
- // this.registerComponent(
46
- // new SimpleParentEntitySearchEngine<SchemaModelDefinition,SchemaModelObject>({
47
- // label: 'ID',
48
- // filterResultsWithMatcher: false,
49
- // type: DataBrowserEntities.SCHEMA_MODEL_DEFINITION,
50
- // getEntities: async (event) => {
51
- // let object = await event.parameters.parent.resolve(event.value);
52
- // if(object){
53
- // return [object]
54
- // }
55
- // return []
56
- // }
57
- // })
58
- // );
59
-
60
48
  this.registerComponent(
61
49
  new SimpleParentEntitySearchEngine<SchemaModelDefinition, SchemaModelObject>({
62
50
  label: 'Label',
@@ -66,6 +54,13 @@ export class SchemaModelObjectEntityDefinition extends EntityDefinition<SchemaMo
66
54
  if (!event.value) {
67
55
  return [];
68
56
  }
57
+ if (validateUUID(event.value)) {
58
+ const object = await event.parameters.parent.resolve(event.value);
59
+ if (object) {
60
+ return [object];
61
+ }
62
+ return [];
63
+ }
69
64
  return await event.parameters.parent.search(event.value);
70
65
  }
71
66
  })
@@ -101,4 +96,11 @@ export class SchemaModelObjectEntityDefinition extends EntityDefinition<SchemaMo
101
96
  getEntityUID(t: SchemaModelObject) {
102
97
  return t.model.id;
103
98
  }
99
+
100
+ isActionAllowedForEntity(action: Action, entity: SchemaModelObject) {
101
+ if (action.id === ViewHasManyAction.ID) {
102
+ return Object.keys(entity.definition.definition.hasMany || {}).length > 0;
103
+ }
104
+ return super.isActionAllowedForEntity(action, entity);
105
+ }
104
106
  }
@@ -5,6 +5,7 @@ import { TypeHandler } from './shared/type-handler';
5
5
 
6
6
  export const attachmentHandler: TypeHandler = {
7
7
  matches: (type) => type instanceof AttachmentType,
8
+ getTypeLabel: () => 'Attachment',
8
9
  encode: async (value: File) => {
9
10
  return Attachment.create({
10
11
  data: await value.arrayBuffer(),
@@ -0,0 +1,84 @@
1
+ import { Variable } from '@journeyapps/db';
2
+ import { ArrayInput, DialogStore2, EntityInput, FormModel, ioc } from '@journeyapps-labs/reactor-mod';
3
+ import { Relationship } from '@journeyapps/parser-schema';
4
+ import { SchemaModelDefinition } from '../../core/SchemaModelDefinition';
5
+ import { SchemaModelObject } from '../../core/SchemaModelObject';
6
+ import { Condition, SimpleFilter, Statement, StatementMatch } from '../../core/query/filters';
7
+ import { DataBrowserEntities } from '../../entities';
8
+ import { ClearableFilterFormDialogDirective } from './filters/ClearableFilterFormDialogDirective';
9
+
10
+ interface BelongsToFilterFormValue {
11
+ values: (SchemaModelObject | null)[];
12
+ }
13
+
14
+ class BelongsToFilterForm extends FormModel<BelongsToFilterFormValue> {
15
+ constructor(private foreignDefinition: SchemaModelDefinition) {
16
+ super();
17
+
18
+ this.addInput(
19
+ new ArrayInput({
20
+ name: 'values',
21
+ label: 'Matches any of',
22
+ value: [null],
23
+ generate: () => {
24
+ return new EntityInput({
25
+ name: 'value',
26
+ label: foreignDefinition.definition.label || foreignDefinition.definition.name,
27
+ entityType: DataBrowserEntities.SCHEMA_MODEL_OBJECT,
28
+ parent: foreignDefinition,
29
+ value: null
30
+ });
31
+ }
32
+ })
33
+ );
34
+ }
35
+
36
+ async hydrateFromFilter(filter?: SimpleFilter) {
37
+ const values = await Promise.all(
38
+ (filter?.statements || [])
39
+ .map((statement) => `${statement.arg || ''}`)
40
+ .filter((id) => !!id)
41
+ .map((id) => this.foreignDefinition.resolve(id))
42
+ );
43
+ this.setValues({
44
+ values: values.filter((value) => !!value).length > 0 ? values.filter((value) => !!value) : [null]
45
+ });
46
+ }
47
+
48
+ toFilter(variable: Variable): SimpleFilter | null {
49
+ const values = (this.value()?.values || []).filter((value) => !!value?.id);
50
+ if (values.length === 0) {
51
+ return null;
52
+ }
53
+ return new SimpleFilter(
54
+ variable,
55
+ values.map((value) => new Statement(Condition.EQUALS, value.id)),
56
+ StatementMatch.ANY
57
+ );
58
+ }
59
+ }
60
+
61
+ export const setupBelongsToFilter = async (options: {
62
+ definition: SchemaModelDefinition;
63
+ relationship: Relationship;
64
+ variable: Variable;
65
+ filter?: SimpleFilter;
66
+ }) => {
67
+ const foreignDefinition = options.definition.connection.getSchemaModelDefinitionByName(
68
+ options.relationship.foreignType.name
69
+ );
70
+ const form = new BelongsToFilterForm(foreignDefinition);
71
+ await form.hydrateFromFilter(options.filter);
72
+ const result = await ioc.get(DialogStore2).showDialog(
73
+ new ClearableFilterFormDialogDirective({
74
+ title: `Filter ${options.relationship.name}`,
75
+ form,
76
+ filter: options.filter,
77
+ handler: async () => {}
78
+ })
79
+ );
80
+ if (!result) {
81
+ return null;
82
+ }
83
+ return form.toFilter(options.variable);
84
+ };
@@ -1,10 +1,39 @@
1
- import { BooleanType } from '@journeyapps/db';
2
- import { BooleanInput, CheckboxWidget } from '@journeyapps-labs/reactor-mod';
1
+ import { BooleanType, Variable } from '@journeyapps/db';
2
+ import { BooleanInput, CheckboxWidget, DialogStore2, FormModel, ioc } from '@journeyapps-labs/reactor-mod';
3
3
  import * as React from 'react';
4
4
  import { TypeHandler } from './shared/type-handler';
5
+ import { ClearableFilterFormDialogDirective } from './filters/ClearableFilterFormDialogDirective';
6
+ import { Condition, SimpleFilter, Statement } from '../../core/query/filters';
7
+
8
+ interface BooleanFilterFormValue {
9
+ value: boolean;
10
+ }
11
+
12
+ class BooleanFilterForm extends FormModel<BooleanFilterFormValue> {
13
+ constructor(value?: boolean) {
14
+ super();
15
+
16
+ this.addInput(
17
+ new BooleanInput({
18
+ name: 'value',
19
+ label: 'Value',
20
+ value: value === true
21
+ })
22
+ );
23
+ }
24
+
25
+ static fromFilter(filter?: SimpleFilter): boolean {
26
+ return filter?.statements?.[0]?.arg === true;
27
+ }
28
+
29
+ toFilter(variable: Variable): SimpleFilter {
30
+ return new SimpleFilter(variable, [new Statement(Condition.EQUALS, this.value().value === true)]);
31
+ }
32
+ }
5
33
 
6
34
  export const booleanHandler: TypeHandler = {
7
35
  matches: (type) => type instanceof BooleanType,
36
+ getTypeLabel: () => 'Boolean',
8
37
  encode: async (value: boolean) => value,
9
38
  decode: async (value: boolean) => value,
10
39
  encodeToScalar: async (value: boolean) => value,
@@ -24,5 +53,20 @@ export const booleanHandler: TypeHandler = {
24
53
  }}
25
54
  />
26
55
  );
56
+ },
57
+ setupFilter: async ({ variable, filter }) => {
58
+ const form = new BooleanFilterForm(BooleanFilterForm.fromFilter(filter));
59
+ const result = await ioc.get(DialogStore2).showDialog(
60
+ new ClearableFilterFormDialogDirective({
61
+ title: `Filter ${variable.label || variable.name}`,
62
+ form,
63
+ filter,
64
+ handler: async () => {}
65
+ })
66
+ );
67
+ if (!result) {
68
+ return null;
69
+ }
70
+ return form.toFilter(variable);
27
71
  }
28
72
  };
@@ -86,6 +86,7 @@ const toDateValue = (value: unknown): Date | undefined => {
86
86
 
87
87
  export const dateHandler: TypeHandler = {
88
88
  matches: (type) => type instanceof DatetimeType || type instanceof DateType,
89
+ getTypeLabel: (type) => (type instanceof DatetimeType ? 'Datetime' : 'Date'),
89
90
  encode: async (value: Date) => new Day(value),
90
91
  decode: async (value: Day | Date) => toDateValue(value),
91
92
  encodeToScalar: async (value: Date) => value?.toISOString() || null,
@@ -30,6 +30,7 @@ export const imageHandler = (context: TypeHandlerContext): TypeHandler => {
30
30
 
31
31
  return {
32
32
  matches: (type) => type instanceof SignatureType || type instanceof PhotoType,
33
+ getTypeLabel: (type) => (type instanceof SignatureType ? 'Signature' : 'Photo'),
33
34
  encode: async (value: ImageMedia) => {
34
35
  return Attachment.create({
35
36
  data: await value.toArrayBuffer()
@@ -6,6 +6,7 @@ import { TypeHandler } from './shared/type-handler';
6
6
 
7
7
  export const locationHandler: TypeHandler = {
8
8
  matches: (type) => type instanceof LocationType,
9
+ getTypeLabel: () => 'Location',
9
10
  encode: async (value: Location) => value,
10
11
  decode: async (value: Location) => value,
11
12
  encodeToScalar: async (value: Location) => {
@@ -6,6 +6,7 @@ import { TypeHandler, TypeHandlerContext } from './shared/type-handler';
6
6
  export const multipleChoiceHandler = (context: TypeHandlerContext): TypeHandler => {
7
7
  return {
8
8
  matches: (type) => type instanceof MultipleChoiceType,
9
+ getTypeLabel: () => 'Multiple choice',
9
10
  encode: async (value: string[]) => value,
10
11
  decode: async (value: string[]) => value,
11
12
  encodeToScalar: async (value: string[]) => JSON.stringify(value || []),
@@ -6,6 +6,7 @@ import { TypeHandler, TypeHandlerContext } from './shared/type-handler';
6
6
  export const multipleChoiceIntegerHandler = (context: TypeHandlerContext): TypeHandler => {
7
7
  return {
8
8
  matches: (type) => type instanceof MultipleChoiceIntegerType,
9
+ getTypeLabel: () => 'Multiple choice integer',
9
10
  encode: async (value: string[]) => value.map((v) => parseInt(v)),
10
11
  decode: async (value: number[]) => value.map((v) => `${v}`),
11
12
  encodeToScalar: async (value: string[]) => JSON.stringify(value || []),
@@ -1,4 +1,4 @@
1
- import { NumberType, Variable } from '@journeyapps/db';
1
+ import { IntegerType, NumberType, Variable } from '@journeyapps/db';
2
2
  import { DialogStore2, FormInput, NumberInput, ioc } from '@journeyapps-labs/reactor-mod';
3
3
  import { TypeHandler } from './shared/type-handler';
4
4
  import { Condition, SimpleFilter, Statement, StatementMatch } from '../../core/query/filters';
@@ -68,7 +68,8 @@ class NumberFilterForm extends ConditionalFilterForm<number> {
68
68
  }
69
69
 
70
70
  export const numberHandler: TypeHandler = {
71
- matches: (type) => type instanceof NumberType,
71
+ matches: (type) => type instanceof NumberType || type instanceof IntegerType,
72
+ getTypeLabel: (type) => (type instanceof IntegerType ? 'Integer' : 'Number'),
72
73
  encode: async (value: number) => value,
73
74
  decode: async (value: number) => value,
74
75
  encodeToScalar: async (value: number) => value,
@@ -9,6 +9,7 @@ export type ScalarValue = string | number | boolean | null;
9
9
 
10
10
  export interface TypeHandler<T extends Type = Type, ENCODED = any, DECODED = any> {
11
11
  matches: (type: Type) => boolean;
12
+ getTypeLabel?: (type: T) => string;
12
13
  generateField: (event: { label: string; name: string; type: T }) => FormInput;
13
14
  generateDisplay: (event: {
14
15
  label: string;
@@ -6,6 +6,7 @@ import { Condition, SimpleFilter, Statement } from '../../core/query/filters';
6
6
 
7
7
  export const singleChoiceHandler: TypeHandler = {
8
8
  matches: (type) => type instanceof SingleChoiceType,
9
+ getTypeLabel: () => 'Single choice',
9
10
  encode: async (value: string) => value,
10
11
  decode: async (value: string) => value,
11
12
  encodeToScalar: async (value: string) => value,
@@ -6,6 +6,7 @@ import { Condition, SimpleFilter, Statement } from '../../core/query/filters';
6
6
 
7
7
  export const singleChoiceIntegerHandler: TypeHandler = {
8
8
  matches: (type) => type instanceof SingleChoiceIntegerType,
9
+ getTypeLabel: () => 'Single choice integer',
9
10
  encode: async (value: string) => parseInt(value),
10
11
  decode: async (value: number) => `${value}`,
11
12
  encodeToScalar: async (value: string) => value,
@@ -150,6 +150,7 @@ class TextFilterForm extends ConditionalFilterForm<string> {
150
150
  export const textHandler = (context: TypeHandlerContext): TypeHandler<TextType, string, string> => {
151
151
  return {
152
152
  matches: (type) => type instanceof TextType,
153
+ getTypeLabel: () => 'Text',
153
154
  encode: async (value: string) => value,
154
155
  decode: async (value: string) => value,
155
156
  encodeToScalar: async (value: string) => value,
@@ -31,6 +31,7 @@ export const FilterControlsWidget: React.FC<FilterControlsWidgetProps> = (props)
31
31
  return {
32
32
  key: field.key,
33
33
  title: field.label,
34
+ group: field.group,
34
35
  action: async () => {
35
36
  await props.simpleQuery.filterState.setupFilterForField(field.key, event.nativeEvent as any);
36
37
  props.goToPage?.(0);