analytica-frontend-lib 1.1.53 → 1.1.55
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/dist/DropdownMenu/index.d.mts +5 -1
- package/dist/DropdownMenu/index.d.ts +5 -1
- package/dist/DropdownMenu/index.js +606 -37
- package/dist/DropdownMenu/index.js.map +1 -1
- package/dist/DropdownMenu/index.mjs +605 -37
- package/dist/DropdownMenu/index.mjs.map +1 -1
- package/dist/Modal/index.d.mts +14 -5
- package/dist/Modal/index.d.ts +14 -5
- package/dist/Modal/index.js +216 -31
- package/dist/Modal/index.js.map +1 -1
- package/dist/Modal/index.mjs +217 -32
- package/dist/Modal/index.mjs.map +1 -1
- package/dist/Modal/utils/videoUtils/index.d.mts +28 -0
- package/dist/Modal/utils/videoUtils/index.d.ts +28 -0
- package/dist/Modal/utils/videoUtils/index.js +76 -0
- package/dist/Modal/utils/videoUtils/index.js.map +1 -0
- package/dist/Modal/utils/videoUtils/index.mjs +49 -0
- package/dist/Modal/utils/videoUtils/index.mjs.map +1 -0
- package/dist/NotificationCard/index.js +716 -295
- package/dist/NotificationCard/index.js.map +1 -1
- package/dist/NotificationCard/index.mjs +713 -292
- package/dist/NotificationCard/index.mjs.map +1 -1
- package/dist/Quiz/index.js +154 -26
- package/dist/Quiz/index.js.map +1 -1
- package/dist/Quiz/index.mjs +166 -38
- package/dist/Quiz/index.mjs.map +1 -1
- package/dist/Search/index.js +625 -58
- package/dist/Search/index.js.map +1 -1
- package/dist/Search/index.mjs +628 -61
- package/dist/Search/index.mjs.map +1 -1
- package/dist/ThemeToggle/index.d.mts +8 -49
- package/dist/ThemeToggle/index.d.ts +8 -49
- package/dist/ThemeToggle/index.js +128 -114
- package/dist/ThemeToggle/index.js.map +1 -1
- package/dist/ThemeToggle/index.mjs +119 -105
- package/dist/ThemeToggle/index.mjs.map +1 -1
- package/dist/hooks/useTheme/index.d.mts +1 -1
- package/dist/hooks/useTheme/index.d.ts +1 -1
- package/dist/hooks/useTheme/index.js.map +1 -1
- package/dist/hooks/useTheme/index.mjs.map +1 -1
- package/dist/index.css +10 -39
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +995 -825
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1083 -914
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +10 -39
- package/dist/styles.css.map +1 -1
- package/package.json +2 -1
|
@@ -23,7 +23,11 @@ __export(ThemeToggle_exports, {
|
|
|
23
23
|
ThemeToggle: () => ThemeToggle
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(ThemeToggle_exports);
|
|
26
|
-
var
|
|
26
|
+
var import_phosphor_react = require("phosphor-react");
|
|
27
|
+
var import_react3 = require("react");
|
|
28
|
+
|
|
29
|
+
// src/components/SelectionButton/SelectionButton.tsx
|
|
30
|
+
var import_react = require("react");
|
|
27
31
|
|
|
28
32
|
// src/utils/utils.ts
|
|
29
33
|
var import_clsx = require("clsx");
|
|
@@ -32,13 +36,67 @@ function cn(...inputs) {
|
|
|
32
36
|
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
// src/components/SelectionButton/SelectionButton.tsx
|
|
40
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
41
|
+
var SelectionButton = (0, import_react.forwardRef)(
|
|
42
|
+
({ icon, label, selected = false, className = "", disabled, ...props }, ref) => {
|
|
43
|
+
const baseClasses = [
|
|
44
|
+
"inline-flex",
|
|
45
|
+
"items-center",
|
|
46
|
+
"justify-start",
|
|
47
|
+
"gap-2",
|
|
48
|
+
"p-4",
|
|
49
|
+
"rounded-xl",
|
|
50
|
+
"cursor-pointer",
|
|
51
|
+
"border",
|
|
52
|
+
"border-border-50",
|
|
53
|
+
"bg-background",
|
|
54
|
+
"text-sm",
|
|
55
|
+
"text-text-700",
|
|
56
|
+
"font-bold",
|
|
57
|
+
"shadow-soft-shadow-1",
|
|
58
|
+
"hover:bg-background-100",
|
|
59
|
+
"focus-visible:outline-none",
|
|
60
|
+
"focus-visible:ring-2",
|
|
61
|
+
"focus-visible:ring-indicator-info",
|
|
62
|
+
"focus-visible:ring-offset-0",
|
|
63
|
+
"focus-visible:shadow-none",
|
|
64
|
+
"active:ring-2",
|
|
65
|
+
"active:ring-primary-950",
|
|
66
|
+
"active:ring-offset-0",
|
|
67
|
+
"active:shadow-none",
|
|
68
|
+
"disabled:opacity-50",
|
|
69
|
+
"disabled:cursor-not-allowed"
|
|
70
|
+
];
|
|
71
|
+
const stateClasses = selected ? ["ring-primary-950", "ring-2", "ring-offset-0", "shadow-none"] : [];
|
|
72
|
+
const allClasses = [...baseClasses, ...stateClasses].join(" ");
|
|
73
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
74
|
+
"button",
|
|
75
|
+
{
|
|
76
|
+
ref,
|
|
77
|
+
type: "button",
|
|
78
|
+
className: cn(allClasses, className),
|
|
79
|
+
disabled,
|
|
80
|
+
"aria-pressed": selected,
|
|
81
|
+
...props,
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex items-center justify-center w-6 h-6", children: icon }),
|
|
84
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label })
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
SelectionButton.displayName = "SelectionButton";
|
|
91
|
+
var SelectionButton_default = SelectionButton;
|
|
92
|
+
|
|
35
93
|
// src/hooks/useTheme.ts
|
|
36
|
-
var
|
|
94
|
+
var import_react2 = require("react");
|
|
37
95
|
var useTheme = () => {
|
|
38
|
-
const [themeMode, setThemeMode] = (0,
|
|
39
|
-
const [isDark, setIsDark] = (0,
|
|
40
|
-
const themeModeRef = (0,
|
|
41
|
-
const applyTheme = (0,
|
|
96
|
+
const [themeMode, setThemeMode] = (0, import_react2.useState)("system");
|
|
97
|
+
const [isDark, setIsDark] = (0, import_react2.useState)(false);
|
|
98
|
+
const themeModeRef = (0, import_react2.useRef)("system");
|
|
99
|
+
const applyTheme = (0, import_react2.useCallback)((mode) => {
|
|
42
100
|
const htmlElement = document.documentElement;
|
|
43
101
|
const originalTheme = htmlElement.getAttribute("data-original-theme");
|
|
44
102
|
if (mode === "dark") {
|
|
@@ -62,7 +120,7 @@ var useTheme = () => {
|
|
|
62
120
|
}
|
|
63
121
|
}
|
|
64
122
|
}, []);
|
|
65
|
-
const toggleTheme = (0,
|
|
123
|
+
const toggleTheme = (0, import_react2.useCallback)(() => {
|
|
66
124
|
let newMode;
|
|
67
125
|
if (themeMode === "light") {
|
|
68
126
|
newMode = "dark";
|
|
@@ -76,7 +134,7 @@ var useTheme = () => {
|
|
|
76
134
|
applyTheme(newMode);
|
|
77
135
|
localStorage.setItem("theme-mode", newMode);
|
|
78
136
|
}, [themeMode, applyTheme]);
|
|
79
|
-
const setTheme = (0,
|
|
137
|
+
const setTheme = (0, import_react2.useCallback)(
|
|
80
138
|
(mode) => {
|
|
81
139
|
setThemeMode(mode);
|
|
82
140
|
themeModeRef.current = mode;
|
|
@@ -85,7 +143,7 @@ var useTheme = () => {
|
|
|
85
143
|
},
|
|
86
144
|
[applyTheme]
|
|
87
145
|
);
|
|
88
|
-
(0,
|
|
146
|
+
(0, import_react2.useEffect)(() => {
|
|
89
147
|
const htmlElement = document.documentElement;
|
|
90
148
|
const currentTheme = htmlElement.getAttribute("data-theme");
|
|
91
149
|
if (currentTheme && !htmlElement.getAttribute("data-original-theme")) {
|
|
@@ -119,116 +177,72 @@ var useTheme = () => {
|
|
|
119
177
|
};
|
|
120
178
|
|
|
121
179
|
// src/components/ThemeToggle/ThemeToggle.tsx
|
|
122
|
-
var
|
|
123
|
-
var ThemeToggle = (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
"
|
|
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",
|
|
180
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
181
|
+
var ThemeToggle = ({
|
|
182
|
+
variant = "default",
|
|
183
|
+
onToggle
|
|
184
|
+
}) => {
|
|
185
|
+
const { themeMode, setTheme } = useTheme();
|
|
186
|
+
const [tempTheme, setTempTheme] = (0, import_react3.useState)(themeMode);
|
|
187
|
+
(0, import_react3.useEffect)(() => {
|
|
188
|
+
setTempTheme(themeMode);
|
|
189
|
+
}, [themeMode]);
|
|
190
|
+
const problemTypes = [
|
|
191
|
+
{
|
|
192
|
+
id: "light",
|
|
193
|
+
title: "Claro",
|
|
194
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_phosphor_react.Sun, { size: 24 })
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: "dark",
|
|
198
|
+
title: "Escuro",
|
|
199
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_phosphor_react.Moon, { size: 24 })
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: "system",
|
|
203
|
+
title: "Sistema",
|
|
204
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
205
|
+
"svg",
|
|
163
206
|
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
] })
|
|
207
|
+
width: "25",
|
|
208
|
+
height: "25",
|
|
209
|
+
viewBox: "0 0 25 25",
|
|
210
|
+
fill: "none",
|
|
211
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
212
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
213
|
+
"path",
|
|
214
|
+
{
|
|
215
|
+
d: "M12.5 2.75C15.085 2.75276 17.5637 3.78054 19.3916 5.6084C21.2195 7.43628 22.2473 9.915 22.25 12.5C22.25 14.4284 21.6778 16.3136 20.6064 17.917C19.5352 19.5201 18.0128 20.7699 16.2314 21.5078C14.4499 22.2458 12.489 22.4387 10.5977 22.0625C8.70642 21.6863 6.96899 20.758 5.60547 19.3945C4.24197 18.031 3.31374 16.2936 2.9375 14.4023C2.56129 12.511 2.75423 10.5501 3.49219 8.76855C4.23012 6.98718 5.47982 5.46483 7.08301 4.39355C8.68639 3.32221 10.5716 2.75 12.5 2.75ZM11.75 4.28516C9.70145 4.47452 7.7973 5.42115 6.41016 6.94043C5.02299 8.4599 4.25247 10.4426 4.25 12.5C4.25247 14.5574 5.02299 16.5401 6.41016 18.0596C7.7973 19.5789 9.70145 20.5255 11.75 20.7148V4.28516Z",
|
|
216
|
+
fill: "#525252"
|
|
217
|
+
}
|
|
218
|
+
)
|
|
177
219
|
}
|
|
178
|
-
)
|
|
220
|
+
)
|
|
179
221
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
] });
|
|
222
|
+
];
|
|
223
|
+
const handleThemeSelect = (selectedTheme) => {
|
|
224
|
+
if (variant === "with-save") {
|
|
225
|
+
setTempTheme(selectedTheme);
|
|
226
|
+
} else {
|
|
227
|
+
setTheme(selectedTheme);
|
|
202
228
|
}
|
|
203
|
-
if (
|
|
204
|
-
|
|
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
|
-
] });
|
|
229
|
+
if (onToggle) {
|
|
230
|
+
onToggle(selectedTheme);
|
|
227
231
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
+
};
|
|
233
|
+
const currentTheme = variant === "with-save" ? tempTheme : themeMode;
|
|
234
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-row gap-2 sm:gap-4 py-2", children: problemTypes.map((type) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
235
|
+
SelectionButton_default,
|
|
236
|
+
{
|
|
237
|
+
icon: type.icon,
|
|
238
|
+
label: type.title,
|
|
239
|
+
selected: currentTheme === type.id,
|
|
240
|
+
onClick: () => handleThemeSelect(type.id),
|
|
241
|
+
className: "w-full p-2 sm:p-4"
|
|
242
|
+
},
|
|
243
|
+
type.id
|
|
244
|
+
)) });
|
|
245
|
+
};
|
|
232
246
|
// Annotate the CommonJS export names for ESM import in node:
|
|
233
247
|
0 && (module.exports = {
|
|
234
248
|
ThemeToggle
|
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/ThemeToggle/ThemeToggle.tsx","../../src/components/SelectionButton/SelectionButton.tsx","../../src/utils/utils.ts","../../src/hooks/useTheme.ts"],"sourcesContent":["import { Moon, Sun } from 'phosphor-react';\nimport { useState, useEffect } from 'react';\nimport SelectionButton from '../SelectionButton/SelectionButton';\nimport type { ThemeMode } from '@/hooks/useTheme';\nimport { useTheme } from '@/hooks/useTheme';\n\ninterface ThemeToggleProps {\n variant?: 'default' | 'with-save';\n onToggle?: (theme: ThemeMode) => void;\n}\n\nexport const ThemeToggle = ({\n variant = 'default',\n onToggle,\n}: ThemeToggleProps) => {\n const { themeMode, setTheme } = useTheme();\n const [tempTheme, setTempTheme] = useState<ThemeMode>(themeMode);\n\n // Update temp theme when themeMode changes externally\n useEffect(() => {\n setTempTheme(themeMode);\n }, [themeMode]);\n\n const problemTypes = [\n {\n id: 'light' as ThemeMode,\n title: 'Claro',\n icon: <Sun size={24} />,\n },\n {\n id: 'dark' as ThemeMode,\n title: 'Escuro',\n icon: <Moon size={24} />,\n },\n {\n id: 'system' as ThemeMode,\n title: 'Sistema',\n icon: (\n <svg\n width=\"25\"\n height=\"25\"\n viewBox=\"0 0 25 25\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M12.5 2.75C15.085 2.75276 17.5637 3.78054 19.3916 5.6084C21.2195 7.43628 22.2473 9.915 22.25 12.5C22.25 14.4284 21.6778 16.3136 20.6064 17.917C19.5352 19.5201 18.0128 20.7699 16.2314 21.5078C14.4499 22.2458 12.489 22.4387 10.5977 22.0625C8.70642 21.6863 6.96899 20.758 5.60547 19.3945C4.24197 18.031 3.31374 16.2936 2.9375 14.4023C2.56129 12.511 2.75423 10.5501 3.49219 8.76855C4.23012 6.98718 5.47982 5.46483 7.08301 4.39355C8.68639 3.32221 10.5716 2.75 12.5 2.75ZM11.75 4.28516C9.70145 4.47452 7.7973 5.42115 6.41016 6.94043C5.02299 8.4599 4.25247 10.4426 4.25 12.5C4.25247 14.5574 5.02299 16.5401 6.41016 18.0596C7.7973 19.5789 9.70145 20.5255 11.75 20.7148V4.28516Z\"\n fill=\"#525252\"\n />\n </svg>\n ),\n },\n ];\n\n const handleThemeSelect = (selectedTheme: ThemeMode) => {\n if (variant === 'with-save') {\n setTempTheme(selectedTheme);\n } else {\n setTheme(selectedTheme);\n }\n\n if (onToggle) {\n onToggle(selectedTheme);\n }\n };\n\n const currentTheme = variant === 'with-save' ? tempTheme : themeMode;\n\n return (\n <div className=\"flex flex-row gap-2 sm:gap-4 py-2\">\n {problemTypes.map((type) => (\n <SelectionButton\n key={type.id}\n icon={type.icon}\n label={type.title}\n selected={currentTheme === type.id}\n onClick={() => handleThemeSelect(type.id)}\n className=\"w-full p-2 sm:p-4\"\n />\n ))}\n </div>\n );\n};\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * SelectionButton component props interface\n */\ntype SelectionButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Texto/label a ser exibido ao lado do ícone */\n label: string;\n /** Estado de seleção do botão */\n selected?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * SelectionButton component for Analytica Ensino platforms\n *\n * Um botão com ícone e texto para ações e navegação com estado de seleção.\n * Ideal para filtros, tags, categorias, seleção de tipos, etc.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param label - O texto/label a ser exibido\n * @param selected - Estado de seleção do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button estilizado\n *\n * @example\n * ```tsx\n * <SelectionButton\n * icon={<TagIcon />}\n * label=\"Categoria\"\n * selected={false}\n * onClick={() => handleSelection()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para foco programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * const handleFocus = () => {\n * buttonRef.current?.focus();\n * };\n *\n * <SelectionButton\n * ref={buttonRef}\n * icon={<TagIcon />}\n * label=\"Categoria\"\n * selected={isSelected}\n * onClick={() => setSelected(!isSelected)}\n * />\n * ```\n */\nconst SelectionButton = forwardRef<HTMLButtonElement, SelectionButtonProps>(\n (\n { icon, label, selected = false, className = '', disabled, ...props },\n ref\n ) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-start',\n 'gap-2',\n 'p-4',\n 'rounded-xl',\n 'cursor-pointer',\n 'border',\n 'border-border-50',\n 'bg-background',\n 'text-sm',\n 'text-text-700',\n 'font-bold',\n 'shadow-soft-shadow-1',\n 'hover:bg-background-100',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-indicator-info',\n 'focus-visible:ring-offset-0',\n 'focus-visible:shadow-none',\n 'active:ring-2',\n 'active:ring-primary-950',\n 'active:ring-offset-0',\n 'active:shadow-none',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n ];\n\n const stateClasses = selected\n ? ['ring-primary-950', 'ring-2', 'ring-offset-0', 'shadow-none']\n : [];\n\n const allClasses = [...baseClasses, ...stateClasses].join(' ');\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n aria-pressed={selected}\n {...props}\n >\n <span className=\"flex items-center justify-center w-6 h-6\">{icon}</span>\n <span>{label}</span>\n </button>\n );\n }\n);\n\nSelectionButton.displayName = 'SelectionButton';\n\nexport default SelectionButton;\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\nexport type 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","import_react","import_jsx_runtime"]}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
// src/components/ThemeToggle/ThemeToggle.tsx
|
|
2
|
+
import { Moon, Sun } from "phosphor-react";
|
|
3
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
4
|
+
|
|
5
|
+
// src/components/SelectionButton/SelectionButton.tsx
|
|
2
6
|
import { forwardRef } from "react";
|
|
3
7
|
|
|
4
8
|
// src/utils/utils.ts
|
|
@@ -8,6 +12,60 @@ function cn(...inputs) {
|
|
|
8
12
|
return twMerge(clsx(inputs));
|
|
9
13
|
}
|
|
10
14
|
|
|
15
|
+
// src/components/SelectionButton/SelectionButton.tsx
|
|
16
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
+
var SelectionButton = forwardRef(
|
|
18
|
+
({ icon, label, selected = false, className = "", disabled, ...props }, ref) => {
|
|
19
|
+
const baseClasses = [
|
|
20
|
+
"inline-flex",
|
|
21
|
+
"items-center",
|
|
22
|
+
"justify-start",
|
|
23
|
+
"gap-2",
|
|
24
|
+
"p-4",
|
|
25
|
+
"rounded-xl",
|
|
26
|
+
"cursor-pointer",
|
|
27
|
+
"border",
|
|
28
|
+
"border-border-50",
|
|
29
|
+
"bg-background",
|
|
30
|
+
"text-sm",
|
|
31
|
+
"text-text-700",
|
|
32
|
+
"font-bold",
|
|
33
|
+
"shadow-soft-shadow-1",
|
|
34
|
+
"hover:bg-background-100",
|
|
35
|
+
"focus-visible:outline-none",
|
|
36
|
+
"focus-visible:ring-2",
|
|
37
|
+
"focus-visible:ring-indicator-info",
|
|
38
|
+
"focus-visible:ring-offset-0",
|
|
39
|
+
"focus-visible:shadow-none",
|
|
40
|
+
"active:ring-2",
|
|
41
|
+
"active:ring-primary-950",
|
|
42
|
+
"active:ring-offset-0",
|
|
43
|
+
"active:shadow-none",
|
|
44
|
+
"disabled:opacity-50",
|
|
45
|
+
"disabled:cursor-not-allowed"
|
|
46
|
+
];
|
|
47
|
+
const stateClasses = selected ? ["ring-primary-950", "ring-2", "ring-offset-0", "shadow-none"] : [];
|
|
48
|
+
const allClasses = [...baseClasses, ...stateClasses].join(" ");
|
|
49
|
+
return /* @__PURE__ */ jsxs(
|
|
50
|
+
"button",
|
|
51
|
+
{
|
|
52
|
+
ref,
|
|
53
|
+
type: "button",
|
|
54
|
+
className: cn(allClasses, className),
|
|
55
|
+
disabled,
|
|
56
|
+
"aria-pressed": selected,
|
|
57
|
+
...props,
|
|
58
|
+
children: [
|
|
59
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center justify-center w-6 h-6", children: icon }),
|
|
60
|
+
/* @__PURE__ */ jsx("span", { children: label })
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
SelectionButton.displayName = "SelectionButton";
|
|
67
|
+
var SelectionButton_default = SelectionButton;
|
|
68
|
+
|
|
11
69
|
// src/hooks/useTheme.ts
|
|
12
70
|
import { useEffect, useState, useCallback, useRef } from "react";
|
|
13
71
|
var useTheme = () => {
|
|
@@ -95,116 +153,72 @@ var useTheme = () => {
|
|
|
95
153
|
};
|
|
96
154
|
|
|
97
155
|
// src/components/ThemeToggle/ThemeToggle.tsx
|
|
98
|
-
import {
|
|
99
|
-
var ThemeToggle =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"
|
|
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",
|
|
156
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
157
|
+
var ThemeToggle = ({
|
|
158
|
+
variant = "default",
|
|
159
|
+
onToggle
|
|
160
|
+
}) => {
|
|
161
|
+
const { themeMode, setTheme } = useTheme();
|
|
162
|
+
const [tempTheme, setTempTheme] = useState2(themeMode);
|
|
163
|
+
useEffect2(() => {
|
|
164
|
+
setTempTheme(themeMode);
|
|
165
|
+
}, [themeMode]);
|
|
166
|
+
const problemTypes = [
|
|
167
|
+
{
|
|
168
|
+
id: "light",
|
|
169
|
+
title: "Claro",
|
|
170
|
+
icon: /* @__PURE__ */ jsx2(Sun, { size: 24 })
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "dark",
|
|
174
|
+
title: "Escuro",
|
|
175
|
+
icon: /* @__PURE__ */ jsx2(Moon, { size: 24 })
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "system",
|
|
179
|
+
title: "Sistema",
|
|
180
|
+
icon: /* @__PURE__ */ jsx2(
|
|
181
|
+
"svg",
|
|
139
182
|
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
] })
|
|
183
|
+
width: "25",
|
|
184
|
+
height: "25",
|
|
185
|
+
viewBox: "0 0 25 25",
|
|
186
|
+
fill: "none",
|
|
187
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
188
|
+
children: /* @__PURE__ */ jsx2(
|
|
189
|
+
"path",
|
|
190
|
+
{
|
|
191
|
+
d: "M12.5 2.75C15.085 2.75276 17.5637 3.78054 19.3916 5.6084C21.2195 7.43628 22.2473 9.915 22.25 12.5C22.25 14.4284 21.6778 16.3136 20.6064 17.917C19.5352 19.5201 18.0128 20.7699 16.2314 21.5078C14.4499 22.2458 12.489 22.4387 10.5977 22.0625C8.70642 21.6863 6.96899 20.758 5.60547 19.3945C4.24197 18.031 3.31374 16.2936 2.9375 14.4023C2.56129 12.511 2.75423 10.5501 3.49219 8.76855C4.23012 6.98718 5.47982 5.46483 7.08301 4.39355C8.68639 3.32221 10.5716 2.75 12.5 2.75ZM11.75 4.28516C9.70145 4.47452 7.7973 5.42115 6.41016 6.94043C5.02299 8.4599 4.25247 10.4426 4.25 12.5C4.25247 14.5574 5.02299 16.5401 6.41016 18.0596C7.7973 19.5789 9.70145 20.5255 11.75 20.7148V4.28516Z",
|
|
192
|
+
fill: "#525252"
|
|
193
|
+
}
|
|
194
|
+
)
|
|
153
195
|
}
|
|
154
|
-
)
|
|
196
|
+
)
|
|
155
197
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
] });
|
|
198
|
+
];
|
|
199
|
+
const handleThemeSelect = (selectedTheme) => {
|
|
200
|
+
if (variant === "with-save") {
|
|
201
|
+
setTempTheme(selectedTheme);
|
|
202
|
+
} else {
|
|
203
|
+
setTheme(selectedTheme);
|
|
178
204
|
}
|
|
179
|
-
if (
|
|
180
|
-
|
|
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
|
-
] });
|
|
205
|
+
if (onToggle) {
|
|
206
|
+
onToggle(selectedTheme);
|
|
203
207
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
+
};
|
|
209
|
+
const currentTheme = variant === "with-save" ? tempTheme : themeMode;
|
|
210
|
+
return /* @__PURE__ */ jsx2("div", { className: "flex flex-row gap-2 sm:gap-4 py-2", children: problemTypes.map((type) => /* @__PURE__ */ jsx2(
|
|
211
|
+
SelectionButton_default,
|
|
212
|
+
{
|
|
213
|
+
icon: type.icon,
|
|
214
|
+
label: type.title,
|
|
215
|
+
selected: currentTheme === type.id,
|
|
216
|
+
onClick: () => handleThemeSelect(type.id),
|
|
217
|
+
className: "w-full p-2 sm:p-4"
|
|
218
|
+
},
|
|
219
|
+
type.id
|
|
220
|
+
)) });
|
|
221
|
+
};
|
|
208
222
|
export {
|
|
209
223
|
ThemeToggle
|
|
210
224
|
};
|