@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,618 @@
1
+ import React, {
2
+ ReactNode,
3
+ useCallback,
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
+ useMemo,
8
+ forwardRef,
9
+ useImperativeHandle,
10
+ } from 'react';
11
+ import {
12
+ Box,
13
+ BoxProps,
14
+ Text,
15
+ Center,
16
+ Loader,
17
+ ScrollArea,
18
+ Stack,
19
+ } from '@mantine/core';
20
+
21
+ // =============================================================================
22
+ // Types
23
+ // =============================================================================
24
+
25
+ export interface VirtualListItem {
26
+ id: string | number;
27
+ [key: string]: any;
28
+ }
29
+
30
+ export interface ArchbaseVirtualListProps<T extends VirtualListItem>
31
+ extends Omit<BoxProps, 'children'> {
32
+ /** Array de itens */
33
+ items: T[];
34
+ /** Altura de cada item em pixels */
35
+ itemHeight: number;
36
+ /** Altura do container */
37
+ height: number | string;
38
+ /** Largura do container */
39
+ width?: number | string;
40
+ /** Quantidade de itens extras para renderizar (overscan) */
41
+ overscan?: number;
42
+ /** Renderizador de item */
43
+ renderItem: (item: T, index: number, style: React.CSSProperties) => ReactNode;
44
+ /** Callback ao alcançar o final */
45
+ onEndReached?: () => void;
46
+ /** Threshold para onEndReached (em pixels) */
47
+ endReachedThreshold?: number;
48
+ /** Estado de carregamento */
49
+ loading?: boolean;
50
+ /** Componente de loading */
51
+ loadingComponent?: ReactNode;
52
+ /** Mensagem quando vazio */
53
+ emptyMessage?: string;
54
+ /** Componente quando vazio */
55
+ emptyComponent?: ReactNode;
56
+ /** Renderizar header */
57
+ header?: ReactNode;
58
+ /** Renderizar footer */
59
+ footer?: ReactNode;
60
+ /** Callback ao scrollar */
61
+ onScroll?: (scrollTop: number) => void;
62
+ /** Scroll inicial */
63
+ initialScrollTop?: number;
64
+ /** Estilo dos itens */
65
+ itemStyle?: React.CSSProperties;
66
+ /** Gap entre itens */
67
+ gap?: number;
68
+ /** Key extractor */
69
+ keyExtractor?: (item: T, index: number) => string | number;
70
+ }
71
+
72
+ export interface ArchbaseVirtualListRef {
73
+ scrollToIndex: (index: number, align?: 'start' | 'center' | 'end') => void;
74
+ scrollToTop: () => void;
75
+ scrollToBottom: () => void;
76
+ getScrollTop: () => number;
77
+ }
78
+
79
+ // =============================================================================
80
+ // ArchbaseVirtualList Component
81
+ // =============================================================================
82
+
83
+ function ArchbaseVirtualListInner<T extends VirtualListItem>(
84
+ {
85
+ items,
86
+ itemHeight,
87
+ height,
88
+ width = '100%',
89
+ overscan = 3,
90
+ renderItem,
91
+ onEndReached,
92
+ endReachedThreshold = 200,
93
+ loading = false,
94
+ loadingComponent,
95
+ emptyMessage = 'Nenhum item encontrado',
96
+ emptyComponent,
97
+ header,
98
+ footer,
99
+ onScroll,
100
+ initialScrollTop = 0,
101
+ itemStyle,
102
+ gap = 0,
103
+ keyExtractor,
104
+ ...boxProps
105
+ }: ArchbaseVirtualListProps<T>,
106
+ ref: React.Ref<ArchbaseVirtualListRef>
107
+ ) {
108
+ const containerRef = useRef<HTMLDivElement>(null);
109
+ const [scrollTop, setScrollTop] = useState(initialScrollTop);
110
+ const [containerHeight, setContainerHeight] = useState(0);
111
+ const endReachedCalled = useRef(false);
112
+
113
+ // Calculate container height
114
+ useEffect(() => {
115
+ if (containerRef.current) {
116
+ setContainerHeight(containerRef.current.clientHeight);
117
+ }
118
+ }, [height]);
119
+
120
+ // Reset end reached flag when items change
121
+ useEffect(() => {
122
+ endReachedCalled.current = false;
123
+ }, [items.length]);
124
+
125
+ // Effective item height (including gap)
126
+ const effectiveItemHeight = itemHeight + gap;
127
+
128
+ // Total height of all items
129
+ const totalHeight = items.length * effectiveItemHeight - gap;
130
+
131
+ // Calculate visible range
132
+ const startIndex = Math.max(0, Math.floor(scrollTop / effectiveItemHeight) - overscan);
133
+ const endIndex = Math.min(
134
+ items.length - 1,
135
+ Math.ceil((scrollTop + containerHeight) / effectiveItemHeight) + overscan
136
+ );
137
+
138
+ // Visible items
139
+ const visibleItems = useMemo(() => {
140
+ const result = [];
141
+ for (let i = startIndex; i <= endIndex; i++) {
142
+ if (items[i]) {
143
+ result.push({ item: items[i], index: i });
144
+ }
145
+ }
146
+ return result;
147
+ }, [items, startIndex, endIndex]);
148
+
149
+ // Handle scroll
150
+ const handleScroll = useCallback(
151
+ (event: React.UIEvent<HTMLDivElement>) => {
152
+ const target = event.currentTarget;
153
+ const newScrollTop = target.scrollTop;
154
+ setScrollTop(newScrollTop);
155
+ onScroll?.(newScrollTop);
156
+
157
+ // Check if reached end
158
+ if (
159
+ onEndReached &&
160
+ !endReachedCalled.current &&
161
+ target.scrollHeight - target.scrollTop - target.clientHeight < endReachedThreshold
162
+ ) {
163
+ endReachedCalled.current = true;
164
+ onEndReached();
165
+ }
166
+ },
167
+ [onScroll, onEndReached, endReachedThreshold]
168
+ );
169
+
170
+ // Imperative methods
171
+ useImperativeHandle(
172
+ ref,
173
+ () => ({
174
+ scrollToIndex: (index: number, align: 'start' | 'center' | 'end' = 'start') => {
175
+ if (!containerRef.current) return;
176
+
177
+ const itemTop = index * effectiveItemHeight;
178
+ let targetScrollTop = itemTop;
179
+
180
+ if (align === 'center') {
181
+ targetScrollTop = itemTop - containerHeight / 2 + itemHeight / 2;
182
+ } else if (align === 'end') {
183
+ targetScrollTop = itemTop - containerHeight + itemHeight;
184
+ }
185
+
186
+ containerRef.current.scrollTop = Math.max(0, targetScrollTop);
187
+ },
188
+ scrollToTop: () => {
189
+ if (containerRef.current) {
190
+ containerRef.current.scrollTop = 0;
191
+ }
192
+ },
193
+ scrollToBottom: () => {
194
+ if (containerRef.current) {
195
+ containerRef.current.scrollTop = containerRef.current.scrollHeight;
196
+ }
197
+ },
198
+ getScrollTop: () => scrollTop,
199
+ }),
200
+ [effectiveItemHeight, containerHeight, itemHeight, scrollTop]
201
+ );
202
+
203
+ // Get item key
204
+ const getKey = useCallback(
205
+ (item: T, index: number) => {
206
+ if (keyExtractor) {
207
+ return keyExtractor(item, index);
208
+ }
209
+ return item.id ?? index;
210
+ },
211
+ [keyExtractor]
212
+ );
213
+
214
+ // Empty state
215
+ if (items.length === 0 && !loading) {
216
+ return (
217
+ <Box style={{ height, width }} {...boxProps}>
218
+ {header}
219
+ {emptyComponent ?? (
220
+ <Center style={{ height: '100%' }}>
221
+ <Text c="dimmed">{emptyMessage}</Text>
222
+ </Center>
223
+ )}
224
+ {footer}
225
+ </Box>
226
+ );
227
+ }
228
+
229
+ return (
230
+ <Box {...boxProps}>
231
+ {header}
232
+ <Box
233
+ ref={containerRef}
234
+ onScroll={handleScroll}
235
+ style={{
236
+ height,
237
+ width,
238
+ overflow: 'auto',
239
+ position: 'relative',
240
+ }}
241
+ >
242
+ {/* Spacer for total height */}
243
+ <Box style={{ height: totalHeight, position: 'relative' }}>
244
+ {/* Visible items */}
245
+ {visibleItems.map(({ item, index }) => {
246
+ const style: React.CSSProperties = {
247
+ position: 'absolute',
248
+ top: index * effectiveItemHeight,
249
+ left: 0,
250
+ right: 0,
251
+ height: itemHeight,
252
+ ...itemStyle,
253
+ };
254
+
255
+ return (
256
+ <React.Fragment key={getKey(item, index)}>
257
+ {renderItem(item, index, style)}
258
+ </React.Fragment>
259
+ );
260
+ })}
261
+ </Box>
262
+
263
+ {/* Loading at bottom */}
264
+ {loading && (
265
+ <Center py="md">
266
+ {loadingComponent ?? <Loader size="sm" />}
267
+ </Center>
268
+ )}
269
+ </Box>
270
+ {footer}
271
+ </Box>
272
+ );
273
+ }
274
+
275
+ export const ArchbaseVirtualList = forwardRef(ArchbaseVirtualListInner) as <
276
+ T extends VirtualListItem
277
+ >(
278
+ props: ArchbaseVirtualListProps<T> & { ref?: React.Ref<ArchbaseVirtualListRef> }
279
+ ) => ReturnType<typeof ArchbaseVirtualListInner>;
280
+
281
+ // =============================================================================
282
+ // ArchbaseVirtualGrid Component
283
+ // =============================================================================
284
+
285
+ export interface ArchbaseVirtualGridProps<T extends VirtualListItem>
286
+ extends Omit<BoxProps, 'children'> {
287
+ /** Array de itens */
288
+ items: T[];
289
+ /** Largura de cada item */
290
+ itemWidth: number;
291
+ /** Altura de cada item */
292
+ itemHeight: number;
293
+ /** Altura do container */
294
+ height: number | string;
295
+ /** Gap entre itens */
296
+ gap?: number;
297
+ /** Overscan de linhas */
298
+ overscan?: number;
299
+ /** Renderizador de item */
300
+ renderItem: (item: T, index: number, style: React.CSSProperties) => ReactNode;
301
+ /** Callback ao alcançar o final */
302
+ onEndReached?: () => void;
303
+ /** Threshold para onEndReached */
304
+ endReachedThreshold?: number;
305
+ /** Estado de carregamento */
306
+ loading?: boolean;
307
+ /** Mensagem quando vazio */
308
+ emptyMessage?: string;
309
+ /** Key extractor */
310
+ keyExtractor?: (item: T, index: number) => string | number;
311
+ }
312
+
313
+ export function ArchbaseVirtualGrid<T extends VirtualListItem>({
314
+ items,
315
+ itemWidth,
316
+ itemHeight,
317
+ height,
318
+ gap = 16,
319
+ overscan = 2,
320
+ renderItem,
321
+ onEndReached,
322
+ endReachedThreshold = 200,
323
+ loading = false,
324
+ emptyMessage = 'Nenhum item encontrado',
325
+ keyExtractor,
326
+ ...boxProps
327
+ }: ArchbaseVirtualGridProps<T>) {
328
+ const containerRef = useRef<HTMLDivElement>(null);
329
+ const [scrollTop, setScrollTop] = useState(0);
330
+ const [containerWidth, setContainerWidth] = useState(0);
331
+ const [containerHeight, setContainerHeight] = useState(0);
332
+ const endReachedCalled = useRef(false);
333
+
334
+ // Calculate dimensions
335
+ useEffect(() => {
336
+ if (containerRef.current) {
337
+ setContainerWidth(containerRef.current.clientWidth);
338
+ setContainerHeight(containerRef.current.clientHeight);
339
+
340
+ const resizeObserver = new ResizeObserver((entries) => {
341
+ const entry = entries[0];
342
+ if (entry) {
343
+ setContainerWidth(entry.contentRect.width);
344
+ setContainerHeight(entry.contentRect.height);
345
+ }
346
+ });
347
+
348
+ resizeObserver.observe(containerRef.current);
349
+ return () => resizeObserver.disconnect();
350
+ }
351
+ }, []);
352
+
353
+ // Calculate columns
354
+ const columns = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
355
+ const rows = Math.ceil(items.length / columns);
356
+ const effectiveRowHeight = itemHeight + gap;
357
+ const totalHeight = rows * effectiveRowHeight - gap;
358
+
359
+ // Calculate visible range
360
+ const startRow = Math.max(0, Math.floor(scrollTop / effectiveRowHeight) - overscan);
361
+ const endRow = Math.min(
362
+ rows - 1,
363
+ Math.ceil((scrollTop + containerHeight) / effectiveRowHeight) + overscan
364
+ );
365
+
366
+ // Visible items
367
+ const visibleItems = useMemo(() => {
368
+ const result = [];
369
+ for (let row = startRow; row <= endRow; row++) {
370
+ for (let col = 0; col < columns; col++) {
371
+ const index = row * columns + col;
372
+ if (index < items.length) {
373
+ result.push({ item: items[index], index, row, col });
374
+ }
375
+ }
376
+ }
377
+ return result;
378
+ }, [items, startRow, endRow, columns]);
379
+
380
+ // Handle scroll
381
+ const handleScroll = useCallback(
382
+ (event: React.UIEvent<HTMLDivElement>) => {
383
+ const target = event.currentTarget;
384
+ setScrollTop(target.scrollTop);
385
+
386
+ if (
387
+ onEndReached &&
388
+ !endReachedCalled.current &&
389
+ target.scrollHeight - target.scrollTop - target.clientHeight < endReachedThreshold
390
+ ) {
391
+ endReachedCalled.current = true;
392
+ onEndReached();
393
+ }
394
+ },
395
+ [onEndReached, endReachedThreshold]
396
+ );
397
+
398
+ // Reset end reached flag
399
+ useEffect(() => {
400
+ endReachedCalled.current = false;
401
+ }, [items.length]);
402
+
403
+ // Get key
404
+ const getKey = useCallback(
405
+ (item: T, index: number) => {
406
+ if (keyExtractor) return keyExtractor(item, index);
407
+ return item.id ?? index;
408
+ },
409
+ [keyExtractor]
410
+ );
411
+
412
+ // Empty state
413
+ if (items.length === 0 && !loading) {
414
+ return (
415
+ <Box style={{ height }} {...boxProps}>
416
+ <Center style={{ height: '100%' }}>
417
+ <Text c="dimmed">{emptyMessage}</Text>
418
+ </Center>
419
+ </Box>
420
+ );
421
+ }
422
+
423
+ return (
424
+ <Box
425
+ ref={containerRef}
426
+ onScroll={handleScroll}
427
+ style={{
428
+ height,
429
+ overflow: 'auto',
430
+ position: 'relative',
431
+ }}
432
+ {...boxProps}
433
+ >
434
+ <Box style={{ height: totalHeight, position: 'relative' }}>
435
+ {visibleItems.map(({ item, index, row, col }) => {
436
+ const style: React.CSSProperties = {
437
+ position: 'absolute',
438
+ top: row * effectiveRowHeight,
439
+ left: col * (itemWidth + gap),
440
+ width: itemWidth,
441
+ height: itemHeight,
442
+ };
443
+
444
+ return (
445
+ <React.Fragment key={getKey(item, index)}>
446
+ {renderItem(item, index, style)}
447
+ </React.Fragment>
448
+ );
449
+ })}
450
+ </Box>
451
+
452
+ {loading && (
453
+ <Center py="md">
454
+ <Loader size="sm" />
455
+ </Center>
456
+ )}
457
+ </Box>
458
+ );
459
+ }
460
+
461
+ // =============================================================================
462
+ // ArchbaseInfiniteList Component
463
+ // =============================================================================
464
+
465
+ export interface ArchbaseInfiniteListProps<T extends VirtualListItem>
466
+ extends Omit<ArchbaseVirtualListProps<T>, 'onEndReached'> {
467
+ /** Função para carregar mais itens */
468
+ loadMore: () => Promise<void>;
469
+ /** Se há mais itens para carregar */
470
+ hasMore: boolean;
471
+ /** Erro ao carregar */
472
+ error?: string | null;
473
+ /** Componente de erro */
474
+ errorComponent?: ReactNode;
475
+ /** Callback para retry */
476
+ onRetry?: () => void;
477
+ }
478
+
479
+ export function ArchbaseInfiniteList<T extends VirtualListItem>({
480
+ loadMore,
481
+ hasMore,
482
+ error,
483
+ errorComponent,
484
+ onRetry,
485
+ loading,
486
+ ...props
487
+ }: ArchbaseInfiniteListProps<T>) {
488
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
489
+
490
+ const handleEndReached = useCallback(async () => {
491
+ if (isLoadingMore || !hasMore || error) return;
492
+
493
+ setIsLoadingMore(true);
494
+ try {
495
+ await loadMore();
496
+ } finally {
497
+ setIsLoadingMore(false);
498
+ }
499
+ }, [isLoadingMore, hasMore, error, loadMore]);
500
+
501
+ const footerContent = error ? (
502
+ errorComponent ?? (
503
+ <Center py="md">
504
+ <Stack align="center" gap="xs">
505
+ <Text c="red" size="sm">
506
+ {error}
507
+ </Text>
508
+ {onRetry && (
509
+ <Text
510
+ size="sm"
511
+ c="blue"
512
+ style={{ cursor: 'pointer' }}
513
+ onClick={onRetry}
514
+ >
515
+ Tentar novamente
516
+ </Text>
517
+ )}
518
+ </Stack>
519
+ </Center>
520
+ )
521
+ ) : null;
522
+
523
+ return (
524
+ <ArchbaseVirtualList
525
+ {...props}
526
+ loading={loading || isLoadingMore}
527
+ onEndReached={handleEndReached}
528
+ footer={
529
+ <>
530
+ {props.footer}
531
+ {footerContent}
532
+ </>
533
+ }
534
+ />
535
+ );
536
+ }
537
+
538
+ // =============================================================================
539
+ // Hook useVirtualList
540
+ // =============================================================================
541
+
542
+ export interface UseVirtualListOptions<T> {
543
+ items: T[];
544
+ itemHeight: number;
545
+ containerHeight: number;
546
+ overscan?: number;
547
+ gap?: number;
548
+ }
549
+
550
+ export function useVirtualList<T>({
551
+ items,
552
+ itemHeight,
553
+ containerHeight,
554
+ overscan = 3,
555
+ gap = 0,
556
+ }: UseVirtualListOptions<T>) {
557
+ const [scrollTop, setScrollTop] = useState(0);
558
+
559
+ const effectiveItemHeight = itemHeight + gap;
560
+ const totalHeight = items.length * effectiveItemHeight - gap;
561
+
562
+ const startIndex = Math.max(0, Math.floor(scrollTop / effectiveItemHeight) - overscan);
563
+ const endIndex = Math.min(
564
+ items.length - 1,
565
+ Math.ceil((scrollTop + containerHeight) / effectiveItemHeight) + overscan
566
+ );
567
+
568
+ const visibleItems = useMemo(() => {
569
+ const result: Array<{ item: T; index: number; style: React.CSSProperties }> = [];
570
+ for (let i = startIndex; i <= endIndex; i++) {
571
+ if (items[i]) {
572
+ result.push({
573
+ item: items[i],
574
+ index: i,
575
+ style: {
576
+ position: 'absolute',
577
+ top: i * effectiveItemHeight,
578
+ left: 0,
579
+ right: 0,
580
+ height: itemHeight,
581
+ },
582
+ });
583
+ }
584
+ }
585
+ return result;
586
+ }, [items, startIndex, endIndex, effectiveItemHeight, itemHeight]);
587
+
588
+ const handleScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
589
+ setScrollTop(event.currentTarget.scrollTop);
590
+ }, []);
591
+
592
+ const scrollToIndex = useCallback(
593
+ (index: number, align: 'start' | 'center' | 'end' = 'start') => {
594
+ const itemTop = index * effectiveItemHeight;
595
+ let targetScrollTop = itemTop;
596
+
597
+ if (align === 'center') {
598
+ targetScrollTop = itemTop - containerHeight / 2 + itemHeight / 2;
599
+ } else if (align === 'end') {
600
+ targetScrollTop = itemTop - containerHeight + itemHeight;
601
+ }
602
+
603
+ return Math.max(0, targetScrollTop);
604
+ },
605
+ [effectiveItemHeight, containerHeight, itemHeight]
606
+ );
607
+
608
+ return {
609
+ totalHeight,
610
+ visibleItems,
611
+ handleScroll,
612
+ scrollToIndex,
613
+ scrollTop,
614
+ setScrollTop,
615
+ };
616
+ }
617
+
618
+ export default ArchbaseVirtualList;
@@ -2,4 +2,97 @@
2
2
  export { Text as ArchbaseText } from '@mantine/core';
3
3
  export { Title as ArchbaseTitle } from '@mantine/core';
4
4
  export { Image as ArchbaseMantineImage } from '@mantine/core';
5
- export { Badge as ArchbaseBadge } from '@mantine/core';
5
+ export { Badge as ArchbaseBadge } from '@mantine/core';
6
+ export { ArchbaseQRCode } from './ArchbaseQRCode';
7
+ export type { ArchbaseQRCodeProps, ArchbaseQRCodeImageSettings } from './ArchbaseQRCode';
8
+ export { ArchbaseDiffViewer, DiffMethod } from './ArchbaseDiffViewer';
9
+ export type { ArchbaseDiffViewerProps } from './ArchbaseDiffViewer';
10
+ export { ArchbaseJsonTree } from './ArchbaseJsonTree';
11
+ export type { ArchbaseJsonTreeProps } from './ArchbaseJsonTree';
12
+ export { ArchbaseCodeViewer } from './ArchbaseCodeViewer';
13
+ export type { ArchbaseCodeViewerProps, ArchbaseCodeViewerTab } from './ArchbaseCodeViewer';
14
+
15
+ export { ArchbaseAnimationWrapper, ArchbaseAnimatedList, ArchbaseFadeInWhenVisible } from './ArchbaseAnimationWrapper';
16
+ export type {
17
+ ArchbaseAnimationWrapperProps,
18
+ ArchbaseAnimatedListProps,
19
+ ArchbaseFadeInWhenVisibleProps,
20
+ ArchbaseAnimationPreset,
21
+ } from './ArchbaseAnimationWrapper';
22
+
23
+ export {
24
+ ArchbaseBarcodeGenerator,
25
+ ArchbaseEAN13Generator,
26
+ ArchbaseEAN8Generator,
27
+ ArchbaseUPCGenerator,
28
+ ArchbaseCODE128Generator,
29
+ } from './ArchbaseBarcodeGenerator';
30
+ export type {
31
+ ArchbaseBarcodeGeneratorProps,
32
+ BarcodeFormat,
33
+ } from './ArchbaseBarcodeGenerator';
34
+
35
+ export { ArchbaseTooltipRich, ArchbaseTooltipUser } from './ArchbaseTooltipRich';
36
+ export type { ArchbaseTooltipRichProps, ArchbaseTooltipUserProps } from './ArchbaseTooltipRich';
37
+
38
+ export {
39
+ ArchbaseMarquee,
40
+ ArchbaseMarqueeLeft,
41
+ ArchbaseMarqueeRight,
42
+ ArchbaseMarqueeUp,
43
+ ArchbaseMarqueeDown,
44
+ ArchbaseTextTicker,
45
+ } from './ArchbaseMarquee';
46
+ export type { ArchbaseMarqueeProps, MarqueeDirection, ArchbaseTextTickerProps } from './ArchbaseMarquee';
47
+
48
+ export {
49
+ ArchbaseCarousel,
50
+ ArchbaseCarouselSlide,
51
+ ArchbaseImageCarousel,
52
+ ArchbaseCardCarousel,
53
+ useArchbaseCarousel,
54
+ } from './ArchbaseCarousel';
55
+ export type {
56
+ ArchbaseCarouselProps,
57
+ ArchbaseCarouselSlideProps,
58
+ ArchbaseImageCarouselProps,
59
+ ImageSlide,
60
+ ArchbaseCardCarouselProps,
61
+ UseArchbaseCarouselOptions,
62
+ } from './ArchbaseCarousel';
63
+
64
+ export {
65
+ ArchbaseVirtualList,
66
+ ArchbaseVirtualGrid,
67
+ ArchbaseInfiniteList,
68
+ useVirtualList,
69
+ } from './ArchbaseVirtualList';
70
+ export type {
71
+ ArchbaseVirtualListProps,
72
+ ArchbaseVirtualListRef,
73
+ VirtualListItem,
74
+ ArchbaseVirtualGridProps,
75
+ ArchbaseInfiniteListProps,
76
+ UseVirtualListOptions,
77
+ } from './ArchbaseVirtualList';
78
+
79
+ export {
80
+ ArchbaseLightbox,
81
+ ArchbaseLightboxProvider,
82
+ useArchbaseLightbox,
83
+ useArchbaseLightboxContext,
84
+ } from './ArchbaseLightbox';
85
+ export type {
86
+ ArchbaseLightboxProps,
87
+ ArchbaseLightboxSlide,
88
+ ArchbaseLightboxProviderProps,
89
+ UseArchbaseLightboxReturn,
90
+ } from './ArchbaseLightbox';
91
+
92
+ export { ArchbasePhotoAlbum } from './ArchbasePhotoAlbum';
93
+ export type {
94
+ ArchbasePhotoAlbumProps,
95
+ ArchbasePhotoAlbumPhoto,
96
+ ArchbasePhotoAlbumLayout,
97
+ } from './ArchbasePhotoAlbum';
98
+