forlogic-core 1.8.2 → 1.8.3
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/README.md +210 -2
- package/dist/README.md +210 -2
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.esm.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/assets/index-2WBeKMgq.css +0 -1
- package/dist/assets/index-Dg0JuUel.js +0 -696
- package/dist/index.html +0 -19
package/README.md
CHANGED
|
@@ -223,7 +223,7 @@ import { Button } from 'forlogic-core'
|
|
|
223
223
|
// Formulários
|
|
224
224
|
Button, Input, Textarea, Label, Select, SelectContent,
|
|
225
225
|
SelectItem, SelectTrigger, SelectValue, Checkbox, RadioGroup,
|
|
226
|
-
RadioGroupItem, Switch, CreatableCombobox
|
|
226
|
+
RadioGroupItem, Switch, CreatableCombobox, EntitySelect, MultiSelect
|
|
227
227
|
|
|
228
228
|
// Layout
|
|
229
229
|
Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle
|
|
@@ -477,6 +477,183 @@ export function DepartmentSelect(props: DepartmentSelectProps) {
|
|
|
477
477
|
|
|
478
478
|
---
|
|
479
479
|
|
|
480
|
+
#### MultiSelect - Seleção Múltipla Genérica
|
|
481
|
+
|
|
482
|
+
**Quando usar?**
|
|
483
|
+
|
|
484
|
+
Use o `MultiSelect` para permitir seleção múltipla de itens com busca inteligente e exibição em badges:
|
|
485
|
+
|
|
486
|
+
- ✅ Seleção de múltiplos processos, colaboradores, treinamentos, etc
|
|
487
|
+
- ✅ Busca com normalização (remove acentos, case-insensitive)
|
|
488
|
+
- ✅ Badges interativos para remover seleções
|
|
489
|
+
- ✅ Estados de loading e erro inclusos
|
|
490
|
+
- ✅ Limite de badges exibidos com contador (+N)
|
|
491
|
+
|
|
492
|
+
**Uso Básico:**
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import { MultiSelect } from 'forlogic-core';
|
|
496
|
+
import { useTrainings } from './trainingService';
|
|
497
|
+
|
|
498
|
+
function MyForm() {
|
|
499
|
+
const { data: trainings = [], isLoading, error } = useTrainings();
|
|
500
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
501
|
+
|
|
502
|
+
return (
|
|
503
|
+
<MultiSelect
|
|
504
|
+
value={selectedIds}
|
|
505
|
+
onChange={setSelectedIds}
|
|
506
|
+
options={trainings}
|
|
507
|
+
isLoading={isLoading}
|
|
508
|
+
error={error}
|
|
509
|
+
getOptionValue={(training) => training.id}
|
|
510
|
+
getOptionLabel={(training) => training.title}
|
|
511
|
+
placeholder="Selecione treinamentos..."
|
|
512
|
+
label="Treinamentos"
|
|
513
|
+
icon={GraduationCap}
|
|
514
|
+
/>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Props do MultiSelect:**
|
|
520
|
+
|
|
521
|
+
| Prop | Tipo | Obrigatório | Descrição |
|
|
522
|
+
| ---- | ---- | ----------- | --------- |
|
|
523
|
+
| `options` | `T[]` | ✅ | Array de itens disponíveis |
|
|
524
|
+
| `value` | `string[]` | ❌ | Array de IDs selecionados |
|
|
525
|
+
| `onChange` | `(value: string[]) => void` | ❌ | Callback quando seleção muda |
|
|
526
|
+
| `getOptionValue` | `(option: T) => string` | ❌ | Extrai valor único (default: `opt.value`) |
|
|
527
|
+
| `getOptionLabel` | `(option: T) => string` | ❌ | Extrai label exibido (default: `opt.label`) |
|
|
528
|
+
| `placeholder` | `string` | ❌ | Placeholder quando nada selecionado |
|
|
529
|
+
| `label` | `string` | ❌ | Label do campo |
|
|
530
|
+
| `icon` | `LucideIcon` | ❌ | Ícone exibido no trigger |
|
|
531
|
+
| `emptyMessage` | `string` | ❌ | Mensagem quando não há itens |
|
|
532
|
+
| `searchPlaceholder` | `string` | ❌ | Placeholder da busca |
|
|
533
|
+
| `disabled` | `boolean` | ❌ | Desabilita o componente |
|
|
534
|
+
| `required` | `boolean` | ❌ | Adiciona asterisco vermelho ao label |
|
|
535
|
+
| `isLoading` | `boolean` | ❌ | Exibe skeleton durante carregamento |
|
|
536
|
+
| `error` | `string \| boolean` | ❌ | Mensagem de erro |
|
|
537
|
+
| `className` | `string` | ❌ | Classes CSS adicionais |
|
|
538
|
+
| `sortOptions` | `boolean` | ❌ | Ordena alfabeticamente (default: true) |
|
|
539
|
+
| `maxDisplayedBadges` | `number` | ❌ | Limite de badges visíveis |
|
|
540
|
+
| `onOpen` | `() => void` | ❌ | Callback ao abrir popover |
|
|
541
|
+
| `onClose` | `() => void` | ❌ | Callback ao fechar popover |
|
|
542
|
+
|
|
543
|
+
**Exemplo com Objetos Customizados:**
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
interface User {
|
|
547
|
+
id: string;
|
|
548
|
+
name: string;
|
|
549
|
+
email: string;
|
|
550
|
+
department: string;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
<MultiSelect<User>
|
|
554
|
+
options={users}
|
|
555
|
+
value={selectedUserIds}
|
|
556
|
+
onChange={setSelectedUserIds}
|
|
557
|
+
getOptionValue={(user) => user.id}
|
|
558
|
+
getOptionLabel={(user) => `${user.name} - ${user.department}`}
|
|
559
|
+
placeholder="Selecione colaboradores..."
|
|
560
|
+
label="Colaboradores"
|
|
561
|
+
icon={Users}
|
|
562
|
+
required
|
|
563
|
+
/>
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Limite de Badges Exibidos:**
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
<MultiSelect
|
|
570
|
+
options={items}
|
|
571
|
+
value={selected}
|
|
572
|
+
onChange={setSelected}
|
|
573
|
+
maxDisplayedBadges={3} // Exibe: [Badge 1] [Badge 2] [Badge 3] [+5]
|
|
574
|
+
placeholder="Selecione itens..."
|
|
575
|
+
/>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Integração com React Hook Form:**
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
import { useForm } from 'react-hook-form';
|
|
582
|
+
import { MultiSelect } from 'forlogic-core';
|
|
583
|
+
|
|
584
|
+
function MyForm() {
|
|
585
|
+
const { control } = useForm();
|
|
586
|
+
const { data: categories = [] } = useCategories();
|
|
587
|
+
|
|
588
|
+
return (
|
|
589
|
+
<FormField
|
|
590
|
+
control={control}
|
|
591
|
+
name="category_ids"
|
|
592
|
+
render={({ field }) => (
|
|
593
|
+
<FormItem>
|
|
594
|
+
<FormControl>
|
|
595
|
+
<MultiSelect
|
|
596
|
+
value={field.value}
|
|
597
|
+
onChange={field.onChange}
|
|
598
|
+
options={categories}
|
|
599
|
+
getOptionValue={(c) => c.id}
|
|
600
|
+
getOptionLabel={(c) => c.name}
|
|
601
|
+
placeholder="Selecione categorias..."
|
|
602
|
+
label="Categorias"
|
|
603
|
+
/>
|
|
604
|
+
</FormControl>
|
|
605
|
+
<FormMessage />
|
|
606
|
+
</FormItem>
|
|
607
|
+
)}
|
|
608
|
+
/>
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**Usando em Formulários CRUD:**
|
|
614
|
+
|
|
615
|
+
Para usar o `MultiSelect` em formulários CRUD gerados automaticamente, crie um wrapper e use `type: 'custom'`:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
// 1. Criar wrapper do MultiSelect
|
|
619
|
+
// src/components/TrainingMultiSelect.tsx
|
|
620
|
+
import { MultiSelect } from 'forlogic-core';
|
|
621
|
+
import { useTrainings } from '@/trainings/trainingService';
|
|
622
|
+
import { GraduationCap } from 'lucide-react';
|
|
623
|
+
|
|
624
|
+
export function TrainingMultiSelect({ value, onChange, disabled, error }: any) {
|
|
625
|
+
const { data: trainings = [], isLoading } = useTrainings();
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<MultiSelect
|
|
629
|
+
value={value || []}
|
|
630
|
+
onChange={onChange}
|
|
631
|
+
options={trainings}
|
|
632
|
+
isLoading={isLoading}
|
|
633
|
+
error={error}
|
|
634
|
+
getOptionValue={(t) => t.id}
|
|
635
|
+
getOptionLabel={(t) => t.title}
|
|
636
|
+
disabled={disabled}
|
|
637
|
+
placeholder="Selecione treinamentos..."
|
|
638
|
+
icon={GraduationCap}
|
|
639
|
+
/>
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// 2. Usar na configuração do formulário com type: 'custom'
|
|
644
|
+
import { TrainingMultiSelect } from './components/TrainingMultiSelect';
|
|
645
|
+
|
|
646
|
+
{
|
|
647
|
+
name: 'training_ids',
|
|
648
|
+
label: 'Treinamentos',
|
|
649
|
+
type: 'custom' as const,
|
|
650
|
+
component: TrainingMultiSelect,
|
|
651
|
+
required: true
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
480
657
|
### 🎨 Campos Customizados no BaseForm
|
|
481
658
|
|
|
482
659
|
O `BaseForm` suporta campos totalmente customizados através do tipo `'custom'`, permitindo usar **qualquer componente React** como campo de formulário.
|
|
@@ -3825,11 +4002,42 @@ const config = useMemo((): CrudPageConfig<Process> => ({
|
|
|
3825
4002
|
|
|
3826
4003
|
Quando `enableBulkActions: true`, as seguintes ações ficam disponíveis automaticamente:
|
|
3827
4004
|
|
|
3828
|
-
- ✅ **Deletar em lote**: Deleta múltiplos itens selecionados (usa a mesma função do delete individual)
|
|
3829
4005
|
- ✅ **Checkbox "Selecionar Todos"**: No header da tabela para selecionar/deselecionar todos os itens da página
|
|
3830
4006
|
- ✅ **Indicador Visual**: Linhas/cards selecionados têm background destacado
|
|
3831
4007
|
- ✅ **Barra de Ações**: Aparece no topo quando há itens selecionados
|
|
3832
4008
|
|
|
4009
|
+
**⚠️ IMPORTANTE - Botão "Remover" Padrão:**
|
|
4010
|
+
|
|
4011
|
+
- ❌ **Se você definir `bulkActions` customizadas**, o botão "Remover" padrão **NÃO** será adicionado automaticamente
|
|
4012
|
+
- ✅ **Se você NÃO definir `bulkActions`**, um botão "Remover" padrão será adicionado automaticamente
|
|
4013
|
+
|
|
4014
|
+
```typescript
|
|
4015
|
+
// ❌ Duplicação: Botão padrão + custom "Remover"
|
|
4016
|
+
bulkActions: [
|
|
4017
|
+
{
|
|
4018
|
+
label: 'Remover', // ← Vai aparecer 2x (padrão + este)
|
|
4019
|
+
icon: Trash2,
|
|
4020
|
+
variant: 'destructive',
|
|
4021
|
+
action: async (items) => { /* ... */ }
|
|
4022
|
+
}
|
|
4023
|
+
]
|
|
4024
|
+
|
|
4025
|
+
// ✅ Correto: Apenas o padrão
|
|
4026
|
+
enableBulkActions: true,
|
|
4027
|
+
// Não definir bulkActions → Usa botão "Remover" padrão
|
|
4028
|
+
|
|
4029
|
+
// ✅ Correto: Apenas ações customizadas (sem padrão)
|
|
4030
|
+
bulkActions: [
|
|
4031
|
+
{
|
|
4032
|
+
label: 'Remover',
|
|
4033
|
+
icon: Trash2,
|
|
4034
|
+
variant: 'destructive',
|
|
4035
|
+
action: async (items) => { /* ... */ }
|
|
4036
|
+
}
|
|
4037
|
+
]
|
|
4038
|
+
```
|
|
4039
|
+
|
|
4040
|
+
|
|
3833
4041
|
### **Ações Customizadas**
|
|
3834
4042
|
|
|
3835
4043
|
Você pode adicionar ações customizadas além do delete padrão:
|
package/dist/README.md
CHANGED
|
@@ -223,7 +223,7 @@ import { Button } from 'forlogic-core'
|
|
|
223
223
|
// Formulários
|
|
224
224
|
Button, Input, Textarea, Label, Select, SelectContent,
|
|
225
225
|
SelectItem, SelectTrigger, SelectValue, Checkbox, RadioGroup,
|
|
226
|
-
RadioGroupItem, Switch, CreatableCombobox
|
|
226
|
+
RadioGroupItem, Switch, CreatableCombobox, EntitySelect, MultiSelect
|
|
227
227
|
|
|
228
228
|
// Layout
|
|
229
229
|
Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle
|
|
@@ -477,6 +477,183 @@ export function DepartmentSelect(props: DepartmentSelectProps) {
|
|
|
477
477
|
|
|
478
478
|
---
|
|
479
479
|
|
|
480
|
+
#### MultiSelect - Seleção Múltipla Genérica
|
|
481
|
+
|
|
482
|
+
**Quando usar?**
|
|
483
|
+
|
|
484
|
+
Use o `MultiSelect` para permitir seleção múltipla de itens com busca inteligente e exibição em badges:
|
|
485
|
+
|
|
486
|
+
- ✅ Seleção de múltiplos processos, colaboradores, treinamentos, etc
|
|
487
|
+
- ✅ Busca com normalização (remove acentos, case-insensitive)
|
|
488
|
+
- ✅ Badges interativos para remover seleções
|
|
489
|
+
- ✅ Estados de loading e erro inclusos
|
|
490
|
+
- ✅ Limite de badges exibidos com contador (+N)
|
|
491
|
+
|
|
492
|
+
**Uso Básico:**
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import { MultiSelect } from 'forlogic-core';
|
|
496
|
+
import { useTrainings } from './trainingService';
|
|
497
|
+
|
|
498
|
+
function MyForm() {
|
|
499
|
+
const { data: trainings = [], isLoading, error } = useTrainings();
|
|
500
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
501
|
+
|
|
502
|
+
return (
|
|
503
|
+
<MultiSelect
|
|
504
|
+
value={selectedIds}
|
|
505
|
+
onChange={setSelectedIds}
|
|
506
|
+
options={trainings}
|
|
507
|
+
isLoading={isLoading}
|
|
508
|
+
error={error}
|
|
509
|
+
getOptionValue={(training) => training.id}
|
|
510
|
+
getOptionLabel={(training) => training.title}
|
|
511
|
+
placeholder="Selecione treinamentos..."
|
|
512
|
+
label="Treinamentos"
|
|
513
|
+
icon={GraduationCap}
|
|
514
|
+
/>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Props do MultiSelect:**
|
|
520
|
+
|
|
521
|
+
| Prop | Tipo | Obrigatório | Descrição |
|
|
522
|
+
| ---- | ---- | ----------- | --------- |
|
|
523
|
+
| `options` | `T[]` | ✅ | Array de itens disponíveis |
|
|
524
|
+
| `value` | `string[]` | ❌ | Array de IDs selecionados |
|
|
525
|
+
| `onChange` | `(value: string[]) => void` | ❌ | Callback quando seleção muda |
|
|
526
|
+
| `getOptionValue` | `(option: T) => string` | ❌ | Extrai valor único (default: `opt.value`) |
|
|
527
|
+
| `getOptionLabel` | `(option: T) => string` | ❌ | Extrai label exibido (default: `opt.label`) |
|
|
528
|
+
| `placeholder` | `string` | ❌ | Placeholder quando nada selecionado |
|
|
529
|
+
| `label` | `string` | ❌ | Label do campo |
|
|
530
|
+
| `icon` | `LucideIcon` | ❌ | Ícone exibido no trigger |
|
|
531
|
+
| `emptyMessage` | `string` | ❌ | Mensagem quando não há itens |
|
|
532
|
+
| `searchPlaceholder` | `string` | ❌ | Placeholder da busca |
|
|
533
|
+
| `disabled` | `boolean` | ❌ | Desabilita o componente |
|
|
534
|
+
| `required` | `boolean` | ❌ | Adiciona asterisco vermelho ao label |
|
|
535
|
+
| `isLoading` | `boolean` | ❌ | Exibe skeleton durante carregamento |
|
|
536
|
+
| `error` | `string \| boolean` | ❌ | Mensagem de erro |
|
|
537
|
+
| `className` | `string` | ❌ | Classes CSS adicionais |
|
|
538
|
+
| `sortOptions` | `boolean` | ❌ | Ordena alfabeticamente (default: true) |
|
|
539
|
+
| `maxDisplayedBadges` | `number` | ❌ | Limite de badges visíveis |
|
|
540
|
+
| `onOpen` | `() => void` | ❌ | Callback ao abrir popover |
|
|
541
|
+
| `onClose` | `() => void` | ❌ | Callback ao fechar popover |
|
|
542
|
+
|
|
543
|
+
**Exemplo com Objetos Customizados:**
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
interface User {
|
|
547
|
+
id: string;
|
|
548
|
+
name: string;
|
|
549
|
+
email: string;
|
|
550
|
+
department: string;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
<MultiSelect<User>
|
|
554
|
+
options={users}
|
|
555
|
+
value={selectedUserIds}
|
|
556
|
+
onChange={setSelectedUserIds}
|
|
557
|
+
getOptionValue={(user) => user.id}
|
|
558
|
+
getOptionLabel={(user) => `${user.name} - ${user.department}`}
|
|
559
|
+
placeholder="Selecione colaboradores..."
|
|
560
|
+
label="Colaboradores"
|
|
561
|
+
icon={Users}
|
|
562
|
+
required
|
|
563
|
+
/>
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Limite de Badges Exibidos:**
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
<MultiSelect
|
|
570
|
+
options={items}
|
|
571
|
+
value={selected}
|
|
572
|
+
onChange={setSelected}
|
|
573
|
+
maxDisplayedBadges={3} // Exibe: [Badge 1] [Badge 2] [Badge 3] [+5]
|
|
574
|
+
placeholder="Selecione itens..."
|
|
575
|
+
/>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Integração com React Hook Form:**
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
import { useForm } from 'react-hook-form';
|
|
582
|
+
import { MultiSelect } from 'forlogic-core';
|
|
583
|
+
|
|
584
|
+
function MyForm() {
|
|
585
|
+
const { control } = useForm();
|
|
586
|
+
const { data: categories = [] } = useCategories();
|
|
587
|
+
|
|
588
|
+
return (
|
|
589
|
+
<FormField
|
|
590
|
+
control={control}
|
|
591
|
+
name="category_ids"
|
|
592
|
+
render={({ field }) => (
|
|
593
|
+
<FormItem>
|
|
594
|
+
<FormControl>
|
|
595
|
+
<MultiSelect
|
|
596
|
+
value={field.value}
|
|
597
|
+
onChange={field.onChange}
|
|
598
|
+
options={categories}
|
|
599
|
+
getOptionValue={(c) => c.id}
|
|
600
|
+
getOptionLabel={(c) => c.name}
|
|
601
|
+
placeholder="Selecione categorias..."
|
|
602
|
+
label="Categorias"
|
|
603
|
+
/>
|
|
604
|
+
</FormControl>
|
|
605
|
+
<FormMessage />
|
|
606
|
+
</FormItem>
|
|
607
|
+
)}
|
|
608
|
+
/>
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**Usando em Formulários CRUD:**
|
|
614
|
+
|
|
615
|
+
Para usar o `MultiSelect` em formulários CRUD gerados automaticamente, crie um wrapper e use `type: 'custom'`:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
// 1. Criar wrapper do MultiSelect
|
|
619
|
+
// src/components/TrainingMultiSelect.tsx
|
|
620
|
+
import { MultiSelect } from 'forlogic-core';
|
|
621
|
+
import { useTrainings } from '@/trainings/trainingService';
|
|
622
|
+
import { GraduationCap } from 'lucide-react';
|
|
623
|
+
|
|
624
|
+
export function TrainingMultiSelect({ value, onChange, disabled, error }: any) {
|
|
625
|
+
const { data: trainings = [], isLoading } = useTrainings();
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<MultiSelect
|
|
629
|
+
value={value || []}
|
|
630
|
+
onChange={onChange}
|
|
631
|
+
options={trainings}
|
|
632
|
+
isLoading={isLoading}
|
|
633
|
+
error={error}
|
|
634
|
+
getOptionValue={(t) => t.id}
|
|
635
|
+
getOptionLabel={(t) => t.title}
|
|
636
|
+
disabled={disabled}
|
|
637
|
+
placeholder="Selecione treinamentos..."
|
|
638
|
+
icon={GraduationCap}
|
|
639
|
+
/>
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// 2. Usar na configuração do formulário com type: 'custom'
|
|
644
|
+
import { TrainingMultiSelect } from './components/TrainingMultiSelect';
|
|
645
|
+
|
|
646
|
+
{
|
|
647
|
+
name: 'training_ids',
|
|
648
|
+
label: 'Treinamentos',
|
|
649
|
+
type: 'custom' as const,
|
|
650
|
+
component: TrainingMultiSelect,
|
|
651
|
+
required: true
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
480
657
|
### 🎨 Campos Customizados no BaseForm
|
|
481
658
|
|
|
482
659
|
O `BaseForm` suporta campos totalmente customizados através do tipo `'custom'`, permitindo usar **qualquer componente React** como campo de formulário.
|
|
@@ -3825,11 +4002,42 @@ const config = useMemo((): CrudPageConfig<Process> => ({
|
|
|
3825
4002
|
|
|
3826
4003
|
Quando `enableBulkActions: true`, as seguintes ações ficam disponíveis automaticamente:
|
|
3827
4004
|
|
|
3828
|
-
- ✅ **Deletar em lote**: Deleta múltiplos itens selecionados (usa a mesma função do delete individual)
|
|
3829
4005
|
- ✅ **Checkbox "Selecionar Todos"**: No header da tabela para selecionar/deselecionar todos os itens da página
|
|
3830
4006
|
- ✅ **Indicador Visual**: Linhas/cards selecionados têm background destacado
|
|
3831
4007
|
- ✅ **Barra de Ações**: Aparece no topo quando há itens selecionados
|
|
3832
4008
|
|
|
4009
|
+
**⚠️ IMPORTANTE - Botão "Remover" Padrão:**
|
|
4010
|
+
|
|
4011
|
+
- ❌ **Se você definir `bulkActions` customizadas**, o botão "Remover" padrão **NÃO** será adicionado automaticamente
|
|
4012
|
+
- ✅ **Se você NÃO definir `bulkActions`**, um botão "Remover" padrão será adicionado automaticamente
|
|
4013
|
+
|
|
4014
|
+
```typescript
|
|
4015
|
+
// ❌ Duplicação: Botão padrão + custom "Remover"
|
|
4016
|
+
bulkActions: [
|
|
4017
|
+
{
|
|
4018
|
+
label: 'Remover', // ← Vai aparecer 2x (padrão + este)
|
|
4019
|
+
icon: Trash2,
|
|
4020
|
+
variant: 'destructive',
|
|
4021
|
+
action: async (items) => { /* ... */ }
|
|
4022
|
+
}
|
|
4023
|
+
]
|
|
4024
|
+
|
|
4025
|
+
// ✅ Correto: Apenas o padrão
|
|
4026
|
+
enableBulkActions: true,
|
|
4027
|
+
// Não definir bulkActions → Usa botão "Remover" padrão
|
|
4028
|
+
|
|
4029
|
+
// ✅ Correto: Apenas ações customizadas (sem padrão)
|
|
4030
|
+
bulkActions: [
|
|
4031
|
+
{
|
|
4032
|
+
label: 'Remover',
|
|
4033
|
+
icon: Trash2,
|
|
4034
|
+
variant: 'destructive',
|
|
4035
|
+
action: async (items) => { /* ... */ }
|
|
4036
|
+
}
|
|
4037
|
+
]
|
|
4038
|
+
```
|
|
4039
|
+
|
|
4040
|
+
|
|
3833
4041
|
### **Ações Customizadas**
|
|
3834
4042
|
|
|
3835
4043
|
Você pode adicionar ações customizadas além do delete padrão:
|