react-graph-grid 0.0.6 → 0.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@ For all questions, please contact rmb@mail.ru
6
6
 
7
7
  Installation
8
8
 
9
- npm install react-grpah-grid
9
+ npm install react-grpah-grid
10
10
 
11
11
 
12
12
  Example
@@ -56,84 +56,117 @@ Example
56
56
  *
57
57
  */
58
58
 
59
- function loadRows(e) {
60
- return new Promise(function (resolve, reject) {
61
- const fetchParams = {
62
- mode: 'cors',
63
- method: 'post',
64
- headers: {
65
- 'Content-Type': 'application/json'
59
+ function loadRows(e) {
60
+ return new Promise(function (resolve, reject) {
61
+ const fetchParams = {
62
+ mode: 'cors',
63
+ method: 'post',
64
+ headers: {
65
+ 'Content-Type': 'application/json'
66
+ },
67
+ body: JSON.stringify(e.params)
68
+ };
69
+
70
+ fetch(`/awesome-api-url/`, fetchParams).then(
71
+ (response) => {
72
+ resolve(response.json());
73
+ }
74
+ )
75
+ .catch(error => {
76
+ reject(error);
77
+ });
78
+ });
79
+ };
80
+
81
+ function loadColumns() {
82
+ return [
83
+ { name: 'Id', sortable: true, filtrable: true },
84
+ { name: 'Name', sortable: true, filtrable: true },
85
+ { name: 'SecondName', sortable: true, filtrable: true },
86
+ { name: 'Date', sortable: true },
87
+ { name: 'Comment', sortable: true, filtrable: true },
88
+ { name: 'HometownId', visible: false },
89
+ {
90
+ name: 'Hometown',
91
+ sortable: true,
92
+ filtrable: true,
93
+ type: 'lookup',
94
+ keyField: 'HometownId',
95
+ refKeyField: 'Id',
96
+ refNameField: 'City',
97
+ getRows: (e) => {
98
+ return new Promise(function (resolve, reject) {
99
+
100
+ const rows = new TestData().getCity(e);
101
+
102
+ if (rows != null) {
103
+ resolve(rows);
104
+ } else {
105
+ reject(Error("Error getting rows"));
106
+ }
107
+ });
108
+ }
109
+ },
110
+ ]
111
+ };
112
+
113
+
114
+ <GridFE
115
+ getRows={loadRows}
116
+ getColumns={loadColumns}
117
+ allowEdit={true}
118
+ />
119
+
120
+ Some grid properties
121
+
122
+ uid - grid uid
123
+ keyField - primary key
124
+ nameField - name field
125
+ parentGrids - parent grids uids
126
+ buttons - buttons array
127
+ [
128
+ {
129
+ id: 1,
130
+ name: 'commit',
131
+ title: 'Commit changes',
132
+ label: 'Commit',
133
+ img: Images.commit,
134
+ click: (e) => grid.commitChanges(e),
135
+ getDisabled: (e) => grid.commitChangesDisabled(e)
66
136
  },
67
- body: JSON.stringify(e.params)
68
- };
69
-
70
- fetch(`/awesome-api-url/`, fetchParams).then(
71
- (response) => {
72
- resolve(response.json());
73
- }
74
- )
75
- .catch(error => {
76
- reject(error);
77
- });
78
- });
79
- };
80
-
81
- function loadColumns() {
82
- return [
83
- { name: 'Id', sortable: true, filtrable: true },
84
- { name: 'Name', sortable: true, filtrable: true },
85
- { name: 'SecondName', sortable: true, filtrable: true },
86
- { name: 'Date', sortable: true },
87
- { name: 'Comment', sortable: true, filtrable: true },
88
- { name: 'HometownId', visible: false },
89
- {
90
- name: 'Hometown',
91
- sortable: true,
92
- filtrable: true,
93
- type: 'lookup',
94
- keyField: 'HometownId',
95
- refKeyField: 'Id',
96
- refNameField: 'City',
97
- getRows: (e) => {
98
- return new Promise(function (resolve, reject) {
99
-
100
- const rows = new TestData().getCity(e);
101
-
102
- if (rows != null) {
103
- resolve(rows);
104
- } else {
105
- reject(Error("Error getting rows"));
137
+ ...
138
+ ]
139
+ multi - rows multiselect through a pocket
140
+ renderCell - custom render grid cell
141
+ pageSize - grid page size
142
+ applyConnection - Returns the condition applied to the grid when the active record
143
+ of the parent grid changes.
144
+ This condition can be passed to your controller to process
145
+ the condition of the relationship of the child table with the parent
146
+
147
+ For example
148
+ applyConnection(e) {
149
+ if (e.parent) {
150
+ return `ChildTable.parentID in (${e.parent.selectedValue()})`;
106
151
  }
107
- });
108
- }
109
- },
110
- ]
111
- };
152
+ }
112
153
 
113
154
 
114
- <GridFE
115
- getRows={loadRows}
116
- getColumns={loadColumns}
117
- allowEditGrid={true}
118
- />
155
+ For more examples see DebugApp.jsx
119
156
 
120
- Some grid properties
157
+ 0.1.0 version
121
158
 
122
- uid - grid uid
123
- parentGrids - parent grids uids
124
- buttons - buttons array [{ id: 1, name: 'commit', title: 'Commit changes', label: 'Commit', img: Images.commit, click: (e) => grid.commitChanges(e), getDisabled: (e) => grid.commitChangesDisabled(e) }, ... ]
125
- multi - rows multiselect through a pocket
126
- renderCell - custom render grid cell
127
- getDefaultLinkContent - returns an object containing a data using when child grid responds to the parent grid active record
128
- pageSize - grid page size
159
+ Removed getDefaultLinkContent, added applyConnection function
129
160
 
161
+ 0.0.7 version
130
162
 
131
- For more examples see DebugApp.jsx
163
+ Fixed GridDB and its dropdown communication
164
+
165
+ 0.0.6 version
166
+
167
+ Fixed GridFE.showColumnsSettings() function
132
168
 
133
169
  0.0.5 version
134
170
 
135
171
  "Adjust column visibility" option added to GridFE.jsx module
136
172
 
137
- 0.0.6 version
138
-
139
- Fixed GridFE.showColumnsSettings() function
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
- {
1
+ {
2
2
  "name": "react-graph-grid",
3
3
  "author": "Mikhail Razumtsev",
4
4
  "description": "A React package containing a grid that can communicate with other grids through a connection graph",
5
5
  "private": false,
6
- "version": "0.0.6",
6
+ "version": "0.1.0",
7
7
  "type": "module",
8
8
  "scripts": {
9
9
  "dev": "vite --port 4000",
@@ -12,8 +12,8 @@
12
12
  "preview": "vite preview"
13
13
  },
14
14
  "dependencies": {
15
- "react": "^19.2.0",
16
- "react-dom": "^19.2.0"
15
+ "react": "^19.2.4",
16
+ "react-dom": "^19.2.4"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@eslint/js": "^9.39.1",
package/src/Card.jsx ADDED
@@ -0,0 +1,333 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { Images } from './Themes/Images';
3
+ import { FieldEdit } from './FieldEdit';
4
+ import { GridFEClass } from './GridFE';
5
+ // =================================================================================================================================================================
6
+ export function Card(props) {
7
+ let card = null;
8
+
9
+ const [gridState, setState] = useState({ grid: card, ind: 0 });
10
+
11
+ card = gridState.grid;
12
+ let needGetRows = false;
13
+ if (!card || card.uid !== props.uid && props.uid != null) {
14
+ card = null;
15
+ if (props.findGrid) {
16
+ card = props.findGrid(props);
17
+ }
18
+ card = card || new CardClass(props);
19
+ needGetRows = !card.changedRow;
20
+ }
21
+
22
+ if (props.init) {
23
+ props.init(card);
24
+ }
25
+
26
+ card.refreshState = function () {
27
+ setState({ grid: card, ind: card.stateind++ });
28
+ }
29
+
30
+ useEffect(() => {
31
+ card.setupEvents();
32
+
33
+ if (needGetRows && card.selectedRow() == null) {
34
+
35
+ card.getRows().then(
36
+ rows => {
37
+ card.rows = rows;
38
+ card.afterGetRows();
39
+ card.refreshState();
40
+ }
41
+ );
42
+ }
43
+ else if (card.columns.length <= 0 && card.getColumns) {
44
+ card.prepareColumns().then(() => card.refreshState());
45
+ }
46
+
47
+ return () => {
48
+ card.clearEvents();
49
+
50
+ if (card.graph && card.graph.nodeCount) {
51
+ card.graph.nodeCount--;
52
+ }
53
+ }
54
+ }, [card, needGetRows])
55
+
56
+ return (card.render());
57
+ }
58
+ // =================================================================================================================================================================
59
+ export class CardClass extends GridFEClass {
60
+
61
+ constructor(props) {
62
+ super(props);
63
+
64
+ const card = this;
65
+
66
+ card.visible = true;
67
+
68
+ card.allowEdit = props.allowEdit;
69
+ card.isVisible = props.isVisible || card.isVisible;
70
+ card.activeRow = props.activeRow || '';
71
+ card.refreshState = card.refreshState || (() => { });
72
+
73
+ card.reqInd = 0;
74
+ card.changedRow = {};
75
+ card.initialRow = props.cardRow;
76
+ Object.assign(card.changedRow, card.initialRow);
77
+
78
+ if (props.isNewRecord) {
79
+ card.isNewRecord = true;
80
+ card.setEditing(true);
81
+ }
82
+
83
+ card.cardButtons = [];
84
+ }
85
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
86
+ render() {
87
+ const card = this;
88
+
89
+ card.addCardButtons();
90
+ card.buttons = card.cardButtons;
91
+
92
+ return (
93
+ <>
94
+ <div className='graph-card-toolbar'
95
+ key={`cardToolbarDiv_${card.id}_`}
96
+ style={{ margin: '0 1em' }}
97
+ >
98
+ {super.renderToolbar()}
99
+ </div>
100
+ <div className="graph-card-div"
101
+ key={`cardBodyDiv_${card.id}_`}
102
+ >
103
+ {
104
+ card.columns.map((col) => { return card.renderField(col) })
105
+ }
106
+ </div>
107
+ {super.renderPopup()}
108
+ </>
109
+ )
110
+ }
111
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
112
+ renderField(col) {
113
+ const card = this;
114
+
115
+ if (col.visible == false) return <></>;
116
+
117
+ const isLookup = col.type === 'lookup';
118
+ let row = card.changedRow;
119
+ let value = isLookup ? row[col.keyField] : row[col.name];
120
+ value = value != null ? value : '';
121
+ if (col.type == null) {
122
+ col.type = '';
123
+ }
124
+ else if (isLookup) {
125
+ col.grid = card;
126
+ }
127
+
128
+ return (
129
+ <div className="graph-card-field"
130
+ key={`cardLookupDiv_${card.id}_${col.id}_`}
131
+ style={{ margin: '0 1em 0.5em 1em' }}
132
+ >
133
+ <span
134
+ key={`cardLookupTitle_${card.id}_${col.id}_`}
135
+ style={{ gridColumn: 'span 3', width: 'calc(100% - 4px)' }}
136
+ >
137
+ {col.title || col.name}
138
+ </span>
139
+ <FieldEdit
140
+ keyPref={card.id + '_card_'}
141
+ column={col}
142
+ value={value}
143
+ text={row[col.name]}
144
+ findFieldEdit={() => { return col._fieldEditObj; }}
145
+ large={true}
146
+ level={card.level}
147
+ disabled={card.allowEdit == null || card.allowEdit != true}
148
+ init={
149
+ (fe) => {
150
+ if (card.isEditing() && !card.changedRow) {
151
+ card.changedRow = {};
152
+ Object.assign(card.changedRow, card.selectedRow());
153
+ }
154
+
155
+ row = !card.isEditing() ? card.selectedRow() : card.changedRow;
156
+
157
+ col._fieldEditObj = fe;
158
+ fe.value = isLookup ? row[col.keyField] : row[col.name];
159
+ fe.value = fe.value != null ? fe.value : '';
160
+
161
+ fe.text = row[col.name];
162
+ }
163
+ }
164
+ onChange={(e) => {
165
+ if (!card.changedRow) {
166
+ card.changedRow = {};
167
+ Object.assign(card.changedRow, card.selectedRow());
168
+ }
169
+
170
+ if (isLookup) {
171
+ card.changedRow[col.keyField] = e.value;
172
+ card.changedRow[col.name] = e.text;
173
+ if (!card.isEditing()) {
174
+ card.setEditing(true);
175
+ }
176
+ card.refreshState();
177
+ }
178
+ else {
179
+ card.changedRow[col.name] = e.value;
180
+ card.setEditing(true);
181
+ card.refreshState();
182
+ }
183
+ }}
184
+ >
185
+ </FieldEdit>
186
+ </div>
187
+ )
188
+
189
+ }
190
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
191
+ addCardButtons() {
192
+ const card = this;
193
+
194
+ if (card._cardButtonsAdded) return;
195
+
196
+ card._cardButtonsAdded = true;
197
+
198
+ //card.cardButtons.push({
199
+ // id: card.cardButtons.length,
200
+ // name: 'edit',
201
+ // title: card.translate('Start edit'),
202
+ // label: images.edit ? '' : card.translate('Start edit'),
203
+ // click: (e) => card.startEditNode(e),
204
+ // img: images.edit
205
+ //});
206
+
207
+ card.cardButtons.push({
208
+ id: card.cardButtons.length,
209
+ name: 'commit',
210
+ title: card.translate('Commit changes'),
211
+ label: card.translate('Commit'),
212
+ img: Images.images.commit,
213
+ click: (e) => card.commitChangesNode(e),
214
+ getDisabled: (e) => card.commitChangesNodeDisabled(e),
215
+ });
216
+
217
+ card.cardButtons.push({
218
+ id: card.cardButtons.length,
219
+ name: 'rollback',
220
+ title: card.translate('Rollback changes'),
221
+ label: card.translate('Rollback'),
222
+ img: Images.images.rollback,
223
+ click: (e) => {
224
+ card.rollbackChangesNode(e);
225
+ if (card.isNewRecord && card.close) card.close(e);
226
+ },
227
+ getDisabled: (e) => card.rollbackChangesNodeDisabled(e),
228
+ });
229
+ }
230
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
231
+ commitChangesNodeDisabled() {
232
+ const card = this;
233
+ if (!card.isEditing()) return true;
234
+
235
+ let requiredColumnsFilled = true;
236
+
237
+ for (let col of card.columns) {
238
+ if (!col.required || col.readonly) continue;
239
+
240
+ let val = card.changedRow[col.name];
241
+ if (col.required && (val == null || val === '')) {
242
+ requiredColumnsFilled = false;
243
+ break;
244
+ }
245
+ }
246
+
247
+ return !requiredColumnsFilled;
248
+ }
249
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
250
+ rollbackChangesNodeDisabled() {
251
+ const card = this;
252
+ return !card.isEditing();
253
+ }
254
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
255
+ saveRow(e) {
256
+ const card = this;
257
+
258
+ if (!card.isRowChanged(e.row, card.changedRow)) return new Promise(function (resolve) { resolve(true); });
259
+
260
+ return new Promise(function (resolve) {
261
+ //e.row = card.initialRow = e.changedRow;
262
+ Object.assign(card.initialRow, card.changedRow);
263
+ resolve(true);
264
+ });
265
+ }
266
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
267
+ commitChangesNode() {
268
+ const card = this;
269
+ card.saveRow({ row: card.initialRow, changedRow: card.changedRow }).then(
270
+ () => {
271
+ if (card.isNewRecord || !card.keyField) {
272
+ card.setEditing(false);
273
+ Object.assign(card.initialRow, card.changedRow);
274
+ card.refreshState();
275
+ }
276
+ else {
277
+ card.getRows().then(
278
+ rows => {
279
+ card.rows = rows;
280
+ card.changedRow = rows[0];
281
+ Object.assign(card.initialRow, card.changedRow);
282
+ card.setEditing(false);
283
+ card.afterGetRows();
284
+ card.refreshState();
285
+ }
286
+ );
287
+ }
288
+ }
289
+ ).catch((message) => {
290
+ Object.assign(card.changedRow, card.initialRow);
291
+ card.refreshState();
292
+ alert(message || 'Error!');
293
+ });
294
+ }
295
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
296
+ rollbackChangesNode() {
297
+ const card = this;
298
+ if (card.isNewRecord || !card.keyField) {
299
+ card.changedRow = {};
300
+ Object.assign(card.changedRow, card.initialRow);
301
+ card.setEditing(false);
302
+ card.refreshState();
303
+ }
304
+ else {
305
+ card.getRows().then(
306
+ rows => {
307
+ card.rows = rows;
308
+ card.changedRow = rows[0];
309
+ Object.assign(card.initialRow, card.changedRow);
310
+ card.setEditing(false);
311
+ card.afterGetRows();
312
+ card.refreshState();
313
+ }
314
+ );
315
+ }
316
+ }
317
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
318
+ selectedRow() {
319
+ const card = this;
320
+ return card.changedRow;
321
+ }
322
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
323
+ getRows() {
324
+ const card = this;
325
+
326
+ return new Promise(function (resolve) {
327
+ const res = [card.initialRow];
328
+ card.totalRows = 1;
329
+ resolve(res);
330
+ });
331
+ }
332
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
333
+ }
package/src/Dropdown.jsx CHANGED
@@ -227,7 +227,7 @@ export class DropdownClass extends ModalClass {
227
227
 
228
228
  dd.refreshState();
229
229
 
230
- dd.getItems({ filter: dd.filter, pageSize: dd.pageSize, pageNumber: dd.pageNumber }).then(
230
+ dd.getItems({ self: dd, filter: dd.filter, pageSize: dd.pageSize, pageNumber: dd.pageNumber }).then(
231
231
  items => {
232
232
  afterGetItems(items);
233
233
  }
@@ -259,7 +259,7 @@ export class DropdownClass extends ModalClass {
259
259
  }
260
260
  else {
261
261
  if (dd.opt.onItemClick) {
262
- dd.opt.onItemClick({ owner: dd.opt.owner, itemId: itemId, dropdown: dd, clientX: e.clientX, clientY: e.clientY, target: e.target });
262
+ dd.opt.onItemClick({ owner: dd.opt.owner, itemId: itemId, self: dd, clientX: e.clientX, clientY: e.clientY, target: e.target });
263
263
  }
264
264
 
265
265
  const clickedItem = dd.items.find(function (item) {
@@ -286,7 +286,7 @@ export class DropdownClass extends ModalClass {
286
286
  case 'enter':
287
287
  if (!dd.activeItem) return;
288
288
 
289
- dd.opt.onItemClick({ owner: dd.opt.owner, itemId: dd.activeItem.id, dropdown: dd });
289
+ dd.opt.onItemClick({ owner: dd.opt.owner, itemId: dd.activeItem.id, self: dd });
290
290
  dd.close();
291
291
  break;
292
292
  case 'down':
package/src/FieldEdit.jsx CHANGED
@@ -115,6 +115,8 @@ export class FieldEditClass extends BaseComponent {
115
115
 
116
116
  // просто разметка 'span 2' etc.
117
117
  fe.gridColumn = props.gridColumn;
118
+
119
+ fe._selectedOptions = [];
118
120
  }
119
121
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
120
122
  static _seq = 0;
package/src/Grid.jsx CHANGED
@@ -166,7 +166,9 @@ export class GridClass extends BaseComponent {
166
166
  grid._waitingRows = false;
167
167
  grid.refreshState();
168
168
  }
169
- ).finally(() => {
169
+ ).catch(() => {
170
+ grid._waitingRows = false;
171
+ }).finally(() => {
170
172
  grid._waitingRows = false;
171
173
  grid.refreshState();
172
174
  });
@@ -289,7 +291,7 @@ export class GridClass extends BaseComponent {
289
291
  gridTemplateRows: '1.5em auto',
290
292
  gridAutoFlow: 'row',
291
293
  width: 'calc(100% + 8px)',
292
- justifyContent: 'space-between',
294
+ //justifyContent: 'space-between',
293
295
  }}
294
296
  >
295
297
  {grid.renderHeaderCell(col, context)}
@@ -473,7 +475,7 @@ export class GridClass extends BaseComponent {
473
475
  for (let key in row) {
474
476
  if (grid.colDict[key]) continue;
475
477
 
476
- const col = grid.getColumn(key);
478
+ const col = grid.createColumn(key);
477
479
 
478
480
  grid.colDict[col.name] = col;
479
481
  res.push(col);
@@ -483,7 +485,7 @@ export class GridClass extends BaseComponent {
483
485
  return res;
484
486
  }
485
487
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
486
- getColumn(name) {
488
+ createColumn(name) {
487
489
  return { name: name };
488
490
  }
489
491
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
package/src/GridCD.jsx ADDED
@@ -0,0 +1,180 @@
1
+ /* eslint-disable no-mixed-operators */
2
+ import { useState, useEffect } from 'react';
3
+ import { GridFEClass } from './GridFE';
4
+ import { Card } from './Card';
5
+ // ==================================================================================================================================================================
6
+ export function GridCD(props) {
7
+ let grid = null;
8
+
9
+ const [gridState, setState] = useState({ grid: grid, ind: 0 });
10
+
11
+ grid = gridState.grid;
12
+ let needGetRows = false;
13
+ if (!grid || grid.uid !== props.uid && props.uid != null) {
14
+ grid = null;
15
+ if (props.findGrid) {
16
+ grid = props.findGrid(props);
17
+ }
18
+ grid = grid || new GridCDClass(props);
19
+ needGetRows = !props.noAutoRefresh && !grid.hasVisibleParentGrids();
20
+ }
21
+
22
+ if (props.init) {
23
+ props.init(grid);
24
+ }
25
+
26
+ grid.refreshState = function () {
27
+ setState({ grid: grid, ind: grid.stateind++ });
28
+ }
29
+
30
+ useEffect(() => {
31
+ grid.setupEvents();
32
+
33
+ if (needGetRows && (grid.rows.length <= 0 || grid.columns.length <= 0) || grid._forceRefresh) {
34
+
35
+ grid._forceRefresh = false;
36
+
37
+ grid._waitingRows = true;
38
+ grid.getRows({ filters: grid.collectFilters(), grid: grid }).then(
39
+ rows => {
40
+ grid.rows = rows;
41
+ grid.afterGetRows();
42
+ grid.refreshState();
43
+ }
44
+ ).finally(() => {
45
+ grid._waitingRows = false;
46
+ grid.refreshState();
47
+ });
48
+ }
49
+ else if (grid.columns.length <= 0 && grid.getColumns) {
50
+ grid.prepareColumns().then(() => grid.refreshState());;
51
+ }
52
+
53
+ return () => {
54
+ grid.clearEvents();
55
+ }
56
+ }, [grid, needGetRows])
57
+
58
+ return (grid.render());
59
+ }
60
+
61
+ // ==================================================================================================================================================================
62
+ export class GridCDClass extends GridFEClass {
63
+
64
+ constructor(props) {
65
+ super(props);
66
+
67
+ const grid = this;
68
+ grid.allowView = true;
69
+
70
+ grid._buttonsDict['view'].getVisible = () => { return true; };
71
+ }
72
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
73
+ render() {
74
+ return (
75
+ <>
76
+ {super.render()}
77
+ </>
78
+ )
79
+ }
80
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
81
+ renderPopupContent(wnd) {
82
+ const grid = this;
83
+ return grid.cardIsShowing ? grid.renderCardContent(wnd) : super.renderPopupContent(wnd);
84
+ }
85
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
86
+ renderCardContent() {
87
+ const grid = this;
88
+ return (
89
+ grid.allowView == false ?
90
+ <div
91
+ className={`graph-filter-title graph-filter-required`}
92
+ >
93
+ {grid.translate('Insufficient rights to view')}
94
+ </div>
95
+ :
96
+ <Card
97
+ cardRow={grid.cardRow || {}}
98
+ isNewRecord={grid.isNewRecord}
99
+ allowEdit={grid.allowEdit}
100
+ uid={(grid.uid || grid.id) + '_card_'}
101
+ keyField={grid.keyField}
102
+ level={grid.level + 1}
103
+ findGrid={() => {
104
+ const selRow = grid.cardRow || grid.selectedRow();
105
+ if (grid._card && selRow && grid._card.changedRow[grid.keyField] === selRow[grid.keyField]) {
106
+ return grid._card;
107
+ }
108
+ }}
109
+ init={(card) => {
110
+ if (grid._card == card) return;
111
+
112
+ grid._card = card;
113
+
114
+ card.visible = true;
115
+ card.columns = [];
116
+ for (let col of grid.columns) {
117
+ const remGetRows = col.getRows;
118
+ delete col._fieldEditObj;
119
+ delete col.grid;
120
+ delete col._filterEditObj;
121
+ delete col.getRows;
122
+
123
+ // WARNING !!! потенциальная ошибка, если пользователь в потомке напихает в колонку объектов или функций !!!
124
+ let clone = structuredClone(col);
125
+ card.columns.push(clone);
126
+
127
+ clone.getRows = col.getRows = remGetRows;
128
+
129
+ col.grid = grid;
130
+ }
131
+ card.close = (e) => {
132
+ grid.onClosePopup(e);
133
+ grid.refreshState();
134
+ }
135
+ }}
136
+ >
137
+ </Card>
138
+ );
139
+ }
140
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
141
+ viewRecord(e) {
142
+ const grid = this;
143
+
144
+ const shift = (grid.level + 1) * 20;
145
+
146
+ grid.cardPos = grid.cardPos || { x: 100 + shift, y: 100 + shift, w: 800, h: 600 };
147
+ grid.popupPos = grid.cardPos;
148
+
149
+ grid.cardRow = grid.selectedRow();
150
+ grid.isNewRecord = false;
151
+ grid.cardIsShowing = true;
152
+ grid.popupIsShowing = true;
153
+ grid.popupTitle = grid.title;
154
+
155
+ grid.refreshState();
156
+ }
157
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
158
+ onClosePopup(e) {
159
+ const grid = this;
160
+
161
+ if (grid.cardIsShowing && grid._card) {
162
+ if (grid._card.isEditing() && (grid.isNewRecord || grid.isRowChanged(grid._card.changedRow, grid.selectedRow()))) {
163
+ e.cancel = !confirm(grid.translate('The row is changed. Discard changes?'));
164
+ if (e.cancel) {
165
+ return;
166
+ }
167
+ }
168
+ delete grid._card;
169
+ }
170
+
171
+ super.onClosePopup(e);
172
+
173
+ if (e.cancel) return;
174
+
175
+ if (grid.cardIsShowing) {
176
+ grid.cardIsShowing = false;
177
+ }
178
+ }
179
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
180
+ }
package/src/GridDB.jsx CHANGED
@@ -152,10 +152,13 @@ export class GridDBClass extends GridPKClass {
152
152
  {super.render()}
153
153
  {grid.renderPager(true)}
154
154
  <Dropdown
155
- init={(dd) => { grid.menuDropdown = dd; }}
155
+ init={(dd) => {
156
+ grid.menuDropdown = dd;
157
+ dd._grid = grid;
158
+ }}
156
159
  closeWhenMiss={true}
157
- getItems={(e) => { return grid.getGridSettings(e); }}
158
- onItemClick={(e) => { grid.onSettingsItemClick(e.itemId); }}>
160
+ getItems={(e) => { return e.self._grid.getGridSettings(e); }}
161
+ onItemClick={(e) => { e.self._grid.onSettingsItemClick(e.itemId); }}>
159
162
  </Dropdown>
160
163
  </>
161
164
  )
@@ -639,6 +642,7 @@ export class GridDBClass extends GridPKClass {
639
642
  whiteSpace: 'nowrap',
640
643
  overflowX: 'hidden',
641
644
  width: sortDir ? 'calc(100% - 10px)' : '',
645
+ textAlign: 'left',
642
646
  }}
643
647
  onClick={(e) => { if (!grid._waitingRows) grid.changeColumnSortOrder(col, e); }}
644
648
  disabled={grid._waitingRows || col.disabled ? 'disabled' : ''}
package/src/GridFE.jsx CHANGED
@@ -68,9 +68,12 @@ export class GridFEClass extends GridFLClass {
68
68
 
69
69
  const grid = this;
70
70
 
71
- grid.allowEditGrid = props.allowEditGrid;
71
+ grid.allowEdit = props.allowEdit;
72
72
 
73
- //grid.changedRow = {};
73
+ grid.allowView = false;
74
+ grid.allowAdd = true;
75
+ grid.allowCopy = true;
76
+ grid.allowDelete = true;
74
77
 
75
78
  grid.closeSelfWnd = grid.closeSelfWnd || (() => { });
76
79
 
@@ -127,13 +130,13 @@ export class GridFEClass extends GridFLClass {
127
130
  delete grid._popupButtons;
128
131
  }
129
132
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
130
- renderPopupContent() {
133
+ renderPopupContent(wnd) {
131
134
  const grid = this;
132
- return grid.columnsSettingsIsShowing ? grid.renderColumnsSettings() : <></>;
135
+ return grid.columnsSettingsIsShowing ? grid.renderColumnsSettings(wnd) : <></>;
133
136
  }
134
137
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
135
138
  renderCell(grid, col, row, selected) {
136
- if (!grid.allowEditGrid && !col.allowVerticalResize || !selected || grid.isDisabled()) return super.renderCell(grid, col, row);
139
+ if (!grid.allowEdit && !col.allowVerticalResize || !selected || grid.isDisabled()) return super.renderCell(grid, col, row);
137
140
 
138
141
  row = !grid.isEditing() || !grid.changedRow ? row : grid.changedRow;
139
142
 
@@ -261,6 +264,7 @@ export class GridFEClass extends GridFLClass {
261
264
  img: Images.images.viewRecord,
262
265
  click: (e) => grid.viewRecord(e),
263
266
  getDisabled: (e) => grid.viewRecordDisabled(e),
267
+ getVisible: () => { return false; },
264
268
  });
265
269
 
266
270
  grid.buttons.push({
@@ -369,6 +373,8 @@ export class GridFEClass extends GridFLClass {
369
373
  const grid = this;
370
374
 
371
375
  grid.getNewRow().then((newRow) => {
376
+ grid.rows.unshift(newRow);
377
+ grid.selectedRowIndex = 0;
372
378
  grid.refreshState();
373
379
  });
374
380
  }
@@ -381,8 +387,10 @@ export class GridFEClass extends GridFLClass {
381
387
  copyRecord(e) {
382
388
  const grid = this;
383
389
 
384
- let newRow;
390
+ const newRow = {};
385
391
  Object.assign(newRow, grid.selectedRow());
392
+ grid.rows.unshift(newRow);
393
+ grid.selectedRowIndex = 0;
386
394
 
387
395
  grid.refreshState();
388
396
  }
@@ -396,7 +404,7 @@ export class GridFEClass extends GridFLClass {
396
404
  const grid = this;
397
405
 
398
406
  if (window.confirm(grid.translate('Delete record') + '?')) {
399
- grid.deleteRow(e).then(() => grid.refresh());
407
+ grid.deleteRow(e).then(() => grid.refreshState());
400
408
  }
401
409
  }
402
410
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -406,11 +414,6 @@ export class GridFEClass extends GridFLClass {
406
414
  }
407
415
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
408
416
  viewRecord(e) {
409
- const grid = this;
410
-
411
- let cardRow = grid.selectedRow();
412
-
413
- grid.refreshState();
414
417
  }
415
418
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
416
419
  viewRecordDisabled(e) {
@@ -432,7 +435,7 @@ export class GridFEClass extends GridFLClass {
432
435
  const grid = this;
433
436
  super.onSelectedRowChanged(e);
434
437
 
435
- if (grid.allowEditGrid && grid.refreshState) {
438
+ if (grid.allowEdit && grid.refreshState) {
436
439
  grid.refreshState();
437
440
  }
438
441
  }
@@ -460,7 +463,7 @@ export class GridFEClass extends GridFLClass {
460
463
  const grid = this;
461
464
  let res;
462
465
 
463
- return (!grid.allowEditGrid || !grid.isEditing()) && !grid.isDisabled();
466
+ return (!grid.allowEdit || !grid.isEditing()) && !grid.isDisabled();
464
467
 
465
468
  const row = grid.rows[rowIndex];
466
469
  await grid.saveRow({ row: row, changedRow: grid.changedRow }).then(
@@ -486,18 +489,33 @@ export class GridFEClass extends GridFLClass {
486
489
  return selected ? '1' : grid.stateind;
487
490
  }
488
491
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
489
- isRowChanged(row) {
492
+ isRowChanged(row, secondRow) {
490
493
  const grid = this;
491
- if (!grid.changedRow) return false;
494
+ secondRow = secondRow || grid.changedRow;
495
+
496
+ if (!secondRow) return false;
492
497
 
493
498
  let res = false;
494
- for (let col in grid.changedRow) {
495
- if (grid.changedRow[col] !== row[col]) return true;
499
+ for (let col of grid.columns) {
500
+ if (secondRow[col.name] !== row[col.name]) return true;
496
501
  }
497
502
 
498
503
  return res;
499
504
  }
500
505
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
506
+ getNewRow() {
507
+ const grid = this;
508
+
509
+ return new Promise(function (resolve, reject) {
510
+ const newRow = {};
511
+ for (let col of grid.columns) {
512
+ newRow[col.name] = '';
513
+ }
514
+ newRow[grid.keyField] = -1;
515
+ resolve(newRow);
516
+ });
517
+ }
518
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
501
519
  saveRow(e) {
502
520
  const grid = this;
503
521
 
@@ -509,6 +527,16 @@ export class GridFEClass extends GridFLClass {
509
527
  });
510
528
  }
511
529
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
530
+ deleteRow(e) {
531
+ const grid = this;
532
+ e.row = e.row || grid.selectedRow();
533
+
534
+ return new Promise(function (resolve, reject) {
535
+ grid.rows = grid.rows.filter(item => item != e.row);
536
+ resolve(true);
537
+ });
538
+ }
539
+ // -------------------------------------------------------------------------------------------------------------------------------------------------------------
512
540
  getGridSettingsList() {
513
541
  const res = super.getGridSettingsList();
514
542
 
package/src/GridFL.jsx CHANGED
@@ -17,7 +17,7 @@ export function GridFL(props) {
17
17
  grid = props.findGrid(props);
18
18
  }
19
19
  grid = grid || new GridFLClass(props);
20
- needGetRows = !props.noAutoRefresh && !grid.hasVisibleParentGrids();
20
+ needGetRows = !props.noAutoRefresh && grid.hasVisibleParentGrids && !grid.hasVisibleParentGrids();
21
21
  }
22
22
 
23
23
  if (props.init) {
@@ -77,25 +77,26 @@ export class GridFLClass extends GridDBClass {
77
77
  <>
78
78
  {super.render()}
79
79
  <Dropdown
80
- getItems={(e) => { return grid.getAutocomleteItems(e); }}
81
- onItemClick={(e) => { grid.onAutocomleteItemClick(e); }}
80
+ getItems={(e) => { return e.self._grid.getAutocomleteItems(e); }}
81
+ onItemClick={(e) => { e.self._grid.onAutocomleteItemClick(e); }}
82
82
  closeWhenMiss={true}
83
83
  init={(dd) => {
84
84
  if (grid._autocompleteDropdown) {
85
85
  dd.visible = grid._autocompleteDropdown.visible;
86
86
  }
87
87
  grid._autocompleteDropdown = dd;
88
+ dd._grid = grid;
88
89
  if (grid._autocompleteRect) {
89
90
  dd.opt.parentRect = grid._autocompleteRect;
90
91
  }
91
92
  }}
92
- onClose={() => {
93
- if (grid._inputingColumn) {
94
- delete grid._inputingColumn;
95
- if (grid.needRefresh()) {
96
- grid.pageNumber = 1;
97
- grid.selectedRowIndex = 0;
98
- grid.refresh();
93
+ onClose={(e) => {
94
+ if (e.self._grid._inputingColumn) {
95
+ delete e.self._grid._inputingColumn;
96
+ if (e.self._grid.needRefresh()) {
97
+ e.self._grid.pageNumber = 1;
98
+ e.self._grid.selectedRowIndex = 0;
99
+ e.self._grid.refresh();
99
100
  }
100
101
  }
101
102
  }}
@@ -280,7 +281,7 @@ export class GridFLClass extends GridDBClass {
280
281
  return;
281
282
  }
282
283
 
283
- e.dropdown.items = [];
284
+ e.self.items = [];
284
285
  grid._autocompleteDropdown.items = [];
285
286
  grid._autocompleteDropdown.visible = false;
286
287
 
@@ -412,7 +413,7 @@ export class GridFLClass extends GridDBClass {
412
413
  }
413
414
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
414
415
  getHeaderGridTemplateColumns(col) {
415
- return col.sortInd == null ? 'auto 12px' : 'auto 22px';
416
+ return col.sortInd == null ? 'auto 16px' : 'auto 22px';
416
417
  }
417
418
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
418
419
  getGridSettingsList() {
package/src/GridGR.jsx CHANGED
@@ -68,8 +68,8 @@ export class GridGRClass extends GridClass {
68
68
  grid.entity = props.entity;
69
69
  }
70
70
 
71
- if (props.getDefaultLinkContent) {
72
- grid.getDefaultLinkContent = props.getDefaultLinkContent;
71
+ if (props.applyConnection) {
72
+ grid.applyConnection = props.applyConnection;
73
73
  }
74
74
 
75
75
  if (!props.graph && (props.parentGrids || props.uid)) {
@@ -139,7 +139,7 @@ export class GridGRClass extends GridClass {
139
139
  let parentGrid = graph.nodesDict[uid];
140
140
  if (parentUids.indexOf(parentGrid.uid) <= 0) continue;
141
141
 
142
- const link = { content: grid.getDefaultLinkContent(), parent: parentGrid, child: grid };
142
+ const link = { parent: parentGrid, child: grid };
143
143
 
144
144
  const lkey = grid.id + '_' + parentGrid.id;
145
145
  graph.linksDict[lkey] = link;
@@ -152,19 +152,10 @@ export class GridGRClass extends GridClass {
152
152
  }
153
153
  }
154
154
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
155
- getDefaultLinkContent() {
156
- const grid = this;
157
- return {
158
- applyLink: function (link) {
159
- if (!link.parent || !link.parent.rows) return '';
160
-
161
- if (link.parent.getConnectContent) {
162
- return link.parent.getConnectContent({ child: grid });
163
- }
155
+ applyConnection(link) {
156
+ if (!link.parent || !link.parent.rows) return '';
164
157
 
165
- return link.parent.selectedValue();
166
- }
167
- };
158
+ return link.parent.selectedValue();
168
159
  }
169
160
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
170
161
  getEntity() {
@@ -228,15 +219,12 @@ export class GridGRClass extends GridClass {
228
219
 
229
220
  for (let uid of grid.parents) {
230
221
  let link = grid.graph.linksDict[grid.id + '_' + grid.graph.nodesDict[uid].id];
231
- if (!link.content) continue;
232
222
 
233
- if (link.content.applyLink) {
234
- let filter = link.content.applyLink(link);
235
- if (filter == null || filter === '') continue;
223
+ let filter = grid.applyConnection(link);
224
+ if (filter == null || filter === '') continue;
236
225
 
237
- let fo = { type: 'graphLink', filter: filter };
238
- filters.push(fo);
239
- }
226
+ let fo = { type: 'graphLink', filter: filter };
227
+ filters.push(fo);
240
228
  }
241
229
 
242
230
  return filters;
package/src/Modal.jsx CHANGED
@@ -69,6 +69,7 @@ export class ModalClass extends BaseComponent {
69
69
  wnd.opt.noHeader = props.noHeader;
70
70
  wnd.opt.noFooter = props.noFooter;
71
71
  wnd.opt.noPadding = props.noPadding;
72
+ wnd.opt.noBodyOverflow = props.noBodyOverflow;
72
73
 
73
74
  wnd.opt.margin = props.margin;
74
75
  wnd.opt.padding = props.padding;
@@ -184,7 +185,7 @@ export class ModalClass extends BaseComponent {
184
185
  key={`window_${wnd.id}_body_`}
185
186
  wnd-body={1}
186
187
  className={wnd.opt.bodyClass}
187
- style={{ padding: wnd.opt.noPadding ? '0' : '', overflow: 'auto', height: '100%' }}
188
+ style={{ padding: wnd.opt.noPadding ? '0' : '', overflow: wnd.opt.noBodyOverflow ? 'hidden' : 'auto', height: '100%' }}
188
189
  >
189
190
  {wnd.renderContent(wnd)}
190
191
  </div>
@@ -309,7 +310,7 @@ export class ModalClass extends BaseComponent {
309
310
  const wnd = this;
310
311
 
311
312
  if (wnd.onClose) {
312
- const ev = {};
313
+ const ev = { self: wnd };
313
314
  wnd.onClose(ev);
314
315
  if (ev.cancel) return;
315
316
  }
@@ -8,6 +8,7 @@ import { GridGR } from '../GridGR';
8
8
  import { GridDB } from '../GridDB';
9
9
  import { GridFL } from '../GridFL';
10
10
  import { GridFE } from '../GridFE';
11
+ import { GridCD } from '../GridCD';
11
12
  import { FieldEdit } from '../FieldEdit';
12
13
  //import { GridINU } from '../GridINU';
13
14
  //import { GraphComponent } from '../GraphComponent';
@@ -134,7 +135,7 @@ function DebugApp() {
134
135
  }
135
136
  </div>
136
137
  <Dropdown init={(dd) => { wnd.ddComponent = dd; }} getItems={GetPopupItems}
137
- onItemClick={(e) => { /*console.log('Item clicked: ' + e.itemId); */e.dropdown.clickedItem = e.itemId; wnd.refreshState(); }}
138
+ onItemClick={(e) => { /*console.log('Item clicked: ' + e.itemId); */e.self.clickedItem = e.itemId; wnd.refreshState(); }}
138
139
  >
139
140
  </Dropdown>
140
141
  </>
@@ -192,7 +193,7 @@ function DebugApp() {
192
193
  case 4:
193
194
  return (
194
195
  <>
195
- <Modal uid="m02" isModal={true} renderContent={(wnd) => { return drawDropdownInModal(wnd) }} closeWhenEscape={true}
196
+ <Modal uid="m02" isModal={true} title="Dropdown" renderContent={(wnd) => { return drawDropdownInModal(wnd) }} closeWhenEscape={true}
196
197
  dimensionsByContent={true}
197
198
  pos={{ x: 100, y: 100, w: 300, h: 250 }}></Modal>
198
199
  </>
@@ -200,6 +201,9 @@ function DebugApp() {
200
201
  case 5:
201
202
  return (
202
203
  <>
204
+ <div className="div-on-menu">
205
+ Change the active record in the parent grid to see the child rows.
206
+ </div>
203
207
  <div className="div-on-menu">
204
208
  {drawClearConsole()}
205
209
  </div>
@@ -214,6 +218,9 @@ function DebugApp() {
214
218
  case 6:
215
219
  return (
216
220
  <>
221
+ <div className="div-on-menu">
222
+ Grid with pager, pocket and sortable columns. Hold shift to sort by multiple columns.
223
+ </div>
217
224
  <div className="div-with-grid">
218
225
  <GridDB getRows={GetFamily} buttons={GetButtons()} getColumns={GetFamilyColumns} multi={true}></GridDB>
219
226
  </div>
@@ -222,6 +229,9 @@ function DebugApp() {
222
229
  case 7:
223
230
  return (
224
231
  <>
232
+ <div className="div-on-menu">
233
+ Grid with column filters.
234
+ </div>
225
235
  <div className="div-with-grid">
226
236
  <GridFL getRows={GetFamily} buttons={GetButtons()} getColumns={GetFamilyColumns}></GridFL>
227
237
  </div>
@@ -265,8 +275,22 @@ function DebugApp() {
265
275
  case 9:
266
276
  return (
267
277
  <>
278
+ <div className="div-on-menu">
279
+ Editable grid.
280
+ </div>
281
+ <div className="div-with-grid">
282
+ <GridFE getRows={GetFamily} getColumns={GetFamilyColumns} allowEdit={true}></GridFE>
283
+ </div>
284
+ </>
285
+ );
286
+ case 10:
287
+ return (
288
+ <>
289
+ <div className="div-on-menu">
290
+ Editable grid with card-based record view.
291
+ </div>
268
292
  <div className="div-with-grid">
269
- <GridFE getRows={GetFamily} getColumns={GetFamilyColumns} allowEditGrid={true}></GridFE>
293
+ <GridCD getRows={GetFamily} getColumns={GetFamilyColumns} allowEdit={true}></GridCD>
270
294
  </div>
271
295
  </>
272
296
  );
@@ -297,7 +321,8 @@ function DebugApp() {
297
321
  <option>7. GridFL</option>
298
322
  <option>8. Field Edit</option>
299
323
  <option>9. GridFE</option>
300
- <option>10. TEST</option>
324
+ <option>10. GridCD</option>
325
+ <option>11. TEST</option>
301
326
  </select>
302
327
  <div className="div-on-menu">
303
328
  {getTestApp()}
@@ -28,8 +28,10 @@
28
28
  'DESC': 'убыв',
29
29
  'ERROR GETTING ROWS': 'Ошибка при получении строк',
30
30
  'EXPAND': 'Развернуть',
31
+ 'EXPORT TO CSV': 'Экспорт в CSV',
31
32
  'EXIT': 'Выход',
32
33
  'FIRST': 'К началу',
34
+ 'INSUFFICIENT RIGHTS TO VIEW': 'Недостаточно прав для просмотра',
33
35
  'INVISIBLE COLUMNS': 'Невидимые колонки',
34
36
  'LARGE BUTTONS': 'Большие',
35
37
  'LAST': 'В конец',
@@ -58,6 +60,7 @@
58
60
  'SMALL BUTTONS': 'Маленькие',
59
61
  'SORT': 'Сортировка',
60
62
  'THEME': 'Тема',
63
+ 'THE ROW IS CHANGED. DISCARD CHANGES?': 'Запись изменена. Отказаться от изменений?',
61
64
  'TOTAL PAGES': 'Всего страниц',
62
65
  'TOTAL ROWS': 'Всего строк',
63
66
  'TREE': 'Дерево',
@@ -133,7 +133,7 @@
133
133
  .grid-header-div-default {
134
134
  user-select: none;
135
135
  display: grid;
136
- grid-template-columns: auto 8px;
136
+ grid-template-columns: auto 8px;
137
137
  grid-template-rows: auto auto;
138
138
  grid-auto-flow: row;
139
139
  align-items: center;