@journeyapps-labs/reactor-mod-data-browser 3.4.0 → 3.6.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 (78) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/@types/actions/schema-model/DeleteSchemaModelAction.d.ts +12 -0
  3. package/dist/@types/core/AbstractConnection.d.ts +2 -0
  4. package/dist/@types/core/SchemaModelDefinition.d.ts +20 -0
  5. package/dist/@types/core/delete-schema-models.d.ts +8 -0
  6. package/dist/@types/entities/SchemaModelTreePresenterComponent.d.ts +10 -0
  7. package/dist/@types/forms/types/shared/ui.d.ts +6 -0
  8. package/dist/@types/index.d.ts +1 -0
  9. package/dist/@types/panels/query/PageResultsWidget.d.ts +5 -1
  10. package/dist/@types/panels/query/QueryPanelFactory.d.ts +8 -0
  11. package/dist/@types/panels/query/TableControlsWidget.d.ts +2 -0
  12. package/dist/@types/panels/query/table-controls/SelectionControlsWidget.d.ts +6 -0
  13. package/dist/@types/preferences/QueryControlPreferences.d.ts +14 -2
  14. package/dist/@types/preferences/SchemaOrderingPreferences.d.ts +22 -0
  15. package/dist/DataBrowserModule.js +4 -0
  16. package/dist/DataBrowserModule.js.map +1 -1
  17. package/dist/actions/schema-model/DeleteSchemaModelAction.js +27 -0
  18. package/dist/actions/schema-model/DeleteSchemaModelAction.js.map +1 -0
  19. package/dist/core/AbstractConnection.js +32 -3
  20. package/dist/core/AbstractConnection.js.map +1 -1
  21. package/dist/core/SchemaModelDefinition.js +73 -17
  22. package/dist/core/SchemaModelDefinition.js.map +1 -1
  23. package/dist/core/delete-schema-models.js +101 -0
  24. package/dist/core/delete-schema-models.js.map +1 -0
  25. package/dist/core/query/query-simple/SimpleQueryColumns.js +55 -50
  26. package/dist/core/query/query-simple/SimpleQueryColumns.js.map +1 -1
  27. package/dist/core/query/widgets/IDCellDisplayWidget.js +9 -4
  28. package/dist/core/query/widgets/IDCellDisplayWidget.js.map +1 -1
  29. package/dist/entities/SchemaModelDefinitionEntityDefinition.js +3 -10
  30. package/dist/entities/SchemaModelDefinitionEntityDefinition.js.map +1 -1
  31. package/dist/entities/SchemaModelObjectEntityDefinition.js +5 -0
  32. package/dist/entities/SchemaModelObjectEntityDefinition.js.map +1 -1
  33. package/dist/entities/SchemaModelTreePresenterComponent.js +23 -0
  34. package/dist/entities/SchemaModelTreePresenterComponent.js.map +1 -0
  35. package/dist/forms/SchemaModelForm.js +31 -29
  36. package/dist/forms/SchemaModelForm.js.map +1 -1
  37. package/dist/index.js +1 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/panels/model/ModelPanelWidget.js +12 -8
  40. package/dist/panels/model/ModelPanelWidget.js.map +1 -1
  41. package/dist/panels/query/PageResultsWidget.js +26 -6
  42. package/dist/panels/query/PageResultsWidget.js.map +1 -1
  43. package/dist/panels/query/QueryPanelFactory.js +39 -4
  44. package/dist/panels/query/QueryPanelFactory.js.map +1 -1
  45. package/dist/panels/query/QueryPanelWidget.js +25 -10
  46. package/dist/panels/query/QueryPanelWidget.js.map +1 -1
  47. package/dist/panels/query/TableControlsWidget.js +2 -0
  48. package/dist/panels/query/TableControlsWidget.js.map +1 -1
  49. package/dist/panels/query/table-controls/SelectionControlsWidget.js +11 -0
  50. package/dist/panels/query/table-controls/SelectionControlsWidget.js.map +1 -0
  51. package/dist/preferences/QueryControlPreferences.js +45 -1
  52. package/dist/preferences/QueryControlPreferences.js.map +1 -1
  53. package/dist/preferences/SchemaOrderingPreferences.js +76 -0
  54. package/dist/preferences/SchemaOrderingPreferences.js.map +1 -0
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/dist-module/bundle.js +74 -74
  57. package/dist-module/bundle.js.map +1 -1
  58. package/package.json +7 -6
  59. package/src/DataBrowserModule.ts +4 -0
  60. package/src/actions/schema-model/DeleteSchemaModelAction.ts +43 -0
  61. package/src/core/AbstractConnection.ts +33 -3
  62. package/src/core/SchemaModelDefinition.ts +97 -17
  63. package/src/core/delete-schema-models.ts +131 -0
  64. package/src/core/query/query-simple/SimpleQueryColumns.tsx +81 -74
  65. package/src/core/query/widgets/IDCellDisplayWidget.tsx +17 -4
  66. package/src/entities/SchemaModelDefinitionEntityDefinition.ts +3 -13
  67. package/src/entities/SchemaModelObjectEntityDefinition.ts +4 -0
  68. package/src/entities/SchemaModelTreePresenterComponent.ts +31 -0
  69. package/src/forms/SchemaModelForm.tsx +46 -41
  70. package/src/index.ts +1 -0
  71. package/src/panels/model/ModelPanelWidget.tsx +15 -8
  72. package/src/panels/query/PageResultsWidget.tsx +45 -8
  73. package/src/panels/query/QueryPanelFactory.tsx +23 -0
  74. package/src/panels/query/QueryPanelWidget.tsx +38 -22
  75. package/src/panels/query/TableControlsWidget.tsx +4 -0
  76. package/src/panels/query/table-controls/SelectionControlsWidget.tsx +34 -0
  77. package/src/preferences/QueryControlPreferences.ts +51 -2
  78. package/src/preferences/SchemaOrderingPreferences.ts +82 -0
@@ -3,9 +3,8 @@ import {
3
3
  EntityActionHandlerComponent,
4
4
  EntityDefinition,
5
5
  EntityDescriberComponent,
6
- inject,
7
6
  InlineEntityEncoderComponent,
8
- InlineTreePresenterComponent,
7
+ inject,
9
8
  SimpleParentEntitySearchEngine
10
9
  } from '@journeyapps-labs/reactor-mod';
11
10
  import { DataBrowserEntities } from '../entities';
@@ -14,6 +13,7 @@ import { AbstractConnection } from '../core/AbstractConnection';
14
13
  import { SchemaModelDefinition } from '../core/SchemaModelDefinition';
15
14
  import { QuerySchemaModelAction } from '../actions/schema-definitions/QuerySchemaModelAction';
16
15
  import { IndexModel } from '../core/IndexModel';
16
+ import { SchemaModelTreePresenterComponent } from './SchemaModelTreePresenterComponent';
17
17
 
18
18
  export interface SchemaModelDefinitionEntityDefinitionEncoded {
19
19
  connection_id: string;
@@ -77,17 +77,7 @@ export class SchemaModelDefinitionEntityDefinition extends EntityDefinition<Sche
77
77
  })
78
78
  );
79
79
 
80
- this.registerComponent(
81
- new InlineTreePresenterComponent({
82
- loadChildrenAsNodesAreOpened: true,
83
- cacheTreeEntities: true,
84
- augmentTreeNodeProps: () => {
85
- return {
86
- openOnSingleClick: false
87
- };
88
- }
89
- })
90
- );
80
+ this.registerComponent(new SchemaModelTreePresenterComponent({ loadChildrenAsNodesAreOpened: true }));
91
81
 
92
82
  this.registerComponent(
93
83
  new SimpleParentEntitySearchEngine<AbstractConnection, SchemaModelDefinition>({
@@ -13,6 +13,7 @@ import { SchemaModelObject } from '../core/SchemaModelObject';
13
13
  import { SchemaModelDefinition } from '../core/SchemaModelDefinition';
14
14
  import { validate as validateUUID } from 'uuid';
15
15
  import { ViewHasManyAction } from '../actions/schema-model/ViewHasManyAction';
16
+ import { DeleteSchemaModelAction } from '../actions/schema-model/DeleteSchemaModelAction';
16
17
 
17
18
  export interface SchemaModelObjectEntityDefinitionEncoded {
18
19
  connection_id: string;
@@ -98,6 +99,9 @@ export class SchemaModelObjectEntityDefinition extends EntityDefinition<SchemaMo
98
99
  }
99
100
 
100
101
  isActionAllowedForEntity(action: Action, entity: SchemaModelObject) {
102
+ if (action.id === DeleteSchemaModelAction.ID) {
103
+ return !!entity.model?.persisted;
104
+ }
101
105
  if (action.id === ViewHasManyAction.ID) {
102
106
  return Object.keys(entity.definition.definition.hasMany || {}).length > 0;
103
107
  }
@@ -0,0 +1,31 @@
1
+ import {
2
+ CachedEntityTreePresenterContext,
3
+ EntityReactorNode,
4
+ EntityTreePresenterComponent,
5
+ ReactorTreeEntity
6
+ } from '@journeyapps-labs/reactor-mod';
7
+ import { SchemaModelDefinition } from '../core/SchemaModelDefinition';
8
+ import { SchemaModelOrderValue, SchemaModelOrderingPreference } from '../preferences/SchemaOrderingPreferences';
9
+
10
+ class SchemaModelTreePresenterContext extends CachedEntityTreePresenterContext<SchemaModelDefinition> {
11
+ getSortedEntities(entities: SchemaModelDefinition[]) {
12
+ if (SchemaModelOrderingPreference.getValue() === SchemaModelOrderValue.AS_DEFINED_IN_SCHEMA) {
13
+ return entities;
14
+ }
15
+ return super.getSortedEntities(entities);
16
+ }
17
+
18
+ protected doGenerateTreeNode(entity: SchemaModelDefinition, options): ReactorTreeEntity {
19
+ return new EntityReactorNode({
20
+ entity,
21
+ definition: this.definition,
22
+ events: options?.events
23
+ });
24
+ }
25
+ }
26
+
27
+ export class SchemaModelTreePresenterComponent extends EntityTreePresenterComponent<SchemaModelDefinition> {
28
+ protected _generateContext() {
29
+ return new SchemaModelTreePresenterContext(this);
30
+ }
31
+ }
@@ -7,6 +7,7 @@ import { DirtyWrapperInput } from './inputs/DirtyWrapperInput';
7
7
  import { TypeEngine, TypeHandler } from './TypeEngine';
8
8
  import { autorun, IReactionDisposer } from 'mobx';
9
9
  import { Variable } from '@journeyapps/db';
10
+ import { OrderedSchemaFieldType } from '../core/SchemaModelDefinition';
10
11
 
11
12
  export interface SchemaModelFormOptions {
12
13
  definition: SchemaModelDefinition;
@@ -104,52 +105,56 @@ export class SchemaModelForm extends FormModel {
104
105
  constructor(protected options: SchemaModelFormOptions) {
105
106
  super();
106
107
  this.bindings = new Set<Binding>();
107
- _.map(options.definition.definition.belongsTo, (relationship) => {
108
- const definition = options.definition.connection.getSchemaModelDefinitionByName(relationship.foreignType.name);
109
-
110
- let entity = new EntityInput({
111
- name: relationship.name,
112
- entityType: DataBrowserEntities.SCHEMA_MODEL_OBJECT,
113
- parent: definition,
114
- label: relationship.name,
115
- value: null
116
- });
117
-
118
- return new Binding({
119
- name: relationship.name,
120
- model: this.options.object,
121
- input: entity,
122
- resolve: () => {
123
- if (!options.object.data.belongs_to[relationship.name]) {
108
+ options.definition
109
+ .getOrderedFieldsAndRelationships()
110
+ .map((entry) => {
111
+ if (entry.type === OrderedSchemaFieldType.BELONGS_TO) {
112
+ const relationship = entry.object;
113
+ const definition = options.definition.connection.getSchemaModelDefinitionByName(
114
+ relationship.foreignType.name
115
+ );
116
+ if (!definition) {
124
117
  return null;
125
118
  }
126
- return definition.resolve(options.object.data.belongs_to[relationship.name]);
119
+
120
+ const entity = new EntityInput({
121
+ name: relationship.name,
122
+ entityType: DataBrowserEntities.SCHEMA_MODEL_OBJECT,
123
+ parent: definition,
124
+ label: relationship.name,
125
+ value: null
126
+ });
127
+
128
+ return new Binding({
129
+ name: relationship.name,
130
+ model: this.options.object,
131
+ input: entity,
132
+ resolve: () => {
133
+ if (!options.object.data.belongs_to[relationship.name]) {
134
+ return null;
135
+ }
136
+ return definition.resolve(options.object.data.belongs_to[relationship.name]);
137
+ }
138
+ });
127
139
  }
128
- });
129
- })
130
- .filter((f) => !!f)
131
- .forEach((binding) => {
132
- this.bindings.add(binding);
133
- this.addInput(new DirtyWrapperInput(binding.input, options.object));
134
- });
135
140
 
136
- _.map(options.definition.definition.attributes, (attribute) => {
137
- let field = this.typeEngine.getHandler(attribute.type)?.generateField({
138
- name: attribute.name,
139
- label: attribute.label,
140
- type: attribute.type
141
- });
142
- if (!field) {
143
- return;
144
- }
141
+ const attribute = entry.object;
142
+ let field = this.typeEngine.getHandler(attribute.type)?.generateField({
143
+ name: attribute.name,
144
+ label: attribute.label,
145
+ type: attribute.type
146
+ });
147
+ if (!field) {
148
+ return null;
149
+ }
145
150
 
146
- return new TypedBinding({
147
- variable: attribute,
148
- model: this.options.object,
149
- input: field,
150
- name: attribute.name
151
- });
152
- })
151
+ return new TypedBinding({
152
+ variable: attribute,
153
+ model: this.options.object,
154
+ input: field,
155
+ name: attribute.name
156
+ });
157
+ })
153
158
  .filter((binding) => !!binding)
154
159
  .forEach((binding) => {
155
160
  this.bindings.add(binding);
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export * from './entities/QueryEntityDefinition';
15
15
  export * from './entities/ConnectionEntityDefinition';
16
16
  export * from './entities/ConnectionFactoryEntityDefinition';
17
17
  export * from './entities/SchemaModelDefinitionEntityDefinition';
18
+ export * from './entities/SchemaModelTreePresenterComponent';
18
19
  export * from './entities/SchemaModelObjectEntityDefinition';
19
20
 
20
21
  export * from './panels/query/QueryPanelFactory';
@@ -4,6 +4,7 @@ import { observer } from 'mobx-react';
4
4
  import styled from '@emotion/styled';
5
5
  import {
6
6
  BorderLayoutWidget,
7
+ Btn,
7
8
  ioc,
8
9
  LoadingPanelWidget,
9
10
  PANEL_CONTENT_PADDING,
@@ -15,6 +16,7 @@ import {
15
16
  } from '@journeyapps-labs/reactor-mod';
16
17
  import { SchemaModelForm } from '../../forms/SchemaModelForm';
17
18
  import { ModelPanelModel } from './ModelPanelFactory';
19
+ import { DeleteSchemaModelAction } from '../../actions/schema-model/DeleteSchemaModelAction';
18
20
 
19
21
  export interface QueryPanelWidgetProps {
20
22
  model: ModelPanelModel;
@@ -55,16 +57,21 @@ export const ModelPanelWidget: React.FC<QueryPanelWidgetProps> = observer((props
55
57
 
56
58
  let top = null;
57
59
  if (props.model.model) {
60
+ const toolbarButtons: Btn[] = props.model.model.model?.persisted
61
+ ? [
62
+ DeleteSchemaModelAction.get().representAsButton(
63
+ {
64
+ targetEntity: props.model.model,
65
+ sourcePanel: props.model
66
+ },
67
+ true
68
+ )
69
+ ].filter((button): button is Btn => !!button)
70
+ : [];
71
+
58
72
  top = (
59
73
  <PanelToolbarWidget
60
- btns={
61
- [
62
- // {
63
- // label: 'Delete object',
64
- // action: () => {}
65
- // }
66
- ]
67
- }
74
+ btns={toolbarButtons}
68
75
  meta={[
69
76
  {
70
77
  label: 'ID',
@@ -1,15 +1,27 @@
1
1
  import * as React from 'react';
2
2
  import { useEffect, useRef } from 'react';
3
- import { Page } from '../../core/query/Page';
4
- import { themed, ioc, ScrollableDivCss, System, TableWidget, LoadingPanelWidget } from '@journeyapps-labs/reactor-mod';
3
+ import { Page, PageRow } from '../../core/query/Page';
4
+ import {
5
+ ComboBoxItem,
6
+ themed,
7
+ ioc,
8
+ ScrollableDivCss,
9
+ System,
10
+ MultiSelectChangeEvent,
11
+ MultiSelectTableWidget,
12
+ LoadingPanelWidget
13
+ } from '@journeyapps-labs/reactor-mod';
5
14
  import { AbstractQuery } from '../../core/query/AbstractQuery';
6
15
  import { observer } from 'mobx-react';
7
16
  import { DataBrowserEntities } from '../../entities';
8
17
  import { SchemaModelObject } from '../../core/SchemaModelObject';
18
+ import { deleteSchemaModels } from '../../core/delete-schema-models';
9
19
 
10
20
  export interface PageResultsWidgetProps {
11
21
  page: Page;
12
22
  query: AbstractQuery;
23
+ selectedModels: SchemaModelObject[];
24
+ onSelectionChange: (event: MultiSelectChangeEvent<PageRow>) => void;
13
25
  scrollTop: number;
14
26
  scrollLeft: number;
15
27
  onScroll: (offsets: { top: number; left: number }) => void;
@@ -18,8 +30,35 @@ export interface PageResultsWidgetProps {
18
30
  export const PageResultsWidget: React.FC<PageResultsWidgetProps> = observer((props) => {
19
31
  const system = ioc.get(System);
20
32
  const rows = props.page.loading ? [] : props.page.asRows();
33
+ const selectedRowKeys = props.selectedModels.map((model) => model.id);
21
34
  const ref = useRef<HTMLDivElement>(null);
22
35
 
36
+ const showContextMenu = (event: React.MouseEvent, row: PageRow) => {
37
+ const definition = system.getDefinition<SchemaModelObject>(DataBrowserEntities.SCHEMA_MODEL_OBJECT);
38
+ const isSelectedRow = props.selectedModels.some((model) => model.id === row.model.id);
39
+
40
+ const selectedItems: ComboBoxItem[] =
41
+ isSelectedRow && props.selectedModels.length > 1
42
+ ? [
43
+ {
44
+ key: 'delete-selected',
45
+ title: `Delete selected [${props.selectedModels.length}]`,
46
+ group: 'Selected',
47
+ icon: 'trash',
48
+ action: async () => {
49
+ await deleteSchemaModels({
50
+ models: props.selectedModels
51
+ });
52
+ }
53
+ }
54
+ ]
55
+ : [];
56
+
57
+ definition.showContextMenuForEntity(row.model, event, {
58
+ additionalItems: selectedItems
59
+ } as any);
60
+ };
61
+
23
62
  useEffect(() => {
24
63
  if (!ref.current) {
25
64
  return;
@@ -43,12 +82,10 @@ export const PageResultsWidget: React.FC<PageResultsWidgetProps> = observer((pro
43
82
  });
44
83
  }}
45
84
  >
46
- <TableWidget
47
- onContextMenu={(event, row) => {
48
- system
49
- .getDefinition<SchemaModelObject>(DataBrowserEntities.SCHEMA_MODEL_OBJECT)
50
- .showContextMenuForEntity(row.model, event);
51
- }}
85
+ <MultiSelectTableWidget
86
+ onContextMenu={showContextMenu}
87
+ selectedRowKeys={selectedRowKeys}
88
+ onSelectionChange={props.onSelectionChange}
52
89
  rows={rows}
53
90
  columns={props.query.getColumns()}
54
91
  />
@@ -10,6 +10,9 @@ import { SavedQueryStore } from '../../stores/SavedQueryStore';
10
10
  import { AbstractConnection } from '../../core/AbstractConnection';
11
11
  import { SharedConnectionPanelFactory } from '../_shared/SharedConnectionPanelFactory';
12
12
  import { Page } from '../../core/query/Page';
13
+ import { SchemaModelObject } from '../../core/SchemaModelObject';
14
+ import { action } from 'mobx';
15
+ import * as _ from 'lodash';
13
16
 
14
17
  export class QueryPanelModel extends ReactorPanelModel {
15
18
  @inject(ConnectionStore)
@@ -24,6 +27,9 @@ export class QueryPanelModel extends ReactorPanelModel {
24
27
  @observable.ref
25
28
  accessor current_page_data: Page | null;
26
29
 
30
+ @observable.ref
31
+ accessor selected_models: SchemaModelObject[];
32
+
27
33
  accessor table_scroll_top: number;
28
34
  accessor table_scroll_left: number;
29
35
 
@@ -33,10 +39,26 @@ export class QueryPanelModel extends ReactorPanelModel {
33
39
  this.query = query;
34
40
  this.current_page = 0;
35
41
  this.current_page_data = null;
42
+ this.selected_models = [];
36
43
  this.table_scroll_top = 0;
37
44
  this.table_scroll_left = 0;
38
45
  }
39
46
 
47
+ @action clearSelection() {
48
+ this.selected_models = [];
49
+ }
50
+
51
+ @action mergeSelectionForPage(event: { page: Page; models: SchemaModelObject[] }) {
52
+ const pageRowKeys = new Set(event.page.asRows().map((row) => row.key));
53
+ const nextSelectedModels = this.selected_models.filter((model) => !pageRowKeys.has(model.id));
54
+ this.selected_models = _.uniqBy([...nextSelectedModels, ...event.models], (model) => model.id);
55
+ }
56
+
57
+ @action async reloadQuery() {
58
+ this.clearSelection();
59
+ await this.query.load();
60
+ }
61
+
40
62
  isSerializable() {
41
63
  return this.query instanceof AbstractSerializableQuery;
42
64
  }
@@ -71,6 +93,7 @@ export class QueryPanelModel extends ReactorPanelModel {
71
93
  }
72
94
  this.current_page = 0;
73
95
  this.current_page_data = null;
96
+ this.clearSelection();
74
97
  this.query = query;
75
98
  await query.load();
76
99
  }
@@ -8,6 +8,8 @@ import { Page } from '../../core/query/Page';
8
8
  import { PageResultsWidget } from './PageResultsWidget';
9
9
  import { TableControlsWidget } from './TableControlsWidget';
10
10
  import { autorun } from 'mobx';
11
+ import { TableControlsPositionPreference, TableControlsPositionValue } from '../../preferences/QueryControlPreferences';
12
+ import { deleteSchemaModels } from '../../core/delete-schema-models';
11
13
 
12
14
  export interface QueryPanelWidgetProps {
13
15
  model: QueryPanelModel;
@@ -93,6 +95,27 @@ export const QueryPanelWidget: React.FC<QueryPanelWidgetProps> = observer((props
93
95
 
94
96
  const activePage =
95
97
  props.model.current_page_data || (props.model.query ? props.model.query.getPage(props.model.current_page) : null);
98
+ const controlsPosition = TableControlsPositionPreference.getValue();
99
+
100
+ const controls = (
101
+ <TableControlsWidget
102
+ query={props.model.query}
103
+ current_page={activePage}
104
+ loading={loading}
105
+ selectedCount={props.model.selected_models.length}
106
+ onDeleteSelected={async () => {
107
+ await deleteSchemaModels({
108
+ models: props.model.selected_models
109
+ });
110
+ }}
111
+ onLoadSavedQuery={async (id) => {
112
+ await props.model.loadSavedQuery(id);
113
+ }}
114
+ goToPage={(index) => {
115
+ props.model.current_page = index;
116
+ }}
117
+ />
118
+ );
96
119
 
97
120
  return (
98
121
  <LoadingPanelWidget loading={!props.model.query || !activePage}>
@@ -101,35 +124,28 @@ export const QueryPanelWidget: React.FC<QueryPanelWidgetProps> = observer((props
101
124
  <S.Container>
102
125
  <BorderLayoutWidget
103
126
  top={
104
- <TableControlsWidget
105
- query={props.model.query}
106
- current_page={activePage}
107
- loading={loading}
108
- onLoadSavedQuery={async (id) => {
109
- await props.model.loadSavedQuery(id);
110
- }}
111
- goToPage={(index) => {
112
- props.model.current_page = index;
113
- }}
114
- />
127
+ controlsPosition === TableControlsPositionValue.TOP ||
128
+ controlsPosition === TableControlsPositionValue.BOTH
129
+ ? controls
130
+ : null
115
131
  }
116
132
  bottom={
117
- <TableControlsWidget
118
- query={props.model.query}
119
- current_page={activePage}
120
- loading={loading}
121
- onLoadSavedQuery={async (id) => {
122
- await props.model.loadSavedQuery(id);
123
- }}
124
- goToPage={(index) => {
125
- props.model.current_page = index;
126
- }}
127
- />
133
+ controlsPosition === TableControlsPositionValue.BOTTOM ||
134
+ controlsPosition === TableControlsPositionValue.BOTH
135
+ ? controls
136
+ : null
128
137
  }
129
138
  >
130
139
  <PageResultsWidget
131
140
  query={props.model.query}
132
141
  page={activePage}
142
+ selectedModels={props.model.selected_models}
143
+ onSelectionChange={(event) => {
144
+ props.model.mergeSelectionForPage({
145
+ page: activePage,
146
+ models: event.rows.map((row) => row.model)
147
+ });
148
+ }}
133
149
  scrollTop={props.model.table_scroll_top}
134
150
  scrollLeft={props.model.table_scroll_left}
135
151
  onScroll={({ top, left }) => {
@@ -12,6 +12,7 @@ import { SortControlsWidget } from './table-controls/SortControlsWidget';
12
12
  import { ChangesControlsWidget } from './table-controls/ChangesControlsWidget';
13
13
  import { FilterControlsWidget } from './table-controls/FilterControlsWidget';
14
14
  import { QueryControlPreferences } from '../../preferences/QueryControlPreferences';
15
+ import { SelectionControlsWidget } from './table-controls/SelectionControlsWidget';
15
16
 
16
17
  export interface TableControlsWidgetProps {
17
18
  current_page: Page;
@@ -20,6 +21,8 @@ export interface TableControlsWidgetProps {
20
21
  query: AbstractQuery;
21
22
  onLoadSavedQuery?: (id: string) => Promise<any> | any;
22
23
  loading?: boolean;
24
+ selectedCount: number;
25
+ onDeleteSelected: () => Promise<void>;
23
26
  }
24
27
 
25
28
  export const TableControlsWidget: React.FC<TableControlsWidgetProps> = observer((props) => {
@@ -52,6 +55,7 @@ export const TableControlsWidget: React.FC<TableControlsWidgetProps> = observer(
52
55
  {simpleQuery && showSortControls ? (
53
56
  <SortControlsWidget simpleQuery={simpleQuery} goToPage={props.goToPage} />
54
57
  ) : null}
58
+ <SelectionControlsWidget selectedCount={props.selectedCount} onDeleteSelected={props.onDeleteSelected} />
55
59
  <ChangesControlsWidget query={props.query} currentPage={props.current_page} />
56
60
  {props.loading ? <S.Loading icon="spinner" spin={true} /> : null}
57
61
  </S.Container>
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+ import {
3
+ InputContainerWidget,
4
+ ioc,
5
+ PanelButtonMode,
6
+ PanelButtonWidget,
7
+ theme,
8
+ ThemeStore
9
+ } from '@journeyapps-labs/reactor-mod';
10
+
11
+ export interface SelectionControlsWidgetProps {
12
+ selectedCount: number;
13
+ onDeleteSelected: () => Promise<void>;
14
+ }
15
+
16
+ export const SelectionControlsWidget: React.FC<SelectionControlsWidgetProps> = (props) => {
17
+ const _theme = ioc.get(ThemeStore).getCurrentTheme(theme);
18
+
19
+ if (props.selectedCount === 0) {
20
+ return null;
21
+ }
22
+
23
+ return (
24
+ <InputContainerWidget label="Selection">
25
+ <PanelButtonWidget
26
+ mode={PanelButtonMode.PRIMARY}
27
+ label={`Delete selected [${props.selectedCount}]`}
28
+ icon="trash"
29
+ iconColor={_theme.status.failed}
30
+ action={props.onDeleteSelected}
31
+ />
32
+ </InputContainerWidget>
33
+ );
34
+ };
@@ -1,9 +1,49 @@
1
- import { BooleanSetting, PrefsStore } from '@journeyapps-labs/reactor-mod';
1
+ import { BooleanSetting, PrefsStore, SetSetting, ioc } from '@journeyapps-labs/reactor-mod';
2
2
 
3
3
  export enum QueryControlPreferences {
4
4
  SHOW_SORT_CONTROLS = 'databrowser/query-controls/show-sort-controls',
5
5
  SHOW_FILTER_CONTROLS = 'databrowser/query-controls/show-filter-controls',
6
- FILTER_NULL_FIELDS_IN_RELATIONSHIP_PEEK = 'databrowser/query-controls/filter-null-fields-in-relationship-peek'
6
+ FILTER_NULL_FIELDS_IN_RELATIONSHIP_PEEK = 'databrowser/query-controls/filter-null-fields-in-relationship-peek',
7
+ SHOW_ID_VALUE_IN_ID_COLUMN = 'databrowser/query-controls/show-id-value-in-id-column',
8
+ TABLE_CONTROLS_POSITION = 'databrowser/query-controls/table-controls-position'
9
+ }
10
+
11
+ export enum TableControlsPositionValue {
12
+ TOP = 'top',
13
+ BOTTOM = 'bottom',
14
+ BOTH = 'both'
15
+ }
16
+
17
+ export class TableControlsPositionPreference extends SetSetting {
18
+ static KEY = QueryControlPreferences.TABLE_CONTROLS_POSITION;
19
+
20
+ constructor() {
21
+ super({
22
+ key: TableControlsPositionPreference.KEY,
23
+ name: 'Table controls position',
24
+ category: 'Query Controls',
25
+ value: TableControlsPositionValue.BOTH,
26
+ options: [
27
+ {
28
+ key: TableControlsPositionValue.TOP,
29
+ label: 'Top'
30
+ },
31
+ {
32
+ key: TableControlsPositionValue.BOTTOM,
33
+ label: 'Bottom'
34
+ },
35
+ {
36
+ key: TableControlsPositionValue.BOTH,
37
+ label: 'Both'
38
+ }
39
+ ]
40
+ });
41
+ }
42
+
43
+ static getValue(): TableControlsPositionValue {
44
+ return ioc.get(PrefsStore).getPreference<TableControlsPositionPreference>(TableControlsPositionPreference.KEY)
45
+ .value as TableControlsPositionValue;
46
+ }
7
47
  }
8
48
 
9
49
  export const registerQueryControlPreferences = (prefsStore: PrefsStore) => {
@@ -31,4 +71,13 @@ export const registerQueryControlPreferences = (prefsStore: PrefsStore) => {
31
71
  category: 'Query Controls'
32
72
  })
33
73
  );
74
+ prefsStore.registerPreference(
75
+ new BooleanSetting({
76
+ key: QueryControlPreferences.SHOW_ID_VALUE_IN_ID_COLUMN,
77
+ checked: true,
78
+ name: 'Show ID value in ID column',
79
+ category: 'Query Controls'
80
+ })
81
+ );
82
+ prefsStore.registerPreference(new TableControlsPositionPreference());
34
83
  };
@@ -0,0 +1,82 @@
1
+ import { ioc, PrefsStore, SetSetting } from '@journeyapps-labs/reactor-mod';
2
+
3
+ export enum SchemaModelOrderValue {
4
+ ALPHABETICAL = 'alphabetical',
5
+ AS_DEFINED_IN_SCHEMA = 'as_defined_in_schema'
6
+ }
7
+
8
+ export class SchemaModelOrderingPreference extends SetSetting {
9
+ static KEY = 'databrowser/schema/model-order';
10
+
11
+ constructor() {
12
+ super({
13
+ key: SchemaModelOrderingPreference.KEY,
14
+ name: 'Schema model order',
15
+ category: 'Schema',
16
+ value: SchemaModelOrderValue.AS_DEFINED_IN_SCHEMA,
17
+ options: [
18
+ {
19
+ key: SchemaModelOrderValue.AS_DEFINED_IN_SCHEMA,
20
+ label: 'As defined in schema'
21
+ },
22
+ {
23
+ key: SchemaModelOrderValue.ALPHABETICAL,
24
+ label: 'Alphabetical'
25
+ }
26
+ ]
27
+ });
28
+ }
29
+
30
+ static getValue(): SchemaModelOrderValue {
31
+ return ioc.get(PrefsStore).getPreference<SchemaModelOrderingPreference>(SchemaModelOrderingPreference.KEY)
32
+ .value as SchemaModelOrderValue;
33
+ }
34
+ }
35
+
36
+ export enum SchemaFieldOrderValue {
37
+ ALPHABETICAL = 'alphabetical',
38
+ BELONGS_TO_FIRST = 'belongs_to_first',
39
+ BELONGS_TO_LAST = 'belongs_to_last',
40
+ AS_DEFINED_IN_SCHEMA = 'as_defined_in_schema'
41
+ }
42
+
43
+ export class SchemaFieldOrderingPreference extends SetSetting {
44
+ static KEY = 'databrowser/schema/field-order';
45
+
46
+ constructor() {
47
+ super({
48
+ key: SchemaFieldOrderingPreference.KEY,
49
+ name: 'Schema field order',
50
+ category: 'Schema',
51
+ value: SchemaFieldOrderValue.AS_DEFINED_IN_SCHEMA,
52
+ options: [
53
+ {
54
+ key: SchemaFieldOrderValue.AS_DEFINED_IN_SCHEMA,
55
+ label: 'As defined in schema'
56
+ },
57
+ {
58
+ key: SchemaFieldOrderValue.ALPHABETICAL,
59
+ label: 'Alphabetical'
60
+ },
61
+ {
62
+ key: SchemaFieldOrderValue.BELONGS_TO_FIRST,
63
+ label: 'Belongs-to first'
64
+ },
65
+ {
66
+ key: SchemaFieldOrderValue.BELONGS_TO_LAST,
67
+ label: 'Belongs-to last'
68
+ }
69
+ ]
70
+ });
71
+ }
72
+
73
+ static getValue(): SchemaFieldOrderValue {
74
+ return ioc.get(PrefsStore).getPreference<SchemaFieldOrderingPreference>(SchemaFieldOrderingPreference.KEY)
75
+ .value as SchemaFieldOrderValue;
76
+ }
77
+ }
78
+
79
+ export const registerSchemaOrderingPreferences = (prefsStore: PrefsStore) => {
80
+ prefsStore.registerPreference(new SchemaModelOrderingPreference());
81
+ prefsStore.registerPreference(new SchemaFieldOrderingPreference());
82
+ };