analytica-frontend-lib 1.1.49 → 1.1.50

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.
@@ -0,0 +1,51 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ButtonHTMLAttributes } from 'react';
3
+
4
+ /**
5
+ * ThemeToggle component props interface
6
+ */
7
+ type ThemeToggleProps = {
8
+ /** Modo de exibição do toggle */
9
+ variant?: 'simple' | 'detailed' | 'buttons';
10
+ /** Tamanho do componente */
11
+ size?: 'sm' | 'md' | 'lg';
12
+ /** Mostrar ícones nos botões */
13
+ showIcons?: boolean;
14
+ /** Mostrar labels nos botões */
15
+ showLabels?: boolean;
16
+ /** Classes CSS adicionais */
17
+ className?: string;
18
+ /** Conteúdo customizado para o botão simples */
19
+ children?: ReactNode;
20
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>;
21
+ /**
22
+ * ThemeToggle component for Analytica Ensino platforms
23
+ *
24
+ * Componente para alternar entre temas light, dark e system.
25
+ * Oferece diferentes variantes de exibição e tamanhos.
26
+ * Integra com o hook useTheme para gerenciamento de estado.
27
+ * Suporta forwardRef para acesso programático ao elemento DOM.
28
+ *
29
+ * @param variant - Modo de exibição (simple, detailed, buttons)
30
+ * @param size - Tamanho do componente (sm, md, lg)
31
+ * @param showIcons - Mostrar ícones nos botões
32
+ * @param showLabels - Mostrar labels nos botões
33
+ * @param className - Classes CSS adicionais
34
+ * @param children - Conteúdo customizado para o botão simples
35
+ */
36
+ declare const ThemeToggle: react.ForwardRefExoticComponent<{
37
+ /** Modo de exibição do toggle */
38
+ variant?: "simple" | "detailed" | "buttons";
39
+ /** Tamanho do componente */
40
+ size?: "sm" | "md" | "lg";
41
+ /** Mostrar ícones nos botões */
42
+ showIcons?: boolean;
43
+ /** Mostrar labels nos botões */
44
+ showLabels?: boolean;
45
+ /** Classes CSS adicionais */
46
+ className?: string;
47
+ /** Conteúdo customizado para o botão simples */
48
+ children?: ReactNode;
49
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onClick"> & react.RefAttributes<HTMLButtonElement>>;
50
+
51
+ export { ThemeToggle, type ThemeToggleProps };
@@ -0,0 +1,51 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ButtonHTMLAttributes } from 'react';
3
+
4
+ /**
5
+ * ThemeToggle component props interface
6
+ */
7
+ type ThemeToggleProps = {
8
+ /** Modo de exibição do toggle */
9
+ variant?: 'simple' | 'detailed' | 'buttons';
10
+ /** Tamanho do componente */
11
+ size?: 'sm' | 'md' | 'lg';
12
+ /** Mostrar ícones nos botões */
13
+ showIcons?: boolean;
14
+ /** Mostrar labels nos botões */
15
+ showLabels?: boolean;
16
+ /** Classes CSS adicionais */
17
+ className?: string;
18
+ /** Conteúdo customizado para o botão simples */
19
+ children?: ReactNode;
20
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>;
21
+ /**
22
+ * ThemeToggle component for Analytica Ensino platforms
23
+ *
24
+ * Componente para alternar entre temas light, dark e system.
25
+ * Oferece diferentes variantes de exibição e tamanhos.
26
+ * Integra com o hook useTheme para gerenciamento de estado.
27
+ * Suporta forwardRef para acesso programático ao elemento DOM.
28
+ *
29
+ * @param variant - Modo de exibição (simple, detailed, buttons)
30
+ * @param size - Tamanho do componente (sm, md, lg)
31
+ * @param showIcons - Mostrar ícones nos botões
32
+ * @param showLabels - Mostrar labels nos botões
33
+ * @param className - Classes CSS adicionais
34
+ * @param children - Conteúdo customizado para o botão simples
35
+ */
36
+ declare const ThemeToggle: react.ForwardRefExoticComponent<{
37
+ /** Modo de exibição do toggle */
38
+ variant?: "simple" | "detailed" | "buttons";
39
+ /** Tamanho do componente */
40
+ size?: "sm" | "md" | "lg";
41
+ /** Mostrar ícones nos botões */
42
+ showIcons?: boolean;
43
+ /** Mostrar labels nos botões */
44
+ showLabels?: boolean;
45
+ /** Classes CSS adicionais */
46
+ className?: string;
47
+ /** Conteúdo customizado para o botão simples */
48
+ children?: ReactNode;
49
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onClick"> & react.RefAttributes<HTMLButtonElement>>;
50
+
51
+ export { ThemeToggle, type ThemeToggleProps };
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/components/ThemeToggle/ThemeToggle.tsx
21
+ var ThemeToggle_exports = {};
22
+ __export(ThemeToggle_exports, {
23
+ ThemeToggle: () => ThemeToggle
24
+ });
25
+ module.exports = __toCommonJS(ThemeToggle_exports);
26
+ var import_react2 = require("react");
27
+
28
+ // src/utils/utils.ts
29
+ var import_clsx = require("clsx");
30
+ var import_tailwind_merge = require("tailwind-merge");
31
+ function cn(...inputs) {
32
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
33
+ }
34
+
35
+ // src/hooks/useTheme.ts
36
+ var import_react = require("react");
37
+ var useTheme = () => {
38
+ const [themeMode, setThemeMode] = (0, import_react.useState)("system");
39
+ const [isDark, setIsDark] = (0, import_react.useState)(false);
40
+ const themeModeRef = (0, import_react.useRef)("system");
41
+ const applyTheme = (0, import_react.useCallback)((mode) => {
42
+ const htmlElement = document.documentElement;
43
+ const originalTheme = htmlElement.getAttribute("data-original-theme");
44
+ if (mode === "dark") {
45
+ htmlElement.setAttribute("data-theme", "dark");
46
+ setIsDark(true);
47
+ } else if (mode === "light") {
48
+ if (originalTheme) {
49
+ htmlElement.setAttribute("data-theme", originalTheme);
50
+ }
51
+ setIsDark(false);
52
+ } else if (mode === "system") {
53
+ const isSystemDark = window.matchMedia(
54
+ "(prefers-color-scheme: dark)"
55
+ ).matches;
56
+ if (isSystemDark) {
57
+ htmlElement.setAttribute("data-theme", "dark");
58
+ setIsDark(true);
59
+ } else if (originalTheme) {
60
+ htmlElement.setAttribute("data-theme", originalTheme);
61
+ setIsDark(false);
62
+ }
63
+ }
64
+ }, []);
65
+ const toggleTheme = (0, import_react.useCallback)(() => {
66
+ let newMode;
67
+ if (themeMode === "light") {
68
+ newMode = "dark";
69
+ } else if (themeMode === "dark") {
70
+ newMode = "light";
71
+ } else {
72
+ newMode = "dark";
73
+ }
74
+ setThemeMode(newMode);
75
+ themeModeRef.current = newMode;
76
+ applyTheme(newMode);
77
+ localStorage.setItem("theme-mode", newMode);
78
+ }, [themeMode, applyTheme]);
79
+ const setTheme = (0, import_react.useCallback)(
80
+ (mode) => {
81
+ setThemeMode(mode);
82
+ themeModeRef.current = mode;
83
+ applyTheme(mode);
84
+ localStorage.setItem("theme-mode", mode);
85
+ },
86
+ [applyTheme]
87
+ );
88
+ (0, import_react.useEffect)(() => {
89
+ const htmlElement = document.documentElement;
90
+ const currentTheme = htmlElement.getAttribute("data-theme");
91
+ if (currentTheme && !htmlElement.getAttribute("data-original-theme")) {
92
+ htmlElement.setAttribute("data-original-theme", currentTheme);
93
+ }
94
+ const savedThemeMode = localStorage.getItem("theme-mode");
95
+ const initialMode = savedThemeMode || "system";
96
+ if (!savedThemeMode) {
97
+ localStorage.setItem("theme-mode", "system");
98
+ }
99
+ setThemeMode(initialMode);
100
+ themeModeRef.current = initialMode;
101
+ applyTheme(initialMode);
102
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
103
+ const handleSystemThemeChange = () => {
104
+ if (themeModeRef.current === "system") {
105
+ applyTheme("system");
106
+ }
107
+ };
108
+ mediaQuery.addEventListener("change", handleSystemThemeChange);
109
+ return () => {
110
+ mediaQuery.removeEventListener("change", handleSystemThemeChange);
111
+ };
112
+ }, [applyTheme]);
113
+ return {
114
+ themeMode,
115
+ isDark,
116
+ toggleTheme,
117
+ setTheme
118
+ };
119
+ };
120
+
121
+ // src/components/ThemeToggle/ThemeToggle.tsx
122
+ var import_jsx_runtime = require("react/jsx-runtime");
123
+ var ThemeToggle = (0, import_react2.forwardRef)(
124
+ ({
125
+ variant = "simple",
126
+ size = "md",
127
+ showIcons = true,
128
+ showLabels = true,
129
+ className,
130
+ children,
131
+ ...props
132
+ }, ref) => {
133
+ const { themeMode, isDark, toggleTheme, setTheme } = useTheme();
134
+ const sizeClasses = {
135
+ sm: "text-sm px-3 py-1.5",
136
+ md: "text-md px-4 py-2",
137
+ lg: "text-lg px-5 py-2.5"
138
+ };
139
+ const activeClasses = "bg-primary-500 text-white";
140
+ const inactiveClasses = "bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600";
141
+ const baseButtonClasses = "inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500";
142
+ const smallButtonClasses = "px-3 py-1.5 rounded-md text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500";
143
+ const renderThemeButton = (theme, icon, label, isActive, buttonSize) => {
144
+ const buttonClasses = buttonSize ? cn(baseButtonClasses, sizeClasses[buttonSize]) : smallButtonClasses;
145
+ const stateClasses = isActive ? activeClasses : inactiveClasses;
146
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
147
+ "button",
148
+ {
149
+ type: "button",
150
+ onClick: () => setTheme(theme),
151
+ className: cn(buttonClasses, stateClasses),
152
+ ...buttonSize ? props : {},
153
+ children: [
154
+ showIcons && icon,
155
+ showLabels && label
156
+ ]
157
+ }
158
+ );
159
+ };
160
+ if (variant === "simple") {
161
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
162
+ "button",
163
+ {
164
+ type: "button",
165
+ ref,
166
+ onClick: toggleTheme,
167
+ className: cn(
168
+ "inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500",
169
+ sizeClasses[size],
170
+ className
171
+ ),
172
+ ...props,
173
+ children: children || /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
174
+ showIcons && (isDark ? "\u2600\uFE0F" : "\u{1F319}"),
175
+ showLabels && (isDark ? "Claro" : "Escuro")
176
+ ] })
177
+ }
178
+ );
179
+ }
180
+ if (variant === "detailed") {
181
+ const getLabel = () => {
182
+ if (themeMode === "system") return "Sistema";
183
+ if (isDark) return "Escuro";
184
+ return "Claro";
185
+ };
186
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("flex flex-col gap-2", className), children: [
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: [
188
+ "Tema: ",
189
+ getLabel()
190
+ ] }),
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-1", children: [
192
+ renderThemeButton("light", "\u2600\uFE0F ", "Claro", themeMode === "light"),
193
+ renderThemeButton("dark", "\u{1F319} ", "Escuro", themeMode === "dark"),
194
+ renderThemeButton(
195
+ "system",
196
+ "\u2699\uFE0F ",
197
+ "Sistema",
198
+ themeMode === "system"
199
+ )
200
+ ] })
201
+ ] });
202
+ }
203
+ if (variant === "buttons") {
204
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("flex gap-2", className), children: [
205
+ renderThemeButton(
206
+ "light",
207
+ "\u2600\uFE0F",
208
+ "Claro",
209
+ themeMode === "light",
210
+ size
211
+ ),
212
+ renderThemeButton(
213
+ "dark",
214
+ "\u{1F319}",
215
+ "Escuro",
216
+ themeMode === "dark",
217
+ size
218
+ ),
219
+ renderThemeButton(
220
+ "system",
221
+ "\u2699\uFE0F",
222
+ "Sistema",
223
+ themeMode === "system",
224
+ size
225
+ )
226
+ ] });
227
+ }
228
+ return null;
229
+ }
230
+ );
231
+ ThemeToggle.displayName = "ThemeToggle";
232
+ // Annotate the CommonJS export names for ESM import in node:
233
+ 0 && (module.exports = {
234
+ ThemeToggle
235
+ });
236
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/ThemeToggle/ThemeToggle.tsx","../../src/utils/utils.ts","../../src/hooks/useTheme.ts"],"sourcesContent":["import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\nimport { useTheme } from '../../hooks/useTheme';\n\n/**\n * ThemeToggle component props interface\n */\nexport type ThemeToggleProps = {\n /** Modo de exibição do toggle */\n variant?: 'simple' | 'detailed' | 'buttons';\n /** Tamanho do componente */\n size?: 'sm' | 'md' | 'lg';\n /** Mostrar ícones nos botões */\n showIcons?: boolean;\n /** Mostrar labels nos botões */\n showLabels?: boolean;\n /** Classes CSS adicionais */\n className?: string;\n /** Conteúdo customizado para o botão simples */\n children?: ReactNode;\n} & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>;\n\n/**\n * ThemeToggle component for Analytica Ensino platforms\n *\n * Componente para alternar entre temas light, dark e system.\n * Oferece diferentes variantes de exibição e tamanhos.\n * Integra com o hook useTheme para gerenciamento de estado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param variant - Modo de exibição (simple, detailed, buttons)\n * @param size - Tamanho do componente (sm, md, lg)\n * @param showIcons - Mostrar ícones nos botões\n * @param showLabels - Mostrar labels nos botões\n * @param className - Classes CSS adicionais\n * @param children - Conteúdo customizado para o botão simples\n */\nexport const ThemeToggle = forwardRef<HTMLButtonElement, ThemeToggleProps>(\n (\n {\n variant = 'simple',\n size = 'md',\n showIcons = true,\n showLabels = true,\n className,\n children,\n ...props\n },\n ref\n ) => {\n const { themeMode, isDark, toggleTheme, setTheme } = useTheme();\n\n // Classes base para tamanhos\n const sizeClasses = {\n sm: 'text-sm px-3 py-1.5',\n md: 'text-md px-4 py-2',\n lg: 'text-lg px-5 py-2.5',\n };\n\n // Classes para botões ativos\n const activeClasses = 'bg-primary-500 text-white';\n const inactiveClasses =\n 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600';\n\n // Classes base para botões\n const baseButtonClasses =\n 'inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500';\n const smallButtonClasses =\n 'px-3 py-1.5 rounded-md text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500';\n\n // Função para renderizar botão de tema\n const renderThemeButton = (\n theme: 'light' | 'dark' | 'system',\n icon: string,\n label: string,\n isActive: boolean,\n buttonSize?: 'sm' | 'md' | 'lg'\n ) => {\n const buttonClasses = buttonSize\n ? cn(baseButtonClasses, sizeClasses[buttonSize])\n : smallButtonClasses;\n const stateClasses = isActive ? activeClasses : inactiveClasses;\n\n return (\n <button\n type=\"button\"\n onClick={() => setTheme(theme)}\n className={cn(buttonClasses, stateClasses)}\n {...(buttonSize ? props : {})}\n >\n {showIcons && icon}\n {showLabels && label}\n </button>\n );\n };\n\n // Renderizar botão simples\n if (variant === 'simple') {\n return (\n <button\n type=\"button\"\n ref={ref}\n onClick={toggleTheme}\n className={cn(\n 'inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',\n sizeClasses[size],\n className\n )}\n {...props}\n >\n {children || (\n <>\n {showIcons && (isDark ? '☀️' : '🌙')}\n {showLabels && (isDark ? 'Claro' : 'Escuro')}\n </>\n )}\n </button>\n );\n }\n\n // Renderizar botões detalhados\n if (variant === 'detailed') {\n const getLabel = () => {\n if (themeMode === 'system') return 'Sistema';\n if (isDark) return 'Escuro';\n return 'Claro';\n };\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"text-sm font-medium text-gray-700 dark:text-gray-300\">\n Tema: {getLabel()}\n </div>\n <div className=\"flex gap-1\">\n {renderThemeButton('light', '☀️ ', 'Claro', themeMode === 'light')}\n {renderThemeButton('dark', '🌙 ', 'Escuro', themeMode === 'dark')}\n {renderThemeButton(\n 'system',\n '⚙️ ',\n 'Sistema',\n themeMode === 'system'\n )}\n </div>\n </div>\n );\n }\n\n // Renderizar botões separados\n if (variant === 'buttons') {\n return (\n <div className={cn('flex gap-2', className)}>\n {renderThemeButton(\n 'light',\n '☀️',\n 'Claro',\n themeMode === 'light',\n size\n )}\n {renderThemeButton(\n 'dark',\n '🌙',\n 'Escuro',\n themeMode === 'dark',\n size\n )}\n {renderThemeButton(\n 'system',\n '⚙️',\n 'Sistema',\n themeMode === 'system',\n size\n )}\n </div>\n );\n }\n\n return null;\n }\n);\n\nThemeToggle.displayName = 'ThemeToggle';\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { useEffect, useState, useCallback, useRef } from 'react';\n\ntype ThemeMode = 'light' | 'dark' | 'system';\n\n/**\n * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema\n * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema\n */\nexport const useTheme = () => {\n const [themeMode, setThemeMode] = useState<ThemeMode>('system');\n const [isDark, setIsDark] = useState(false);\n\n // Ref para manter o estado atual do tema de forma síncrona\n const themeModeRef = useRef<ThemeMode>('system');\n\n // Função para aplicar o tema baseado no modo selecionado\n const applyTheme = useCallback((mode: ThemeMode) => {\n const htmlElement = document.documentElement;\n const originalTheme = htmlElement.getAttribute('data-original-theme');\n\n if (mode === 'dark') {\n htmlElement.setAttribute('data-theme', 'dark');\n setIsDark(true);\n } else if (mode === 'light') {\n if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n }\n setIsDark(false);\n } else if (mode === 'system') {\n const isSystemDark = window.matchMedia(\n '(prefers-color-scheme: dark)'\n ).matches;\n if (isSystemDark) {\n htmlElement.setAttribute('data-theme', 'dark');\n setIsDark(true);\n } else if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n setIsDark(false);\n }\n }\n }, []);\n\n // Função para alternar entre os temas\n const toggleTheme = useCallback(() => {\n let newMode: ThemeMode;\n if (themeMode === 'light') {\n newMode = 'dark';\n } else if (themeMode === 'dark') {\n newMode = 'light';\n } else {\n // Se estiver em 'system', vai para 'dark'\n newMode = 'dark';\n }\n setThemeMode(newMode);\n themeModeRef.current = newMode;\n applyTheme(newMode);\n localStorage.setItem('theme-mode', newMode);\n }, [themeMode, applyTheme]);\n\n // Função para definir um tema específico\n const setTheme = useCallback(\n (mode: ThemeMode) => {\n setThemeMode(mode);\n themeModeRef.current = mode;\n applyTheme(mode);\n localStorage.setItem('theme-mode', mode);\n },\n [applyTheme]\n );\n\n useEffect(() => {\n const htmlElement = document.documentElement;\n\n // Salva o theme original do white label na primeira execução\n const currentTheme = htmlElement.getAttribute('data-theme');\n if (currentTheme && !htmlElement.getAttribute('data-original-theme')) {\n htmlElement.setAttribute('data-original-theme', currentTheme);\n }\n\n // Carrega o tema salvo no localStorage ou usa 'system' como padrão\n const savedThemeMode = localStorage.getItem('theme-mode') as ThemeMode;\n const initialMode = savedThemeMode || 'system';\n\n // Se não há tema salvo, persiste 'system' como padrão\n if (!savedThemeMode) {\n localStorage.setItem('theme-mode', 'system');\n }\n\n setThemeMode(initialMode);\n themeModeRef.current = initialMode;\n applyTheme(initialMode);\n\n // Listener para mudanças nas preferências do sistema (apenas quando mode é 'system')\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n const handleSystemThemeChange = () => {\n // Usa o ref para ter acesso ao estado atual de forma síncrona\n if (themeModeRef.current === 'system') {\n applyTheme('system');\n }\n };\n\n mediaQuery.addEventListener('change', handleSystemThemeChange);\n\n return () => {\n mediaQuery.removeEventListener('change', handleSystemThemeChange);\n };\n }, [applyTheme]);\n\n return {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n };\n};\n"],"mappings":"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","names":["import_react"]}
@@ -0,0 +1,211 @@
1
+ // src/components/ThemeToggle/ThemeToggle.tsx
2
+ import { forwardRef } from "react";
3
+
4
+ // src/utils/utils.ts
5
+ import { clsx } from "clsx";
6
+ import { twMerge } from "tailwind-merge";
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+
11
+ // src/hooks/useTheme.ts
12
+ import { useEffect, useState, useCallback, useRef } from "react";
13
+ var useTheme = () => {
14
+ const [themeMode, setThemeMode] = useState("system");
15
+ const [isDark, setIsDark] = useState(false);
16
+ const themeModeRef = useRef("system");
17
+ const applyTheme = useCallback((mode) => {
18
+ const htmlElement = document.documentElement;
19
+ const originalTheme = htmlElement.getAttribute("data-original-theme");
20
+ if (mode === "dark") {
21
+ htmlElement.setAttribute("data-theme", "dark");
22
+ setIsDark(true);
23
+ } else if (mode === "light") {
24
+ if (originalTheme) {
25
+ htmlElement.setAttribute("data-theme", originalTheme);
26
+ }
27
+ setIsDark(false);
28
+ } else if (mode === "system") {
29
+ const isSystemDark = window.matchMedia(
30
+ "(prefers-color-scheme: dark)"
31
+ ).matches;
32
+ if (isSystemDark) {
33
+ htmlElement.setAttribute("data-theme", "dark");
34
+ setIsDark(true);
35
+ } else if (originalTheme) {
36
+ htmlElement.setAttribute("data-theme", originalTheme);
37
+ setIsDark(false);
38
+ }
39
+ }
40
+ }, []);
41
+ const toggleTheme = useCallback(() => {
42
+ let newMode;
43
+ if (themeMode === "light") {
44
+ newMode = "dark";
45
+ } else if (themeMode === "dark") {
46
+ newMode = "light";
47
+ } else {
48
+ newMode = "dark";
49
+ }
50
+ setThemeMode(newMode);
51
+ themeModeRef.current = newMode;
52
+ applyTheme(newMode);
53
+ localStorage.setItem("theme-mode", newMode);
54
+ }, [themeMode, applyTheme]);
55
+ const setTheme = useCallback(
56
+ (mode) => {
57
+ setThemeMode(mode);
58
+ themeModeRef.current = mode;
59
+ applyTheme(mode);
60
+ localStorage.setItem("theme-mode", mode);
61
+ },
62
+ [applyTheme]
63
+ );
64
+ useEffect(() => {
65
+ const htmlElement = document.documentElement;
66
+ const currentTheme = htmlElement.getAttribute("data-theme");
67
+ if (currentTheme && !htmlElement.getAttribute("data-original-theme")) {
68
+ htmlElement.setAttribute("data-original-theme", currentTheme);
69
+ }
70
+ const savedThemeMode = localStorage.getItem("theme-mode");
71
+ const initialMode = savedThemeMode || "system";
72
+ if (!savedThemeMode) {
73
+ localStorage.setItem("theme-mode", "system");
74
+ }
75
+ setThemeMode(initialMode);
76
+ themeModeRef.current = initialMode;
77
+ applyTheme(initialMode);
78
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
79
+ const handleSystemThemeChange = () => {
80
+ if (themeModeRef.current === "system") {
81
+ applyTheme("system");
82
+ }
83
+ };
84
+ mediaQuery.addEventListener("change", handleSystemThemeChange);
85
+ return () => {
86
+ mediaQuery.removeEventListener("change", handleSystemThemeChange);
87
+ };
88
+ }, [applyTheme]);
89
+ return {
90
+ themeMode,
91
+ isDark,
92
+ toggleTheme,
93
+ setTheme
94
+ };
95
+ };
96
+
97
+ // src/components/ThemeToggle/ThemeToggle.tsx
98
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
99
+ var ThemeToggle = forwardRef(
100
+ ({
101
+ variant = "simple",
102
+ size = "md",
103
+ showIcons = true,
104
+ showLabels = true,
105
+ className,
106
+ children,
107
+ ...props
108
+ }, ref) => {
109
+ const { themeMode, isDark, toggleTheme, setTheme } = useTheme();
110
+ const sizeClasses = {
111
+ sm: "text-sm px-3 py-1.5",
112
+ md: "text-md px-4 py-2",
113
+ lg: "text-lg px-5 py-2.5"
114
+ };
115
+ const activeClasses = "bg-primary-500 text-white";
116
+ const inactiveClasses = "bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600";
117
+ const baseButtonClasses = "inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500";
118
+ const smallButtonClasses = "px-3 py-1.5 rounded-md text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500";
119
+ const renderThemeButton = (theme, icon, label, isActive, buttonSize) => {
120
+ const buttonClasses = buttonSize ? cn(baseButtonClasses, sizeClasses[buttonSize]) : smallButtonClasses;
121
+ const stateClasses = isActive ? activeClasses : inactiveClasses;
122
+ return /* @__PURE__ */ jsxs(
123
+ "button",
124
+ {
125
+ type: "button",
126
+ onClick: () => setTheme(theme),
127
+ className: cn(buttonClasses, stateClasses),
128
+ ...buttonSize ? props : {},
129
+ children: [
130
+ showIcons && icon,
131
+ showLabels && label
132
+ ]
133
+ }
134
+ );
135
+ };
136
+ if (variant === "simple") {
137
+ return /* @__PURE__ */ jsx(
138
+ "button",
139
+ {
140
+ type: "button",
141
+ ref,
142
+ onClick: toggleTheme,
143
+ className: cn(
144
+ "inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500",
145
+ sizeClasses[size],
146
+ className
147
+ ),
148
+ ...props,
149
+ children: children || /* @__PURE__ */ jsxs(Fragment, { children: [
150
+ showIcons && (isDark ? "\u2600\uFE0F" : "\u{1F319}"),
151
+ showLabels && (isDark ? "Claro" : "Escuro")
152
+ ] })
153
+ }
154
+ );
155
+ }
156
+ if (variant === "detailed") {
157
+ const getLabel = () => {
158
+ if (themeMode === "system") return "Sistema";
159
+ if (isDark) return "Escuro";
160
+ return "Claro";
161
+ };
162
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-2", className), children: [
163
+ /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: [
164
+ "Tema: ",
165
+ getLabel()
166
+ ] }),
167
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
168
+ renderThemeButton("light", "\u2600\uFE0F ", "Claro", themeMode === "light"),
169
+ renderThemeButton("dark", "\u{1F319} ", "Escuro", themeMode === "dark"),
170
+ renderThemeButton(
171
+ "system",
172
+ "\u2699\uFE0F ",
173
+ "Sistema",
174
+ themeMode === "system"
175
+ )
176
+ ] })
177
+ ] });
178
+ }
179
+ if (variant === "buttons") {
180
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-2", className), children: [
181
+ renderThemeButton(
182
+ "light",
183
+ "\u2600\uFE0F",
184
+ "Claro",
185
+ themeMode === "light",
186
+ size
187
+ ),
188
+ renderThemeButton(
189
+ "dark",
190
+ "\u{1F319}",
191
+ "Escuro",
192
+ themeMode === "dark",
193
+ size
194
+ ),
195
+ renderThemeButton(
196
+ "system",
197
+ "\u2699\uFE0F",
198
+ "Sistema",
199
+ themeMode === "system",
200
+ size
201
+ )
202
+ ] });
203
+ }
204
+ return null;
205
+ }
206
+ );
207
+ ThemeToggle.displayName = "ThemeToggle";
208
+ export {
209
+ ThemeToggle
210
+ };
211
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/ThemeToggle/ThemeToggle.tsx","../../src/utils/utils.ts","../../src/hooks/useTheme.ts"],"sourcesContent":["import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\nimport { useTheme } from '../../hooks/useTheme';\n\n/**\n * ThemeToggle component props interface\n */\nexport type ThemeToggleProps = {\n /** Modo de exibição do toggle */\n variant?: 'simple' | 'detailed' | 'buttons';\n /** Tamanho do componente */\n size?: 'sm' | 'md' | 'lg';\n /** Mostrar ícones nos botões */\n showIcons?: boolean;\n /** Mostrar labels nos botões */\n showLabels?: boolean;\n /** Classes CSS adicionais */\n className?: string;\n /** Conteúdo customizado para o botão simples */\n children?: ReactNode;\n} & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>;\n\n/**\n * ThemeToggle component for Analytica Ensino platforms\n *\n * Componente para alternar entre temas light, dark e system.\n * Oferece diferentes variantes de exibição e tamanhos.\n * Integra com o hook useTheme para gerenciamento de estado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param variant - Modo de exibição (simple, detailed, buttons)\n * @param size - Tamanho do componente (sm, md, lg)\n * @param showIcons - Mostrar ícones nos botões\n * @param showLabels - Mostrar labels nos botões\n * @param className - Classes CSS adicionais\n * @param children - Conteúdo customizado para o botão simples\n */\nexport const ThemeToggle = forwardRef<HTMLButtonElement, ThemeToggleProps>(\n (\n {\n variant = 'simple',\n size = 'md',\n showIcons = true,\n showLabels = true,\n className,\n children,\n ...props\n },\n ref\n ) => {\n const { themeMode, isDark, toggleTheme, setTheme } = useTheme();\n\n // Classes base para tamanhos\n const sizeClasses = {\n sm: 'text-sm px-3 py-1.5',\n md: 'text-md px-4 py-2',\n lg: 'text-lg px-5 py-2.5',\n };\n\n // Classes para botões ativos\n const activeClasses = 'bg-primary-500 text-white';\n const inactiveClasses =\n 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600';\n\n // Classes base para botões\n const baseButtonClasses =\n 'inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500';\n const smallButtonClasses =\n 'px-3 py-1.5 rounded-md text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500';\n\n // Função para renderizar botão de tema\n const renderThemeButton = (\n theme: 'light' | 'dark' | 'system',\n icon: string,\n label: string,\n isActive: boolean,\n buttonSize?: 'sm' | 'md' | 'lg'\n ) => {\n const buttonClasses = buttonSize\n ? cn(baseButtonClasses, sizeClasses[buttonSize])\n : smallButtonClasses;\n const stateClasses = isActive ? activeClasses : inactiveClasses;\n\n return (\n <button\n type=\"button\"\n onClick={() => setTheme(theme)}\n className={cn(buttonClasses, stateClasses)}\n {...(buttonSize ? props : {})}\n >\n {showIcons && icon}\n {showLabels && label}\n </button>\n );\n };\n\n // Renderizar botão simples\n if (variant === 'simple') {\n return (\n <button\n type=\"button\"\n ref={ref}\n onClick={toggleTheme}\n className={cn(\n 'inline-flex items-center justify-center gap-2 rounded-md border border-gray-300 dark:border-gray-600 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',\n sizeClasses[size],\n className\n )}\n {...props}\n >\n {children || (\n <>\n {showIcons && (isDark ? '☀️' : '🌙')}\n {showLabels && (isDark ? 'Claro' : 'Escuro')}\n </>\n )}\n </button>\n );\n }\n\n // Renderizar botões detalhados\n if (variant === 'detailed') {\n const getLabel = () => {\n if (themeMode === 'system') return 'Sistema';\n if (isDark) return 'Escuro';\n return 'Claro';\n };\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"text-sm font-medium text-gray-700 dark:text-gray-300\">\n Tema: {getLabel()}\n </div>\n <div className=\"flex gap-1\">\n {renderThemeButton('light', '☀️ ', 'Claro', themeMode === 'light')}\n {renderThemeButton('dark', '🌙 ', 'Escuro', themeMode === 'dark')}\n {renderThemeButton(\n 'system',\n '⚙️ ',\n 'Sistema',\n themeMode === 'system'\n )}\n </div>\n </div>\n );\n }\n\n // Renderizar botões separados\n if (variant === 'buttons') {\n return (\n <div className={cn('flex gap-2', className)}>\n {renderThemeButton(\n 'light',\n '☀️',\n 'Claro',\n themeMode === 'light',\n size\n )}\n {renderThemeButton(\n 'dark',\n '🌙',\n 'Escuro',\n themeMode === 'dark',\n size\n )}\n {renderThemeButton(\n 'system',\n '⚙️',\n 'Sistema',\n themeMode === 'system',\n size\n )}\n </div>\n );\n }\n\n return null;\n }\n);\n\nThemeToggle.displayName = 'ThemeToggle';\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { useEffect, useState, useCallback, useRef } from 'react';\n\ntype ThemeMode = 'light' | 'dark' | 'system';\n\n/**\n * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema\n * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema\n */\nexport const useTheme = () => {\n const [themeMode, setThemeMode] = useState<ThemeMode>('system');\n const [isDark, setIsDark] = useState(false);\n\n // Ref para manter o estado atual do tema de forma síncrona\n const themeModeRef = useRef<ThemeMode>('system');\n\n // Função para aplicar o tema baseado no modo selecionado\n const applyTheme = useCallback((mode: ThemeMode) => {\n const htmlElement = document.documentElement;\n const originalTheme = htmlElement.getAttribute('data-original-theme');\n\n if (mode === 'dark') {\n htmlElement.setAttribute('data-theme', 'dark');\n setIsDark(true);\n } else if (mode === 'light') {\n if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n }\n setIsDark(false);\n } else if (mode === 'system') {\n const isSystemDark = window.matchMedia(\n '(prefers-color-scheme: dark)'\n ).matches;\n if (isSystemDark) {\n htmlElement.setAttribute('data-theme', 'dark');\n setIsDark(true);\n } else if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n setIsDark(false);\n }\n }\n }, []);\n\n // Função para alternar entre os temas\n const toggleTheme = useCallback(() => {\n let newMode: ThemeMode;\n if (themeMode === 'light') {\n newMode = 'dark';\n } else if (themeMode === 'dark') {\n newMode = 'light';\n } else {\n // Se estiver em 'system', vai para 'dark'\n newMode = 'dark';\n }\n setThemeMode(newMode);\n themeModeRef.current = newMode;\n applyTheme(newMode);\n localStorage.setItem('theme-mode', newMode);\n }, [themeMode, applyTheme]);\n\n // Função para definir um tema específico\n const setTheme = useCallback(\n (mode: ThemeMode) => {\n setThemeMode(mode);\n themeModeRef.current = mode;\n applyTheme(mode);\n localStorage.setItem('theme-mode', mode);\n },\n [applyTheme]\n );\n\n useEffect(() => {\n const htmlElement = document.documentElement;\n\n // Salva o theme original do white label na primeira execução\n const currentTheme = htmlElement.getAttribute('data-theme');\n if (currentTheme && !htmlElement.getAttribute('data-original-theme')) {\n htmlElement.setAttribute('data-original-theme', currentTheme);\n }\n\n // Carrega o tema salvo no localStorage ou usa 'system' como padrão\n const savedThemeMode = localStorage.getItem('theme-mode') as ThemeMode;\n const initialMode = savedThemeMode || 'system';\n\n // Se não há tema salvo, persiste 'system' como padrão\n if (!savedThemeMode) {\n localStorage.setItem('theme-mode', 'system');\n }\n\n setThemeMode(initialMode);\n themeModeRef.current = initialMode;\n applyTheme(initialMode);\n\n // Listener para mudanças nas preferências do sistema (apenas quando mode é 'system')\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n const handleSystemThemeChange = () => {\n // Usa o ref para ter acesso ao estado atual de forma síncrona\n if (themeModeRef.current === 'system') {\n applyTheme('system');\n }\n };\n\n mediaQuery.addEventListener('change', handleSystemThemeChange);\n\n return () => {\n mediaQuery.removeEventListener('change', handleSystemThemeChange);\n };\n }, [applyTheme]);\n\n return {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n };\n};\n"],"mappings":";AAAA,SAA0C,kBAAkB;;;ACA5D,SAAS,YAA6B;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;ACLA,SAAS,WAAW,UAAU,aAAa,cAAc;AAQlD,IAAM,WAAW,MAAM;AAC5B,QAAM,CAAC,WAAW,YAAY,IAAI,SAAoB,QAAQ;AAC9D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAG1C,QAAM,eAAe,OAAkB,QAAQ;AAG/C,QAAM,aAAa,YAAY,CAAC,SAAoB;AAClD,UAAM,cAAc,SAAS;AAC7B,UAAM,gBAAgB,YAAY,aAAa,qBAAqB;AAEpE,QAAI,SAAS,QAAQ;AACnB,kBAAY,aAAa,cAAc,MAAM;AAC7C,gBAAU,IAAI;AAAA,IAChB,WAAW,SAAS,SAAS;AAC3B,UAAI,eAAe;AACjB,oBAAY,aAAa,cAAc,aAAa;AAAA,MACtD;AACA,gBAAU,KAAK;AAAA,IACjB,WAAW,SAAS,UAAU;AAC5B,YAAM,eAAe,OAAO;AAAA,QAC1B;AAAA,MACF,EAAE;AACF,UAAI,cAAc;AAChB,oBAAY,aAAa,cAAc,MAAM;AAC7C,kBAAU,IAAI;AAAA,MAChB,WAAW,eAAe;AACxB,oBAAY,aAAa,cAAc,aAAa;AACpD,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI;AACJ,QAAI,cAAc,SAAS;AACzB,gBAAU;AAAA,IACZ,WAAW,cAAc,QAAQ;AAC/B,gBAAU;AAAA,IACZ,OAAO;AAEL,gBAAU;AAAA,IACZ;AACA,iBAAa,OAAO;AACpB,iBAAa,UAAU;AACvB,eAAW,OAAO;AAClB,iBAAa,QAAQ,cAAc,OAAO;AAAA,EAC5C,GAAG,CAAC,WAAW,UAAU,CAAC;AAG1B,QAAM,WAAW;AAAA,IACf,CAAC,SAAoB;AACnB,mBAAa,IAAI;AACjB,mBAAa,UAAU;AACvB,iBAAW,IAAI;AACf,mBAAa,QAAQ,cAAc,IAAI;AAAA,IACzC;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,YAAU,MAAM;AACd,UAAM,cAAc,SAAS;AAG7B,UAAM,eAAe,YAAY,aAAa,YAAY;AAC1D,QAAI,gBAAgB,CAAC,YAAY,aAAa,qBAAqB,GAAG;AACpE,kBAAY,aAAa,uBAAuB,YAAY;AAAA,IAC9D;AAGA,UAAM,iBAAiB,aAAa,QAAQ,YAAY;AACxD,UAAM,cAAc,kBAAkB;AAGtC,QAAI,CAAC,gBAAgB;AACnB,mBAAa,QAAQ,cAAc,QAAQ;AAAA,IAC7C;AAEA,iBAAa,WAAW;AACxB,iBAAa,UAAU;AACvB,eAAW,WAAW;AAGtB,UAAM,aAAa,OAAO,WAAW,8BAA8B;AACnE,UAAM,0BAA0B,MAAM;AAEpC,UAAI,aAAa,YAAY,UAAU;AACrC,mBAAW,QAAQ;AAAA,MACrB;AAAA,IACF;AAEA,eAAW,iBAAiB,UAAU,uBAAuB;AAE7D,WAAO,MAAM;AACX,iBAAW,oBAAoB,UAAU,uBAAuB;AAAA,IAClE;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAEf,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AF9BQ,SA2BI,UAZJ,KAfA;AA/CD,IAAM,cAAc;AAAA,EACzB,CACE;AAAA,IACE,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,EAAE,WAAW,QAAQ,aAAa,SAAS,IAAI,SAAS;AAG9D,UAAM,cAAc;AAAA,MAClB,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,IACN;AAGA,UAAM,gBAAgB;AACtB,UAAM,kBACJ;AAGF,UAAM,oBACJ;AACF,UAAM,qBACJ;AAGF,UAAM,oBAAoB,CACxB,OACA,MACA,OACA,UACA,eACG;AACH,YAAM,gBAAgB,aAClB,GAAG,mBAAmB,YAAY,UAAU,CAAC,IAC7C;AACJ,YAAM,eAAe,WAAW,gBAAgB;AAEhD,aACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,SAAS,KAAK;AAAA,UAC7B,WAAW,GAAG,eAAe,YAAY;AAAA,UACxC,GAAI,aAAa,QAAQ,CAAC;AAAA,UAE1B;AAAA,yBAAa;AAAA,YACb,cAAc;AAAA;AAAA;AAAA,MACjB;AAAA,IAEJ;AAGA,QAAI,YAAY,UAAU;AACxB,aACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL;AAAA,UACA,SAAS;AAAA,UACT,WAAW;AAAA,YACT;AAAA,YACA,YAAY,IAAI;AAAA,YAChB;AAAA,UACF;AAAA,UACC,GAAG;AAAA,UAEH,sBACC,iCACG;AAAA,0BAAc,SAAS,iBAAO;AAAA,YAC9B,eAAe,SAAS,UAAU;AAAA,aACrC;AAAA;AAAA,MAEJ;AAAA,IAEJ;AAGA,QAAI,YAAY,YAAY;AAC1B,YAAM,WAAW,MAAM;AACrB,YAAI,cAAc,SAAU,QAAO;AACnC,YAAI,OAAQ,QAAO;AACnB,eAAO;AAAA,MACT;AACA,aACE,qBAAC,SAAI,WAAW,GAAG,uBAAuB,SAAS,GACjD;AAAA,6BAAC,SAAI,WAAU,wDAAuD;AAAA;AAAA,UAC7D,SAAS;AAAA,WAClB;AAAA,QACA,qBAAC,SAAI,WAAU,cACZ;AAAA,4BAAkB,SAAS,iBAAO,SAAS,cAAc,OAAO;AAAA,UAChE,kBAAkB,QAAQ,cAAO,UAAU,cAAc,MAAM;AAAA,UAC/D;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA,cAAc;AAAA,UAChB;AAAA,WACF;AAAA,SACF;AAAA,IAEJ;AAGA,QAAI,YAAY,WAAW;AACzB,aACE,qBAAC,SAAI,WAAW,GAAG,cAAc,SAAS,GACvC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd;AAAA,QACF;AAAA,QACC;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd;AAAA,QACF;AAAA,QACC;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd;AAAA,QACF;AAAA,SACF;AAAA,IAEJ;AAEA,WAAO;AAAA,EACT;AACF;AAEA,YAAY,cAAc;","names":[]}
@@ -1,8 +1,13 @@
1
+ type ThemeMode = 'light' | 'dark' | 'system';
1
2
  /**
2
- * Hook para detectar preferências do sistema e aplicar dark mode automaticamente
3
- * Este hook aplica o theme baseado nas preferências do sistema (prefers-color-scheme)
4
- * na inicialização do componente.
3
+ * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema
4
+ * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema
5
5
  */
6
- declare const useTheme: () => void;
6
+ declare const useTheme: () => {
7
+ themeMode: ThemeMode;
8
+ isDark: boolean;
9
+ toggleTheme: () => void;
10
+ setTheme: (mode: ThemeMode) => void;
11
+ };
7
12
 
8
13
  export { useTheme };
@@ -1,8 +1,13 @@
1
+ type ThemeMode = 'light' | 'dark' | 'system';
1
2
  /**
2
- * Hook para detectar preferências do sistema e aplicar dark mode automaticamente
3
- * Este hook aplica o theme baseado nas preferências do sistema (prefers-color-scheme)
4
- * na inicialização do componente.
3
+ * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema
4
+ * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema
5
5
  */
6
- declare const useTheme: () => void;
6
+ declare const useTheme: () => {
7
+ themeMode: ThemeMode;
8
+ isDark: boolean;
9
+ toggleTheme: () => void;
10
+ setTheme: (mode: ThemeMode) => void;
11
+ };
7
12
 
8
13
  export { useTheme };