rharuow-ds 1.8.2 → 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
 
@@ -692,51 +1062,168 @@ Veja a story do componente no Storybook para demonstrações completas:
692
1062
 
693
1063
  ## 🎨 Customização de Tema
694
1064
 
695
- O rharuow-ds utiliza **CSS Variables** para permitir customização fácil do tema. Você pode modificar as cores primárias do design system definindo as seguintes variáveis CSS:
1065
+ O rharuow-ds utiliza um **sistema de cores baseado em brand colors** (cores da marca) que permite criar uma experiência visual coesa em toda a sua aplicação. Todos os componentes (Card, Table, Select, Tooltip) derivam suas cores das variáveis primárias e secundárias que você define.
1066
+
1067
+ ### 🌈 Sistema de Cores
1068
+
1069
+ #### Variáveis Principais
1070
+
1071
+ ```css
1072
+ :root {
1073
+ /* Cores Primárias - Personalizáveis */
1074
+ --primary: #2563eb; /* Cor principal da marca */
1075
+ --primary-hover: #1d4ed8; /* Hover da cor principal */
1076
+ --primary-light: #dbeafe; /* Versão clara para fundos */
1077
+
1078
+ /* Cores Secundárias - Personalizáveis */
1079
+ --secondary: #64748b; /* Cor secundária */
1080
+ --secondary-hover: #475569; /* Hover da cor secundária */
1081
+ --secondary-light: #f1f5f9; /* Versão clara para fundos */
1082
+ }
1083
+ ```
1084
+
1085
+ #### Como os Componentes Usam as Cores
1086
+
1087
+ Os componentes **derivam automaticamente** suas cores das variáveis primárias:
1088
+
1089
+ - **Card Header**: Mescla 5% da cor primária com fundo neutro
1090
+ - **Table Header**: Mescla 8% da cor primária com fundo neutro
1091
+ - **Table Hover**: Mescla 10% da cor primária com fundo neutro
1092
+ - **Select Selected**: Usa diretamente `--primary-light`
1093
+ - **Elementos Selecionados**: Consistentemente usam a cor primária clara
696
1094
 
697
- ### Variáveis Disponíveis
1095
+ ### 💡 Como Personalizar
1096
+
1097
+ #### Método 1: CSS Global (Recomendado)
1098
+
1099
+ No seu arquivo CSS principal (`index.css` ou `App.css`):
698
1100
 
699
1101
  ```css
1102
+ /* Importar o DS primeiro */
1103
+ @import 'rharuow-ds/dist/styles.css';
1104
+
1105
+ /* Depois sobrescrever as cores da marca */
700
1106
  :root {
701
- --primary: #2563eb; /* Cor primária principal */
702
- --primary-hover: #dbeafe; /* Cor para hover/background */
703
- --primary-text: #fff; /* Cor do texto em backgrounds primários */
704
- --input-bg: #fff; /* Background dos inputs */
705
- --input-text: #222; /* Cor do texto dos inputs */
1107
+ --primary: #8b5cf6; /* Roxo */
1108
+ --primary-hover: #7c3aed;
1109
+ --primary-light: #ede9fe;
1110
+
1111
+ --secondary: #ec4899; /* Rosa */
1112
+ --secondary-hover: #db2777;
1113
+ --secondary-light: #fce7f3;
1114
+ }
1115
+
1116
+ /* Para dark mode, customize também */
1117
+ [data-theme="dark"], .dark {
1118
+ --primary: #a78bfa; /* Roxo mais claro para dark */
1119
+ --primary-hover: #8b5cf6;
1120
+ --primary-light: #4c1d95;
1121
+
1122
+ --secondary: #f472b6;
1123
+ --secondary-hover: #ec4899;
1124
+ --secondary-light: #831843;
706
1125
  }
707
1126
  ```
708
1127
 
709
- ### Exemplo de Customização
1128
+ #### Método 2: JavaScript/React
1129
+
1130
+ ```typescript
1131
+ // App.tsx ou main.tsx
1132
+ useEffect(() => {
1133
+ const root = document.documentElement;
1134
+
1135
+ root.style.setProperty('--primary', '#f59e0b');
1136
+ root.style.setProperty('--primary-hover', '#d97706');
1137
+ root.style.setProperty('--primary-light', '#fef3c7');
1138
+ }, []);
1139
+ ```
1140
+
1141
+ ### 🎯 Exemplos de Paletas
710
1142
 
711
1143
  ```css
712
- /* Tema vermelho */
1144
+ /* Paleta Corporativa (Azul) */
1145
+ :root {
1146
+ --primary: #0ea5e9;
1147
+ --primary-hover: #0284c7;
1148
+ --primary-light: #e0f2fe;
1149
+ }
1150
+
1151
+ /* Paleta Moderna (Roxo/Rosa) */
713
1152
  :root {
714
- --primary: #dc2626;
715
- --primary-hover: #fecaca;
716
- --primary-text: #fff;
1153
+ --primary: #8b5cf6;
1154
+ --primary-hover: #7c3aed;
1155
+ --primary-light: #ede9fe;
1156
+ --secondary: #ec4899;
717
1157
  }
718
1158
 
719
- /* Tema verde */
1159
+ /* Paleta Natureza (Verde) */
720
1160
  :root {
721
- --primary: #059669;
722
- --primary-hover: #d1fae5;
723
- --primary-text: #fff;
1161
+ --primary: #10b981;
1162
+ --primary-hover: #059669;
1163
+ --primary-light: #d1fae5;
724
1164
  }
725
1165
 
726
- /* Tema roxo */
1166
+ /* Paleta Minimalista (Cinza) */
1167
+ :root {
1168
+ --primary: #6b7280;
1169
+ --primary-hover: #4b5563;
1170
+ --primary-light: #f3f4f6;
1171
+ }
1172
+ ```
1173
+
1174
+ ### 🔧 Customização Avançada
1175
+
1176
+ Para controle total, você pode sobrescrever variáveis específicas:
1177
+
1178
+ ```css
727
1179
  :root {
728
- --primary: #7c3aed;
729
- --primary-hover: #e9d5ff;
730
- --primary-text: #fff;
1180
+ /* Cores base da marca */
1181
+ --primary: #8b5cf6;
1182
+
1183
+ /* Customização específica de Card */
1184
+ --card-header-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1185
+ --card-header-border: #8b5cf6;
1186
+
1187
+ /* Customização específica de Table */
1188
+ --table-header-bg: #f3e8ff;
1189
+ --table-row-selected: #ede9fe;
731
1190
  }
732
1191
  ```
733
1192
 
1193
+ ### 📚 Variáveis Disponíveis por Componente
1194
+
1195
+ #### Card
1196
+ - `--card-bg`, `--card-border`, `--card-text`
1197
+ - `--card-header-bg`, `--card-header-border`
1198
+ - `--card-footer-bg`, `--card-footer-border`
1199
+
1200
+ #### Table
1201
+ - `--table-bg`, `--table-border`, `--table-text`
1202
+ - `--table-header-bg`, `--table-row-hover`, `--table-row-selected`
1203
+
1204
+ #### Select/AsyncSelect/MultiSelect
1205
+ - `--select-dropdown-bg`, `--select-dropdown-border`
1206
+ - `--select-dropdown-hover`, `--select-dropdown-selected`
1207
+
1208
+ #### Tooltip
1209
+ - `--tooltip-bg`, `--tooltip-text`
1210
+
1211
+ ### 🌓 Dark Mode
1212
+
1213
+ O sistema ajusta automaticamente as cores no dark mode:
1214
+
1215
+ ```css
1216
+ /* Ative o dark mode adicionando o atributo */
1217
+ <html data-theme="dark">
1218
+ <!-- ou -->
1219
+ <html class="dark">
1220
+ ```
1221
+
734
1222
  ### No Storybook
735
1223
 
736
- Na documentação do Storybook, você pode testar diferentes temas usando os controles na toolbar:
1224
+ Na [documentação do Storybook](https://rharuow.github.io/rharuow-ds-docs/), você pode testar diferentes temas na story **"Theme Customization"**.
737
1225
 
738
- - 🎨 **Primary Color**: Muda a cor principal
739
- - 🌈 **Primary Hover**: Muda a cor de hover/background
1226
+ Para mais detalhes, consulte [THEME_CUSTOMIZATION.md](./THEME_CUSTOMIZATION.md).
740
1227
 
741
1228
  ---
742
1229