@ciromaciel/dashboard-kit 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/ColorSchemeToggle.jsx +56 -0
- package/src/components/SidebarShell.jsx +25 -13
- package/src/components/Terminal.jsx +9 -4
- package/src/index.js +2 -1
- package/src/theme.js +5 -0
- package/src/tokens.css +105 -1
package/package.json
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ActionIcon, Tooltip, useMantineColorScheme } from '@mantine/core'
|
|
2
|
+
import { useMounted } from '@mantine/hooks'
|
|
3
|
+
import { IconDeviceDesktop, IconMoon, IconSun } from '@tabler/icons-react'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The theme button — one icon that cycles Automático → Claro → Escuro
|
|
7
|
+
* (decided 14/10/2026).
|
|
8
|
+
*
|
|
9
|
+
* Automático follows the device and is the default for anyone who never chose.
|
|
10
|
+
* A single button hides its state in the icon, so the state is said in words
|
|
11
|
+
* twice: the tooltip and the accessible name both read "Tema: Claro. Mudar
|
|
12
|
+
* para Escuro." — what it is now, and what one click does.
|
|
13
|
+
*
|
|
14
|
+
* The icon waits for the mount. Mantine reads the stored choice from
|
|
15
|
+
* localStorage on the client only; a prerendered page would otherwise paint the
|
|
16
|
+
* server's icon and swap it on hydration.
|
|
17
|
+
*
|
|
18
|
+
* The same component lives in the components-kit for the sites; the two must
|
|
19
|
+
* move together.
|
|
20
|
+
*/
|
|
21
|
+
const NEXT = { auto: 'light', light: 'dark', dark: 'auto' }
|
|
22
|
+
const NAME = { auto: 'Automático', light: 'Claro', dark: 'Escuro' }
|
|
23
|
+
const ICON = { auto: IconDeviceDesktop, light: IconSun, dark: IconMoon }
|
|
24
|
+
|
|
25
|
+
export function ColorSchemeToggle({ size = 36, ...props }) {
|
|
26
|
+
const { colorScheme, setColorScheme } = useMantineColorScheme()
|
|
27
|
+
const isMounted = useMounted()
|
|
28
|
+
const current = isMounted ? colorScheme : 'auto'
|
|
29
|
+
const Icon = ICON[current]
|
|
30
|
+
const label = `Tema: ${NAME[current]}. Mudar para ${NAME[NEXT[current]]}.`
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Tooltip
|
|
34
|
+
label={label}
|
|
35
|
+
position="top"
|
|
36
|
+
withArrow={false}
|
|
37
|
+
>
|
|
38
|
+
<ActionIcon
|
|
39
|
+
data-color-scheme-toggle
|
|
40
|
+
variant="subtle"
|
|
41
|
+
color="gray.9"
|
|
42
|
+
size={size}
|
|
43
|
+
radius={0}
|
|
44
|
+
aria-label={label}
|
|
45
|
+
onClick={() => setColorScheme(NEXT[current])}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
<Icon
|
|
49
|
+
size={18}
|
|
50
|
+
stroke={1.5}
|
|
51
|
+
aria-hidden
|
|
52
|
+
/>
|
|
53
|
+
</ActionIcon>
|
|
54
|
+
</Tooltip>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ActionIcon, Box, Group, ScrollArea, Stack, Text, TextInput, Tooltip } from '@mantine/core'
|
|
2
|
-
import { IconSearch, IconX } from '@tabler/icons-react'
|
|
2
|
+
import { IconPlus, IconSearch, IconX } from '@tabler/icons-react'
|
|
3
3
|
|
|
4
4
|
import { GroupLabel } from './GroupLabel'
|
|
5
5
|
import { ListSkeleton } from './ListSkeleton'
|
|
6
6
|
import { NavItem } from './NavItem'
|
|
7
|
+
import { ColorSchemeToggle } from './ColorSchemeToggle'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* A coluna lateral de um painel, por CONFIGURAÇÃO em vez de por cópia.
|
|
@@ -39,8 +40,13 @@ import { NavItem } from './NavItem'
|
|
|
39
40
|
* Nada de quota entra aqui. O painel resolve o gate e passa `createAllowed`
|
|
40
41
|
* booleano — a coluna não sabe o que é um plano.
|
|
41
42
|
*/
|
|
42
|
-
/**
|
|
43
|
-
|
|
43
|
+
/**
|
|
44
|
+
* O botão de criar na linha da busca: quadrado de 36px, preto, colado à busca;
|
|
45
|
+
* em contorno cinza quando o plano chegou ao limite. É a forma única do "+" em
|
|
46
|
+
* todo painel — exportado como `CreateButton` para as colunas que ainda não
|
|
47
|
+
* são o SidebarShell. Nunca um item de lista "+ Novo …" abaixo da busca.
|
|
48
|
+
*/
|
|
49
|
+
export function CreateButton({ icon: Icon = IconPlus, label, isAllowed = true, onClick }) {
|
|
44
50
|
return (
|
|
45
51
|
<Tooltip
|
|
46
52
|
label={label}
|
|
@@ -196,7 +202,7 @@ export function SidebarShell({
|
|
|
196
202
|
}
|
|
197
203
|
styles={{ input: { height: 36, minHeight: 36, fontSize: 13, background: 'var(--mantine-color-gray-1)' } }}
|
|
198
204
|
/>
|
|
199
|
-
{onCreate && <
|
|
205
|
+
{onCreate && <CreateButton {...criar} />}
|
|
200
206
|
</Group>
|
|
201
207
|
)}
|
|
202
208
|
|
|
@@ -301,15 +307,21 @@ export function SidebarShell({
|
|
|
301
307
|
menu dele, junto de Conta e Assinatura. Na coluna, competiriam com
|
|
302
308
|
a lista — que é o assunto da coluna.
|
|
303
309
|
*/}
|
|
304
|
-
{
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
310
|
+
{/*
|
|
311
|
+
O botão de tema mora ao lado do cartão: é preferência da pessoa,
|
|
312
|
+
como Conta, e não compete com a lista. Sem cartão, ele fica só.
|
|
313
|
+
*/}
|
|
314
|
+
<Group
|
|
315
|
+
pt={16}
|
|
316
|
+
w="100%"
|
|
317
|
+
gap={8}
|
|
318
|
+
wrap="nowrap"
|
|
319
|
+
align="center"
|
|
320
|
+
style={{ borderTop: 'var(--rlg-border)' }}
|
|
321
|
+
>
|
|
322
|
+
{footer && <Box style={{ flex: 1, minWidth: 0 }}>{footer}</Box>}
|
|
323
|
+
<ColorSchemeToggle style={footer ? undefined : { marginLeft: 'auto' }} />
|
|
324
|
+
</Group>
|
|
313
325
|
|
|
314
326
|
{children}
|
|
315
327
|
</Stack>
|
|
@@ -17,13 +17,18 @@ import { IconCopy, IconCheck } from '@tabler/icons-react'
|
|
|
17
17
|
* usa em FeaturesGrid layout="split"), para que painel e site mostrem código
|
|
18
18
|
* com a mesma cara. Os valores vieram de lá, não foram escolhidos aqui.
|
|
19
19
|
*
|
|
20
|
-
* O fundo é
|
|
20
|
+
* O fundo é #11181C — a única superfície escura do sistema. Ela se justifica
|
|
21
21
|
* porque código é conteúdo citado, não interface: o contraste invertido marca
|
|
22
22
|
* "isto é a máquina falando", e é o mesmo tratamento de qualquer terminal que
|
|
23
23
|
* o desenvolvedor já tem aberto ao lado.
|
|
24
|
+
*
|
|
25
|
+
* É LITERAL, não `var(--mantine-color-gray-9)`, de propósito: o terminal é
|
|
26
|
+
* escuro nos dois temas. No modo escuro o gray.9 vira claro, e o texto branco
|
|
27
|
+
* daqui ficaria branco sobre branco. A borda é `--rlg-shell-edge`, que no
|
|
28
|
+
* escuro clareia para o terminal não se fundir com a página.
|
|
24
29
|
*/
|
|
25
30
|
const DARK = {
|
|
26
|
-
shell: '
|
|
31
|
+
shell: '#11181C',
|
|
27
32
|
bar: 'rgba(255, 255, 255, 0.06)',
|
|
28
33
|
/*
|
|
29
34
|
* O token vem de `tokens.css` do kit, com fallback ao literal que as
|
|
@@ -51,7 +56,7 @@ export function Terminal({ children, label, copy = true, actions, style }) {
|
|
|
51
56
|
return (
|
|
52
57
|
<Box
|
|
53
58
|
style={{
|
|
54
|
-
border: '1px solid var(--
|
|
59
|
+
border: '1px solid var(--rlg-shell-edge, #11181C)',
|
|
55
60
|
background: DARK.shell,
|
|
56
61
|
overflow: 'hidden',
|
|
57
62
|
...style,
|
|
@@ -168,7 +173,7 @@ export function TerminalOutput({ children, label = 'output', tone = 'default' })
|
|
|
168
173
|
return (
|
|
169
174
|
<Box
|
|
170
175
|
style={{
|
|
171
|
-
border: '1px solid var(--
|
|
176
|
+
border: '1px solid var(--rlg-shell-edge, #11181C)',
|
|
172
177
|
borderTop: 'none',
|
|
173
178
|
background: DARK.shell,
|
|
174
179
|
overflow: 'hidden',
|
package/src/index.js
CHANGED
|
@@ -84,7 +84,8 @@ export { ActiveResource } from './components/ActiveResource'
|
|
|
84
84
|
export { ColumnBrand } from './components/ColumnBrand'
|
|
85
85
|
export { ListSkeleton } from './components/ListSkeleton'
|
|
86
86
|
export { DashboardFrame, SIDEBAR_WIDTH } from './components/DashboardFrame'
|
|
87
|
-
export { SidebarShell } from './components/SidebarShell'
|
|
87
|
+
export { SidebarShell, CreateButton } from './components/SidebarShell'
|
|
88
|
+
export { ColorSchemeToggle } from './components/ColorSchemeToggle'
|
|
88
89
|
export { AUTH_LABELS, AUTH_TEXTS } from './auth-labels'
|
|
89
90
|
export { CardSkeleton } from './components/CardSkeleton'
|
|
90
91
|
export { MoreResults } from './components/MoreResults'
|
package/src/theme.js
CHANGED
|
@@ -98,6 +98,11 @@ export const panelTheme = createTheme({
|
|
|
98
98
|
* disc, bar); green.7 is the ink for text on white (5.0:1). Like red,
|
|
99
99
|
* it never appears without the word or the glyph beside it.
|
|
100
100
|
*/
|
|
101
|
+
/* A rampa que o Mantine usa para as próprias superfícies no escuro
|
|
102
|
+
(corpo, campo, botão default, texto apagado). São os mesmos valores
|
|
103
|
+
do bloco escuro do CSS: dark.7 é a base, dark.6 a superfície
|
|
104
|
+
elevada, dark.4 a borda, dark.0–3 as tintas. */
|
|
105
|
+
dark: ['#D3D8DE', '#B6BEC8', '#98A1AC', '#7D8794', '#2F3840', '#2A333B', '#1A2127', '#0B0F12', '#080B0D', '#050708'],
|
|
101
106
|
green: ['#F0FDF4', '#DCFCE7', '#BBF7D0', '#86EFAC', '#4ADE80', '#22C55E', '#16A34A', '#15803D', '#166534', '#14532D'],
|
|
102
107
|
|
|
103
108
|
red: ['#FFF5F5', '#FFE3E3', '#FFC9C9', '#FFA8A8', '#FF8787', '#FF6B6B', '#E03131', '#C92A2A', '#B02525', '#962020'],
|
package/src/tokens.css
CHANGED
|
@@ -90,7 +90,18 @@
|
|
|
90
90
|
* runtime, after this sheet.
|
|
91
91
|
*/
|
|
92
92
|
@media (prefers-contrast: more) {
|
|
93
|
-
:root:root {
|
|
93
|
+
:root:root:root[data-mantine-color-scheme='dark'] {
|
|
94
|
+
/* No escuro o deslocamento é para o claro: o filete aparece, o label
|
|
95
|
+
e a legenda sobem um degrau. */
|
|
96
|
+
--mantine-color-gray-2: #3a444d;
|
|
97
|
+
--mantine-color-gray-3: #4b5563;
|
|
98
|
+
--mantine-color-gray-4: #b6bec8;
|
|
99
|
+
--mantine-color-gray-5: #d3d8de;
|
|
100
|
+
--mantine-color-gray-6: #e5e7eb;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
:root:root[data-mantine-color-scheme='light'],
|
|
104
|
+
:root:root:not([data-mantine-color-scheme]) {
|
|
94
105
|
--mantine-color-gray-2: #9ca3af;
|
|
95
106
|
--mantine-color-gray-3: #9ca3af;
|
|
96
107
|
--mantine-color-gray-4: #4b5563;
|
|
@@ -122,3 +133,96 @@
|
|
|
122
133
|
:root:root[data-mantine-color-scheme='light'] {
|
|
123
134
|
--mantine-color-text: var(--mantine-color-gray-6);
|
|
124
135
|
}
|
|
136
|
+
|
|
137
|
+
/* ===========================================================================
|
|
138
|
+
* MODO ESCURO (14/10/2026) — a rampa por elevação.
|
|
139
|
+
*
|
|
140
|
+
* A regra da Apple: a base recua e cada camada que sobe clareia — página,
|
|
141
|
+
* cartão e modal, menu aberto. Não é a rampa invertida (a proposta antiga
|
|
142
|
+
* espelhava gray.0 ↔ gray.9, e o modal herdava a cor exata da página).
|
|
143
|
+
*
|
|
144
|
+
* Os NOMES não mudam: gray.0 continua sendo "base", gray.9 continua sendo
|
|
145
|
+
* "título". Muda o valor que cada papel recebe, e todo `c="gray.6"` e
|
|
146
|
+
* `var(--mantine-color-gray-2)` já escrito acompanha sozinho. O Mantine monta
|
|
147
|
+
* `--mantine-color-gray-filled` e afins como `var(--mantine-color-gray-9)`,
|
|
148
|
+
* então o botão preenchido também vira.
|
|
149
|
+
*
|
|
150
|
+
* "white" vira a superfície elevada (#1A2127), e isso resolve duas coisas
|
|
151
|
+
* de uma vez: o Paper `bg="white"` sobe um degrau acima da página, e o texto
|
|
152
|
+
* que o autoContrast pôs em "white" sobre um preenchimento gray.9 — agora
|
|
153
|
+
* claro — fica escuro e legível.
|
|
154
|
+
*
|
|
155
|
+
* O que NÃO acompanha é o hexadecimal escrito à mão. Superfícies que são
|
|
156
|
+
* escuras nos dois temas (terminal, bloco de código, player) escrevem as
|
|
157
|
+
* suas cores literais de propósito; todo o resto usa o token.
|
|
158
|
+
*
|
|
159
|
+
* Mesmo bloco no global.css do components-kit e no tokens.css do
|
|
160
|
+
* dashboard-kit — os dois andam juntos.
|
|
161
|
+
* =========================================================================== */
|
|
162
|
+
:root:root[data-mantine-color-scheme='dark'] {
|
|
163
|
+
color-scheme: dark;
|
|
164
|
+
|
|
165
|
+
--mantine-color-gray-0: #0b0f12; /* base da página */
|
|
166
|
+
--mantine-color-gray-1: #12181d; /* palco alternado, hover */
|
|
167
|
+
--mantine-color-gray-2: #222a31; /* filete */
|
|
168
|
+
--mantine-color-gray-3: #2f3840; /* borda forte, desabilitado */
|
|
169
|
+
--mantine-color-gray-4: #7d8794; /* label caixa alta — 5,3:1 */
|
|
170
|
+
--mantine-color-gray-5: #98a1ac; /* legenda — 7,4:1 */
|
|
171
|
+
--mantine-color-gray-6: #b6bec8; /* corpo — 10,3:1 */
|
|
172
|
+
--mantine-color-gray-7: #d3d8de; /* dado — 13,4:1 */
|
|
173
|
+
--mantine-color-gray-8: #e5e7eb;
|
|
174
|
+
--mantine-color-gray-9: #f3f4f6; /* título, botão — 17,5:1 */
|
|
175
|
+
|
|
176
|
+
--mantine-color-white: #1a2127; /* superfície elevada; tinta sobre preenchimento claro */
|
|
177
|
+
--mantine-color-body: #0b0f12;
|
|
178
|
+
--mantine-color-text: var(--mantine-color-gray-6);
|
|
179
|
+
--mantine-color-dimmed: var(--mantine-color-gray-5);
|
|
180
|
+
--mantine-color-placeholder: var(--mantine-color-gray-4);
|
|
181
|
+
--mantine-color-default: #1a2127;
|
|
182
|
+
--mantine-color-default-hover: #232b32;
|
|
183
|
+
--mantine-color-default-color: var(--mantine-color-gray-9);
|
|
184
|
+
--mantine-color-default-border: var(--mantine-color-gray-3);
|
|
185
|
+
|
|
186
|
+
/* Vermelho e verde clareiam: sobre a base escura, o red.6 (#E03131) e o
|
|
187
|
+
green.6 (#16A34A) do claro ficam abaixo de 4,5:1 como texto. */
|
|
188
|
+
--mantine-color-red-0: #2a1416;
|
|
189
|
+
--mantine-color-red-1: #3a1a1d;
|
|
190
|
+
--mantine-color-red-2: #5a2427;
|
|
191
|
+
--mantine-color-red-6: #ff6b6b;
|
|
192
|
+
--mantine-color-red-7: #ff8787;
|
|
193
|
+
--mantine-color-red-filled: #ff6b6b;
|
|
194
|
+
--mantine-color-red-filled-hover: #ff8787;
|
|
195
|
+
--mantine-color-green-6: #22c55e;
|
|
196
|
+
--mantine-color-green-7: #4ade80;
|
|
197
|
+
|
|
198
|
+
--rlg-elevated: #1a2127; /* modal, gaveta */
|
|
199
|
+
--rlg-elevated-2: #232b32; /* menu, popover, lista suspensa, notificação */
|
|
200
|
+
--rlg-shell-edge: #2f3840; /* borda do terminal, escuro nos dois temas */
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* As camadas que sobem. No claro todas são brancas e a borda separa; no
|
|
204
|
+
escuro cada uma clareia um degrau. */
|
|
205
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Modal-content,
|
|
206
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Modal-header,
|
|
207
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Drawer-content,
|
|
208
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Drawer-header {
|
|
209
|
+
background-color: var(--rlg-elevated);
|
|
210
|
+
}
|
|
211
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Popover-dropdown,
|
|
212
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Menu-dropdown,
|
|
213
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Combobox-dropdown,
|
|
214
|
+
:root[data-mantine-color-scheme='dark'] .mantine-HoverCard-dropdown,
|
|
215
|
+
:root[data-mantine-color-scheme='dark'] .mantine-Notification-root {
|
|
216
|
+
background-color: var(--rlg-elevated-2);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Imagens. A ilustração da casa é monocromática por regra (traço na rampa
|
|
220
|
+
cinza), então no escuro ela inverte: o traço preto vira claro e o fundo
|
|
221
|
+
branco some na página. Foto e captura só baixam o brilho, como a Apple
|
|
222
|
+
pede para imagem de fundo branco. `data-keep-colors` desliga as duas. */
|
|
223
|
+
:root[data-mantine-color-scheme='dark'] img[src$='.svg']:not([data-keep-colors]) {
|
|
224
|
+
filter: invert(1) hue-rotate(180deg);
|
|
225
|
+
}
|
|
226
|
+
:root[data-mantine-color-scheme='dark'] img:not([src$='.svg']):not([data-keep-colors]) {
|
|
227
|
+
filter: brightness(0.88);
|
|
228
|
+
}
|