@archbase/components 3.0.25 → 4.0.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.
Files changed (168) hide show
  1. package/dist/buttons/ArchbaseSpeedDial.d.ts +59 -0
  2. package/dist/buttons/index.d.ts +2 -0
  3. package/dist/charts/ArchbaseArcGauge.d.ts +55 -0
  4. package/dist/charts/ArchbaseGantt.d.ts +70 -0
  5. package/dist/charts/ArchbaseLinearGauge.d.ts +55 -0
  6. package/dist/charts/ArchbaseResourceTimeline.d.ts +61 -0
  7. package/dist/charts/ArchbaseSparkline.d.ts +45 -0
  8. package/dist/charts/index.d.ts +10 -0
  9. package/dist/containers/ArchbaseExpansionPanel.d.ts +60 -0
  10. package/dist/containers/ArchbaseFloatingPanel.d.ts +91 -0
  11. package/dist/containers/index.d.ts +4 -0
  12. package/dist/datagrid/export/ArchbaseDataGridExcelExport.d.ts +67 -0
  13. package/dist/datagrid/export/index.d.ts +2 -0
  14. package/dist/datagrid/index.d.ts +1 -0
  15. package/dist/display/ArchbaseAnimationWrapper.d.ts +73 -0
  16. package/dist/display/ArchbaseBarcodeGenerator.d.ts +50 -0
  17. package/dist/display/ArchbaseCarousel.d.ts +100 -0
  18. package/dist/display/ArchbaseCodeViewer.d.ts +63 -0
  19. package/dist/display/ArchbaseDiffViewer.d.ts +41 -0
  20. package/dist/display/ArchbaseJsonTree.d.ts +34 -0
  21. package/dist/display/ArchbaseLightbox.d.ts +89 -0
  22. package/dist/display/ArchbaseMarquee.d.ts +46 -0
  23. package/dist/display/ArchbasePhotoAlbum.d.ts +58 -0
  24. package/dist/display/ArchbaseQRCode.d.ts +44 -0
  25. package/dist/display/ArchbaseTooltipRich.d.ts +82 -0
  26. package/dist/display/ArchbaseVirtualList.d.ts +116 -0
  27. package/dist/display/index.d.ts +24 -0
  28. package/dist/editors/ArchbaseAIPromptInput.d.ts +69 -0
  29. package/dist/editors/ArchbaseAsyncMultiSelect.d.ts +1 -1
  30. package/dist/editors/ArchbaseAsyncSelect.d.ts +1 -1
  31. package/dist/editors/ArchbaseBarcodeScanner.d.ts +63 -0
  32. package/dist/editors/ArchbaseCepInput.d.ts +26 -0
  33. package/dist/editors/ArchbaseColorGradientPicker.d.ts +80 -0
  34. package/dist/editors/ArchbaseCpfCnpjInput.d.ts +21 -0
  35. package/dist/editors/ArchbaseCurrencyInput.d.ts +25 -0
  36. package/dist/editors/ArchbaseDualListbox.d.ts +61 -0
  37. package/dist/editors/ArchbaseInPlaceEditor.d.ts +67 -0
  38. package/dist/editors/ArchbaseLightGrid.d.ts +56 -0
  39. package/dist/editors/ArchbaseMentionInput.d.ts +75 -0
  40. package/dist/editors/ArchbaseMultiEmail.d.ts +53 -0
  41. package/dist/editors/ArchbaseMultiViewCalendar.d.ts +47 -0
  42. package/dist/editors/ArchbaseNumberStepper.d.ts +66 -0
  43. package/dist/editors/ArchbaseOTPInput.d.ts +27 -0
  44. package/dist/editors/ArchbasePhoneInput.d.ts +52 -0
  45. package/dist/editors/ArchbaseRangeSlider.d.ts +22 -0
  46. package/dist/editors/ArchbaseSignaturePad.d.ts +60 -0
  47. package/dist/editors/ArchbaseSpeechToTextInput.d.ts +106 -0
  48. package/dist/editors/ArchbaseSpreadsheet.d.ts +56 -0
  49. package/dist/editors/ArchbaseTagInput.d.ts +55 -0
  50. package/dist/editors/index.d.ts +40 -0
  51. package/dist/feedback/ArchbaseChunkProgressBar.d.ts +35 -0
  52. package/dist/feedback/ArchbaseRipple.d.ts +43 -0
  53. package/dist/feedback/ArchbaseSkeleton.d.ts +115 -0
  54. package/dist/feedback/index.d.ts +6 -0
  55. package/dist/filters/ArchbaseColumnSelector.d.ts +38 -0
  56. package/dist/filters/index.d.ts +2 -0
  57. package/dist/forms/ArchbaseFieldArray.d.ts +80 -0
  58. package/dist/forms/ArchbaseFormWizard.d.ts +73 -0
  59. package/dist/forms/ArchbaseStepper.d.ts +51 -0
  60. package/dist/forms/index.d.ts +8 -0
  61. package/dist/forms/useArchbaseFormWizard.d.ts +74 -0
  62. package/dist/hooks/index.d.ts +2 -0
  63. package/dist/hooks/useArchbaseConfirm.d.ts +59 -0
  64. package/dist/image/editor/index.d.ts +43 -4
  65. package/dist/image/index.d.ts +1 -0
  66. package/dist/index.css +1 -1
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.js +22510 -13820
  69. package/dist/list/ArchbaseTreeList.d.ts +76 -0
  70. package/dist/list/index.d.ts +2 -0
  71. package/dist/navigation/ArchbaseActionSheet.d.ts +70 -0
  72. package/dist/navigation/ArchbaseAppBar.d.ts +63 -0
  73. package/dist/navigation/ArchbaseBottomNavigation.d.ts +61 -0
  74. package/dist/navigation/ArchbaseContextMenu.d.ts +76 -0
  75. package/dist/navigation/ArchbaseKeyboardShortcuts.d.ts +85 -0
  76. package/dist/navigation/ArchbaseSpotlight.d.ts +69 -0
  77. package/dist/navigation/index.d.ts +12 -0
  78. package/dist/notification/ArchbaseNotificationCenter.d.ts +56 -0
  79. package/dist/notification/index.d.ts +2 -0
  80. package/dist/onboarding/ArchbaseOnboardingTour.d.ts +27 -76
  81. package/dist/viewers/ArchbaseDocViewer.d.ts +40 -0
  82. package/dist/viewers/ArchbasePdfBuilder.d.ts +53 -0
  83. package/dist/viewers/ArchbaseXmlViewer.d.ts +36 -0
  84. package/dist/viewers/index.d.ts +6 -0
  85. package/package.json +41 -18
  86. package/src/buttons/ArchbaseSpeedDial.tsx +282 -0
  87. package/src/buttons/index.ts +3 -0
  88. package/src/charts/ArchbaseArcGauge.tsx +325 -0
  89. package/src/charts/ArchbaseGantt.tsx +494 -0
  90. package/src/charts/ArchbaseLinearGauge.tsx +288 -0
  91. package/src/charts/ArchbaseResourceTimeline.tsx +433 -0
  92. package/src/charts/ArchbaseSparkline.tsx +193 -0
  93. package/src/charts/index.ts +20 -1
  94. package/src/containers/ArchbaseExpansionPanel.tsx +208 -0
  95. package/src/containers/ArchbaseFloatingPanel.tsx +448 -0
  96. package/src/containers/index.ts +17 -0
  97. package/src/datagrid/components/pagination/grid-pagination.tsx +2 -2
  98. package/src/datagrid/export/ArchbaseDataGridExcelExport.tsx +293 -0
  99. package/src/datagrid/export/index.ts +8 -0
  100. package/src/datagrid/index.tsx +2 -0
  101. package/src/display/ArchbaseAnimationWrapper.tsx +272 -0
  102. package/src/display/ArchbaseBarcodeGenerator.tsx +303 -0
  103. package/src/display/ArchbaseCarousel.tsx +679 -0
  104. package/src/display/ArchbaseCodeViewer.tsx +271 -0
  105. package/src/display/ArchbaseDiffViewer.tsx +108 -0
  106. package/src/display/ArchbaseJsonTree.tsx +150 -0
  107. package/src/display/ArchbaseLightbox.tsx +279 -0
  108. package/src/display/ArchbaseMarquee.tsx +268 -0
  109. package/src/display/ArchbasePhotoAlbum.tsx +415 -0
  110. package/src/display/ArchbaseQRCode.tsx +137 -0
  111. package/src/display/ArchbaseTooltipRich.tsx +317 -0
  112. package/src/display/ArchbaseVirtualList.tsx +618 -0
  113. package/src/display/index.ts +94 -1
  114. package/src/editors/ArchbaseAIPromptInput.tsx +432 -0
  115. package/src/editors/ArchbaseBarcodeScanner.tsx +570 -0
  116. package/src/editors/ArchbaseCepInput.tsx +173 -0
  117. package/src/editors/ArchbaseColorGradientPicker.tsx +460 -0
  118. package/src/editors/ArchbaseCpfCnpjInput.tsx +193 -0
  119. package/src/editors/ArchbaseCurrencyInput.tsx +100 -0
  120. package/src/editors/ArchbaseDualListbox.tsx +671 -0
  121. package/src/editors/ArchbaseInPlaceEditor.tsx +406 -0
  122. package/src/editors/ArchbaseLightGrid.tsx +538 -0
  123. package/src/editors/ArchbaseMentionInput.tsx +384 -0
  124. package/src/editors/ArchbaseMultiEmail.tsx +367 -0
  125. package/src/editors/ArchbaseMultiViewCalendar.tsx +439 -0
  126. package/src/editors/ArchbaseNumberStepper.tsx +219 -0
  127. package/src/editors/ArchbaseOTPInput.tsx +133 -0
  128. package/src/editors/ArchbaseOperationHoursEditor.tsx +1 -1
  129. package/src/editors/ArchbasePhoneInput.tsx +355 -0
  130. package/src/editors/ArchbaseRangeSlider.tsx +109 -0
  131. package/src/editors/ArchbaseSignaturePad.tsx +425 -0
  132. package/src/editors/ArchbaseSpeechToTextInput.tsx +444 -0
  133. package/src/editors/ArchbaseSpreadsheet.tsx +388 -0
  134. package/src/editors/ArchbaseTagInput.tsx +326 -0
  135. package/src/editors/index.tsx +92 -0
  136. package/src/feedback/ArchbaseChunkProgressBar.tsx +156 -0
  137. package/src/feedback/ArchbaseRipple.tsx +278 -0
  138. package/src/feedback/ArchbaseSkeleton.tsx +419 -0
  139. package/src/feedback/index.ts +22 -1
  140. package/src/filters/ArchbaseColumnSelector.tsx +268 -0
  141. package/src/filters/index.ts +2 -0
  142. package/src/forms/ArchbaseFieldArray.tsx +305 -0
  143. package/src/forms/ArchbaseFormWizard.tsx +284 -0
  144. package/src/forms/ArchbaseStepper.tsx +137 -0
  145. package/src/forms/index.ts +24 -2
  146. package/src/forms/useArchbaseFormWizard.ts +283 -0
  147. package/src/hooks/index.ts +12 -0
  148. package/src/hooks/useArchbaseConfirm.ts +293 -0
  149. package/src/image/editor/functions/image-processing.ts +5 -0
  150. package/src/image/editor/index.tsx +305 -391
  151. package/src/image/index.ts +1 -0
  152. package/src/index.ts +3 -0
  153. package/src/list/ArchbaseTreeList.tsx +422 -0
  154. package/src/list/index.ts +3 -0
  155. package/src/navigation/ArchbaseActionSheet.tsx +301 -0
  156. package/src/navigation/ArchbaseAppBar.tsx +233 -0
  157. package/src/navigation/ArchbaseBottomNavigation.tsx +243 -0
  158. package/src/navigation/ArchbaseContextMenu.tsx +298 -0
  159. package/src/navigation/ArchbaseKeyboardShortcuts.tsx +472 -0
  160. package/src/navigation/ArchbaseSpotlight.tsx +483 -0
  161. package/src/navigation/index.ts +48 -1
  162. package/src/notification/ArchbaseNotificationCenter.tsx +502 -0
  163. package/src/notification/index.ts +2 -0
  164. package/src/onboarding/ArchbaseOnboardingTour.tsx +229 -102
  165. package/src/viewers/ArchbaseDocViewer.tsx +584 -0
  166. package/src/viewers/ArchbasePdfBuilder.tsx +291 -0
  167. package/src/viewers/ArchbaseXmlViewer.tsx +142 -0
  168. package/src/viewers/index.ts +12 -0
@@ -0,0 +1,538 @@
1
+ /**
2
+ * ArchbaseLightGrid — grid leve baseado em sheet-happens com integração ao DataSource.
3
+ * Ideal para exibição e edição tabular de alto desempenho com milhares de linhas.
4
+ * @status experimental
5
+ */
6
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
+ import { Box, Text, useMantineTheme, useComputedColorScheme } from '@mantine/core';
8
+ import { useForceUpdate } from '@mantine/hooks';
9
+ import Sheet from 'sheet-happens';
10
+ import 'sheet-happens/dist/index.css';
11
+ import type { DataSourceEvent, IArchbaseDataSourceBase } from '@archbase/data';
12
+ import { DataSourceEventNames, useArchbaseDataSourceListener } from '@archbase/data';
13
+ import type { CSSProperties } from 'react';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Types
17
+ // ---------------------------------------------------------------------------
18
+
19
+ export interface ArchbaseLightGridColumn<T> {
20
+ /** Campo do registro (keyof T ou path com notação de ponto) */
21
+ field: keyof T | string;
22
+ /** Texto do cabeçalho */
23
+ header: string;
24
+ /** Largura da coluna em pixels */
25
+ width?: number;
26
+ /** Tipo de dado da coluna */
27
+ type?: 'text' | 'number' | 'date' | 'boolean' | 'currency' | 'custom';
28
+ /** Formato (ex.: "DD/MM/YYYY", "0,0.00", "R$ #.##0,00") */
29
+ format?: string;
30
+ /** Se a coluna e editavel */
31
+ editable?: boolean;
32
+ /** Se a coluna deve ficar fixa a esquerda */
33
+ frozen?: boolean;
34
+ /** Alinhamento do texto */
35
+ align?: 'left' | 'center' | 'right';
36
+ }
37
+
38
+ export interface ArchbaseLightGridProps<T, ID> {
39
+ /** Fonte de dados */
40
+ dataSource?: IArchbaseDataSourceBase<T>;
41
+ /** Definicao das colunas */
42
+ columns: ArchbaseLightGridColumn<T>[];
43
+ /** Dados standalone (usado quando nao ha dataSource) */
44
+ data?: T[];
45
+ /** Habilita edicao de celulas */
46
+ editable?: boolean;
47
+ /** Altura do grid */
48
+ height?: string | number;
49
+ /** Largura do grid */
50
+ width?: string | number;
51
+ /** Exibe numeracao de linhas */
52
+ showRowNumbers?: boolean;
53
+ /** Callback quando uma celula e alterada */
54
+ onCellChange?: (record: T, field: string, oldValue: any, newValue: any) => void;
55
+ /** Callback quando a selecao muda */
56
+ onSelectionChange?: (selection: { row: number; col: number }[]) => void;
57
+ /** Somente leitura (sobrescreve editable) */
58
+ readOnly?: boolean;
59
+ /** Label exibido acima do grid */
60
+ label?: string;
61
+ /** Descricao exibida abaixo do label */
62
+ description?: string;
63
+ /** Estilo CSS do container */
64
+ style?: CSSProperties;
65
+ /** Classe CSS do container */
66
+ className?: string;
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Helpers
71
+ // ---------------------------------------------------------------------------
72
+
73
+ function getNestedValue(obj: any, path: string): any {
74
+ if (!obj || !path) return undefined;
75
+ const parts = path.split('.');
76
+ let current = obj;
77
+ for (const part of parts) {
78
+ if (current == null) return undefined;
79
+ current = current[part];
80
+ }
81
+ return current;
82
+ }
83
+
84
+ function setNestedValue(obj: any, path: string, value: any): void {
85
+ if (!obj || !path) return;
86
+ const parts = path.split('.');
87
+ let current = obj;
88
+ for (let i = 0; i < parts.length - 1; i++) {
89
+ if (current[parts[i]] == null) {
90
+ current[parts[i]] = {};
91
+ }
92
+ current = current[parts[i]];
93
+ }
94
+ current[parts[parts.length - 1]] = value;
95
+ }
96
+
97
+ function formatCellValue(
98
+ value: any,
99
+ type: ArchbaseLightGridColumn<any>['type'],
100
+ format?: string,
101
+ ): string {
102
+ if (value == null) return '';
103
+
104
+ switch (type) {
105
+ case 'boolean':
106
+ return value ? 'Sim' : 'Nao';
107
+
108
+ case 'number':
109
+ if (typeof value === 'number') {
110
+ if (format) {
111
+ try {
112
+ const decimals = (format.split('.')[1] || '').length;
113
+ return value.toLocaleString('pt-BR', {
114
+ minimumFractionDigits: decimals,
115
+ maximumFractionDigits: decimals,
116
+ });
117
+ } catch {
118
+ return String(value);
119
+ }
120
+ }
121
+ return value.toLocaleString('pt-BR');
122
+ }
123
+ return String(value);
124
+
125
+ case 'currency':
126
+ if (typeof value === 'number') {
127
+ return value.toLocaleString('pt-BR', {
128
+ style: 'currency',
129
+ currency: 'BRL',
130
+ });
131
+ }
132
+ return String(value);
133
+
134
+ case 'date':
135
+ if (value instanceof Date) {
136
+ return value.toLocaleDateString('pt-BR');
137
+ }
138
+ if (typeof value === 'string') {
139
+ try {
140
+ const d = new Date(value);
141
+ if (!isNaN(d.getTime())) {
142
+ return d.toLocaleDateString('pt-BR');
143
+ }
144
+ } catch {
145
+ // fall through
146
+ }
147
+ }
148
+ return String(value);
149
+
150
+ case 'text':
151
+ default:
152
+ return String(value);
153
+ }
154
+ }
155
+
156
+ function parseCellInput(
157
+ input: string,
158
+ type: ArchbaseLightGridColumn<any>['type'],
159
+ ): any {
160
+ if (input === '' || input == null) return null;
161
+
162
+ switch (type) {
163
+ case 'boolean':
164
+ return (
165
+ input.toLowerCase() === 'true' ||
166
+ input.toLowerCase() === 'sim' ||
167
+ input === '1'
168
+ );
169
+
170
+ case 'number':
171
+ case 'currency': {
172
+ const cleaned = input.replace(/[R$\s.]/g, '').replace(',', '.');
173
+ const num = Number(cleaned);
174
+ return isNaN(num) ? input : num;
175
+ }
176
+
177
+ case 'date': {
178
+ // Tenta parsear DD/MM/YYYY
179
+ const parts = input.split('/');
180
+ if (parts.length === 3) {
181
+ const d = new Date(Number(parts[2]), Number(parts[1]) - 1, Number(parts[0]));
182
+ if (!isNaN(d.getTime())) return d.toISOString();
183
+ }
184
+ // Tenta ISO
185
+ const iso = new Date(input);
186
+ if (!isNaN(iso.getTime())) return iso.toISOString();
187
+ return input;
188
+ }
189
+
190
+ default:
191
+ return input;
192
+ }
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Component
197
+ // ---------------------------------------------------------------------------
198
+
199
+ function ArchbaseLightGridInner<T, ID>(
200
+ props: ArchbaseLightGridProps<T, ID>,
201
+ ) {
202
+ const {
203
+ dataSource,
204
+ columns,
205
+ data: standaloneData,
206
+ editable = false,
207
+ height = '400px',
208
+ width = '100%',
209
+ showRowNumbers = true,
210
+ onCellChange,
211
+ onSelectionChange,
212
+ readOnly = false,
213
+ label,
214
+ description,
215
+ style,
216
+ className,
217
+ } = props;
218
+
219
+ const theme = useMantineTheme();
220
+ const computedColorScheme = useComputedColorScheme('light');
221
+ const isDark = computedColorScheme === 'dark';
222
+ const forceUpdate = useForceUpdate();
223
+
224
+ const [records, setRecords] = useState<T[]>([]);
225
+ const [selectedRow, setSelectedRow] = useState<number>(0);
226
+ const recordsRef = useRef<T[]>([]);
227
+
228
+ // Sincroniza dados do DataSource ou standalone
229
+ const syncRecords = useCallback(() => {
230
+ if (dataSource) {
231
+ const dsRecords = dataSource.browseRecords() || [];
232
+ recordsRef.current = dsRecords;
233
+ setRecords(dsRecords);
234
+ } else if (standaloneData) {
235
+ recordsRef.current = standaloneData;
236
+ setRecords(standaloneData);
237
+ }
238
+ }, [dataSource, standaloneData]);
239
+
240
+ // Numero de colunas congeladas
241
+ const frozenCols = useMemo(
242
+ () => columns.filter((c) => c.frozen).length,
243
+ [columns],
244
+ );
245
+
246
+ // Listener do DataSource
247
+ const dataSourceEvent = useCallback(
248
+ (event: DataSourceEvent<T>) => {
249
+ if (
250
+ event.type === DataSourceEventNames.dataChanged ||
251
+ event.type === DataSourceEventNames.recordChanged ||
252
+ event.type === DataSourceEventNames.refreshData ||
253
+ event.type === DataSourceEventNames.afterAppend ||
254
+ event.type === DataSourceEventNames.afterRemove ||
255
+ event.type === DataSourceEventNames.afterInsert ||
256
+ event.type === DataSourceEventNames.afterSave ||
257
+ event.type === DataSourceEventNames.afterCancel ||
258
+ event.type === DataSourceEventNames.afterEdit
259
+ ) {
260
+ syncRecords();
261
+ forceUpdate();
262
+ }
263
+
264
+ if (event.type === DataSourceEventNames.afterScroll) {
265
+ if (dataSource) {
266
+ const idx = dataSource.getCurrentIndex();
267
+ setSelectedRow(idx);
268
+ }
269
+ }
270
+
271
+ if (event.type === DataSourceEventNames.fieldChanged) {
272
+ syncRecords();
273
+ forceUpdate();
274
+ }
275
+ },
276
+ [dataSource, syncRecords, forceUpdate],
277
+ );
278
+
279
+ // Ref estavel para o listener
280
+ const dataSourceEventRef = useRef(dataSourceEvent);
281
+ useEffect(() => {
282
+ dataSourceEventRef.current = dataSourceEvent;
283
+ }, [dataSourceEvent]);
284
+
285
+ const stableListener = useCallback((event: DataSourceEvent<T>) => {
286
+ dataSourceEventRef.current(event);
287
+ }, []);
288
+
289
+ // Registra listener no DataSource
290
+ useEffect(() => {
291
+ if (dataSource) {
292
+ dataSource.addListener(stableListener);
293
+ syncRecords();
294
+ setSelectedRow(dataSource.getCurrentIndex());
295
+ }
296
+ return () => {
297
+ if (dataSource) {
298
+ dataSource.removeListener(stableListener);
299
+ }
300
+ };
301
+ }, [(dataSource as any)?.uuid || dataSource?.getName()]);
302
+
303
+ // Sincroniza dados standalone
304
+ useEffect(() => {
305
+ if (!dataSource && standaloneData) {
306
+ syncRecords();
307
+ }
308
+ }, [standaloneData, dataSource]);
309
+
310
+ // -----------------------------------------------------------------------
311
+ // sheet-happens callbacks
312
+ // -----------------------------------------------------------------------
313
+
314
+ const cellWidth = useCallback(
315
+ (col: number): number => {
316
+ if (col < 0 || col >= columns.length) return 100;
317
+ return columns[col].width ?? 100;
318
+ },
319
+ [columns],
320
+ );
321
+
322
+ const cellHeight = useCallback((): number => {
323
+ return 28;
324
+ }, []);
325
+
326
+ const sourceData = useCallback(
327
+ (row: number, col: number): string => {
328
+ if (row < 0 || row >= records.length) return '';
329
+ if (col < 0 || col >= columns.length) return '';
330
+
331
+ const record = records[row];
332
+ const column = columns[col];
333
+ const field = String(column.field);
334
+ const rawValue = getNestedValue(record, field);
335
+
336
+ return formatCellValue(rawValue, column.type, column.format);
337
+ },
338
+ [records, columns],
339
+ );
340
+
341
+ // editData is a getter in sheet-happens: (row, col) => string
342
+ // Returns the current editing value for a cell (empty string for no edit state)
343
+ const editDataMap = useRef<Record<string, string>>({});
344
+
345
+ const editData = useCallback(
346
+ (row: number, col: number): string => {
347
+ const key = `${row}:${col}`;
348
+ return editDataMap.current[key] ?? '';
349
+ },
350
+ [],
351
+ );
352
+
353
+ // onChange handles cell value changes in sheet-happens
354
+ const handleCellChanges = useCallback(
355
+ (changes: Array<{ x: number; y: number; value: string | number }>) => {
356
+ for (const change of changes) {
357
+ const { x: col, y: row, value: rawValue } = change;
358
+ const value = String(rawValue);
359
+ if (readOnly || !editable) return;
360
+ if (row < 0 || row >= records.length) return;
361
+ if (col < 0 || col >= columns.length) return;
362
+
363
+ const column = columns[col];
364
+ if (column.editable === false) return;
365
+
366
+ const record = records[row];
367
+ const field = String(column.field);
368
+ const oldValue = getNestedValue(record, field);
369
+ const newValue = parseCellInput(value, column.type);
370
+
371
+ if (dataSource) {
372
+ if (dataSource.getCurrentIndex() !== row) {
373
+ dataSource.gotoRecord(row);
374
+ }
375
+ dataSource.setFieldValue(field, newValue);
376
+ } else {
377
+ setNestedValue(record, field, newValue);
378
+ setRecords([...records]);
379
+ }
380
+
381
+ if (onCellChange) {
382
+ onCellChange(record, field, oldValue, newValue);
383
+ }
384
+ }
385
+ },
386
+ [readOnly, editable, records, columns, dataSource, onCellChange],
387
+ );
388
+
389
+ const isReadOnly = useCallback(
390
+ (row: number, col: number): boolean => {
391
+ if (readOnly || !editable) return true;
392
+ if (col < 0 || col >= columns.length) return true;
393
+ const column = columns[col];
394
+ return column.editable === false;
395
+ },
396
+ [readOnly, editable, columns],
397
+ );
398
+
399
+ // Headers
400
+ const headerData = useCallback(
401
+ (col: number): string => {
402
+ if (col < 0 || col >= columns.length) return '';
403
+ return columns[col].header;
404
+ },
405
+ [columns],
406
+ );
407
+
408
+ // Selection change
409
+ const onSelectionChanged = useCallback(
410
+ (
411
+ x1: number,
412
+ y1: number,
413
+ x2: number,
414
+ y2: number,
415
+ ) => {
416
+ // Navega para a linha selecionada no DataSource
417
+ if (dataSource && y1 >= 0 && y1 < records.length) {
418
+ if (dataSource.getCurrentIndex() !== y1) {
419
+ dataSource.gotoRecord(y1);
420
+ }
421
+ }
422
+ setSelectedRow(y1);
423
+
424
+ if (onSelectionChange) {
425
+ const selection: { row: number; col: number }[] = [];
426
+ for (let r = Math.min(y1, y2); r <= Math.max(y1, y2); r++) {
427
+ for (let c = Math.min(x1, x2); c <= Math.max(x1, x2); c++) {
428
+ selection.push({ row: r, col: c });
429
+ }
430
+ }
431
+ onSelectionChange(selection);
432
+ }
433
+ },
434
+ [dataSource, records.length, onSelectionChange],
435
+ );
436
+
437
+ // Cell style para dark mode e alinhamento
438
+ const cellStyle = useCallback(
439
+ (
440
+ x: number,
441
+ y: number,
442
+ ): Record<string, any> => {
443
+ const style: Record<string, any> = {};
444
+
445
+ if (isDark) {
446
+ style.color = theme.colors.dark[0];
447
+ style.backgroundColor =
448
+ y === selectedRow
449
+ ? theme.colors.dark[5]
450
+ : theme.colors.dark[7];
451
+ } else {
452
+ style.backgroundColor =
453
+ y === selectedRow
454
+ ? theme.colors.blue[0]
455
+ : '#ffffff';
456
+ }
457
+
458
+ if (x >= 0 && x < columns.length) {
459
+ const column = columns[x];
460
+ if (column.align) {
461
+ style.textAlign = column.align;
462
+ } else if (
463
+ column.type === 'number' ||
464
+ column.type === 'currency'
465
+ ) {
466
+ style.textAlign = 'right';
467
+ }
468
+ }
469
+
470
+ return style;
471
+ },
472
+ [isDark, theme, columns, selectedRow],
473
+ );
474
+
475
+ // Header style para dark mode
476
+ const headerStyle = useCallback(
477
+ (): Record<string, any> => {
478
+ if (isDark) {
479
+ return {
480
+ color: theme.colors.dark[0],
481
+ backgroundColor: theme.colors.dark[6],
482
+ fontWeight: 600,
483
+ };
484
+ }
485
+ return {
486
+ fontWeight: 600,
487
+ };
488
+ },
489
+ [isDark, theme],
490
+ );
491
+
492
+ // -----------------------------------------------------------------------
493
+ // Render
494
+ // -----------------------------------------------------------------------
495
+
496
+ const containerStyle: CSSProperties = {
497
+ width,
498
+ height,
499
+ display: 'flex',
500
+ flexDirection: 'column',
501
+ ...style,
502
+ };
503
+
504
+ return (
505
+ <Box className={className} style={containerStyle}>
506
+ {label && (
507
+ <Text fw={500} size="sm" mb={2}>
508
+ {label}
509
+ </Text>
510
+ )}
511
+ {description && (
512
+ <Text size="xs" c="dimmed" mb={4}>
513
+ {description}
514
+ </Text>
515
+ )}
516
+ <Box style={{ flex: 1, minHeight: 0, overflow: 'hidden' }}>
517
+ <Sheet
518
+ sourceData={sourceData}
519
+ editData={editData}
520
+ cellWidth={cellWidth}
521
+ cellHeight={cellHeight}
522
+ columnHeaders={headerData}
523
+ cellStyle={cellStyle}
524
+ readOnly={isReadOnly}
525
+ onChange={handleCellChanges}
526
+ onSelectionChanged={onSelectionChanged}
527
+ sheetStyle={{
528
+ freezeColumns: frozenCols,
529
+ } as any}
530
+ />
531
+ </Box>
532
+ </Box>
533
+ );
534
+ }
535
+
536
+ export const ArchbaseLightGrid = React.memo(ArchbaseLightGridInner) as typeof ArchbaseLightGridInner;
537
+
538
+ (ArchbaseLightGrid as any).displayName = 'ArchbaseLightGrid';