@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.
- package/CHANGELOG.md +17 -0
- package/dist/@types/actions/schema-model/ViewHasManyAction.d.ts +12 -0
- package/dist/@types/core/SchemaModelDefinition.d.ts +12 -5
- package/dist/@types/core/query/StandardModelFields.d.ts +4 -0
- package/dist/@types/core/query/query-simple/SimpleQueryFilterState.d.ts +4 -6
- package/dist/@types/core/query/widgets/BelongsToDisplayWidget.d.ts +1 -0
- package/dist/@types/core/query/widgets/ColumnDisplayWidget.d.ts +1 -0
- package/dist/@types/core/query/widgets/IDCellDisplayWidget.d.ts +5 -0
- package/dist/@types/core/query/widgets/PeekRelationshipButton.d.ts +1 -0
- package/dist/@types/core/query/widgets/SmartBelongsToDisplayWidget.d.ts +2 -0
- package/dist/@types/core/query/widgets/SmartColumnWidget.d.ts +7 -2
- package/dist/@types/core/query/widgets/SmartFilterWidget.d.ts +6 -0
- package/dist/@types/entities/SchemaModelObjectEntityDefinition.d.ts +2 -1
- package/dist/@types/forms/types/belongs-to-filter.d.ts +10 -0
- package/dist/@types/forms/types/shared/type-handler.d.ts +1 -0
- package/dist/DataBrowserModule.js +2 -0
- package/dist/DataBrowserModule.js.map +1 -1
- package/dist/actions/schema-model/ViewHasManyAction.js +112 -0
- package/dist/actions/schema-model/ViewHasManyAction.js.map +1 -0
- package/dist/core/SchemaModelDefinition.js +70 -12
- package/dist/core/SchemaModelDefinition.js.map +1 -1
- package/dist/core/query/StandardModelFields.js +3 -0
- package/dist/core/query/StandardModelFields.js.map +1 -1
- package/dist/core/query/query-simple/SimpleQueryColumns.js +36 -11
- package/dist/core/query/query-simple/SimpleQueryColumns.js.map +1 -1
- package/dist/core/query/query-simple/SimpleQueryFilterState.js +35 -7
- package/dist/core/query/query-simple/SimpleQueryFilterState.js.map +1 -1
- package/dist/core/query/query-simple/SimpleQuerySortState.js +0 -1
- package/dist/core/query/query-simple/SimpleQuerySortState.js.map +1 -1
- package/dist/core/query/widgets/BelongsToDisplayWidget.js +1 -1
- package/dist/core/query/widgets/BelongsToDisplayWidget.js.map +1 -1
- package/dist/core/query/widgets/ColumnDisplayWidget.js +13 -2
- package/dist/core/query/widgets/ColumnDisplayWidget.js.map +1 -1
- package/dist/core/query/widgets/IDCellDisplayWidget.js +33 -0
- package/dist/core/query/widgets/IDCellDisplayWidget.js.map +1 -0
- package/dist/core/query/widgets/PeekRelationshipButton.js +23 -16
- package/dist/core/query/widgets/PeekRelationshipButton.js.map +1 -1
- package/dist/core/query/widgets/SmartBelongsToDisplayWidget.js +1 -1
- package/dist/core/query/widgets/SmartBelongsToDisplayWidget.js.map +1 -1
- package/dist/core/query/widgets/SmartColumnWidget.js +3 -13
- package/dist/core/query/widgets/SmartColumnWidget.js.map +1 -1
- package/dist/core/query/widgets/SmartFilterWidget.js +10 -7
- package/dist/core/query/widgets/SmartFilterWidget.js.map +1 -1
- package/dist/entities/SchemaModelObjectEntityDefinition.js +15 -14
- package/dist/entities/SchemaModelObjectEntityDefinition.js.map +1 -1
- package/dist/forms/types/attachment-handler.js +1 -0
- package/dist/forms/types/attachment-handler.js.map +1 -1
- package/dist/forms/types/belongs-to-filter.js +57 -0
- package/dist/forms/types/belongs-to-filter.js.map +1 -0
- package/dist/forms/types/boolean-handler.js +34 -1
- package/dist/forms/types/boolean-handler.js.map +1 -1
- package/dist/forms/types/date-handler.js +1 -0
- package/dist/forms/types/date-handler.js.map +1 -1
- package/dist/forms/types/image-handler.js +1 -0
- package/dist/forms/types/image-handler.js.map +1 -1
- package/dist/forms/types/location-handler.js +1 -0
- package/dist/forms/types/location-handler.js.map +1 -1
- package/dist/forms/types/multiple-choice-handler.js +1 -0
- package/dist/forms/types/multiple-choice-handler.js.map +1 -1
- package/dist/forms/types/multiple-choice-integer-handler.js +1 -0
- package/dist/forms/types/multiple-choice-integer-handler.js.map +1 -1
- package/dist/forms/types/number-handler.js +3 -2
- package/dist/forms/types/number-handler.js.map +1 -1
- package/dist/forms/types/single-choice-handler.js +1 -0
- package/dist/forms/types/single-choice-handler.js.map +1 -1
- package/dist/forms/types/single-choice-integer-handler.js +1 -0
- package/dist/forms/types/single-choice-integer-handler.js.map +1 -1
- package/dist/forms/types/text-handler.js +1 -0
- package/dist/forms/types/text-handler.js.map +1 -1
- package/dist/panels/query/table-controls/FilterControlsWidget.js +1 -0
- package/dist/panels/query/table-controls/FilterControlsWidget.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist-module/bundle.js +51 -35
- package/dist-module/bundle.js.map +1 -1
- package/package.json +1 -1
- package/src/DataBrowserModule.ts +2 -0
- package/src/actions/schema-model/ViewHasManyAction.ts +114 -0
- package/src/core/SchemaModelDefinition.ts +90 -14
- package/src/core/query/StandardModelFields.ts +8 -0
- package/src/core/query/query-simple/SimpleQueryColumns.tsx +49 -13
- package/src/core/query/query-simple/SimpleQueryFilterState.ts +42 -9
- package/src/core/query/query-simple/SimpleQuerySortState.ts +0 -1
- package/src/core/query/widgets/BelongsToDisplayWidget.tsx +2 -1
- package/src/core/query/widgets/ColumnDisplayWidget.tsx +17 -4
- package/src/core/query/widgets/IDCellDisplayWidget.tsx +46 -0
- package/src/core/query/widgets/PeekRelationshipButton.tsx +23 -16
- package/src/core/query/widgets/SmartBelongsToDisplayWidget.tsx +3 -0
- package/src/core/query/widgets/SmartColumnWidget.tsx +23 -20
- package/src/core/query/widgets/SmartFilterWidget.tsx +14 -5
- package/src/entities/SchemaModelObjectEntityDefinition.ts +17 -15
- package/src/forms/types/attachment-handler.tsx +1 -0
- package/src/forms/types/belongs-to-filter.tsx +84 -0
- package/src/forms/types/boolean-handler.tsx +46 -2
- package/src/forms/types/date-handler.tsx +1 -0
- package/src/forms/types/image-handler.tsx +1 -0
- package/src/forms/types/location-handler.tsx +1 -0
- package/src/forms/types/multiple-choice-handler.tsx +1 -0
- package/src/forms/types/multiple-choice-integer-handler.tsx +1 -0
- package/src/forms/types/number-handler.tsx +3 -2
- package/src/forms/types/shared/type-handler.ts +1 -0
- package/src/forms/types/single-choice-handler.tsx +1 -0
- package/src/forms/types/single-choice-integer-handler.tsx +1 -0
- package/src/forms/types/text-handler.tsx +1 -0
- 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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
{
|
|
36
|
-
|
|
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
|
|
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);
|