rharuow-ds 1.9.0 → 1.10.0

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 CHANGED
@@ -44,7 +44,7 @@ Um Design System moderno em React com integração completa ao React Hook Form,
44
44
  ## 🌟 Características
45
45
 
46
46
  - ⚛️ **React 18+** com TypeScript
47
- - 🧩 **15 componentes** prontos para uso (Input, Textarea, Select, AsyncSelect, MultiSelect, MultiAsyncSelect, RadioGroup, Button, Card, Table, Tooltip, Accordion, AsideSheet, ImageInput)
47
+ - 🧩 **17 componentes** prontos para uso (Input, Textarea, Select, AsyncSelect, MultiSelect, MultiAsyncSelect, RadioGroup, Button, Card, Table, Tooltip, Accordion, AsideSheet, Modal, Toaster, ImageInput)
48
48
  - 💡 **Filtro digitável** em componentes Select - Digite para encontrar opções rapidamente
49
49
  - 🔗 **Integração nativa** com React Hook Form
50
50
  - 🎨 **Customização via CSS Variables** - Mude o tema facilmente
@@ -112,6 +112,8 @@ npm install react-hook-form
112
112
  Tooltip,
113
113
  Accordion,
114
114
  AsideSheet,
115
+ Modal,
116
+ Toaster,
115
117
  ImageInput,
116
118
  } from "rharuow-ds";
117
119
 
@@ -199,6 +201,8 @@ npm install react-hook-form
199
201
  Tooltip,
200
202
  Accordion,
201
203
  AsideSheet,
204
+ Modal,
205
+ Toaster,
202
206
  ImageInput,
203
207
  } from "rharuow-ds";
204
208
 
@@ -582,7 +586,373 @@ Veja a story do componente no Storybook para demonstrações e variações (left
582
586
 
583
587
  [Storybook — AsideSheet](https://rharuow.github.io/rharuow-ds-docs/?path=/story/asidesheet--default)
584
588
 
585
- ### 📷 **ImageInput**
589
+ ### 🎭 **Modal**
590
+
591
+ Componente de diálogo modal para exibir conteúdo sobreposto à página principal.
592
+
593
+ - ✅ Overlay com transparência configurável
594
+ - ✅ Múltiplos tamanhos: sm, md, lg, xl, full
595
+ - ✅ Controle de fechamento via overlay, ESC ou botão X
596
+ - ✅ Prevenção de scroll do body quando aberto
597
+ - ✅ Animações suaves de entrada/saída
598
+ - ✅ Sub-componentes para estruturação: Header, Body, Footer
599
+ - ✅ Renderização via Portal (React Portal)
600
+ - ✅ Acessível: role="dialog", aria-modal
601
+
602
+ Props principais:
603
+
604
+ - `open: boolean` — controla visibilidade do modal
605
+ - `onClose: () => void` — callback chamado ao fechar
606
+ - `size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'` — tamanho do modal (padrão: 'md')
607
+ - `closeOnOverlayClick?: boolean` — fecha ao clicar fora (padrão: true)
608
+ - `closeOnEscape?: boolean` — fecha ao pressionar ESC (padrão: true)
609
+ - `showCloseButton?: boolean` — exibe botão X de fechar (padrão: true)
610
+ - `className?: string` — classes adicionais para o container do modal
611
+
612
+ Exemplo de uso básico:
613
+
614
+ ```tsx
615
+ import React from 'react';
616
+ import { Modal, Button } from 'rharuow-ds';
617
+
618
+ function Example() {
619
+ const [open, setOpen] = React.useState(false);
620
+
621
+ return (
622
+ <div>
623
+ <Button onClick={() => setOpen(true)}>Abrir Modal</Button>
624
+
625
+ <Modal open={open} onClose={() => setOpen(false)}>
626
+ <h2>Título do Modal</h2>
627
+ <p>Conteúdo do modal aqui.</p>
628
+ </Modal>
629
+ </div>
630
+ );
631
+ }
632
+ ```
633
+
634
+ Exemplo com estrutura completa:
635
+
636
+ ```tsx
637
+ import React from 'react';
638
+ import { Modal, Button } from 'rharuow-ds';
639
+
640
+ function Example() {
641
+ const [open, setOpen] = React.useState(false);
642
+
643
+ return (
644
+ <div>
645
+ <Button onClick={() => setOpen(true)}>Confirmar Ação</Button>
646
+
647
+ <Modal
648
+ open={open}
649
+ onClose={() => setOpen(false)}
650
+ size="md"
651
+ >
652
+ <Modal.Header>
653
+ <h2 className="text-2xl font-bold">Confirmar Exclusão</h2>
654
+ <p className="text-sm text-gray-500">Esta ação não pode ser desfeita</p>
655
+ </Modal.Header>
656
+
657
+ <Modal.Body>
658
+ <p className="text-gray-700">
659
+ Você tem certeza que deseja excluir este item?
660
+ Todos os dados associados serão removidos permanentemente.
661
+ </p>
662
+ </Modal.Body>
663
+
664
+ <Modal.Footer>
665
+ <Button variant="outline" onClick={() => setOpen(false)}>
666
+ Cancelar
667
+ </Button>
668
+ <Button onClick={() => {
669
+ // Executar ação
670
+ setOpen(false);
671
+ }}>
672
+ Confirmar
673
+ </Button>
674
+ </Modal.Footer>
675
+ </Modal>
676
+ </div>
677
+ );
678
+ }
679
+ ```
680
+
681
+ Exemplo com formulário integrado:
682
+
683
+ ```tsx
684
+ import React from 'react';
685
+ import { Modal, Button, Input } from 'rharuow-ds';
686
+ import { FormProvider, useForm } from 'react-hook-form';
687
+
688
+ function FormModal() {
689
+ const [open, setOpen] = React.useState(false);
690
+ const methods = useForm();
691
+
692
+ const onSubmit = (data: any) => {
693
+ console.log('Form data:', data);
694
+ setOpen(false);
695
+ methods.reset();
696
+ };
697
+
698
+ return (
699
+ <div>
700
+ <Button onClick={() => setOpen(true)}>Novo Cadastro</Button>
701
+
702
+ <Modal
703
+ open={open}
704
+ onClose={() => setOpen(false)}
705
+ size="lg"
706
+ closeOnOverlayClick={false}
707
+ >
708
+ <FormProvider {...methods}>
709
+ <form onSubmit={methods.handleSubmit(onSubmit)}>
710
+ <Modal.Header>
711
+ <h2 className="text-2xl font-bold">Cadastrar Usuário</h2>
712
+ </Modal.Header>
713
+
714
+ <Modal.Body>
715
+ <div className="space-y-4">
716
+ <Input label="Nome completo" name="name" required />
717
+ <Input label="E-mail" name="email" type="email" required />
718
+ <Input label="Telefone" name="phone" />
719
+ </div>
720
+ </Modal.Body>
721
+
722
+ <Modal.Footer>
723
+ <Button
724
+ type="button"
725
+ variant="outline"
726
+ onClick={() => setOpen(false)}
727
+ >
728
+ Cancelar
729
+ </Button>
730
+ <Button type="submit">Salvar</Button>
731
+ </Modal.Footer>
732
+ </form>
733
+ </FormProvider>
734
+ </Modal>
735
+ </div>
736
+ );
737
+ }
738
+ ```
739
+
740
+ Veja a story do componente no Storybook para mais exemplos e variações:
741
+
742
+ [Storybook — Modal](https://rharuow.github.io/rharuow-ds-docs/?path=/story/components-modal--basic)
743
+
744
+ ### � **Toaster**
745
+
746
+ Sistema completo de notificações toast para feedback ao usuário com múltiplas variantes e posicionamento flexível.
747
+
748
+ - ✅ 5 variantes de toast: success, error, warning, info, default
749
+ - ✅ 6 posições configuráveis na tela
750
+ - ✅ Auto-dismiss com duração customizável
751
+ - ✅ Toast permanente (duration: 0)
752
+ - ✅ Ícones automáticos por variante
753
+ - ✅ Animações suaves de entrada e saída
754
+ - ✅ Limite de toasts simultâneos (padrão: 5)
755
+ - ✅ Callbacks ao fechar
756
+ - ✅ Hook `useToast` para uso simplificado
757
+ - ✅ Gerenciamento via Context API
758
+
759
+ **Configuração inicial:**
760
+
761
+ O Toaster precisa ser configurado uma única vez no nível superior da aplicação:
762
+
763
+ ```tsx
764
+ import React from 'react';
765
+ import { ToasterProvider } from 'rharuow-ds';
766
+
767
+ function App() {
768
+ return (
769
+ <ToasterProvider position="top-right" maxToasts={5}>
770
+ {/* Sua aplicação aqui */}
771
+ <YourApp />
772
+ </ToasterProvider>
773
+ );
774
+ }
775
+ ```
776
+
777
+ Props do `ToasterProvider`:
778
+
779
+ - `position?: ToastPosition` - Posição dos toasts na tela (padrão: 'top-right')
780
+ - Opções: 'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'
781
+ - `maxToasts?: number` - Número máximo de toasts simultâneos (padrão: 5)
782
+
783
+ **Uso básico com hook `useToast`:**
784
+
785
+ ```tsx
786
+ import React from 'react';
787
+ import { useToast, Button } from 'rharuow-ds';
788
+
789
+ function MyComponent() {
790
+ const toast = useToast();
791
+
792
+ return (
793
+ <div>
794
+ <Button onClick={() => toast.success('Operação realizada com sucesso!')}>
795
+ Success
796
+ </Button>
797
+
798
+ <Button onClick={() => toast.error('Erro ao processar requisição')}>
799
+ Error
800
+ </Button>
801
+
802
+ <Button onClick={() => toast.warning('Atenção: verifique os dados')}>
803
+ Warning
804
+ </Button>
805
+
806
+ <Button onClick={() => toast.info('Você tem 3 novas mensagens')}>
807
+ Info
808
+ </Button>
809
+ </div>
810
+ );
811
+ }
812
+ ```
813
+
814
+ **Toasts com duração customizada:**
815
+
816
+ ```tsx
817
+ import React from 'react';
818
+ import { useToast, Button } from 'rharuow-ds';
819
+
820
+ function CustomDuration() {
821
+ const toast = useToast();
822
+
823
+ return (
824
+ <div>
825
+ {/* Toast rápido - 2 segundos */}
826
+ <Button onClick={() => toast.success('Toast rápido', 2000)}>
827
+ 2 Segundos
828
+ </Button>
829
+
830
+ {/* Toast longo - 10 segundos */}
831
+ <Button onClick={() => toast.info('Toast longo', 10000)}>
832
+ 10 Segundos
833
+ </Button>
834
+
835
+ {/* Toast permanente - não fecha automaticamente */}
836
+ <Button
837
+ onClick={() => toast.toast('Toast permanente', { duration: 0 })}
838
+ >
839
+ Permanente
840
+ </Button>
841
+ </div>
842
+ );
843
+ }
844
+ ```
845
+
846
+ **Toast com callback ao fechar:**
847
+
848
+ ```tsx
849
+ import React from 'react';
850
+ import { useToaster, Button } from 'rharuow-ds';
851
+
852
+ function WithCallback() {
853
+ const { addToast } = useToaster();
854
+
855
+ const handleAction = () => {
856
+ addToast({
857
+ message: 'Processando dados...',
858
+ variant: 'info',
859
+ duration: 3000,
860
+ onClose: () => {
861
+ console.log('Toast fechado!');
862
+ // Executar ação após fechamento
863
+ performNextAction();
864
+ },
865
+ });
866
+ };
867
+
868
+ return <Button onClick={handleAction}>Iniciar Processo</Button>;
869
+ }
870
+ ```
871
+
872
+ **Exemplo completo em um formulário:**
873
+
874
+ ```tsx
875
+ import React from 'react';
876
+ import { useForm, FormProvider } from 'react-hook-form';
877
+ import { Input, Button, useToast } from 'rharuow-ds';
878
+
879
+ function FormWithToast() {
880
+ const methods = useForm();
881
+ const toast = useToast();
882
+
883
+ const onSubmit = async (data: any) => {
884
+ try {
885
+ // Simular chamada à API
886
+ await saveData(data);
887
+
888
+ toast.success('Dados salvos com sucesso!');
889
+ methods.reset();
890
+ } catch (error) {
891
+ toast.error('Erro ao salvar dados. Tente novamente.');
892
+ console.error(error);
893
+ }
894
+ };
895
+
896
+ return (
897
+ <FormProvider {...methods}>
898
+ <form onSubmit={methods.handleSubmit(onSubmit)}>
899
+ <Input label="Nome" name="name" required />
900
+ <Input label="E-mail" name="email" type="email" required />
901
+
902
+ <Button type="submit">Salvar</Button>
903
+ </form>
904
+ </FormProvider>
905
+ );
906
+ }
907
+ ```
908
+
909
+ **API do hook `useToast`:**
910
+
911
+ ```typescript
912
+ const toast = useToast();
913
+
914
+ // Métodos disponíveis:
915
+ toast.success(message: string, duration?: number)
916
+ toast.error(message: string, duration?: number)
917
+ toast.warning(message: string, duration?: number)
918
+ toast.info(message: string, duration?: number)
919
+ toast.toast(message: string, options?: ToastOptions)
920
+ ```
921
+
922
+ **API avançada com `useToaster`:**
923
+
924
+ ```typescript
925
+ const { addToast, removeToast, clearAll, toasts } = useToaster();
926
+
927
+ // Adicionar toast com controle total
928
+ const id = addToast({
929
+ message: 'Mensagem personalizada',
930
+ variant: 'success',
931
+ duration: 5000,
932
+ onClose: () => console.log('Fechado'),
933
+ });
934
+
935
+ // Remover toast específico
936
+ removeToast(id);
937
+
938
+ // Limpar todos os toasts
939
+ clearAll();
940
+ ```
941
+
942
+ **Dicas de uso:**
943
+
944
+ - Use `success` para operações bem-sucedidas (save, delete, update)
945
+ - Use `error` para falhas e erros
946
+ - Use `warning` para avisos que requerem atenção
947
+ - Use `info` para informações gerais
948
+ - Configure `duration: 0` para toasts que precisam de ação manual do usuário
949
+ - Posicione toasts conforme o contexto: top para notificações gerais, bottom para ações específicas
950
+
951
+ Veja a story do componente no Storybook para demonstrações interativas:
952
+
953
+ [Storybook — Toaster](https://rharuow.github.io/rharuow-ds-docs/?path=/story/components-toaster--top-right)
954
+
955
+ ### �📷 **ImageInput**
586
956
 
587
957
  Componente para seleção e upload de imagens com preview e ações de confirmação/remoção:
588
958