@ciromaciel/dashboard-kit 1.2.0 → 1.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ciromaciel/dashboard-kit",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
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 43px, 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={43}
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={18}
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
@@ -106,21 +136,45 @@ export function SidebarShell({
106
136
  >
107
137
  {header}
108
138
 
139
+ {/*
140
+ A busca tem a geometria de um item da coluna: mesma borda
141
+ esquerda, ícone de 18 na mesma coluna (12px da borda), texto onde
142
+ começa o rótulo de um item (42px) e a altura de um item (43px).
143
+ Recuada e mais baixa, ela fazia três bordas esquerdas numa coluna
144
+ só.
145
+
146
+ CRIAR MORA NA LINHA DA BUSCA: um quadrado preto de 43px 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.
159
+ */}
109
160
  {mostrarBusca && (
110
- <Box
111
- px={12}
112
- pb={8}
161
+ <Group
162
+ gap={6}
163
+ pb={6}
113
164
  w="100%"
165
+ wrap="nowrap"
114
166
  >
115
167
  <TextInput
168
+ style={{ flex: 1, minWidth: 0 }}
116
169
  value={term}
117
170
  onChange={e => onTermChange?.(e.currentTarget.value)}
118
171
  onKeyDown={e => e.key === 'Escape' && onTermChange?.('')}
119
172
  placeholder={searchPlaceholder}
120
173
  aria-label={searchPlaceholder}
174
+ leftSectionWidth={42}
121
175
  leftSection={
122
176
  <IconSearch
123
- size={14}
177
+ size={18}
124
178
  stroke={1.5}
125
179
  />
126
180
  }
@@ -140,9 +194,10 @@ export function SidebarShell({
140
194
  </ActionIcon>
141
195
  ) : null
142
196
  }
143
- styles={{ input: { fontSize: 12, background: 'var(--mantine-color-gray-1)' } }}
197
+ styles={{ input: { height: 43, minHeight: 43, fontSize: 13, background: 'var(--mantine-color-gray-1)' } }}
144
198
  />
145
- </Box>
199
+ {onCreate && <CreateSquare {...criar} />}
200
+ </Group>
146
201
  )}
147
202
 
148
203
  {activeResource ? (
@@ -153,37 +208,14 @@ export function SidebarShell({
153
208
  />
154
209
  ) : (
155
210
  <>
156
- {/*
157
- Criar fica ANTES da lista: com muitos recursos a lista
158
- rola, e uma ação no fim de uma área de rolagem é uma ação
159
- que se procura.
160
-
161
- No limite do plano o item NÃO some — ele leva para os
162
- planos. Escondê-lo deixaria o limite invisível, e a pessoa
163
- procuraria o botão em vez de descobrir que precisa de
164
- upgrade.
165
-
166
- O CONTORNO É O QUE O SEPARA DA NAVEGAÇÃO. Na coluna, sem
167
- moldura é um lugar para ir, preenchido de preto é onde você
168
- está, e com contorno é algo que você cria. Preto sólido
169
- diria "você está aqui"; cor gastaria a única exceção do
170
- sistema, o vermelho de perda. A borda é a mesma linha fina
171
- dos contadores ao lado dos itens.
172
- */}
173
- {onCreate && (
174
- <Box
175
- w="100%"
176
- mb={4}
177
- data-create
178
- style={{ border: '1px solid var(--mantine-color-gray-3)' }}
179
- >
180
- <NavItem
181
- icon={createIcon}
182
- label={<span style={{ fontWeight: 700 }}>{createAllowed ? createLabel : createBlockedLabel}</span>}
183
- muted={!createAllowed}
184
- onClick={createAllowed ? onCreate : onCreateBlocked}
185
- />
186
- </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
+ />
187
219
  )}
188
220
 
189
221
  {groupLabel && <GroupLabel>{groupLabel}</GroupLabel>}