@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,243 @@
1
+ import React, { forwardRef, useCallback, useState, ReactNode } from 'react';
2
+ import {
3
+ Box,
4
+ Text,
5
+ Badge,
6
+ MantineColor,
7
+ MantineSize,
8
+ } from '@mantine/core';
9
+
10
+ export interface ArchbaseBottomNavigationItem {
11
+ /** Chave única */
12
+ key: string;
13
+ /** Label do item */
14
+ label: string;
15
+ /** Ícone */
16
+ icon: ReactNode;
17
+ /** Ícone quando ativo */
18
+ activeIcon?: ReactNode;
19
+ /** Badge */
20
+ badge?: number | string;
21
+ /** Cor do badge */
22
+ badgeColor?: MantineColor;
23
+ /** Se está desabilitado */
24
+ disabled?: boolean;
25
+ /** Link (navegação) */
26
+ href?: string;
27
+ /** Callback ao clicar */
28
+ onClick?: () => void;
29
+ }
30
+
31
+ export interface ArchbaseBottomNavigationProps {
32
+ /** Itens da navegação */
33
+ items: ArchbaseBottomNavigationItem[];
34
+ /** Item ativo (controlado) */
35
+ value?: string;
36
+ /** Item ativo padrão */
37
+ defaultValue?: string;
38
+ /** Callback ao mudar */
39
+ onChange?: (value: string) => void;
40
+ /** Altura da barra */
41
+ height?: number;
42
+ /** Cor de destaque */
43
+ activeColor?: MantineColor;
44
+ /** Cor inativa */
45
+ inactiveColor?: MantineColor;
46
+ /** Tamanho do ícone */
47
+ iconSize?: number;
48
+ /** Tamanho do texto */
49
+ labelSize?: MantineSize;
50
+ /** Se deve mostrar labels */
51
+ showLabels?: boolean;
52
+ /** Se deve mostrar labels apenas no ativo */
53
+ showActiveLabel?: boolean;
54
+ /** Se deve ter sombra */
55
+ withShadow?: boolean;
56
+ /** Se deve ter borda superior */
57
+ withBorder?: boolean;
58
+ /** Cor de fundo */
59
+ backgroundColor?: string;
60
+ /** Se está fixo na parte inferior */
61
+ fixed?: boolean;
62
+ /** Z-index quando fixo */
63
+ zIndex?: number;
64
+ /** Classe CSS */
65
+ className?: string;
66
+ /** Estilo */
67
+ style?: React.CSSProperties;
68
+ }
69
+
70
+ export const ArchbaseBottomNavigation = forwardRef<HTMLDivElement, ArchbaseBottomNavigationProps>(
71
+ (
72
+ {
73
+ items,
74
+ value: controlledValue,
75
+ defaultValue,
76
+ onChange,
77
+ height = 64,
78
+ activeColor = 'blue',
79
+ inactiveColor = 'gray',
80
+ iconSize = 24,
81
+ labelSize = 'xs',
82
+ showLabels = true,
83
+ showActiveLabel = false,
84
+ withShadow = true,
85
+ withBorder = true,
86
+ backgroundColor = 'var(--mantine-color-body)',
87
+ fixed = true,
88
+ zIndex = 100,
89
+ className,
90
+ style,
91
+ },
92
+ ref
93
+ ) => {
94
+ const [internalValue, setInternalValue] = useState(defaultValue || items[0]?.key);
95
+
96
+ const currentValue = controlledValue !== undefined ? controlledValue : internalValue;
97
+
98
+ const handleItemClick = useCallback(
99
+ (item: ArchbaseBottomNavigationItem) => {
100
+ if (item.disabled) return;
101
+
102
+ if (controlledValue === undefined) {
103
+ setInternalValue(item.key);
104
+ }
105
+ onChange?.(item.key);
106
+ item.onClick?.();
107
+ },
108
+ [controlledValue, onChange]
109
+ );
110
+
111
+ return (
112
+ <Box
113
+ ref={ref}
114
+ className={className}
115
+ style={{
116
+ ...style,
117
+ position: fixed ? 'fixed' : 'relative',
118
+ bottom: 0,
119
+ left: 0,
120
+ right: 0,
121
+ height,
122
+ backgroundColor,
123
+ borderTop: withBorder ? '1px solid var(--mantine-color-gray-3)' : undefined,
124
+ boxShadow: withShadow ? '0 -2px 10px rgba(0, 0, 0, 0.1)' : undefined,
125
+ zIndex: fixed ? zIndex : undefined,
126
+ paddingBottom: 'env(safe-area-inset-bottom, 0px)',
127
+ display: 'flex',
128
+ alignItems: 'center',
129
+ justifyContent: 'space-around',
130
+ }}
131
+ >
132
+ {items.map((item) => {
133
+ const isActive = currentValue === item.key;
134
+ const shouldShowLabel = showLabels || (showActiveLabel && isActive);
135
+ const color = isActive ? activeColor : inactiveColor;
136
+ const icon = isActive && item.activeIcon ? item.activeIcon : item.icon;
137
+
138
+ const content = (
139
+ <Box
140
+ key={item.key}
141
+ onClick={() => handleItemClick(item)}
142
+ style={{
143
+ flex: 1,
144
+ display: 'flex',
145
+ flexDirection: 'column',
146
+ alignItems: 'center',
147
+ justifyContent: 'center',
148
+ height: '100%',
149
+ cursor: item.disabled ? 'not-allowed' : 'pointer',
150
+ opacity: item.disabled ? 0.5 : 1,
151
+ transition: 'all 0.2s ease',
152
+ position: 'relative',
153
+ }}
154
+ >
155
+ {/* Badge */}
156
+ {item.badge !== undefined && (
157
+ <Badge
158
+ color={item.badgeColor || 'red'}
159
+ size="xs"
160
+ variant="filled"
161
+ style={{
162
+ position: 'absolute',
163
+ top: 4,
164
+ right: '50%',
165
+ transform: 'translateX(calc(50% + 12px))',
166
+ minWidth: 18,
167
+ height: 18,
168
+ padding: '0 4px',
169
+ }}
170
+ >
171
+ {typeof item.badge === 'number' && item.badge > 99 ? '99+' : item.badge}
172
+ </Badge>
173
+ )}
174
+
175
+ {/* Icon */}
176
+ <Box
177
+ style={{
178
+ color: `var(--mantine-color-${color}-6)`,
179
+ transition: 'transform 0.2s ease, color 0.2s ease',
180
+ transform: isActive ? 'scale(1.1)' : 'scale(1)',
181
+ }}
182
+ >
183
+ {React.isValidElement(icon) ? React.cloneElement(icon, { size: iconSize } as Record<string, unknown>) : icon}
184
+ </Box>
185
+
186
+ {/* Label */}
187
+ {shouldShowLabel && (
188
+ <Text
189
+ size={labelSize}
190
+ c={`${color}.6`}
191
+ mt={2}
192
+ fw={isActive ? 600 : 400}
193
+ style={{
194
+ transition: 'color 0.2s ease',
195
+ }}
196
+ >
197
+ {item.label}
198
+ </Text>
199
+ )}
200
+
201
+ {/* Active indicator */}
202
+ {isActive && (
203
+ <Box
204
+ style={{
205
+ position: 'absolute',
206
+ top: 0,
207
+ left: '50%',
208
+ transform: 'translateX(-50%)',
209
+ width: 32,
210
+ height: 3,
211
+ backgroundColor: `var(--mantine-color-${activeColor}-6)`,
212
+ borderRadius: '0 0 4px 4px',
213
+ }}
214
+ />
215
+ )}
216
+ </Box>
217
+ );
218
+
219
+ // Se tem href, renderiza como link
220
+ if (item.href) {
221
+ return (
222
+ <Box
223
+ key={item.key}
224
+ component="a"
225
+ href={item.href}
226
+ style={{
227
+ flex: 1,
228
+ textDecoration: 'none',
229
+ }}
230
+ >
231
+ {content}
232
+ </Box>
233
+ );
234
+ }
235
+
236
+ return content;
237
+ })}
238
+ </Box>
239
+ );
240
+ }
241
+ );
242
+
243
+ ArchbaseBottomNavigation.displayName = 'ArchbaseBottomNavigation';
@@ -0,0 +1,298 @@
1
+ import React, { forwardRef, useCallback, useState, ReactNode, MouseEvent as ReactMouseEvent } from 'react';
2
+ import {
3
+ Menu,
4
+ MenuProps,
5
+ Box,
6
+ Text,
7
+ Divider,
8
+ MantineColor,
9
+ MantineSize,
10
+ Kbd,
11
+ } from '@mantine/core';
12
+ import { IconChevronRight } from '@tabler/icons-react';
13
+
14
+ export interface ArchbaseContextMenuItem {
15
+ /** Chave única do item */
16
+ key: string;
17
+ /** Tipo do item */
18
+ type?: 'item' | 'divider' | 'label';
19
+ /** Label do item */
20
+ label?: string;
21
+ /** Ícone do item */
22
+ icon?: ReactNode;
23
+ /** Ícone à direita */
24
+ rightSection?: ReactNode;
25
+ /** Atalho de teclado */
26
+ shortcut?: string;
27
+ /** Cor do item */
28
+ color?: MantineColor;
29
+ /** Se está desabilitado */
30
+ disabled?: boolean;
31
+ /** Callback ao clicar */
32
+ onClick?: () => void;
33
+ /** Subitems (submenu) */
34
+ children?: ArchbaseContextMenuItem[];
35
+ }
36
+
37
+ export interface ArchbaseContextMenuProps {
38
+ /** Itens do menu */
39
+ items: ArchbaseContextMenuItem[];
40
+ /** Elemento alvo (elemento que abre o menu ao clicar com botão direito) */
41
+ children: ReactNode;
42
+ /** Se está desabilitado */
43
+ disabled?: boolean;
44
+ /** Tamanho do menu */
45
+ size?: MantineSize;
46
+ /** Largura do menu */
47
+ width?: number | string;
48
+ /** Se deve mostrar sombra */
49
+ shadow?: MantineSize;
50
+ /** Raio das bordas */
51
+ radius?: MantineSize;
52
+ /** Offset do menu */
53
+ offset?: number;
54
+ /** Classe CSS do menu */
55
+ menuClassName?: string;
56
+ /** Estilo do menu */
57
+ menuStyle?: React.CSSProperties;
58
+ /** Callback ao abrir */
59
+ onOpen?: () => void;
60
+ /** Callback ao fechar */
61
+ onClose?: () => void;
62
+ /** Classe CSS */
63
+ className?: string;
64
+ /** Estilo */
65
+ style?: React.CSSProperties;
66
+ }
67
+
68
+ const ContextMenuItemComponent: React.FC<{
69
+ item: ArchbaseContextMenuItem;
70
+ onClose: () => void;
71
+ }> = ({ item, onClose }) => {
72
+ if (item.type === 'divider') {
73
+ return <Menu.Divider />;
74
+ }
75
+
76
+ if (item.type === 'label') {
77
+ return <Menu.Label>{item.label}</Menu.Label>;
78
+ }
79
+
80
+ // Se tem filhos, é um submenu
81
+ if (item.children && item.children.length > 0) {
82
+ return (
83
+ <Menu.Item
84
+ disabled={item.disabled}
85
+ color={item.color}
86
+ leftSection={item.icon}
87
+ rightSection={<IconChevronRight size={14} />}
88
+ >
89
+ <Menu trigger="hover" position="right-start" offset={5}>
90
+ <Menu.Target>
91
+ <Text size="sm">{item.label}</Text>
92
+ </Menu.Target>
93
+ <Menu.Dropdown>
94
+ {item.children.map((child) => (
95
+ <ContextMenuItemComponent key={child.key} item={child} onClose={onClose} />
96
+ ))}
97
+ </Menu.Dropdown>
98
+ </Menu>
99
+ </Menu.Item>
100
+ );
101
+ }
102
+
103
+ return (
104
+ <Menu.Item
105
+ disabled={item.disabled}
106
+ color={item.color}
107
+ leftSection={item.icon}
108
+ rightSection={
109
+ item.shortcut ? (
110
+ <Kbd size="xs">{item.shortcut}</Kbd>
111
+ ) : (
112
+ item.rightSection
113
+ )
114
+ }
115
+ onClick={() => {
116
+ item.onClick?.();
117
+ onClose();
118
+ }}
119
+ >
120
+ {item.label}
121
+ </Menu.Item>
122
+ );
123
+ };
124
+
125
+ export const ArchbaseContextMenu = forwardRef<HTMLDivElement, ArchbaseContextMenuProps>(
126
+ (
127
+ {
128
+ items,
129
+ children,
130
+ disabled = false,
131
+ size,
132
+ width = 200,
133
+ shadow = 'md',
134
+ radius = 'sm',
135
+ offset = 5,
136
+ menuClassName,
137
+ menuStyle,
138
+ onOpen,
139
+ onClose,
140
+ className,
141
+ style,
142
+ },
143
+ ref
144
+ ) => {
145
+ const [opened, setOpened] = useState(false);
146
+ const [position, setPosition] = useState({ x: 0, y: 0 });
147
+
148
+ const handleContextMenu = useCallback(
149
+ (event: ReactMouseEvent) => {
150
+ if (disabled) return;
151
+
152
+ event.preventDefault();
153
+ event.stopPropagation();
154
+
155
+ setPosition({ x: event.clientX, y: event.clientY });
156
+ setOpened(true);
157
+ onOpen?.();
158
+ },
159
+ [disabled, onOpen]
160
+ );
161
+
162
+ const handleClose = useCallback(() => {
163
+ setOpened(false);
164
+ onClose?.();
165
+ }, [onClose]);
166
+
167
+ return (
168
+ <>
169
+ <Box
170
+ ref={ref}
171
+ className={className}
172
+ style={style}
173
+ onContextMenu={handleContextMenu}
174
+ >
175
+ {children}
176
+ </Box>
177
+
178
+ <Menu
179
+ opened={opened}
180
+ onClose={handleClose}
181
+ position="bottom-start"
182
+ offset={offset}
183
+ shadow={shadow}
184
+ radius={radius}
185
+ width={width}
186
+ styles={{
187
+ dropdown: {
188
+ position: 'fixed',
189
+ left: position.x,
190
+ top: position.y,
191
+ ...menuStyle,
192
+ },
193
+ }}
194
+ classNames={{ dropdown: menuClassName }}
195
+ >
196
+ <Menu.Target>
197
+ <div style={{ position: 'fixed', left: position.x, top: position.y, width: 1, height: 1 }} />
198
+ </Menu.Target>
199
+ <Menu.Dropdown>
200
+ {items.map((item) => (
201
+ <ContextMenuItemComponent key={item.key} item={item} onClose={handleClose} />
202
+ ))}
203
+ </Menu.Dropdown>
204
+ </Menu>
205
+ </>
206
+ );
207
+ }
208
+ );
209
+
210
+ ArchbaseContextMenu.displayName = 'ArchbaseContextMenu';
211
+
212
+ // Hook para uso programático
213
+ export interface UseArchbaseContextMenuOptions {
214
+ items: ArchbaseContextMenuItem[];
215
+ onOpen?: () => void;
216
+ onClose?: () => void;
217
+ }
218
+
219
+ export interface UseArchbaseContextMenuReturn {
220
+ opened: boolean;
221
+ position: { x: number; y: number };
222
+ open: (event: ReactMouseEvent) => void;
223
+ close: () => void;
224
+ getHandlers: () => {
225
+ onContextMenu: (event: ReactMouseEvent) => void;
226
+ };
227
+ Menu: React.FC<{ children?: ReactNode }>;
228
+ }
229
+
230
+ export function useArchbaseContextMenu(
231
+ options: UseArchbaseContextMenuOptions
232
+ ): UseArchbaseContextMenuReturn {
233
+ const [opened, setOpened] = useState(false);
234
+ const [position, setPosition] = useState({ x: 0, y: 0 });
235
+
236
+ const open = useCallback(
237
+ (event: ReactMouseEvent) => {
238
+ event.preventDefault();
239
+ event.stopPropagation();
240
+ setPosition({ x: event.clientX, y: event.clientY });
241
+ setOpened(true);
242
+ options.onOpen?.();
243
+ },
244
+ [options]
245
+ );
246
+
247
+ const close = useCallback(() => {
248
+ setOpened(false);
249
+ options.onClose?.();
250
+ }, [options]);
251
+
252
+ const getHandlers = useCallback(
253
+ () => ({
254
+ onContextMenu: open,
255
+ }),
256
+ [open]
257
+ );
258
+
259
+ const ContextMenuComponent: React.FC<{ children?: ReactNode }> = useCallback(
260
+ () => (
261
+ <Menu
262
+ opened={opened}
263
+ onClose={close}
264
+ position="bottom-start"
265
+ offset={5}
266
+ shadow="md"
267
+ radius="sm"
268
+ width={200}
269
+ styles={{
270
+ dropdown: {
271
+ position: 'fixed',
272
+ left: position.x,
273
+ top: position.y,
274
+ },
275
+ }}
276
+ >
277
+ <Menu.Target>
278
+ <div style={{ position: 'fixed', left: position.x, top: position.y, width: 1, height: 1 }} />
279
+ </Menu.Target>
280
+ <Menu.Dropdown>
281
+ {options.items.map((item) => (
282
+ <ContextMenuItemComponent key={item.key} item={item} onClose={close} />
283
+ ))}
284
+ </Menu.Dropdown>
285
+ </Menu>
286
+ ),
287
+ [close, opened, options.items, position.x, position.y]
288
+ );
289
+
290
+ return {
291
+ opened,
292
+ position,
293
+ open,
294
+ close,
295
+ getHandlers,
296
+ Menu: ContextMenuComponent,
297
+ };
298
+ }