drf-react-by-schema 0.1.0 → 0.2.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.
@@ -1,48 +1,21 @@
1
- import React, { useEffect, useState, useReducer, useRef } from 'react';
2
- import moment from 'moment';
1
+ import React, { useEffect, useState, useRef, forwardRef } from 'react';
2
+ // import moment from 'moment';
3
3
  // import Mask from 'string-mask';
4
4
  import {
5
- GridApi,
6
5
  DataGrid,
7
6
  GridEditInputCell,
8
7
  GridRowModes,
9
- GridToolbarContainer,
10
- GridToolbarColumnsButton,
11
- GridToolbarFilterButton,
12
- GridToolbarDensitySelector,
13
- GridToolbarExport,
14
- GridToolbarQuickFilter,
15
- GridFooter,
16
- GridFooterContainer,
17
8
  GridActionsCellItem,
18
9
  gridVisibleSortedRowIdsSelector,
19
- useGridApiContext,
20
- gridStringOrNumberComparator,
21
- getGridNumericOperators,
22
- getGridDateOperators,
23
- GridFilterOperator
10
+ gridStringOrNumberComparator
24
11
  } from '@mui/x-data-grid';
25
12
  import * as Yup from 'yup';
26
- import { Link } from 'react-router-dom';
27
13
  import Box from '@mui/material/Box';
28
14
  import CircularProgress from '@mui/material/CircularProgress';
29
- import TextField from '@mui/material/TextField';
30
- import Button from '@mui/material/Button';
31
- import Menu from '@mui/material/Menu';
32
- import MenuItem from '@mui/material/MenuItem';
33
- import SyncIcon from '@mui/icons-material/Sync';
34
- import ExpandIcon from '@mui/icons-material/Expand';
35
- import AddIcon from '@mui/icons-material/Add';
36
15
  import EditIcon from '@mui/icons-material/Edit';
37
16
  import ClearIcon from '@mui/icons-material/Clear';
38
17
  import CheckIcon from '@mui/icons-material/Check';
39
18
  import UndoIcon from '@mui/icons-material/Undo';
40
- import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
41
- import { NumericFormat, PatternFormat } from 'react-number-format';
42
- import Dialog from '@mui/material/Dialog';
43
- import DialogActions from '@mui/material/DialogActions';
44
- import DialogContent from '@mui/material/DialogContent';
45
- import DialogTitle from '@mui/material/DialogTitle';
46
19
  import Snackbar from '@mui/material/Snackbar';
47
20
  import Alert from '@mui/material/Alert';
48
21
 
@@ -50,700 +23,59 @@ import { Layout } from '../styles';
50
23
  import { getAutoComplete } from '../api';
51
24
  import {
52
25
  Item,
53
- Schema,
26
+ SchemaType,
54
27
  Choice,
55
28
  Id,
56
29
  GridEnrichedBySchemaColDef,
57
- reducer,
58
- getTmpId,
59
30
  isTmpId,
60
31
  emptyByType,
61
32
  buildGenericYupValidationSchema
62
33
  } from '../utils';
63
34
  import { SxProps } from '@mui/material';
64
35
  import { ObjectShape } from 'yup/lib/object';
36
+ import { DRFReactBySchemaContext, DRFReactBySchemaContextType } from '../context/DRFReactBySchemaProvider';
37
+ import { quantityOnlyOperators } from './DataGridBySchemaEditable/utils';
38
+ import { CustomToolbar } from './DataGridBySchemaEditable/CustomToolbar';
39
+ import { SelectEditInputCell } from './DataGridBySchemaEditable/SelectEditInputCell';
40
+ import { GridDecimalInput } from './DataGridBySchemaEditable/GridDecimalInput';
41
+ import { GridPatternInput } from './DataGridBySchemaEditable/GridPatternInput';
42
+ import { FooterToolbar } from './DataGridBySchemaEditable/FooterToolbar';
43
+ import { ConfirmDialog } from './DataGridBySchemaEditable/ConfirmDialog';
65
44
 
66
- const filter = createFilterOptions();
67
45
  const stringMask = require('string-mask');
68
-
69
- // returns width of the biggest row inside a column
70
- function maxOfCol (colIndex: number) {
71
- const invisibleContainer = document.createElement('div');
72
-
73
- invisibleContainer.style.visibility = 'hidden';
74
- invisibleContainer.style.zIndex = '-9999999999';
75
- invisibleContainer.style.position = 'absolute';
76
- invisibleContainer.style.fontSize = '14px';
77
- invisibleContainer.style.top = '0';
78
- invisibleContainer.style.left = '0';
79
- document.body.append(invisibleContainer);
80
- const widths: any[] = [];
81
- document.querySelectorAll(
82
- `[aria-colindex='${colIndex}']`
83
- ).forEach(cell => {
84
- const invisibleCell = document.createElement('div');
85
- invisibleCell.innerHTML = cell.innerHTML;
86
- invisibleCell.style.width = 'max-content';
87
- invisibleCell.style.maxWidth = 'none';
88
- invisibleCell.style.minWidth = 'none';
89
- invisibleContainer.append(invisibleCell);
90
- widths.push(Math.ceil(invisibleCell.clientWidth));
91
- });
92
- let max = Math.max(...widths);
93
- if (max !== 0 && max < 50) {
94
- max = 50;
95
- }
96
- invisibleContainer.remove();
97
- return max;
98
- }
99
-
100
- type ResizeType = 'condense' | 'maxContent' | 'fitScreen';
101
-
102
- function resizeColumns (
103
- columns: GridEnrichedBySchemaColDef[],
104
- resizeType: ResizeType,
105
- apiRef: React.MutableRefObject<GridApi>
106
- ) {
107
- const cols = [...columns];
108
- cols.forEach((col, index: number) => {
109
- if (resizeType === 'fitScreen') {
110
- delete col.width;
111
- col.minWidth = 80;
112
- if (col.isIndexField) {
113
- col.flex = 1;
114
- }
115
- } else if (resizeType === 'maxContent') {
116
- const maxColWidth = maxOfCol(index);
117
- delete col.flex;
118
- delete col.minWidth;
119
- col.width = maxColWidth + 22;
120
- } else {
121
- col.width = 0;
122
- }
123
- });
124
- return cols;
125
- }
126
-
127
- type CustomToolbarProps = {
128
- preparedColumns: GridEnrichedBySchemaColDef[],
129
- setPreparedColumns: (p:null | GridEnrichedBySchemaColDef[]) => void
130
- };
131
- const CustomToolbar = ({
132
- preparedColumns,
133
- setPreparedColumns
134
- }:CustomToolbarProps) => {
135
- const apiRef = useGridApiContext();
136
- const [resizeMenuAnchorEl, setResizeMenuAnchorEl] = useState<any>(null);
137
- const isResizeMenuOpen = Boolean(resizeMenuAnchorEl);
138
- const openResizeMenu = (event:React.MouseEvent) => {
139
- setResizeMenuAnchorEl(event.currentTarget);
140
- };
141
- const closeResizeMenu = () => {
142
- setResizeMenuAnchorEl(null);
143
- };
144
- return (
145
- <GridToolbarContainer sx={{ justifyContent: 'space-between' }}>
146
- <div style={{ display: 'flex', flexWrap: 'wrap' }}>
147
- <GridToolbarColumnsButton sx={{ ml: '10px', fontSize: '13px' }} />
148
- <GridToolbarFilterButton sx={{ ml: '10px', fontSize: '13px' }} />
149
- <GridToolbarDensitySelector sx={{ ml: '10px', fontSize: '13px' }} />
150
- <Button onClick={openResizeMenu} sx={{ ml: '0px', fontSize: '13px' }}>
151
- <ExpandIcon sx={{ transform: 'rotate(90deg)', mr: '6px' }} />
152
- Ajustar
153
- </Button>
154
- <Menu
155
- anchorEl={resizeMenuAnchorEl}
156
- open={isResizeMenuOpen}
157
- onClose={closeResizeMenu}
158
- >
159
- <MenuItem
160
- onClick={() => {
161
- closeResizeMenu();
162
- setPreparedColumns(resizeColumns(
163
- preparedColumns,
164
- 'fitScreen',
165
- apiRef
166
- ));
167
- }}
168
- >
169
- Ajustar à tela
170
- </MenuItem>
171
- <MenuItem
172
- onClick={() => {
173
- closeResizeMenu();
174
- setPreparedColumns(resizeColumns(
175
- preparedColumns,
176
- 'maxContent',
177
- apiRef
178
- ));
179
- }}
180
- >
181
- Ajustar ao conteúdo
182
- </MenuItem>
183
- </Menu>
184
- <GridToolbarExport sx={{ ml: '0px', fontSize: '13px' }} />
185
- </div>
186
- <div>
187
- <GridToolbarQuickFilter />
188
- </div>
189
- </GridToolbarContainer>
190
- );
191
- };
192
-
193
- type SelectEditInputCellProps = {
194
- field: string,
195
- id: number | string,
196
- value?: any,
197
- column: GridEnrichedBySchemaColDef,
198
- type: string,
199
- optionsAC: { current: Record<string, Item[]> | null },
200
- isIndexField: boolean,
201
- multiple?: boolean,
202
- sx?: SxProps
203
- };
204
- function SelectEditInputCell ({
205
- id,
206
- value,
207
- field,
208
- column,
209
- type,
210
- optionsAC,
211
- isIndexField,
212
- multiple = false,
213
- sx = {}
214
- }: SelectEditInputCellProps) {
215
- // TODO: allow edit option label, as in formautocomplete!
216
- const apiRef = useGridApiContext();
217
-
218
- const handleChange = async (newValue:any) => {
219
- await apiRef.current.setEditCellValue({ id, field, value: newValue });
220
- apiRef.current.stopCellEditMode({ id, field });
221
- };
222
-
223
- const labelKey = (['field', 'nested object'].includes(type) || isIndexField)
224
- ? 'label'
225
- : 'display_name';
226
- const valueKey = (['field', 'nested object'].includes(type) || isIndexField)
227
- ? 'id'
228
- : 'value';
229
-
230
- let creatableProps = {};
231
- if (column.creatable || isIndexField) {
232
- creatableProps = {
233
- freesolo: 'true',
234
- filterOptions: (options:Record<string, any>[], params:any) => {
235
- const filtered = filter(options, params);
236
- const inputValue = (params.inputValue)
237
- ? params.inputValue
238
- : '';
239
- const inputValueLower = inputValue.trim().toLowerCase();
240
- // Suggest the creation of a new value
241
- const isExisting = options.some(option => inputValueLower === option[labelKey].trim().toLowerCase());
242
- if (inputValue !== '' && !isExisting) {
243
- filtered.push({
244
- inputValue,
245
- [labelKey]: `Criar "${inputValue}"`
246
- });
247
- }
248
- return filtered;
249
- },
250
- handleHomeEndKeys: true,
251
- getOptionLabel: (option:string | Record<string, any>) => {
252
- // Value selected with enter, right from the input
253
- if (typeof option === 'string') {
254
- return option;
255
- }
256
- // Criar "xxx" option created dynamically
257
- if (option.inputValue) {
258
- return option.inputValue;
259
- }
260
- // Regular option
261
- return option[labelKey];
262
- },
263
- renderOption: (props:any, option:Record<string, any>) => {
264
- return (<li key={option[valueKey]} {...props}>{option[labelKey]}</li>);
265
- }
266
- };
267
- };
268
-
269
- return (
270
- <Autocomplete
271
- key={field}
272
- id={field}
273
- value={value}
274
- options={optionsAC.current && optionsAC.current[field] ? optionsAC.current[field] : []}
275
- selectOnFocus
276
- autoHighlight
277
- multiple={multiple}
278
- isOptionEqualToValue={(option, value) => {
279
- return (option[labelKey] === value[labelKey]);
280
- }}
281
- getOptionLabel={(option) => {
282
- return option[labelKey];
283
- }}
284
- onChange={(e, value) => {
285
- if (!column.creatable && !isIndexField) {
286
- handleChange(value);
287
- return;
288
- }
289
- let newValue = value;
290
- if (typeof newValue === 'string') {
291
- const tmpId = getTmpId();
292
- newValue = {
293
- [valueKey]: tmpId,
294
- [labelKey]: newValue
295
- };
296
- }
297
- if (newValue && newValue.inputValue) {
298
- const tmpId = getTmpId();
299
- newValue = {
300
- [valueKey]: tmpId,
301
- [labelKey]: newValue.inputValue
302
- };
303
- }
304
- handleChange(newValue);
305
- }}
306
- fullWidth
307
- renderInput={params => (
308
- <TextField
309
- {...params}
310
- sx={sx}
311
- />
312
- )}
313
-
314
- {...creatableProps}
315
- />
316
- );
317
- }
318
-
319
- type GridDecimalInputProps = {
320
- field: string,
321
- id: number | string,
322
- value?: any,
323
- column: object
324
- };
325
- function GridDecimalInput ({
326
- id,
327
- value,
328
- field
329
- }:GridDecimalInputProps) {
330
- const apiRef = useGridApiContext();
331
- const decimalScale = 2;
332
- const disableCurrency = true;
333
- const handleChange = async (newValue:any) => {
334
- await apiRef.current.setEditCellValue({ id, field, value: newValue });
335
- apiRef.current.stopCellEditMode({ id, field });
336
- };
337
- return (
338
- <NumericFormat
339
- key={field}
340
- id={field}
341
- onValueChange={(values, sourceInfo) => {
342
- handleChange(values.value);
343
- }}
344
- value={value}
345
- thousandSeparator='.'
346
- decimalSeparator=','
347
- decimalScale={decimalScale}
348
- fixedDecimalScale={true}
349
- valueIsNumericString
350
- prefix={disableCurrency ? '' : 'R$ '}
351
- customInput={TextField}
352
- />
353
- );
354
- }
355
-
356
- type GridPatternInputProps = {
357
- field: string,
358
- id: number | string,
359
- value?: any,
360
- patternFormat?: string
46
+ const moment = require('moment');
47
+
48
+ interface DataGridBySchemaEditableProps {
49
+ schema: SchemaType;
50
+ data: Item[];
51
+ columns: GridEnrichedBySchemaColDef[];
52
+ model: string;
53
+ fieldKey?: string;
54
+ labelKey?: string;
55
+ index?: number;
56
+ name?: string;
57
+ indexField?: string;
58
+ addExistingModel?: string;
59
+ indexFieldMinWidth?: number;
60
+ indexFieldBasePath?: string;
61
+ stateToLink?: object;
62
+ minWidth?: number;
63
+ onEditRelatedModelSave?: (p: any) => Id | { data: Item } | { errors: Item };
64
+ onDeleteRelatedModel?: (p: any) => any;
65
+ customColumnOperations?: (p: any) => GridEnrichedBySchemaColDef;
66
+ customLinkDestination?: (p: any) => string;
67
+ LinkComponent?: any;
68
+ onProcessRow?: (p: any) => void;
69
+ onDataChange?: (p: any) => void;
70
+ isEditable?: boolean;
71
+ sx?: SxProps;
72
+ isAutoHeight?: boolean;
73
+ defaultValues?: Item;
74
+ hideFooterPagination?: boolean;
75
+ setVisibleRows?: (p: any) => void;
361
76
  };
362
- function GridPatternInput ({
363
- id,
364
- value,
365
- field,
366
- patternFormat = 'cpf'
367
- }:GridPatternInputProps) {
368
- const apiRef = useGridApiContext();
369
- const handleChange = async (newValue:any) => {
370
- await apiRef.current.setEditCellValue({ id, field, value: newValue });
371
- apiRef.current.stopCellEditMode({ id, field });
372
- };
373
- return (
374
- <PatternFormat
375
- key={field}
376
- id={field}
377
- onValueChange={(values, sourceInfo) => {
378
- handleChange(values.value);
379
- }}
380
- value={value}
381
- valueIsNumericString
382
- format={patternFormat}
383
- mask="_"
384
- customInput={TextField}
385
- />
386
- );
387
- }
388
77
 
389
- type FooterToolbarProps = {
390
- name: string,
391
- setRowModesModel: (p:any) => any,
392
- dataGrid: { data:Record<string, any>[] },
393
- setDataGrid: (p:any) => any,
394
- emptyItem: { current:Record<string, any> },
395
- indexField: string,
396
- isEditable: boolean
397
- };
398
- function FooterToolbar ({
399
- name,
400
- setRowModesModel,
401
- dataGrid,
402
- setDataGrid,
403
- emptyItem,
404
- indexField,
405
- isEditable
406
- }:FooterToolbarProps) {
407
- const handleClick = () => {
408
- const id = getTmpId();
409
- emptyItem.current.id = id;
410
- const newData = [
411
- { ...emptyItem.current },
412
- ...dataGrid.data
413
- ];
414
- setDataGrid({
415
- data: newData
416
- });
417
- setRowModesModel((oldModel:any) => ({
418
- ...oldModel,
419
- [id]: { mode: GridRowModes.Edit, fieldToFocus: indexField }
420
- }));
421
- // Ugly hack to scroll to top, since scroll to cell is only available in Pro
422
- const el = document.querySelector(`.dataGrid_${name} .MuiDataGrid-virtualScroller`);
423
- // console.log(el, name);
424
- if (el) {
425
- el.scrollTop = 0;
426
- setTimeout(() => {
427
- el.scrollTop = 0;
428
- }, 10);
429
- }
430
- };
431
-
432
- return (
433
- <GridFooterContainer>
434
- {isEditable &&
435
- <Button
436
- color="primary"
437
- startIcon={<AddIcon />}
438
- onClick={handleClick}
439
- sx={{ ml: 2 }}
440
- >
441
- Adicionar
442
- </Button>
443
- }
444
- <GridFooter
445
- sx={
446
- (isEditable)
447
- ? { border: 'none' }
448
- : { width: '100%' }
449
- }
450
- />
451
- </GridFooterContainer>
452
- );
453
- }
454
-
455
-
456
- // FILTROS "ENTRE":
457
-
458
- const SUBMIT_FILTER_STROKE_TIME = 500;
459
- type InputIntervalProps = {
460
- applyValue: (p: any) => void,
461
- focusElementRef?: null | ((p: any) => void) | {
462
- current: any
463
- },
464
- item: {
465
- columnField: string,
466
- id: number | string,
467
- operatorValue: string,
468
- value: any
469
- },
470
- type: string
471
- };
472
- function InputInterval ({
473
- item,
474
- applyValue,
475
- focusElementRef = null,
476
- type
477
- }:InputIntervalProps) {
478
- const filterTimeout = useRef<any>();
479
- const [filterValueState, setFilterValueState] = React.useState(item.value ?? '');
480
-
481
- const [applying, setIsApplying] = React.useState(false);
482
-
483
- React.useEffect(() => {
484
- return () => {
485
- clearTimeout(filterTimeout.current);
486
- };
487
- }, []);
488
-
489
- React.useEffect(() => {
490
- const itemValue = item.value ?? [undefined, undefined];
491
- setFilterValueState(itemValue);
492
- }, [item.value]);
493
-
494
- const updateFilterValue = (lowerBound:any, upperBound:any) => {
495
- clearTimeout(filterTimeout.current);
496
- setFilterValueState([lowerBound, upperBound]);
497
-
498
- setIsApplying(true);
499
- filterTimeout.current = setTimeout(() => {
500
- setIsApplying(false);
501
- applyValue({ ...item, value: [lowerBound, upperBound] });
502
- }, SUBMIT_FILTER_STROKE_TIME);
503
- };
504
-
505
- const handleUpperFilterChange = (event:React.ChangeEvent<HTMLInputElement> | { target: { value: string }}) => {
506
- const newUpperBound = event.target?.value;
507
- updateFilterValue(filterValueState[0], newUpperBound);
508
- };
509
- const handleLowerFilterChange = (event:React.ChangeEvent<HTMLInputElement> | { target: { value: string }}) => {
510
- const newLowerBound = event.target?.value;
511
- updateFilterValue(newLowerBound, filterValueState[1]);
512
- };
513
-
514
- return (
515
- <Box
516
- sx={{
517
- display: 'inline-flex',
518
- flexDirection: 'row',
519
- alignItems: 'end',
520
- height: 48,
521
- pl: '20px'
522
- }}
523
- >
524
- {type === 'number' &&
525
- <>
526
- <TextField
527
- name="lower-bound-input"
528
- placeholder="De"
529
- label="De"
530
- variant="standard"
531
- value={Number(filterValueState[0])}
532
- onChange={handleLowerFilterChange}
533
- type="number"
534
- inputRef={focusElementRef}
535
- sx={{ mr: 2, minWidth: 130 }}
536
- />
537
- <TextField
538
- name="upper-bound-input"
539
- placeholder="Até"
540
- label="Até"
541
- variant="standard"
542
- value={Number(filterValueState[1])}
543
- onChange={handleUpperFilterChange}
544
- type="number"
545
- sx={{ minWidth: 130 }}
546
- InputProps={applying ? { endAdornment: <SyncIcon /> } : {}}
547
- />
548
- </>
549
- }
550
- {type === 'float' &&
551
- <>
552
- <NumericFormat
553
- name="lower-bound-input"
554
- placeholder="De"
555
- label="De"
556
- variant="standard"
557
- value={Number(filterValueState[0])}
558
- onValueChange={(values, sourceInfo) => {
559
- handleLowerFilterChange({ target: { value: values.value } });
560
- }}
561
- thousandSeparator='.'
562
- decimalSeparator=','
563
- decimalScale={2}
564
- fixedDecimalScale={true}
565
- valueIsNumericString
566
- inputRef={focusElementRef}
567
- sx={{ mr: 2, minWidth: 130 }}
568
- customInput={TextField}
569
- />
570
- <NumericFormat
571
- name="upper-bound-input"
572
- placeholder="Até"
573
- label="Até"
574
- variant="standard"
575
- value={Number(filterValueState[1])}
576
- onValueChange={(values, sourceInfo) => {
577
- handleUpperFilterChange({ target: { value: values.value } });
578
- }}
579
- thousandSeparator='.'
580
- decimalSeparator=','
581
- decimalScale={2}
582
- fixedDecimalScale={true}
583
- valueIsNumericString
584
- InputProps={applying ? { endAdornment: <SyncIcon /> } : {}}
585
- sx={{ minWidth: 130 }}
586
- customInput={TextField}
587
- />
588
- </>
589
- }
590
- {type === 'date' &&
591
- <>
592
- <TextField
593
- name="lower-bound-input"
594
- label="De"
595
- variant="standard"
596
- value={filterValueState[0] || ''}
597
- onChange={handleLowerFilterChange}
598
- type="date"
599
- inputRef={focusElementRef}
600
- InputLabelProps={{ shrink: true }}
601
- sx={{ mr: 2, minWidth: 130 }}
602
- />
603
- <TextField
604
- name="upper-bound-input"
605
- label="Até"
606
- variant="standard"
607
- value={filterValueState[1] || ''}
608
- onChange={handleUpperFilterChange}
609
- type="date"
610
- InputProps={applying ? { endAdornment: <SyncIcon /> } : {}}
611
- InputLabelProps={{ shrink: true }}
612
- sx={{ minWidth: 130 }}
613
- />
614
- </>
615
- }
616
- </Box>
617
- );
618
- }
619
-
620
- function InputNumberInterval (props: any) {
621
- return (
622
- <InputInterval
623
- {...props}
624
- type="number"
625
- />
626
- );
627
- }
628
- function InputDateInterval (props: any) {
629
- return (
630
- <InputInterval
631
- {...props}
632
- type="date"
633
- />
634
- );
635
- }
636
- function InputFloatInterval (props: any) {
637
- return (
638
- <InputInterval
639
- {...props}
640
- type="float"
641
- />
642
- );
643
- }
644
-
645
- const quantityOnlyOperators = ({ type }:{ type: string }) => {
646
- const builtInFilters = (type === 'date')
647
- ? getGridDateOperators()
648
- : getGridNumericOperators();
649
- let InputComponent = InputNumberInterval;
650
- if (type === 'date') {
651
- InputComponent = InputDateInterval;
652
- }
653
- if (type === 'float') {
654
- InputComponent = InputFloatInterval;
655
- }
656
- return [
657
- ...builtInFilters,
658
- {
659
- label: 'entre',
660
- value: 'entre',
661
- getApplyFilterFn: (filterItem: any) => {
662
- if (!Array.isArray(filterItem.value) || filterItem.value.length !== 2) {
663
- return null;
664
- }
665
- if (filterItem.value[0] === null || filterItem.value[1] === null) {
666
- return null;
667
- }
668
-
669
- return ({ value }: { value: any }) => {
670
- return (
671
- value !== null &&
672
- filterItem.value[0] <= value &&
673
- value <= filterItem.value[1]
674
- );
675
- };
676
- },
677
- InputComponent
678
- }
679
- ] as GridFilterOperator[];
680
- };
681
-
682
- type FConfirmDialogProps = {
683
- open: boolean,
684
- onClose: (p: any) => void,
685
- onConfirm: (p: any) => void
686
- };
687
- const FConfirmDialog = ({
688
- open,
689
- onClose,
690
- onConfirm
691
- }: FConfirmDialogProps) => {
692
- return (
693
- <Dialog open={open} onClose={onClose}>
694
- <DialogTitle>
695
- Confirmar
696
- </DialogTitle>
697
- <DialogContent>
698
- Tem certeza de que você quer remover este item?
699
- </DialogContent>
700
- <DialogActions>
701
- <Button
702
- onClick={onClose}
703
- >
704
- Cancelar
705
- </Button>
706
- <Button
707
- onClick={onConfirm}
708
- >
709
- Remover
710
- </Button>
711
- </DialogActions>
712
- </Dialog>
713
- );
714
- }
715
- const ConfirmDialog = React.memo(FConfirmDialog);
716
-
717
- type DataGridBySchemaEditableProps = {
718
- schema: Schema,
719
- data: Item[],
720
- columns: GridEnrichedBySchemaColDef[],
721
- model: string,
722
- fieldKey?: string,
723
- labelKey?: string,
724
- index?: number,
725
- name?: string,
726
- updateUrl: string,
727
- indexField?: string,
728
- addExistingModel?: string,
729
- indexFieldMinWidth?: number,
730
- indexFieldBasePath?: string,
731
- stateToLink?: object,
732
- minWidth?: number,
733
- onEditRelatedModelSave?: (p: any) => Id | { data: Item } | { errors: Item },
734
- onDeleteRelatedModel?: (p: any) => any,
735
- customColumnOperations?: (p: any) => GridEnrichedBySchemaColDef,
736
- customLinkDestination?: (p: any) => string,
737
- onProcessRow?: (p: any) => void,
738
- onDataChange?: (p: any) => void,
739
- isEditable?: boolean,
740
- sx?: SxProps,
741
- isAutoHeight?: boolean,
742
- defaultValues?: Item,
743
- hideFooterPagination?: boolean,
744
- setVisibleRows?: (p: any) => void
745
- };
746
- const DataGridBySchemaEditable = React.forwardRef((
78
+ const DataGridBySchemaEditable = forwardRef((
747
79
  {
748
80
  schema,
749
81
  data,
@@ -753,7 +85,6 @@ const DataGridBySchemaEditable = React.forwardRef((
753
85
  labelKey = 'nome',
754
86
  index,
755
87
  name = Math.floor(Math.random() * 1000000).toString(),
756
- updateUrl,
757
88
  indexField = 'nome',
758
89
  addExistingModel,
759
90
  indexFieldMinWidth = 350,
@@ -764,6 +95,7 @@ const DataGridBySchemaEditable = React.forwardRef((
764
95
  onDeleteRelatedModel,
765
96
  customColumnOperations,
766
97
  customLinkDestination,
98
+ LinkComponent,
767
99
  onProcessRow,
768
100
  onDataChange,
769
101
  isEditable = false,
@@ -776,13 +108,16 @@ const DataGridBySchemaEditable = React.forwardRef((
776
108
  }: DataGridBySchemaEditableProps,
777
109
  ref
778
110
  ) => {
779
- const initialSnackBar = {
111
+ const { serverEndPoint } = DRFReactBySchemaContext
112
+ ? React.useContext(DRFReactBySchemaContext) as DRFReactBySchemaContextType
113
+ : { serverEndPoint: null };
114
+ const initialSnackBar:Record<string, any> = {
780
115
  open: false,
781
116
  msg: '',
782
117
  severity: 'info'
783
118
  };
784
- const [snackBar, setSnackBar] = useReducer(reducer, initialSnackBar);
785
- const [dataGrid, setDataGrid] = useState<{ data: Item[] }>({ data: [] });
119
+ const [snackBar, setSnackBar] = useState(initialSnackBar);
120
+ const [dataGrid, setDataGrid] = useState<{ data:Item[] }>({ data: [] });
786
121
  const [preparedColumns, setPreparedColumns] = useState<null | GridEnrichedBySchemaColDef[]>(null);
787
122
  const [dataGridLoading, setDataGridLoading] = useState(false);
788
123
  const [rowModesModel, setRowModesModel] = useState<Record<string, any>>({});
@@ -800,8 +135,12 @@ const DataGridBySchemaEditable = React.forwardRef((
800
135
  continue;
801
136
  }
802
137
  if (['field', 'nested object'].includes(schema[col.field].type) && !(col.field in optionsAC.current)) {
803
- const options = await getAutoComplete(col.field);
804
- optionsAC.current[col.field] = options;
138
+ const options = await getAutoComplete({ model: col.field, serverEndPoint });
139
+ if (options) {
140
+ optionsAC.current[col.field] = options;
141
+ continue;
142
+ }
143
+ console.log(`Couldn't load autocomplete options from '${col.field}'`);
805
144
  continue;
806
145
  }
807
146
  if (schema[col.field].type === 'choice' && !(col.field in optionsAC.current)) {
@@ -809,8 +148,14 @@ const DataGridBySchemaEditable = React.forwardRef((
809
148
  continue;
810
149
  }
811
150
  if (col.field === indexField && addExistingModel && !optionsAC.current[col.field]) {
812
- const options = await getAutoComplete(addExistingModel);
813
- optionsAC.current[col.field] = options;
151
+ const options = await getAutoComplete({ model: addExistingModel, serverEndPoint });
152
+ if (options) {
153
+ optionsAC.current[col.field] = options;
154
+ continue;
155
+ }
156
+ if (!(col.field in optionsAC.current)) {
157
+ delete optionsAC.current[col.field];
158
+ }
814
159
  }
815
160
  }
816
161
  };
@@ -999,15 +344,16 @@ const DataGridBySchemaEditable = React.forwardRef((
999
344
  if (col.field === indexField) {
1000
345
  column.flex = 1;
1001
346
  column.renderCell = params => {
1002
- if (customLinkDestination) {
347
+ if (LinkComponent && customLinkDestination) {
1003
348
  return (
1004
- <Link to={`${customLinkDestination(params)}`} state={stateToLink}>
349
+ <LinkComponent to={`${customLinkDestination(params)}`} state={stateToLink}>
1005
350
  {params.formattedValue}
1006
- </Link>
351
+ </LinkComponent>
1007
352
  );
1008
353
  }
1009
354
 
1010
355
  if (
356
+ !LinkComponent ||
1011
357
  !indexFieldBasePath ||
1012
358
  (
1013
359
  ['field', 'nested object'].includes(schema[col.field].type) &&
@@ -1022,9 +368,9 @@ const DataGridBySchemaEditable = React.forwardRef((
1022
368
  : params.row.id.toString();
1023
369
 
1024
370
  return (
1025
- <Link to={`${indexFieldBasePath}${targetId}`} state={stateToLink}>
371
+ <LinkComponent to={`${indexFieldBasePath}${targetId}`} state={stateToLink}>
1026
372
  {params.formattedValue}
1027
- </Link>
373
+ </LinkComponent>
1028
374
  );
1029
375
  };
1030
376
  if (
@@ -1385,7 +731,7 @@ const DataGridBySchemaEditable = React.forwardRef((
1385
731
  <Snackbar
1386
732
  open={snackBar.open}
1387
733
  autoHideDuration={5000}
1388
- onClose={() => { setSnackBar({ open: false }); }}
734
+ onClose={() => { setSnackBar({ ...initialSnackBar, open: false }); }}
1389
735
  anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
1390
736
  >
1391
737
  <Alert severity={snackBar.severity}>
@@ -1398,4 +744,4 @@ const DataGridBySchemaEditable = React.forwardRef((
1398
744
 
1399
745
  DataGridBySchemaEditable.displayName = 'DataGridBySchemaEditable';
1400
746
 
1401
- export default DataGridBySchemaEditable;
747
+ export default DataGridBySchemaEditable;