@ciromaciel/dashboard-kit 1.3.1 → 1.5.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/ActiveResource.jsx +1 -1
- package/src/components/ColumnBrand.jsx +56 -0
- package/src/components/DataTable.jsx +1 -1
- package/src/components/DeleteModal.jsx +2 -2
- package/src/components/EmptyState.jsx +1 -1
- package/src/components/HoldToConfirm.jsx +6 -1
- package/src/components/Metric.jsx +2 -2
- package/src/components/MoreResults.jsx +3 -3
- package/src/components/PageHeader.jsx +2 -2
- package/src/components/ResourceItem.jsx +1 -1
- package/src/components/SectionCard.jsx +2 -2
- package/src/components/SupportModal.jsx +2 -2
- package/src/components/TableToolbar.jsx +1 -1
- package/src/components/Wordmark.jsx +1 -1
- package/src/components/ZenLabel.jsx +1 -1
- package/src/components/notify.js +13 -2
- package/src/index.js +1 -0
- package/src/tokens.css +58 -0
package/package.json
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Box, Text, UnstyledButton } from '@mantine/core'
|
|
2
|
+
|
|
3
|
+
import { Wordmark } from './Wordmark'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* O topo da coluna: o nome do produto e, embaixo, o que ele cuida.
|
|
7
|
+
*
|
|
8
|
+
* VEIO DE SETE CÓPIAS IGUAIS (Blogs, Forms, Links, Newsletter, Pages, Tasks,
|
|
9
|
+
* Videos) e uma quase igual (People, com cinza e espaçamento próprios). A
|
|
10
|
+
* linha de baixo virou padrão em 10/10/2026 (Ciro): todo painel diz, em duas
|
|
11
|
+
* palavras, o que cabe nele — "Artigos · Publicação", "Edições · Assinantes".
|
|
12
|
+
* Ela orienta quem chega e separa produtos de nome parecido na mesma linha.
|
|
13
|
+
*
|
|
14
|
+
* O nome é a volta para `/` (a lista de recursos, ou o Início no painel sem
|
|
15
|
+
* recurso): o mesmo destino do item "Todos os …" da coluna.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} name O nome do produto ("Blogs").
|
|
18
|
+
* @param {string} tagline Os dois termos, separados por " · ". Some quando
|
|
19
|
+
* não há: uma linha vazia ocuparia espaço sem dizer nada.
|
|
20
|
+
* @param {Function} onClick Ir para `/`.
|
|
21
|
+
* @param {string} label O rótulo de acessibilidade do nome ("Todos os blogs").
|
|
22
|
+
*/
|
|
23
|
+
export function ColumnBrand({ name, tagline, onClick, label }) {
|
|
24
|
+
return (
|
|
25
|
+
<Box
|
|
26
|
+
px={12}
|
|
27
|
+
pb={8}
|
|
28
|
+
>
|
|
29
|
+
<UnstyledButton
|
|
30
|
+
onClick={onClick}
|
|
31
|
+
aria-label={label ?? name}
|
|
32
|
+
w="100%"
|
|
33
|
+
style={{ display: 'block', textAlign: 'center' }}
|
|
34
|
+
>
|
|
35
|
+
<Wordmark
|
|
36
|
+
name={name}
|
|
37
|
+
size={28}
|
|
38
|
+
/>
|
|
39
|
+
</UnstyledButton>
|
|
40
|
+
|
|
41
|
+
{tagline && (
|
|
42
|
+
<Text
|
|
43
|
+
ta="center"
|
|
44
|
+
fz={10}
|
|
45
|
+
fw={800}
|
|
46
|
+
c="gray.4"
|
|
47
|
+
tt="uppercase"
|
|
48
|
+
lts="1.5px"
|
|
49
|
+
mt={6}
|
|
50
|
+
>
|
|
51
|
+
{tagline}
|
|
52
|
+
</Text>
|
|
53
|
+
)}
|
|
54
|
+
</Box>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -25,7 +25,7 @@ import { templateFor, ROW_PADDING, HEAD_PADDING } from './table-grid'
|
|
|
25
25
|
* never injects the table's structure — it only says what each cell shows.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
const HEAD = { fz: 10, fw: 800, c: 'gray.5', tt: 'uppercase',
|
|
28
|
+
const HEAD = { fz: 10, fw: 800, c: 'gray.5', tt: 'uppercase', lts: '1.5px', lh: 1.4 }
|
|
29
29
|
|
|
30
30
|
export function DataTable({ columns, rows, getRowKey, onRowClick, isRowSelected, quietSelection, getRowLabel, toolbar, footer, emptyState, loading, skeleton }) {
|
|
31
31
|
const clickable = typeof onRowClick === 'function'
|
|
@@ -162,7 +162,7 @@ export function DeleteModal({
|
|
|
162
162
|
fw={800}
|
|
163
163
|
c="red.6"
|
|
164
164
|
tt="uppercase"
|
|
165
|
-
|
|
165
|
+
lts="1.5px"
|
|
166
166
|
>
|
|
167
167
|
{eyebrow}
|
|
168
168
|
</Text>
|
|
@@ -182,7 +182,7 @@ export function DeleteModal({
|
|
|
182
182
|
fz={scale.fz}
|
|
183
183
|
lh={1.15}
|
|
184
184
|
mt={12}
|
|
185
|
-
|
|
185
|
+
lts="-0.04em"
|
|
186
186
|
c="gray.9"
|
|
187
187
|
/*
|
|
188
188
|
* `break-all` only where the title IS the identifier. On the
|
|
@@ -138,8 +138,13 @@ export function HoldToConfirmButton({ children, holdingLabel, onConfirm, disable
|
|
|
138
138
|
{...props}
|
|
139
139
|
>
|
|
140
140
|
{active ? (
|
|
141
|
+
/* The countdown inherits the button's colour. It was gray.6,
|
|
142
|
+
which on the filled red.6 variant measured about 1.3:1 —
|
|
143
|
+
the number that says how long is left was unreadable.
|
|
144
|
+
Explicit `inherit`, because the panel themes give Text a
|
|
145
|
+
default colour that would otherwise win. */
|
|
141
146
|
<Text
|
|
142
|
-
c="
|
|
147
|
+
c="inherit"
|
|
143
148
|
span
|
|
144
149
|
inherit
|
|
145
150
|
>
|
|
@@ -110,7 +110,7 @@ export function Metric({ label, value, unit, note, hint, delta, deltaSuffix = '%
|
|
|
110
110
|
fw={900}
|
|
111
111
|
c="gray.9"
|
|
112
112
|
lh={1}
|
|
113
|
-
|
|
113
|
+
lts="-0.04em"
|
|
114
114
|
style={{ overflowWrap: 'anywhere' }}
|
|
115
115
|
>
|
|
116
116
|
{value ?? '—'}
|
|
@@ -134,7 +134,7 @@ export function Metric({ label, value, unit, note, hint, delta, deltaSuffix = '%
|
|
|
134
134
|
fw={800}
|
|
135
135
|
c="gray.4"
|
|
136
136
|
tt="uppercase"
|
|
137
|
-
|
|
137
|
+
lts="1.5px"
|
|
138
138
|
lh={1.4}
|
|
139
139
|
mt={12}
|
|
140
140
|
>
|
|
@@ -77,7 +77,7 @@ export function MoreResults({ loading, hasMore, onLoadMore, shown, total, noun =
|
|
|
77
77
|
fw={800}
|
|
78
78
|
c="gray.4"
|
|
79
79
|
tt="uppercase"
|
|
80
|
-
|
|
80
|
+
lts="1.5px"
|
|
81
81
|
>
|
|
82
82
|
{total} {total === 1 ? noun : plural} · fim da lista
|
|
83
83
|
</Text>
|
|
@@ -103,7 +103,7 @@ export function MoreResults({ loading, hasMore, onLoadMore, shown, total, noun =
|
|
|
103
103
|
fw={800}
|
|
104
104
|
c="gray.4"
|
|
105
105
|
tt="uppercase"
|
|
106
|
-
|
|
106
|
+
lts="1.5px"
|
|
107
107
|
/* `aria-live` because a scroll-triggered load has no
|
|
108
108
|
focus: without this, a screen reader would not know
|
|
109
109
|
the list had grown. */
|
|
@@ -127,7 +127,7 @@ export function MoreResults({ loading, hasMore, onLoadMore, shown, total, noun =
|
|
|
127
127
|
fw={800}
|
|
128
128
|
c="gray.4"
|
|
129
129
|
tt="uppercase"
|
|
130
|
-
|
|
130
|
+
lts="1.5px"
|
|
131
131
|
>
|
|
132
132
|
{shown} de {total}
|
|
133
133
|
</Text>
|
|
@@ -64,7 +64,7 @@ export function PageHeader({ crumb, title, subtitle, actions, order = 1, mb = 32
|
|
|
64
64
|
fw={800}
|
|
65
65
|
c="gray.4"
|
|
66
66
|
tt="uppercase"
|
|
67
|
-
|
|
67
|
+
lts="1.5px"
|
|
68
68
|
lh={1.4}
|
|
69
69
|
>
|
|
70
70
|
{trilha}
|
|
@@ -91,7 +91,7 @@ export function PageHeader({ crumb, title, subtitle, actions, order = 1, mb = 32
|
|
|
91
91
|
*/
|
|
92
92
|
fw={900}
|
|
93
93
|
lh={1}
|
|
94
|
-
|
|
94
|
+
lts="-0.04em"
|
|
95
95
|
c="gray.9"
|
|
96
96
|
style={{ overflowWrap: 'anywhere' }}
|
|
97
97
|
>
|
|
@@ -56,7 +56,7 @@ export function SectionCard({ eyebrow, title, lead, action, children }) {
|
|
|
56
56
|
c="gray.4"
|
|
57
57
|
tt="uppercase"
|
|
58
58
|
mb={6}
|
|
59
|
-
|
|
59
|
+
lts="1.5px"
|
|
60
60
|
>
|
|
61
61
|
{eyebrow}
|
|
62
62
|
</Text>
|
|
@@ -66,7 +66,7 @@ export function SectionCard({ eyebrow, title, lead, action, children }) {
|
|
|
66
66
|
fz={18}
|
|
67
67
|
fw={800}
|
|
68
68
|
lh={1.2}
|
|
69
|
-
|
|
69
|
+
lts="-0.02em"
|
|
70
70
|
c="gray.9"
|
|
71
71
|
>
|
|
72
72
|
{title}
|
|
@@ -111,7 +111,7 @@ export function HealthRow({ health, host }) {
|
|
|
111
111
|
/>
|
|
112
112
|
<Text
|
|
113
113
|
fz={12}
|
|
114
|
-
fw={
|
|
114
|
+
fw={500}
|
|
115
115
|
c="gray.9"
|
|
116
116
|
lineClamp={1}
|
|
117
117
|
>
|
|
@@ -198,7 +198,7 @@ export function SupportModal({
|
|
|
198
198
|
fz={{ base: 26, sm: 36 }}
|
|
199
199
|
lh={1.05}
|
|
200
200
|
mt={12}
|
|
201
|
-
|
|
201
|
+
lts="-0.04em"
|
|
202
202
|
c="gray.9"
|
|
203
203
|
>
|
|
204
204
|
{title}{' '}
|
|
@@ -40,7 +40,7 @@ export function Wordmark({ name, variant = 'mark', size, ...props }) {
|
|
|
40
40
|
fz={size ?? (isLabel ? 11 : 28)}
|
|
41
41
|
fw={isLabel ? 800 : 900}
|
|
42
42
|
tt={isLabel ? 'uppercase' : undefined}
|
|
43
|
-
|
|
43
|
+
lts={isLabel ? '1.5px' : '-0.04em'}
|
|
44
44
|
lh={isLabel ? 1.4 : 1}
|
|
45
45
|
c={isLabel ? 'gray.4' : 'gray.9'}
|
|
46
46
|
ta="center"
|
package/src/components/notify.js
CHANGED
|
@@ -43,20 +43,31 @@ const icon = Component => createElement(Component, { size: 18, stroke: 2, color:
|
|
|
43
43
|
* rule — `--mantine-color-black`, a pure black the system's ramp does not
|
|
44
44
|
* contain (gray.9 is #11181C precisely to avoid it).
|
|
45
45
|
*/
|
|
46
|
-
const show = (color, glyph, title, message) =>
|
|
46
|
+
const show = (color, glyph, title, message, options) =>
|
|
47
47
|
notifications.show({
|
|
48
48
|
...(message === undefined ? { message: title } : { title, message }),
|
|
49
49
|
color,
|
|
50
50
|
icon: glyph,
|
|
51
|
+
...options,
|
|
51
52
|
})
|
|
52
53
|
|
|
54
|
+
/**
|
|
55
|
+
* An error stays until it is dismissed.
|
|
56
|
+
*
|
|
57
|
+
* Apple's rule is to prefer explicit dismissal over views that vanish on a
|
|
58
|
+
* timer, and WCAG 2.2.1 says the same: an error read in five seconds is an
|
|
59
|
+
* error missed by whoever looked away. Success and info still leave on their
|
|
60
|
+
* own — they ask for nothing.
|
|
61
|
+
*/
|
|
62
|
+
const STICKY = { autoClose: false }
|
|
63
|
+
|
|
53
64
|
export const notify = {
|
|
54
65
|
success(title, message) {
|
|
55
66
|
show('teal', icon(IconCheck), title, message)
|
|
56
67
|
},
|
|
57
68
|
|
|
58
69
|
error(title, message) {
|
|
59
|
-
show('red', icon(IconAlertCircle), title, message)
|
|
70
|
+
show('red', icon(IconAlertCircle), title, message, STICKY)
|
|
60
71
|
},
|
|
61
72
|
|
|
62
73
|
info(title, message) {
|
package/src/index.js
CHANGED
|
@@ -80,6 +80,7 @@ export { NavItem } from './components/NavItem'
|
|
|
80
80
|
export { GroupLabel } from './components/GroupLabel'
|
|
81
81
|
export { ResourceItem } from './components/ResourceItem'
|
|
82
82
|
export { ActiveResource } from './components/ActiveResource'
|
|
83
|
+
export { ColumnBrand } from './components/ColumnBrand'
|
|
83
84
|
export { ListSkeleton } from './components/ListSkeleton'
|
|
84
85
|
export { DashboardFrame, SIDEBAR_WIDTH } from './components/DashboardFrame'
|
|
85
86
|
export { SidebarShell } from './components/SidebarShell'
|
package/src/tokens.css
CHANGED
|
@@ -56,3 +56,61 @@
|
|
|
56
56
|
.rlg-danger:focus-visible {
|
|
57
57
|
outline-color: var(--mantine-color-red-7);
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* The rules below are the same floor the components-kit ships in its global
|
|
62
|
+
* stylesheet. The panels don't load that sheet, so they are repeated here — the
|
|
63
|
+
* two files must move together.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* One focus ring everywhere: gray.9, 2px, 2px away, square like the element it
|
|
68
|
+
* surrounds. `.rlg-danger` above keeps the shape and changes only the colour.
|
|
69
|
+
*/
|
|
70
|
+
:focus-visible {
|
|
71
|
+
outline: 2px solid var(--mantine-color-gray-9);
|
|
72
|
+
outline-offset: 2px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* 44px under a finger. A panel is dense on purpose — 36px search, 36px create
|
|
77
|
+
* button, `sm` buttons — and that is right for a mouse. A thumb needs Apple's
|
|
78
|
+
* 44px (WCAG 2.5.5), so only coarse pointers get the floor.
|
|
79
|
+
*/
|
|
80
|
+
@media (pointer: coarse) {
|
|
81
|
+
.mantine-Button-root,
|
|
82
|
+
.mantine-ActionIcon-root,
|
|
83
|
+
.mantine-CloseButton-root {
|
|
84
|
+
min-height: 44px;
|
|
85
|
+
min-width: 44px;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Increased contrast moves the ramp one step darker where it is weakest: the
|
|
91
|
+
* gray.4 label (2.6:1) becomes gray.6, gray.5 becomes gray.7, and the gray.2
|
|
92
|
+
* hairline becomes gray.4. `:root:root` beats the `:root` Mantine injects at
|
|
93
|
+
* runtime, after this sheet.
|
|
94
|
+
*/
|
|
95
|
+
@media (prefers-contrast: more) {
|
|
96
|
+
:root:root {
|
|
97
|
+
--mantine-color-gray-2: #9ca3af;
|
|
98
|
+
--mantine-color-gray-3: #9ca3af;
|
|
99
|
+
--mantine-color-gray-4: #4b5563;
|
|
100
|
+
--mantine-color-gray-5: #374151;
|
|
101
|
+
--mantine-color-gray-6: #374151;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* Reduced motion: the sidebar appears instead of sliding in. Background and
|
|
107
|
+
* colour fades on rows and menu items stay — a fade is what Apple asks motion
|
|
108
|
+
* to become, not something to remove.
|
|
109
|
+
*/
|
|
110
|
+
@media (prefers-reduced-motion: reduce) {
|
|
111
|
+
.mantine-AppShell-navbar,
|
|
112
|
+
.mantine-AppShell-main,
|
|
113
|
+
.mantine-AppShell-header {
|
|
114
|
+
transition: none !important;
|
|
115
|
+
}
|
|
116
|
+
}
|