@ciromaciel/dashboard-kit 1.2.1 → 1.3.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ciromaciel/dashboard-kit",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "The components every dashboard shares: table, empty state, loading state, destructive confirm, danger zone and wordmark.",
@@ -1,4 +1,4 @@
1
- import { ActionIcon, Box, ScrollArea, Stack, Text, TextInput } from '@mantine/core'
1
+ import { ActionIcon, Box, Group, ScrollArea, Stack, Text, TextInput, Tooltip } from '@mantine/core'
2
2
  import { IconSearch, IconX } from '@tabler/icons-react'
3
3
 
4
4
  import { GroupLabel } from './GroupLabel'
@@ -39,6 +39,35 @@ import { NavItem } from './NavItem'
39
39
  * Nada de quota entra aqui. O painel resolve o gate e passa `createAllowed`
40
40
  * booleano — a coluna não sabe o que é um plano.
41
41
  */
42
+ /** O botão de criar na linha da busca: quadrado de 36px, preto; em contorno cinza no limite do plano. */
43
+ function CreateSquare({ icon: Icon, label, isAllowed, onClick }) {
44
+ return (
45
+ <Tooltip
46
+ label={label}
47
+ position="bottom"
48
+ withArrow={false}
49
+ >
50
+ <ActionIcon
51
+ data-create
52
+ onClick={onClick}
53
+ aria-label={label}
54
+ size={36}
55
+ radius={0}
56
+ variant={isAllowed ? 'filled' : 'default'}
57
+ color="gray.9"
58
+ // O realce do Mantine puxa o preto para um azul; aqui ele fica no cinza do sistema.
59
+ style={{ flex: 'none', '--ai-hover': 'var(--mantine-color-gray-7)' }}
60
+ >
61
+ <Icon
62
+ size={16}
63
+ stroke={2}
64
+ style={isAllowed ? undefined : { color: 'var(--mantine-color-gray-5)' }}
65
+ />
66
+ </ActionIcon>
67
+ </Tooltip>
68
+ )
69
+ }
70
+
42
71
  export function SidebarShell({
43
72
  // ── fora do recurso ──────────────────────────────────────────────────
44
73
  items = [],
@@ -94,6 +123,7 @@ export function SidebarShell({
94
123
  const mostrarBusca = searchThreshold !== null && !activeResource && (items.length >= searchThreshold || Boolean(term))
95
124
 
96
125
  const faixas = partition ? partition(filtrados) : null
126
+ const criar = { icon: createIcon, label: createAllowed ? createLabel : createBlockedLabel, isAllowed: createAllowed, onClick: createAllowed ? onCreate : onCreateBlocked }
97
127
 
98
128
  return (
99
129
  <Stack
@@ -107,18 +137,35 @@ export function SidebarShell({
107
137
  {header}
108
138
 
109
139
  {/*
110
- A busca tem a geometria de um item da coluna: mesma borda
111
- esquerda, ícone de 18 na mesma coluna (12px da borda), texto onde
112
- começa o rótulo de um item (42px) e a altura de um item (43px), a
113
- mesma do criar logo abaixo. Recuada e mais baixa, ela fazia três bordas
114
- esquerdas numa coluna só.
140
+ A busca se alinha com a lista: mesma borda esquerda, ícone
141
+ centrado na coluna dos ícones dos itens e texto onde começa o
142
+ rótulo de um item (42px). A altura é a de um campo, 36px, menor
143
+ que um item (43px): com a altura de um item, busca e criar
144
+ pesavam mais que a lista que servem (Ciro, 10/10/2026).
145
+
146
+ CRIAR MORA NA LINHA DA BUSCA: um quadrado preto de 36px com o
147
+ ícone, colado à direita do campo (escolha do Ciro em 10/10/2026,
148
+ entre cinco opções). Buscar e criar viram uma faixa só de
149
+ ferramentas, e a lista começa limpa logo abaixo. O preto aqui não
150
+ se confunde com o item aberto: aquele é uma faixa larga com texto,
151
+ este é um quadrado que só um botão tem. O nome da ação vai no
152
+ rótulo de acessibilidade e na dica ao passar o mouse.
153
+
154
+ Criar fica ANTES da lista: com muitos recursos a lista rola, e uma
155
+ ação no fim de uma área de rolagem é uma ação que se procura. No
156
+ limite do plano o botão NÃO some — ele leva para os planos, em
157
+ contorno cinza em vez de preto. Escondê-lo deixaria o limite
158
+ invisível.
115
159
  */}
116
160
  {mostrarBusca && (
117
- <Box
161
+ <Group
162
+ gap={6}
118
163
  pb={6}
119
164
  w="100%"
165
+ wrap="nowrap"
120
166
  >
121
167
  <TextInput
168
+ style={{ flex: 1, minWidth: 0 }}
122
169
  value={term}
123
170
  onChange={e => onTermChange?.(e.currentTarget.value)}
124
171
  onKeyDown={e => e.key === 'Escape' && onTermChange?.('')}
@@ -127,7 +174,7 @@ export function SidebarShell({
127
174
  leftSectionWidth={42}
128
175
  leftSection={
129
176
  <IconSearch
130
- size={18}
177
+ size={16}
131
178
  stroke={1.5}
132
179
  />
133
180
  }
@@ -147,9 +194,10 @@ export function SidebarShell({
147
194
  </ActionIcon>
148
195
  ) : null
149
196
  }
150
- styles={{ input: { height: 43, minHeight: 43, fontSize: 13, background: 'var(--mantine-color-gray-1)' } }}
197
+ styles={{ input: { height: 36, minHeight: 36, fontSize: 13, background: 'var(--mantine-color-gray-1)' } }}
151
198
  />
152
- </Box>
199
+ {onCreate && <CreateSquare {...criar} />}
200
+ </Group>
153
201
  )}
154
202
 
155
203
  {activeResource ? (
@@ -160,38 +208,14 @@ export function SidebarShell({
160
208
  />
161
209
  ) : (
162
210
  <>
163
- {/*
164
- Criar fica ANTES da lista: com muitos recursos a lista
165
- rola, e uma ação no fim de uma área de rolagem é uma ação
166
- que se procura.
167
-
168
- No limite do plano o item NÃO some — ele leva para os
169
- planos. Escondê-lo deixaria o limite invisível, e a pessoa
170
- procuraria o botão em vez de descobrir que precisa de
171
- upgrade.
172
-
173
- O CONTORNO É O QUE O SEPARA DA NAVEGAÇÃO. Na coluna, sem
174
- moldura é um lugar para ir, preenchido de preto é onde você
175
- está, e com contorno é algo que você cria. Preto sólido
176
- diria "você está aqui"; cor gastaria a única exceção do
177
- sistema, o vermelho de perda. A borda é a mesma linha fina
178
- dos contadores ao lado dos itens, desenhada por dentro (sombra
179
- inset) para o item manter a altura dos outros.
180
- */}
181
- {onCreate && (
182
- <Box
183
- w="100%"
184
- mb={4}
185
- data-create
186
- style={{ boxShadow: 'inset 0 0 0 1px var(--mantine-color-gray-3)' }}
187
- >
188
- <NavItem
189
- icon={createIcon}
190
- label={<span style={{ fontWeight: 700 }}>{createAllowed ? createLabel : createBlockedLabel}</span>}
191
- muted={!createAllowed}
192
- onClick={createAllowed ? onCreate : onCreateBlocked}
193
- />
194
- </Box>
211
+ {/* Sem busca (o Monitors, painéis sem lista longa), criar volta a ser um item da coluna. */}
212
+ {onCreate && !mostrarBusca && (
213
+ <NavItem
214
+ icon={criar.icon}
215
+ label={criar.label}
216
+ muted={!createAllowed}
217
+ onClick={criar.onClick}
218
+ />
195
219
  )}
196
220
 
197
221
  {groupLabel && <GroupLabel>{groupLabel}</GroupLabel>}