@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.
- package/dist/buttons/ArchbaseSpeedDial.d.ts +59 -0
- package/dist/buttons/index.d.ts +2 -0
- package/dist/charts/ArchbaseArcGauge.d.ts +55 -0
- package/dist/charts/ArchbaseGantt.d.ts +70 -0
- package/dist/charts/ArchbaseLinearGauge.d.ts +55 -0
- package/dist/charts/ArchbaseResourceTimeline.d.ts +61 -0
- package/dist/charts/ArchbaseSparkline.d.ts +45 -0
- package/dist/charts/index.d.ts +10 -0
- package/dist/containers/ArchbaseExpansionPanel.d.ts +60 -0
- package/dist/containers/ArchbaseFloatingPanel.d.ts +91 -0
- package/dist/containers/index.d.ts +4 -0
- package/dist/datagrid/export/ArchbaseDataGridExcelExport.d.ts +67 -0
- package/dist/datagrid/export/index.d.ts +2 -0
- package/dist/datagrid/index.d.ts +1 -0
- package/dist/display/ArchbaseAnimationWrapper.d.ts +73 -0
- package/dist/display/ArchbaseBarcodeGenerator.d.ts +50 -0
- package/dist/display/ArchbaseCarousel.d.ts +100 -0
- package/dist/display/ArchbaseCodeViewer.d.ts +63 -0
- package/dist/display/ArchbaseDiffViewer.d.ts +41 -0
- package/dist/display/ArchbaseJsonTree.d.ts +34 -0
- package/dist/display/ArchbaseLightbox.d.ts +89 -0
- package/dist/display/ArchbaseMarquee.d.ts +46 -0
- package/dist/display/ArchbasePhotoAlbum.d.ts +58 -0
- package/dist/display/ArchbaseQRCode.d.ts +44 -0
- package/dist/display/ArchbaseTooltipRich.d.ts +82 -0
- package/dist/display/ArchbaseVirtualList.d.ts +116 -0
- package/dist/display/index.d.ts +24 -0
- package/dist/editors/ArchbaseAIPromptInput.d.ts +69 -0
- package/dist/editors/ArchbaseAsyncMultiSelect.d.ts +1 -1
- package/dist/editors/ArchbaseAsyncSelect.d.ts +1 -1
- package/dist/editors/ArchbaseBarcodeScanner.d.ts +63 -0
- package/dist/editors/ArchbaseCepInput.d.ts +26 -0
- package/dist/editors/ArchbaseColorGradientPicker.d.ts +80 -0
- package/dist/editors/ArchbaseCpfCnpjInput.d.ts +21 -0
- package/dist/editors/ArchbaseCurrencyInput.d.ts +25 -0
- package/dist/editors/ArchbaseDualListbox.d.ts +61 -0
- package/dist/editors/ArchbaseInPlaceEditor.d.ts +67 -0
- package/dist/editors/ArchbaseLightGrid.d.ts +56 -0
- package/dist/editors/ArchbaseMentionInput.d.ts +75 -0
- package/dist/editors/ArchbaseMultiEmail.d.ts +53 -0
- package/dist/editors/ArchbaseMultiViewCalendar.d.ts +47 -0
- package/dist/editors/ArchbaseNumberStepper.d.ts +66 -0
- package/dist/editors/ArchbaseOTPInput.d.ts +27 -0
- package/dist/editors/ArchbasePhoneInput.d.ts +52 -0
- package/dist/editors/ArchbaseRangeSlider.d.ts +22 -0
- package/dist/editors/ArchbaseSignaturePad.d.ts +60 -0
- package/dist/editors/ArchbaseSpeechToTextInput.d.ts +106 -0
- package/dist/editors/ArchbaseSpreadsheet.d.ts +56 -0
- package/dist/editors/ArchbaseTagInput.d.ts +55 -0
- package/dist/editors/index.d.ts +40 -0
- package/dist/feedback/ArchbaseChunkProgressBar.d.ts +35 -0
- package/dist/feedback/ArchbaseRipple.d.ts +43 -0
- package/dist/feedback/ArchbaseSkeleton.d.ts +115 -0
- package/dist/feedback/index.d.ts +6 -0
- package/dist/filters/ArchbaseColumnSelector.d.ts +38 -0
- package/dist/filters/index.d.ts +2 -0
- package/dist/forms/ArchbaseFieldArray.d.ts +80 -0
- package/dist/forms/ArchbaseFormWizard.d.ts +73 -0
- package/dist/forms/ArchbaseStepper.d.ts +51 -0
- package/dist/forms/index.d.ts +8 -0
- package/dist/forms/useArchbaseFormWizard.d.ts +74 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useArchbaseConfirm.d.ts +59 -0
- package/dist/image/editor/index.d.ts +43 -4
- package/dist/image/index.d.ts +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +22510 -13820
- package/dist/list/ArchbaseTreeList.d.ts +76 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/navigation/ArchbaseActionSheet.d.ts +70 -0
- package/dist/navigation/ArchbaseAppBar.d.ts +63 -0
- package/dist/navigation/ArchbaseBottomNavigation.d.ts +61 -0
- package/dist/navigation/ArchbaseContextMenu.d.ts +76 -0
- package/dist/navigation/ArchbaseKeyboardShortcuts.d.ts +85 -0
- package/dist/navigation/ArchbaseSpotlight.d.ts +69 -0
- package/dist/navigation/index.d.ts +12 -0
- package/dist/notification/ArchbaseNotificationCenter.d.ts +56 -0
- package/dist/notification/index.d.ts +2 -0
- package/dist/onboarding/ArchbaseOnboardingTour.d.ts +27 -76
- package/dist/viewers/ArchbaseDocViewer.d.ts +40 -0
- package/dist/viewers/ArchbasePdfBuilder.d.ts +53 -0
- package/dist/viewers/ArchbaseXmlViewer.d.ts +36 -0
- package/dist/viewers/index.d.ts +6 -0
- package/package.json +41 -18
- package/src/buttons/ArchbaseSpeedDial.tsx +282 -0
- package/src/buttons/index.ts +3 -0
- package/src/charts/ArchbaseArcGauge.tsx +325 -0
- package/src/charts/ArchbaseGantt.tsx +494 -0
- package/src/charts/ArchbaseLinearGauge.tsx +288 -0
- package/src/charts/ArchbaseResourceTimeline.tsx +433 -0
- package/src/charts/ArchbaseSparkline.tsx +193 -0
- package/src/charts/index.ts +20 -1
- package/src/containers/ArchbaseExpansionPanel.tsx +208 -0
- package/src/containers/ArchbaseFloatingPanel.tsx +448 -0
- package/src/containers/index.ts +17 -0
- package/src/datagrid/components/pagination/grid-pagination.tsx +2 -2
- package/src/datagrid/export/ArchbaseDataGridExcelExport.tsx +293 -0
- package/src/datagrid/export/index.ts +8 -0
- package/src/datagrid/index.tsx +2 -0
- package/src/display/ArchbaseAnimationWrapper.tsx +272 -0
- package/src/display/ArchbaseBarcodeGenerator.tsx +303 -0
- package/src/display/ArchbaseCarousel.tsx +679 -0
- package/src/display/ArchbaseCodeViewer.tsx +271 -0
- package/src/display/ArchbaseDiffViewer.tsx +108 -0
- package/src/display/ArchbaseJsonTree.tsx +150 -0
- package/src/display/ArchbaseLightbox.tsx +279 -0
- package/src/display/ArchbaseMarquee.tsx +268 -0
- package/src/display/ArchbasePhotoAlbum.tsx +415 -0
- package/src/display/ArchbaseQRCode.tsx +137 -0
- package/src/display/ArchbaseTooltipRich.tsx +317 -0
- package/src/display/ArchbaseVirtualList.tsx +618 -0
- package/src/display/index.ts +94 -1
- package/src/editors/ArchbaseAIPromptInput.tsx +432 -0
- package/src/editors/ArchbaseBarcodeScanner.tsx +570 -0
- package/src/editors/ArchbaseCepInput.tsx +173 -0
- package/src/editors/ArchbaseColorGradientPicker.tsx +460 -0
- package/src/editors/ArchbaseCpfCnpjInput.tsx +193 -0
- package/src/editors/ArchbaseCurrencyInput.tsx +100 -0
- package/src/editors/ArchbaseDualListbox.tsx +671 -0
- package/src/editors/ArchbaseInPlaceEditor.tsx +406 -0
- package/src/editors/ArchbaseLightGrid.tsx +538 -0
- package/src/editors/ArchbaseMentionInput.tsx +384 -0
- package/src/editors/ArchbaseMultiEmail.tsx +367 -0
- package/src/editors/ArchbaseMultiViewCalendar.tsx +439 -0
- package/src/editors/ArchbaseNumberStepper.tsx +219 -0
- package/src/editors/ArchbaseOTPInput.tsx +133 -0
- package/src/editors/ArchbaseOperationHoursEditor.tsx +1 -1
- package/src/editors/ArchbasePhoneInput.tsx +355 -0
- package/src/editors/ArchbaseRangeSlider.tsx +109 -0
- package/src/editors/ArchbaseSignaturePad.tsx +425 -0
- package/src/editors/ArchbaseSpeechToTextInput.tsx +444 -0
- package/src/editors/ArchbaseSpreadsheet.tsx +388 -0
- package/src/editors/ArchbaseTagInput.tsx +326 -0
- package/src/editors/index.tsx +92 -0
- package/src/feedback/ArchbaseChunkProgressBar.tsx +156 -0
- package/src/feedback/ArchbaseRipple.tsx +278 -0
- package/src/feedback/ArchbaseSkeleton.tsx +419 -0
- package/src/feedback/index.ts +22 -1
- package/src/filters/ArchbaseColumnSelector.tsx +268 -0
- package/src/filters/index.ts +2 -0
- package/src/forms/ArchbaseFieldArray.tsx +305 -0
- package/src/forms/ArchbaseFormWizard.tsx +284 -0
- package/src/forms/ArchbaseStepper.tsx +137 -0
- package/src/forms/index.ts +24 -2
- package/src/forms/useArchbaseFormWizard.ts +283 -0
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useArchbaseConfirm.ts +293 -0
- package/src/image/editor/functions/image-processing.ts +5 -0
- package/src/image/editor/index.tsx +305 -391
- package/src/image/index.ts +1 -0
- package/src/index.ts +3 -0
- package/src/list/ArchbaseTreeList.tsx +422 -0
- package/src/list/index.ts +3 -0
- package/src/navigation/ArchbaseActionSheet.tsx +301 -0
- package/src/navigation/ArchbaseAppBar.tsx +233 -0
- package/src/navigation/ArchbaseBottomNavigation.tsx +243 -0
- package/src/navigation/ArchbaseContextMenu.tsx +298 -0
- package/src/navigation/ArchbaseKeyboardShortcuts.tsx +472 -0
- package/src/navigation/ArchbaseSpotlight.tsx +483 -0
- package/src/navigation/index.ts +48 -1
- package/src/notification/ArchbaseNotificationCenter.tsx +502 -0
- package/src/notification/index.ts +2 -0
- package/src/onboarding/ArchbaseOnboardingTour.tsx +229 -102
- package/src/viewers/ArchbaseDocViewer.tsx +584 -0
- package/src/viewers/ArchbasePdfBuilder.tsx +291 -0
- package/src/viewers/ArchbaseXmlViewer.tsx +142 -0
- package/src/viewers/index.ts +12 -0
package/src/filters/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Componente principal
|
|
2
2
|
export { ArchbaseCompositeFilters } from './ArchbaseCompositeFilters';
|
|
3
|
+
export { ArchbaseColumnSelector } from './ArchbaseColumnSelector';
|
|
4
|
+
export type { ArchbaseColumnSelectorProps, ArchbaseColumnItem } from './ArchbaseColumnSelector';
|
|
3
5
|
|
|
4
6
|
// Componentes
|
|
5
7
|
export { FilterPill } from './components/FilterPill';
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import React, { forwardRef, useCallback, ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Button,
|
|
5
|
+
ActionIcon,
|
|
6
|
+
Group,
|
|
7
|
+
Stack,
|
|
8
|
+
Text,
|
|
9
|
+
Paper,
|
|
10
|
+
MantineColor,
|
|
11
|
+
MantineSize,
|
|
12
|
+
} from '@mantine/core';
|
|
13
|
+
import { IconPlus, IconTrash, IconGripVertical } from '@tabler/icons-react';
|
|
14
|
+
import { useFieldArray, UseFieldArrayReturn, Control, FieldValues, ArrayPath, FieldArrayWithId } from 'react-hook-form';
|
|
15
|
+
|
|
16
|
+
export interface ArchbaseFieldArrayItemProps<T extends FieldValues> {
|
|
17
|
+
/** Índice do item */
|
|
18
|
+
index: number;
|
|
19
|
+
/** Dados do item */
|
|
20
|
+
item: FieldArrayWithId<T>;
|
|
21
|
+
/** Função para remover o item */
|
|
22
|
+
remove: () => void;
|
|
23
|
+
/** Se pode remover (baseado em minItems) */
|
|
24
|
+
canRemove: boolean;
|
|
25
|
+
/** Se pode arrastar */
|
|
26
|
+
canDrag: boolean;
|
|
27
|
+
/** Props de drag handle */
|
|
28
|
+
dragHandleProps?: Record<string, any>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ArchbaseFieldArrayProps<T extends FieldValues> {
|
|
32
|
+
/** Nome do campo no form */
|
|
33
|
+
name: ArrayPath<T>;
|
|
34
|
+
/** Control do react-hook-form */
|
|
35
|
+
control: Control<T>;
|
|
36
|
+
/** Renderizador do item */
|
|
37
|
+
renderItem: (props: ArchbaseFieldArrayItemProps<T>) => ReactNode;
|
|
38
|
+
/** Valor padrão para novos itens */
|
|
39
|
+
defaultValue?: Record<string, any>;
|
|
40
|
+
/** Mínimo de itens */
|
|
41
|
+
minItems?: number;
|
|
42
|
+
/** Máximo de itens */
|
|
43
|
+
maxItems?: number;
|
|
44
|
+
/** Label do botão adicionar */
|
|
45
|
+
addLabel?: string;
|
|
46
|
+
/** Componente de empty state */
|
|
47
|
+
emptyState?: ReactNode;
|
|
48
|
+
/** Se permite reordenar */
|
|
49
|
+
sortable?: boolean;
|
|
50
|
+
/** Se deve mostrar o header */
|
|
51
|
+
showHeader?: boolean;
|
|
52
|
+
/** Título do campo */
|
|
53
|
+
title?: string;
|
|
54
|
+
/** Descrição do campo */
|
|
55
|
+
description?: string;
|
|
56
|
+
/** Cor do botão adicionar */
|
|
57
|
+
addButtonColor?: MantineColor;
|
|
58
|
+
/** Variante do botão adicionar */
|
|
59
|
+
addButtonVariant?: 'filled' | 'light' | 'outline' | 'subtle' | 'default';
|
|
60
|
+
/** Tamanho */
|
|
61
|
+
size?: MantineSize;
|
|
62
|
+
/** Se deve usar Paper em volta de cada item */
|
|
63
|
+
withPaper?: boolean;
|
|
64
|
+
/** Se deve mostrar índice */
|
|
65
|
+
showIndex?: boolean;
|
|
66
|
+
/** Espaçamento entre itens */
|
|
67
|
+
gap?: MantineSize | number;
|
|
68
|
+
/** Se está desabilitado */
|
|
69
|
+
disabled?: boolean;
|
|
70
|
+
/** Callback quando array muda */
|
|
71
|
+
onArrayChange?: (items: any[]) => void;
|
|
72
|
+
/** Classe CSS */
|
|
73
|
+
className?: string;
|
|
74
|
+
/** Estilo */
|
|
75
|
+
style?: React.CSSProperties;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function ArchbaseFieldArrayInner<T extends FieldValues>(
|
|
79
|
+
{
|
|
80
|
+
name,
|
|
81
|
+
control,
|
|
82
|
+
renderItem,
|
|
83
|
+
defaultValue = {},
|
|
84
|
+
minItems = 0,
|
|
85
|
+
maxItems,
|
|
86
|
+
addLabel = 'Adicionar',
|
|
87
|
+
emptyState,
|
|
88
|
+
sortable = false,
|
|
89
|
+
showHeader = true,
|
|
90
|
+
title,
|
|
91
|
+
description,
|
|
92
|
+
addButtonColor = 'blue',
|
|
93
|
+
addButtonVariant = 'light',
|
|
94
|
+
size = 'sm',
|
|
95
|
+
withPaper = true,
|
|
96
|
+
showIndex = false,
|
|
97
|
+
gap = 'sm',
|
|
98
|
+
disabled = false,
|
|
99
|
+
onArrayChange,
|
|
100
|
+
className,
|
|
101
|
+
style,
|
|
102
|
+
}: ArchbaseFieldArrayProps<T>,
|
|
103
|
+
ref: React.Ref<HTMLDivElement>
|
|
104
|
+
) {
|
|
105
|
+
const { fields, append, remove, move, swap } = useFieldArray({
|
|
106
|
+
control,
|
|
107
|
+
name,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const handleAdd = useCallback(() => {
|
|
111
|
+
if (maxItems && fields.length >= maxItems) return;
|
|
112
|
+
append(defaultValue as any);
|
|
113
|
+
onArrayChange?.([...fields, defaultValue]);
|
|
114
|
+
}, [append, defaultValue, fields, maxItems, onArrayChange]);
|
|
115
|
+
|
|
116
|
+
const handleRemove = useCallback(
|
|
117
|
+
(index: number) => {
|
|
118
|
+
if (fields.length <= minItems) return;
|
|
119
|
+
remove(index);
|
|
120
|
+
onArrayChange?.(fields.filter((_, i) => i !== index));
|
|
121
|
+
},
|
|
122
|
+
[fields, minItems, onArrayChange, remove]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const canAdd = !maxItems || fields.length < maxItems;
|
|
126
|
+
const canRemove = fields.length > minItems;
|
|
127
|
+
|
|
128
|
+
const DefaultEmptyState = (
|
|
129
|
+
<Paper p="xl" withBorder style={{ textAlign: 'center' }}>
|
|
130
|
+
<Text c="dimmed" size="sm">
|
|
131
|
+
Nenhum item adicionado
|
|
132
|
+
</Text>
|
|
133
|
+
<Button
|
|
134
|
+
mt="sm"
|
|
135
|
+
variant={addButtonVariant}
|
|
136
|
+
color={addButtonColor}
|
|
137
|
+
size={size}
|
|
138
|
+
leftSection={<IconPlus size={16} />}
|
|
139
|
+
onClick={handleAdd}
|
|
140
|
+
disabled={disabled || !canAdd}
|
|
141
|
+
>
|
|
142
|
+
{addLabel}
|
|
143
|
+
</Button>
|
|
144
|
+
</Paper>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<Stack ref={ref} gap={gap} className={className} style={style}>
|
|
149
|
+
{/* Header */}
|
|
150
|
+
{showHeader && (title || description) && (
|
|
151
|
+
<Box>
|
|
152
|
+
{title && (
|
|
153
|
+
<Text fw={500} size="sm">
|
|
154
|
+
{title}
|
|
155
|
+
</Text>
|
|
156
|
+
)}
|
|
157
|
+
{description && (
|
|
158
|
+
<Text size="xs" c="dimmed">
|
|
159
|
+
{description}
|
|
160
|
+
</Text>
|
|
161
|
+
)}
|
|
162
|
+
</Box>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{/* Items */}
|
|
166
|
+
{fields.length === 0 ? (
|
|
167
|
+
emptyState || DefaultEmptyState
|
|
168
|
+
) : (
|
|
169
|
+
<Stack gap={gap}>
|
|
170
|
+
{fields.map((field, index) => {
|
|
171
|
+
const itemContent = (
|
|
172
|
+
<Group gap="sm" wrap="nowrap" align="flex-start">
|
|
173
|
+
{/* Drag handle */}
|
|
174
|
+
{sortable && (
|
|
175
|
+
<ActionIcon
|
|
176
|
+
variant="subtle"
|
|
177
|
+
color="gray"
|
|
178
|
+
size={size}
|
|
179
|
+
style={{ cursor: 'grab' }}
|
|
180
|
+
disabled={disabled}
|
|
181
|
+
>
|
|
182
|
+
<IconGripVertical size={16} />
|
|
183
|
+
</ActionIcon>
|
|
184
|
+
)}
|
|
185
|
+
|
|
186
|
+
{/* Index */}
|
|
187
|
+
{showIndex && (
|
|
188
|
+
<Text size="sm" c="dimmed" w={30} ta="center">
|
|
189
|
+
{index + 1}
|
|
190
|
+
</Text>
|
|
191
|
+
)}
|
|
192
|
+
|
|
193
|
+
{/* Item content */}
|
|
194
|
+
<Box style={{ flex: 1 }}>
|
|
195
|
+
{renderItem({
|
|
196
|
+
index,
|
|
197
|
+
item: field,
|
|
198
|
+
remove: () => handleRemove(index),
|
|
199
|
+
canRemove: canRemove && !disabled,
|
|
200
|
+
canDrag: sortable && !disabled,
|
|
201
|
+
})}
|
|
202
|
+
</Box>
|
|
203
|
+
|
|
204
|
+
{/* Remove button */}
|
|
205
|
+
<ActionIcon
|
|
206
|
+
variant="subtle"
|
|
207
|
+
color="red"
|
|
208
|
+
size={size}
|
|
209
|
+
onClick={() => handleRemove(index)}
|
|
210
|
+
disabled={!canRemove || disabled}
|
|
211
|
+
title="Remover"
|
|
212
|
+
>
|
|
213
|
+
<IconTrash size={16} />
|
|
214
|
+
</ActionIcon>
|
|
215
|
+
</Group>
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
return withPaper ? (
|
|
219
|
+
<Paper key={field.id} p="sm" withBorder>
|
|
220
|
+
{itemContent}
|
|
221
|
+
</Paper>
|
|
222
|
+
) : (
|
|
223
|
+
<Box key={field.id}>{itemContent}</Box>
|
|
224
|
+
);
|
|
225
|
+
})}
|
|
226
|
+
|
|
227
|
+
{/* Add button */}
|
|
228
|
+
{canAdd && (
|
|
229
|
+
<Button
|
|
230
|
+
variant={addButtonVariant}
|
|
231
|
+
color={addButtonColor}
|
|
232
|
+
size={size}
|
|
233
|
+
leftSection={<IconPlus size={16} />}
|
|
234
|
+
onClick={handleAdd}
|
|
235
|
+
disabled={disabled}
|
|
236
|
+
>
|
|
237
|
+
{addLabel}
|
|
238
|
+
</Button>
|
|
239
|
+
)}
|
|
240
|
+
</Stack>
|
|
241
|
+
)}
|
|
242
|
+
</Stack>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export const ArchbaseFieldArray = forwardRef(ArchbaseFieldArrayInner) as <T extends FieldValues>(
|
|
247
|
+
props: ArchbaseFieldArrayProps<T> & { ref?: React.Ref<HTMLDivElement> }
|
|
248
|
+
) => React.ReactElement;
|
|
249
|
+
|
|
250
|
+
(ArchbaseFieldArray as any).displayName = 'ArchbaseFieldArray';
|
|
251
|
+
|
|
252
|
+
// ================== Hook simplificado ==================
|
|
253
|
+
|
|
254
|
+
export interface UseArchbaseFieldArrayOptions<T extends FieldValues> {
|
|
255
|
+
name: ArrayPath<T>;
|
|
256
|
+
control: Control<T>;
|
|
257
|
+
minItems?: number;
|
|
258
|
+
maxItems?: number;
|
|
259
|
+
defaultValue?: Record<string, any>;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export interface UseArchbaseFieldArrayReturn<T extends FieldValues>
|
|
263
|
+
extends UseFieldArrayReturn<T> {
|
|
264
|
+
canAdd: boolean;
|
|
265
|
+
canRemove: boolean;
|
|
266
|
+
addItem: () => void;
|
|
267
|
+
removeItem: (index: number) => void;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function useArchbaseFieldArray<T extends FieldValues>(
|
|
271
|
+
options: UseArchbaseFieldArrayOptions<T>
|
|
272
|
+
): UseArchbaseFieldArrayReturn<T> {
|
|
273
|
+
const { minItems = 0, maxItems, defaultValue = {} } = options;
|
|
274
|
+
|
|
275
|
+
const fieldArray = useFieldArray({
|
|
276
|
+
control: options.control,
|
|
277
|
+
name: options.name,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const canAdd = !maxItems || fieldArray.fields.length < maxItems;
|
|
281
|
+
const canRemove = fieldArray.fields.length > minItems;
|
|
282
|
+
|
|
283
|
+
const addItem = useCallback(() => {
|
|
284
|
+
if (canAdd) {
|
|
285
|
+
fieldArray.append(defaultValue as any);
|
|
286
|
+
}
|
|
287
|
+
}, [canAdd, defaultValue, fieldArray]);
|
|
288
|
+
|
|
289
|
+
const removeItem = useCallback(
|
|
290
|
+
(index: number) => {
|
|
291
|
+
if (canRemove) {
|
|
292
|
+
fieldArray.remove(index);
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
[canRemove, fieldArray]
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
...fieldArray,
|
|
300
|
+
canAdd,
|
|
301
|
+
canRemove,
|
|
302
|
+
addItem,
|
|
303
|
+
removeItem,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import React, { forwardRef, ReactNode, useCallback, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Stepper,
|
|
4
|
+
Button,
|
|
5
|
+
Group,
|
|
6
|
+
Stack,
|
|
7
|
+
Paper,
|
|
8
|
+
MantineColor,
|
|
9
|
+
MantineSize,
|
|
10
|
+
Box,
|
|
11
|
+
Alert,
|
|
12
|
+
Loader,
|
|
13
|
+
} from '@mantine/core';
|
|
14
|
+
import { IconAlertCircle, IconCheck, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
|
|
15
|
+
import { ArchbaseDataSource } from '@archbase/data';
|
|
16
|
+
import { getI18nextInstance } from '@archbase/core';
|
|
17
|
+
import { useArchbaseFormWizard, FormWizardStep } from './useArchbaseFormWizard';
|
|
18
|
+
|
|
19
|
+
export interface ArchbaseFormWizardStep<T = any> extends FormWizardStep<T> {
|
|
20
|
+
/** Componente React a ser renderizado no step */
|
|
21
|
+
component: ReactNode | ((props: { data: T; stepIndex: number }) => ReactNode);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ArchbaseFormWizardProps<T extends object, ID = string> {
|
|
25
|
+
/** Lista de steps do wizard */
|
|
26
|
+
steps: ArchbaseFormWizardStep<T>[];
|
|
27
|
+
/** DataSource vinculado ao formulário */
|
|
28
|
+
dataSource?: ArchbaseDataSource<T, ID>;
|
|
29
|
+
/** Step inicial */
|
|
30
|
+
initialStep?: number;
|
|
31
|
+
/** Callback ao completar todos os steps */
|
|
32
|
+
onComplete?: (data: T) => void | Promise<void>;
|
|
33
|
+
/** Callback ao mudar de step */
|
|
34
|
+
onStepChange?: (fromStep: number, toStep: number, data: T) => void;
|
|
35
|
+
/** Se deve validar ao avançar */
|
|
36
|
+
validateOnNext?: boolean;
|
|
37
|
+
/** Se permite voltar para steps anteriores */
|
|
38
|
+
allowBack?: boolean;
|
|
39
|
+
/** Se permite clicar nos steps para navegar */
|
|
40
|
+
allowStepClick?: boolean;
|
|
41
|
+
/** Orientação do stepper */
|
|
42
|
+
orientation?: 'horizontal' | 'vertical';
|
|
43
|
+
/** Tamanho do stepper */
|
|
44
|
+
size?: MantineSize;
|
|
45
|
+
/** Cor do stepper */
|
|
46
|
+
color?: MantineColor;
|
|
47
|
+
/** Label do botão anterior */
|
|
48
|
+
backLabel?: string;
|
|
49
|
+
/** Label do botão próximo */
|
|
50
|
+
nextLabel?: string;
|
|
51
|
+
/** Label do botão finalizar */
|
|
52
|
+
completeLabel?: string;
|
|
53
|
+
/** Ícone do botão anterior */
|
|
54
|
+
backIcon?: ReactNode;
|
|
55
|
+
/** Ícone do botão próximo */
|
|
56
|
+
nextIcon?: ReactNode;
|
|
57
|
+
/** Ícone do botão finalizar */
|
|
58
|
+
completeIcon?: ReactNode;
|
|
59
|
+
/** Se deve mostrar os botões de navegação */
|
|
60
|
+
showNavigation?: boolean;
|
|
61
|
+
/** Posição dos botões de navegação */
|
|
62
|
+
navigationPosition?: 'top' | 'bottom' | 'both';
|
|
63
|
+
/** Componente de revisão final (opcional) */
|
|
64
|
+
reviewComponent?: ReactNode | ((props: { data: T }) => ReactNode);
|
|
65
|
+
/** Componente de conclusão */
|
|
66
|
+
completedComponent?: ReactNode | ((props: { data: T; onReset: () => void }) => ReactNode);
|
|
67
|
+
/** Se deve mostrar o step de revisão */
|
|
68
|
+
showReviewStep?: boolean;
|
|
69
|
+
/** Padding do conteúdo */
|
|
70
|
+
contentPadding?: number | string;
|
|
71
|
+
/** Altura mínima do conteúdo */
|
|
72
|
+
contentMinHeight?: number | string;
|
|
73
|
+
/** Classe CSS adicional */
|
|
74
|
+
className?: string;
|
|
75
|
+
/** Estilos adicionais */
|
|
76
|
+
style?: React.CSSProperties;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const ArchbaseFormWizard = forwardRef<HTMLDivElement, ArchbaseFormWizardProps<any, any>>(
|
|
80
|
+
<T extends object, ID = string>(
|
|
81
|
+
{
|
|
82
|
+
steps,
|
|
83
|
+
dataSource,
|
|
84
|
+
initialStep = 0,
|
|
85
|
+
onComplete,
|
|
86
|
+
onStepChange,
|
|
87
|
+
validateOnNext = true,
|
|
88
|
+
allowBack = true,
|
|
89
|
+
allowStepClick = false,
|
|
90
|
+
orientation = 'horizontal',
|
|
91
|
+
size = 'md',
|
|
92
|
+
color,
|
|
93
|
+
backLabel,
|
|
94
|
+
nextLabel,
|
|
95
|
+
completeLabel,
|
|
96
|
+
backIcon = <IconChevronLeft size={16} />,
|
|
97
|
+
nextIcon = <IconChevronRight size={16} />,
|
|
98
|
+
completeIcon = <IconCheck size={16} />,
|
|
99
|
+
showNavigation = true,
|
|
100
|
+
navigationPosition = 'bottom',
|
|
101
|
+
reviewComponent,
|
|
102
|
+
completedComponent,
|
|
103
|
+
showReviewStep = false,
|
|
104
|
+
contentPadding = 'md',
|
|
105
|
+
contentMinHeight = 300,
|
|
106
|
+
className,
|
|
107
|
+
style,
|
|
108
|
+
}: ArchbaseFormWizardProps<T, ID>,
|
|
109
|
+
ref: React.Ref<HTMLDivElement>
|
|
110
|
+
) => {
|
|
111
|
+
const t = getI18nextInstance().t;
|
|
112
|
+
|
|
113
|
+
const wizardSteps = useMemo(() => {
|
|
114
|
+
const baseSteps = [...steps];
|
|
115
|
+
if (showReviewStep && reviewComponent) {
|
|
116
|
+
baseSteps.push({
|
|
117
|
+
id: '__review__',
|
|
118
|
+
title: t('archbase:Revisão'),
|
|
119
|
+
description: t('archbase:Revise suas informações'),
|
|
120
|
+
component: reviewComponent,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return baseSteps;
|
|
124
|
+
}, [steps, showReviewStep, reviewComponent, t]);
|
|
125
|
+
|
|
126
|
+
const wizard = useArchbaseFormWizard<T, ID>({
|
|
127
|
+
steps: wizardSteps,
|
|
128
|
+
dataSource,
|
|
129
|
+
initialStep,
|
|
130
|
+
onComplete,
|
|
131
|
+
onStepChange,
|
|
132
|
+
validateOnNext,
|
|
133
|
+
allowBack,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const getData = useCallback((): T => {
|
|
137
|
+
if (dataSource) {
|
|
138
|
+
return dataSource.getCurrentRecord() as T;
|
|
139
|
+
}
|
|
140
|
+
return {} as T;
|
|
141
|
+
}, [dataSource]);
|
|
142
|
+
|
|
143
|
+
const handleStepClick = useCallback(
|
|
144
|
+
async (stepIndex: number) => {
|
|
145
|
+
if (!allowStepClick) return;
|
|
146
|
+
await wizard.goToStep(stepIndex);
|
|
147
|
+
},
|
|
148
|
+
[allowStepClick, wizard]
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const renderStepContent = useCallback(
|
|
152
|
+
(step: ArchbaseFormWizardStep<T>, index: number) => {
|
|
153
|
+
if (typeof step.component === 'function') {
|
|
154
|
+
return step.component({ data: getData(), stepIndex: index });
|
|
155
|
+
}
|
|
156
|
+
return step.component;
|
|
157
|
+
},
|
|
158
|
+
[getData]
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const renderNavigation = useCallback(() => {
|
|
162
|
+
if (!showNavigation) return null;
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<Group justify="space-between" mt="xl">
|
|
166
|
+
<Button
|
|
167
|
+
variant="default"
|
|
168
|
+
onClick={wizard.prevStep}
|
|
169
|
+
disabled={!wizard.canGoBack}
|
|
170
|
+
leftSection={backIcon}
|
|
171
|
+
>
|
|
172
|
+
{backLabel || t('archbase:Anterior')}
|
|
173
|
+
</Button>
|
|
174
|
+
|
|
175
|
+
{wizard.isLastStep ? (
|
|
176
|
+
<Button
|
|
177
|
+
onClick={wizard.complete}
|
|
178
|
+
loading={wizard.isValidating}
|
|
179
|
+
rightSection={completeIcon}
|
|
180
|
+
color={color}
|
|
181
|
+
>
|
|
182
|
+
{completeLabel || t('archbase:Finalizar')}
|
|
183
|
+
</Button>
|
|
184
|
+
) : (
|
|
185
|
+
<Button
|
|
186
|
+
onClick={wizard.nextStep}
|
|
187
|
+
loading={wizard.isValidating}
|
|
188
|
+
rightSection={nextIcon}
|
|
189
|
+
color={color}
|
|
190
|
+
>
|
|
191
|
+
{nextLabel || t('archbase:Próximo')}
|
|
192
|
+
</Button>
|
|
193
|
+
)}
|
|
194
|
+
</Group>
|
|
195
|
+
);
|
|
196
|
+
}, [
|
|
197
|
+
backIcon,
|
|
198
|
+
backLabel,
|
|
199
|
+
color,
|
|
200
|
+
completeIcon,
|
|
201
|
+
completeLabel,
|
|
202
|
+
nextIcon,
|
|
203
|
+
nextLabel,
|
|
204
|
+
showNavigation,
|
|
205
|
+
t,
|
|
206
|
+
wizard,
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
const renderCompleted = useCallback(() => {
|
|
210
|
+
if (completedComponent) {
|
|
211
|
+
if (typeof completedComponent === 'function') {
|
|
212
|
+
return completedComponent({ data: getData(), onReset: wizard.reset });
|
|
213
|
+
}
|
|
214
|
+
return completedComponent;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<Stack align="center" gap="md" py="xl">
|
|
219
|
+
<IconCheck size={64} color="var(--mantine-color-green-6)" />
|
|
220
|
+
<Box ta="center">
|
|
221
|
+
<h3>{t('archbase:Concluído!')}</h3>
|
|
222
|
+
<p>{t('archbase:Todos os passos foram completados com sucesso.')}</p>
|
|
223
|
+
</Box>
|
|
224
|
+
<Button variant="light" onClick={wizard.reset}>
|
|
225
|
+
{t('archbase:Recomeçar')}
|
|
226
|
+
</Button>
|
|
227
|
+
</Stack>
|
|
228
|
+
);
|
|
229
|
+
}, [completedComponent, getData, t, wizard.reset]);
|
|
230
|
+
|
|
231
|
+
if (wizard.isCompleted) {
|
|
232
|
+
return (
|
|
233
|
+
<Paper ref={ref} p={contentPadding} className={className} style={style}>
|
|
234
|
+
{renderCompleted()}
|
|
235
|
+
</Paper>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<Stack ref={ref} className={className} style={style} gap="md">
|
|
241
|
+
{navigationPosition === 'top' && renderNavigation()}
|
|
242
|
+
|
|
243
|
+
<Stepper
|
|
244
|
+
active={wizard.currentStep}
|
|
245
|
+
onStepClick={allowStepClick ? handleStepClick : undefined}
|
|
246
|
+
orientation={orientation}
|
|
247
|
+
size={size}
|
|
248
|
+
color={color}
|
|
249
|
+
>
|
|
250
|
+
{wizardSteps.map((step, index) => (
|
|
251
|
+
<Stepper.Step
|
|
252
|
+
key={step.id || index}
|
|
253
|
+
label={step.title}
|
|
254
|
+
description={step.description}
|
|
255
|
+
icon={step.icon}
|
|
256
|
+
allowStepSelect={allowStepClick && wizard.isStepCompleted(index)}
|
|
257
|
+
completedIcon={wizard.isStepCompleted(index) ? <IconCheck size={16} /> : undefined}
|
|
258
|
+
>
|
|
259
|
+
<Box mih={contentMinHeight} py={contentPadding}>
|
|
260
|
+
{wizard.validationError && (
|
|
261
|
+
<Alert
|
|
262
|
+
icon={<IconAlertCircle size={16} />}
|
|
263
|
+
title={t('archbase:Erro de validação')}
|
|
264
|
+
color="red"
|
|
265
|
+
mb="md"
|
|
266
|
+
>
|
|
267
|
+
{wizard.validationError}
|
|
268
|
+
</Alert>
|
|
269
|
+
)}
|
|
270
|
+
{renderStepContent(step, index)}
|
|
271
|
+
</Box>
|
|
272
|
+
</Stepper.Step>
|
|
273
|
+
))}
|
|
274
|
+
</Stepper>
|
|
275
|
+
|
|
276
|
+
{(navigationPosition === 'bottom' || navigationPosition === 'both') && renderNavigation()}
|
|
277
|
+
</Stack>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
) as <T extends object, ID = string>(
|
|
281
|
+
props: ArchbaseFormWizardProps<T, ID> & { ref?: React.Ref<HTMLDivElement> }
|
|
282
|
+
) => React.ReactElement;
|
|
283
|
+
|
|
284
|
+
(ArchbaseFormWizard as any).displayName = 'ArchbaseFormWizard';
|