react-graph-grid 0.0.7 → 0.1.1

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,12 +6,12 @@ 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
13
13
 
14
- import { GridFE } from '../../node_modules/react-graph-grid/src/GridFE';
14
+ import { GridCD } from '../../node_modules/react-graph-grid/src/GridCD';
15
15
  import TestData from '../../node_modules/react-graph-grid/src/Tests/TestData';
16
16
 
17
17
  ...
@@ -56,88 +56,137 @@ 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
+ <GridCD
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
+ Your App.jsx should look like this
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
+ import DebugApp from '../node_modules/react-graph-grid/src/Tests/DebugApp'
160
+ import '../node_modules/react-graph-grid/src/css/default.css'
129
161
 
162
+ function App() {
163
+ return (
164
+ <>
165
+ <DebugApp></DebugApp>
166
+ </>
167
+ )
168
+ }
130
169
 
131
- For more examples see DebugApp.jsx
170
+ export default App
132
171
 
133
- 0.0.5 version
134
172
 
135
- "Adjust column visibility" option added to GridFE.jsx module
173
+ 0.1.1 version
174
+
175
+ Readme file updated
176
+
177
+ 0.1.0 version
178
+
179
+ Removed getDefaultLinkContent, added applyConnection function
180
+
181
+ 0.0.7 version
182
+
183
+ Fixed GridDB and its dropdown communication
136
184
 
137
185
  0.0.6 version
138
186
 
139
187
  Fixed GridFE.showColumnsSettings() function
140
188
 
141
- 0.0.7 version
189
+ 0.0.5 version
190
+
191
+ "Adjust column visibility" option added to GridFE.jsx module
142
192
 
143
- Fixed GridDB and its dropdown communication
package/package.json CHANGED
@@ -3,7 +3,7 @@
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.7",
6
+ "version": "0.1.1",
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/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
@@ -291,7 +291,7 @@ export class GridClass extends BaseComponent {
291
291
  gridTemplateRows: '1.5em auto',
292
292
  gridAutoFlow: 'row',
293
293
  width: 'calc(100% + 8px)',
294
- justifyContent: 'space-between',
294
+ //justifyContent: 'space-between',
295
295
  }}
296
296
  >
297
297
  {grid.renderHeaderCell(col, context)}
@@ -475,7 +475,7 @@ export class GridClass extends BaseComponent {
475
475
  for (let key in row) {
476
476
  if (grid.colDict[key]) continue;
477
477
 
478
- const col = grid.getColumn(key);
478
+ const col = grid.createColumn(key);
479
479
 
480
480
  grid.colDict[col.name] = col;
481
481
  res.push(col);
@@ -485,7 +485,7 @@ export class GridClass extends BaseComponent {
485
485
  return res;
486
486
  }
487
487
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
488
- getColumn(name) {
488
+ createColumn(name) {
489
489
  return { name: name };
490
490
  }
491
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
@@ -642,6 +642,7 @@ export class GridDBClass extends GridPKClass {
642
642
  whiteSpace: 'nowrap',
643
643
  overflowX: 'hidden',
644
644
  width: sortDir ? 'calc(100% - 10px)' : '',
645
+ textAlign: 'left',
645
646
  }}
646
647
  onClick={(e) => { if (!grid._waitingRows) grid.changeColumnSortOrder(col, e); }}
647
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
@@ -413,7 +413,7 @@ export class GridFLClass extends GridDBClass {
413
413
  }
414
414
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
415
415
  getHeaderGridTemplateColumns(col) {
416
- return col.sortInd == null ? 'auto 12px' : 'auto 22px';
416
+ return col.sortInd == null ? 'auto 16px' : 'auto 22px';
417
417
  }
418
418
  // -------------------------------------------------------------------------------------------------------------------------------------------------------------
419
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;
@@ -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';
@@ -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;