@evolution-soft/ui 1.0.0 → 1.0.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/cli/index.js +386 -0
- package/components/button-icon-lottie/index.tsx +46 -0
- package/components/fullscreen-mode/index.tsx +82 -0
- package/components/header/components/buttons.tsx +102 -0
- package/components/header/index.tsx +146 -0
- package/components/loading-default/index.tsx +90 -0
- package/components/lottie-icon/index.tsx +78 -0
- package/components/not-found-default/index.tsx +68 -0
- package/components/settings-modal/index.tsx +225 -0
- package/components/sidebar/index.tsx +645 -0
- package/components/subtitle/index.tsx +60 -0
- package/components/theme-transition/index.tsx +142 -0
- package/components/title/index.tsx +66 -0
- package/components/tooltip-indicator/index.tsx +30 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +157 -0
- package/components/ui/alert.tsx +66 -0
- package/components/ui/aspect-ratio.tsx +11 -0
- package/components/ui/avatar.tsx +53 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/breadcrumb.tsx +109 -0
- package/components/ui/button.tsx +58 -0
- package/components/ui/calendar.tsx +78 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +360 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +177 -0
- package/components/ui/context-menu.tsx +252 -0
- package/components/ui/dialog.tsx +135 -0
- package/components/ui/divisor.tsx +9 -0
- package/components/ui/drawer.tsx +132 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/emoji-picker.tsx +76 -0
- package/components/ui/form.tsx +168 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/input-mask.tsx +46 -0
- package/components/ui/input-otp.tsx +77 -0
- package/components/ui/input.tsx +61 -0
- package/components/ui/label.tsx +24 -0
- package/components/ui/menubar.tsx +276 -0
- package/components/ui/multiselect.tsx +105 -0
- package/components/ui/navigation-menu.tsx +168 -0
- package/components/ui/pagination.tsx +127 -0
- package/components/ui/popover.tsx +48 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/radio-group.tsx +45 -0
- package/components/ui/resizable.tsx +65 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/searchable-select.tsx +211 -0
- package/components/ui/select.tsx +189 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/sheet.tsx +139 -0
- package/components/ui/sidebar.tsx +727 -0
- package/components/ui/skeleton.tsx +144 -0
- package/components/ui/slider.tsx +63 -0
- package/components/ui/sonner.tsx +26 -0
- package/components/ui/switch.tsx +31 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/tabs.tsx +76 -0
- package/components/ui/textarea.tsx +18 -0
- package/components/ui/theme-toggle.tsx +89 -0
- package/components/ui/toggle-group.tsx +73 -0
- package/components/ui/toggle.tsx +47 -0
- package/components/ui/tooltip.tsx +61 -0
- package/components/ui/use-mobile.ts +21 -0
- package/components/ui/utils.ts +6 -0
- package/contexts/AnimationSettingsContext.tsx +85 -0
- package/contexts/AuthContext.tsx +80 -0
- package/contexts/ThemeContext.tsx +70 -0
- package/hooks/useAnimationSettings.ts +2 -0
- package/hooks/usePermissions.ts +4 -0
- package/lib/persistentFilters.ts +120 -0
- package/lib/utils.ts +2 -0
- package/package.json +11 -2
- package/stores/theme.ts +30 -0
- package/stores/useThemeStore.ts +32 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
5
|
+
|
|
6
|
+
import { cn } from "./utils";
|
|
7
|
+
|
|
8
|
+
function TooltipProvider({
|
|
9
|
+
delayDuration = 0,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
12
|
+
return (
|
|
13
|
+
<TooltipPrimitive.Provider
|
|
14
|
+
data-slot="tooltip-provider"
|
|
15
|
+
delayDuration={delayDuration}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Tooltip({
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
24
|
+
return (
|
|
25
|
+
<TooltipProvider>
|
|
26
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
27
|
+
</TooltipProvider>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function TooltipTrigger({
|
|
32
|
+
...props
|
|
33
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
34
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function TooltipContent({
|
|
38
|
+
className,
|
|
39
|
+
sideOffset = 0,
|
|
40
|
+
children,
|
|
41
|
+
...props
|
|
42
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
43
|
+
return (
|
|
44
|
+
<TooltipPrimitive.Portal>
|
|
45
|
+
<TooltipPrimitive.Content
|
|
46
|
+
data-slot="tooltip-content"
|
|
47
|
+
sideOffset={sideOffset}
|
|
48
|
+
className={cn(
|
|
49
|
+
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
|
56
|
+
</TooltipPrimitive.Content>
|
|
57
|
+
</TooltipPrimitive.Portal>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768;
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
7
|
+
undefined,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
+
const onChange = () => {
|
|
13
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
+
};
|
|
15
|
+
mql.addEventListener("change", onChange);
|
|
16
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
+
return () => mql.removeEventListener("change", onChange);
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
return !!isMobile;
|
|
21
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useState, useEffect } from 'react';
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
interface AnimationSettingsContextType {
|
|
7
|
+
lottieAnimationsEnabled: boolean;
|
|
8
|
+
otherLottieAnimationsEnabled: boolean;
|
|
9
|
+
setLottieAnimationsEnabled: (enabled: boolean) => void;
|
|
10
|
+
setOtherLottieAnimationsEnabled: (enabled: boolean) => void;
|
|
11
|
+
toggleLottieAnimations: () => void;
|
|
12
|
+
toggleOtherLottieAnimations: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const AnimationSettingsContext = createContext<AnimationSettingsContextType | undefined>(undefined);
|
|
16
|
+
|
|
17
|
+
interface AnimationSettingsProviderProps {
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function AnimationSettingsProvider({ children }: AnimationSettingsProviderProps) {
|
|
22
|
+
const [lottieAnimationsEnabled, setLottieAnimationsEnabledState] = useState(true);
|
|
23
|
+
const [otherLottieAnimationsEnabled, setOtherLottieAnimationsEnabledState] = useState(true);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const savedAnimations = localStorage.getItem('lottie-animations-enabled');
|
|
27
|
+
if (savedAnimations !== null) {
|
|
28
|
+
setLottieAnimationsEnabledState(JSON.parse(savedAnimations));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const savedOtherAnimations = localStorage.getItem('other-lottie-animations-enabled');
|
|
32
|
+
if (savedOtherAnimations !== null) {
|
|
33
|
+
setOtherLottieAnimationsEnabledState(JSON.parse(savedOtherAnimations));
|
|
34
|
+
}
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
const setLottieAnimationsEnabled = (enabled: boolean) => {
|
|
38
|
+
setLottieAnimationsEnabledState(enabled);
|
|
39
|
+
localStorage.setItem('lottie-animations-enabled', JSON.stringify(enabled));
|
|
40
|
+
|
|
41
|
+
window.dispatchEvent(new CustomEvent('lottie-settings-changed', {
|
|
42
|
+
detail: { enabled }
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const setOtherLottieAnimationsEnabled = (enabled: boolean) => {
|
|
47
|
+
setOtherLottieAnimationsEnabledState(enabled);
|
|
48
|
+
localStorage.setItem('other-lottie-animations-enabled', JSON.stringify(enabled));
|
|
49
|
+
|
|
50
|
+
window.dispatchEvent(new CustomEvent('other-lottie-settings-changed', {
|
|
51
|
+
detail: { enabled }
|
|
52
|
+
}));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const toggleLottieAnimations = () => {
|
|
56
|
+
setLottieAnimationsEnabled(!lottieAnimationsEnabled);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const toggleOtherLottieAnimations = () => {
|
|
60
|
+
setOtherLottieAnimationsEnabled(!otherLottieAnimationsEnabled);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<AnimationSettingsContext.Provider
|
|
65
|
+
value={{
|
|
66
|
+
lottieAnimationsEnabled,
|
|
67
|
+
otherLottieAnimationsEnabled,
|
|
68
|
+
setLottieAnimationsEnabled,
|
|
69
|
+
setOtherLottieAnimationsEnabled,
|
|
70
|
+
toggleLottieAnimations,
|
|
71
|
+
toggleOtherLottieAnimations,
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
{children}
|
|
75
|
+
</AnimationSettingsContext.Provider>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useAnimationSettings() {
|
|
80
|
+
const context = useContext(AnimationSettingsContext);
|
|
81
|
+
if (context === undefined) {
|
|
82
|
+
throw new Error('useAnimationSettings must be used within an AnimationSettingsProvider');
|
|
83
|
+
}
|
|
84
|
+
return context;
|
|
85
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { createContext, useContext, useState, useEffect } from 'react';
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface User {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
email: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface AuthContextType {
|
|
13
|
+
user: User | null;
|
|
14
|
+
isAuthenticated: boolean;
|
|
15
|
+
login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>;
|
|
16
|
+
logout: () => Promise<void>;
|
|
17
|
+
initialLoading: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
21
|
+
|
|
22
|
+
export function useAuth() {
|
|
23
|
+
const context = useContext(AuthContext);
|
|
24
|
+
if (context === undefined) {
|
|
25
|
+
throw new Error('useAuth must be used within an AuthProvider');
|
|
26
|
+
}
|
|
27
|
+
return context;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface AuthProviderProps {
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function AuthProvider({ children }: AuthProviderProps) {
|
|
35
|
+
const [user, setUser] = useState<User | null>(null);
|
|
36
|
+
const [loading, setLoading] = useState(true);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
try {
|
|
40
|
+
const savedUser = localStorage.getItem('auth_user');
|
|
41
|
+
if (savedUser) {
|
|
42
|
+
setUser(JSON.parse(savedUser));
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
localStorage.removeItem('auth_user');
|
|
46
|
+
}
|
|
47
|
+
setLoading(false);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
const login = async (email: string, password: string): Promise<{ success: boolean; error?: string }> => {
|
|
51
|
+
try {
|
|
52
|
+
// Stub: accept any credentials in Storybook
|
|
53
|
+
const mockUser: User = { id: '1', name: 'Storybook User', email };
|
|
54
|
+
setUser(mockUser);
|
|
55
|
+
localStorage.setItem('auth_user', JSON.stringify(mockUser));
|
|
56
|
+
return { success: true };
|
|
57
|
+
} catch {
|
|
58
|
+
return { success: false, error: 'Erro de conexão. Tente novamente.' };
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const logout = async () => {
|
|
63
|
+
setUser(null);
|
|
64
|
+
localStorage.removeItem('auth_user');
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const value: AuthContextType = {
|
|
68
|
+
user,
|
|
69
|
+
isAuthenticated: !!user,
|
|
70
|
+
login,
|
|
71
|
+
logout,
|
|
72
|
+
initialLoading: loading
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<AuthContext.Provider value={value}>
|
|
77
|
+
{children}
|
|
78
|
+
</AuthContext.Provider>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
type Theme = 'light' | 'dark';
|
|
4
|
+
|
|
5
|
+
interface ThemeContextType {
|
|
6
|
+
theme: Theme;
|
|
7
|
+
toggleTheme: () => void;
|
|
8
|
+
mounted: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
12
|
+
|
|
13
|
+
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
14
|
+
const [mounted, setMounted] = useState(false);
|
|
15
|
+
const [theme, setTheme] = useState<Theme>('light');
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setMounted(true);
|
|
19
|
+
|
|
20
|
+
const savedTheme = localStorage.getItem('theme') as Theme;
|
|
21
|
+
let initialTheme: Theme = 'light';
|
|
22
|
+
|
|
23
|
+
if (savedTheme && (savedTheme === 'light' || savedTheme === 'dark')) {
|
|
24
|
+
initialTheme = savedTheme;
|
|
25
|
+
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
26
|
+
initialTheme = 'dark';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setTheme(initialTheme);
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!mounted) return;
|
|
34
|
+
|
|
35
|
+
const root = window.document.documentElement;
|
|
36
|
+
root.classList.remove('light', 'dark');
|
|
37
|
+
|
|
38
|
+
root.classList.add(theme);
|
|
39
|
+
|
|
40
|
+
localStorage.setItem('theme', theme);
|
|
41
|
+
|
|
42
|
+
root.style.display = 'none';
|
|
43
|
+
root.offsetHeight;
|
|
44
|
+
root.style.display = '';
|
|
45
|
+
}, [theme, mounted]);
|
|
46
|
+
|
|
47
|
+
const toggleTheme = () => {
|
|
48
|
+
document.body.classList.add('theme-transition-disabled');
|
|
49
|
+
|
|
50
|
+
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
|
|
51
|
+
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
document.body.classList.remove('theme-transition-disabled');
|
|
54
|
+
}, 50);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<ThemeContext.Provider value={{ theme, toggleTheme, mounted }}>
|
|
59
|
+
{children}
|
|
60
|
+
</ThemeContext.Provider>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function useTheme() {
|
|
65
|
+
const context = useContext(ThemeContext);
|
|
66
|
+
if (context === undefined) {
|
|
67
|
+
throw new Error('useTheme must be used within a ThemeProvider');
|
|
68
|
+
}
|
|
69
|
+
return context;
|
|
70
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilitários para gerenciamento de filtros persistentes
|
|
3
|
+
* Permite salvar e carregar filtros entre sessões para diferentes módulos
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface FilterConfig {
|
|
7
|
+
page?: number;
|
|
8
|
+
itemsPerPage?: number;
|
|
9
|
+
search?: string | null;
|
|
10
|
+
sortOrder?: 'asc' | 'desc';
|
|
11
|
+
sortField?: string;
|
|
12
|
+
timestamp?: number;
|
|
13
|
+
|
|
14
|
+
situacao?: string | null;
|
|
15
|
+
startDate?: string | null;
|
|
16
|
+
endDate?: string | null;
|
|
17
|
+
searchTerm?: string;
|
|
18
|
+
selectedLines?: string[];
|
|
19
|
+
selectedLinhas?: number[];
|
|
20
|
+
possuiHorarios?: string;
|
|
21
|
+
selectedSituacao?: "Ativo" | "Inativo" | undefined;
|
|
22
|
+
filtroTurno?: string;
|
|
23
|
+
filtroAgrupamento?: string;
|
|
24
|
+
filtroLinhaProducao?: string;
|
|
25
|
+
filtroStatus?: string;
|
|
26
|
+
filtroOperador?: string;
|
|
27
|
+
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class PersistentFilters {
|
|
32
|
+
private static readonly SETTINGS_KEY = 'persistent-filters-enabled';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Verifica se os filtros persistentes estão habilitados
|
|
36
|
+
*/
|
|
37
|
+
static isEnabled(): boolean {
|
|
38
|
+
const setting = localStorage.getItem(this.SETTINGS_KEY);
|
|
39
|
+
return setting ? JSON.parse(setting) : false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Salva os filtros de um módulo específico
|
|
44
|
+
*/
|
|
45
|
+
static saveFilters(module: string, filters: FilterConfig): void {
|
|
46
|
+
if (!this.isEnabled()) return;
|
|
47
|
+
|
|
48
|
+
const filtersWithTimestamp = {
|
|
49
|
+
...filters,
|
|
50
|
+
timestamp: Date.now()
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const key = `${module}-filters`;
|
|
54
|
+
localStorage.setItem(key, JSON.stringify(filtersWithTimestamp));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Carrega os filtros salvos de um módulo específico
|
|
59
|
+
*/
|
|
60
|
+
static loadFilters(module: string): FilterConfig | null {
|
|
61
|
+
if (!this.isEnabled()) return null;
|
|
62
|
+
|
|
63
|
+
const key = `${module}-filters`;
|
|
64
|
+
const saved = localStorage.getItem(key);
|
|
65
|
+
|
|
66
|
+
if (!saved) return null;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(saved);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`Erro ao carregar filtros do módulo ${module}:`, error);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Remove os filtros salvos de um módulo específico
|
|
78
|
+
*/
|
|
79
|
+
static clearFilters(module: string): void {
|
|
80
|
+
const key = `${module}-filters`;
|
|
81
|
+
localStorage.removeItem(key);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Remove todos os filtros salvos de todos os módulos
|
|
86
|
+
*/
|
|
87
|
+
static clearAllFilters(): void {
|
|
88
|
+
const keys = Object.keys(localStorage).filter(key => key.endsWith('-filters'));
|
|
89
|
+
keys.forEach(key => localStorage.removeItem(key));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Lista todos os módulos que possuem filtros salvos
|
|
94
|
+
*/
|
|
95
|
+
static getModulesWithSavedFilters(): string[] {
|
|
96
|
+
const keys = Object.keys(localStorage).filter(key => key.endsWith('-filters'));
|
|
97
|
+
return keys.map(key => key.replace('-filters', ''));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Hook personalizado para usar filtros persistentes em componentes React
|
|
102
|
+
*/
|
|
103
|
+
static useModuleFilters(module: string, defaultFilters: FilterConfig) {
|
|
104
|
+
return {
|
|
105
|
+
saveFilters: (filters: FilterConfig) => this.saveFilters(module, filters),
|
|
106
|
+
loadFilters: () => this.loadFilters(module) || defaultFilters,
|
|
107
|
+
clearFilters: () => this.clearFilters(module),
|
|
108
|
+
isEnabled: () => this.isEnabled()
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Constantes para nomes de módulos
|
|
115
|
+
*/
|
|
116
|
+
export const MODULES = {
|
|
117
|
+
// DASHBOARD: 'dashboard',
|
|
118
|
+
} as const;
|
|
119
|
+
|
|
120
|
+
export type ModuleName = typeof MODULES[keyof typeof MODULES];
|
package/lib/utils.ts
ADDED
package/package.json
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolution-soft/ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"cli",
|
|
12
|
+
"components",
|
|
13
|
+
"contexts",
|
|
14
|
+
"hooks",
|
|
15
|
+
"lib",
|
|
16
|
+
"stores"
|
|
11
17
|
],
|
|
12
18
|
"sideEffects": [
|
|
13
19
|
"**/*.css"
|
|
14
20
|
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"evolution-ui": "./cli/index.js"
|
|
23
|
+
},
|
|
15
24
|
"exports": {
|
|
16
25
|
".": {
|
|
17
26
|
"types": "./dist/index.d.ts",
|
package/stores/theme.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { createJSONStorage, persist } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
export type ThemeProps = 'dark' | 'light'
|
|
5
|
+
|
|
6
|
+
interface UseThemeStoreProps {
|
|
7
|
+
darkMode: boolean,
|
|
8
|
+
setDarkMode: (e: boolean) => void;
|
|
9
|
+
color: "cyan" | "orange" | "violet" | "emerald"
|
|
10
|
+
setColor: (e: "cyan" | "orange" | "violet" | "emerald") => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const useStore = create<UseThemeStoreProps>()(persist(
|
|
14
|
+
(set) => ({
|
|
15
|
+
darkMode: false,
|
|
16
|
+
setDarkMode: (e) => set({
|
|
17
|
+
darkMode: e
|
|
18
|
+
}),
|
|
19
|
+
color: 'cyan',
|
|
20
|
+
setColor: (e) => set({
|
|
21
|
+
color: e
|
|
22
|
+
})
|
|
23
|
+
}),
|
|
24
|
+
{
|
|
25
|
+
name: 'theme-storage',
|
|
26
|
+
storage: createJSONStorage(() => localStorage),
|
|
27
|
+
}
|
|
28
|
+
));
|
|
29
|
+
|
|
30
|
+
export const useThemeStore = useStore;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
export function useThemeStore() {
|
|
5
|
+
const [theme, setTheme] = useState<'light' | 'dark'>(() => {
|
|
6
|
+
if (typeof window !== "undefined") {
|
|
7
|
+
return (localStorage.getItem("theme") as 'light' | 'dark') || "light";
|
|
8
|
+
}
|
|
9
|
+
return "light";
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (typeof window !== "undefined") {
|
|
14
|
+
const root = document.documentElement;
|
|
15
|
+
root.classList.remove('light', 'dark');
|
|
16
|
+
root.classList.add(theme);
|
|
17
|
+
localStorage.setItem("theme", theme);
|
|
18
|
+
}
|
|
19
|
+
}, [theme]);
|
|
20
|
+
|
|
21
|
+
const toggleTheme = () => {
|
|
22
|
+
document.body.classList.add('theme-transition-disabled');
|
|
23
|
+
|
|
24
|
+
setTheme(prev => prev === "light" ? "dark" : "light");
|
|
25
|
+
|
|
26
|
+
setTimeout(() => {
|
|
27
|
+
document.body.classList.remove('theme-transition-disabled');
|
|
28
|
+
}, 50);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return { theme, toggleTheme };
|
|
32
|
+
}
|