@ffid-br/tailor-ui 0.5.0 → 0.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/README.md +120 -2
- package/dist/index.es.js +1144 -651
- package/dist/index.umd.js +1 -1
- package/dist/style.css +292 -1
- package/dist/types/components/CinematicIntro.d.ts +2 -0
- package/dist/types/components/MascoteVetor.d.ts +1 -1
- package/dist/types/components/TailorBrand.d.ts +86 -0
- package/dist/types/components/TailorField.d.ts +19 -0
- package/dist/types/components/TailorLoader.d.ts +23 -5
- package/dist/types/components/TailorPageHeader.d.ts +47 -0
- package/dist/types/components/TailorResult.d.ts +27 -0
- package/dist/types/components/TailorSidebar.d.ts +0 -5
- package/dist/types/components/TailorSkeleton.d.ts +19 -0
- package/dist/types/components/TailorTable.d.ts +58 -0
- package/dist/types/components/TailorToaster.d.ts +19 -0
- package/dist/types/components/TailorTypewriter.d.ts +15 -0
- package/dist/types/i18n.d.ts +35 -0
- package/dist/types/index.d.ts +18 -0
- package/package.json +1 -1
package/dist/style.css
CHANGED
|
@@ -18,7 +18,289 @@
|
|
|
18
18
|
--tailor-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/*
|
|
21
|
+
/*
|
|
22
|
+
* Marca do cliente: componentes que leem estas variáveis vestem a marca de quem
|
|
23
|
+
* usa. Padrão Tailor aqui, em :where() para qualquer CSS do app sobrescrever
|
|
24
|
+
* (ou o componente TailorMarca). Tema escuro: `.dark` ou `[data-theme="dark"]`.
|
|
25
|
+
*/
|
|
26
|
+
:where(:root) {
|
|
27
|
+
/* Seguem os tokens do Tailwind do app: quem já troca --color-tailor-green pela
|
|
28
|
+
cor do cliente (accounts, brandTheme) leva o cabeçalho junto. */
|
|
29
|
+
--tailor-brand-primary: var(--color-tailor-green, var(--tailor-green));
|
|
30
|
+
--tailor-brand-primary-text: var(--color-tailor-ink, var(--tailor-ink));
|
|
31
|
+
--tailor-brand-text: var(--tailor-black);
|
|
32
|
+
--tailor-brand-text-muted: #666666;
|
|
33
|
+
--tailor-brand-border: #E5E5E5;
|
|
34
|
+
--tailor-brand-background: transparent;
|
|
35
|
+
--tailor-brand-heading-font: var(--tailor-font);
|
|
36
|
+
--tailor-brand-body-font: var(--tailor-font);
|
|
37
|
+
--tailor-brand-heading-weight: 500;
|
|
38
|
+
--tailor-brand-heading-case: none;
|
|
39
|
+
--tailor-brand-radius: 0;
|
|
40
|
+
}
|
|
41
|
+
:where(.dark, [data-theme="dark"]) {
|
|
42
|
+
--tailor-brand-primary-text: var(--color-tailor-green, var(--tailor-green));
|
|
43
|
+
--tailor-brand-text: var(--tailor-white);
|
|
44
|
+
--tailor-brand-text-muted: #A3A3A3;
|
|
45
|
+
--tailor-brand-border: rgba(255, 255, 255, 0.1);
|
|
46
|
+
}
|
|
47
|
+
.tailor-brand { display: contents; }
|
|
48
|
+
|
|
49
|
+
/* TailorPageHeader — cada medida tem a sua variável --tailor-header-*. */
|
|
50
|
+
.tailor-ph {
|
|
51
|
+
display: flex; flex-direction: column; gap: 21px;
|
|
52
|
+
padding: var(--tailor-header-padding, 0 0 21px);
|
|
53
|
+
background: var(--tailor-header-background, var(--tailor-brand-background));
|
|
54
|
+
border-bottom: var(--tailor-header-rule, 1px solid var(--tailor-brand-border));
|
|
55
|
+
color: var(--tailor-brand-text);
|
|
56
|
+
font-family: var(--tailor-brand-body-font);
|
|
57
|
+
}
|
|
58
|
+
.tailor-ph__row { display: flex; flex-direction: column; gap: 21px; }
|
|
59
|
+
@media (min-width: 640px) {
|
|
60
|
+
.tailor-ph__row { flex-direction: row; align-items: flex-end; justify-content: space-between; }
|
|
61
|
+
}
|
|
62
|
+
.tailor-ph__main { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
|
|
63
|
+
.tailor-ph__back {
|
|
64
|
+
display: inline-flex; align-items: center; gap: 6px; padding: 0; border: 0; background: none; cursor: pointer;
|
|
65
|
+
font: inherit; font-size: 13px; color: var(--tailor-brand-text-muted); text-decoration: none; border-radius: var(--tailor-brand-radius);
|
|
66
|
+
}
|
|
67
|
+
.tailor-ph__back:hover, .tailor-ph__breadcrumb-link:hover { color: var(--tailor-brand-text); }
|
|
68
|
+
.tailor-ph__breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; padding: 0; list-style: none; font-size: 13px; color: var(--tailor-brand-text-muted); }
|
|
69
|
+
.tailor-ph__breadcrumbs li { display: flex; align-items: center; gap: 6px; }
|
|
70
|
+
.tailor-ph__breadcrumb-link { color: inherit; text-decoration: none; }
|
|
71
|
+
.tailor-ph__breadcrumbs [aria-current="page"] { color: var(--tailor-brand-text); }
|
|
72
|
+
.tailor-ph__back:focus-visible, .tailor-ph__breadcrumb-link:focus-visible { outline: 3px solid var(--tailor-brand-primary); outline-offset: 2px; }
|
|
73
|
+
.tailor-ph__eyebrow {
|
|
74
|
+
display: flex; align-items: center; gap: 8px; margin: 5px 0 0;
|
|
75
|
+
font-size: 11px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
|
|
76
|
+
color: var(--tailor-brand-primary-text);
|
|
77
|
+
}
|
|
78
|
+
.tailor-ph__dot { width: 8px; height: 8px; background: var(--tailor-brand-primary); border-radius: var(--tailor-brand-radius); }
|
|
79
|
+
.tailor-ph__title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 13px; }
|
|
80
|
+
.tailor-ph__title {
|
|
81
|
+
margin: 0; line-height: 1.1;
|
|
82
|
+
font-family: var(--tailor-brand-heading-font); font-weight: var(--tailor-brand-heading-weight);
|
|
83
|
+
text-transform: var(--tailor-brand-heading-case);
|
|
84
|
+
font-size: var(--tailor-header-title-size, clamp(26px, 4vw, 42px));
|
|
85
|
+
}
|
|
86
|
+
.tailor-ph__description { margin: 0; max-width: 610px; font-size: 16px; line-height: 1.618; color: var(--tailor-brand-text-muted); }
|
|
87
|
+
.tailor-ph__aside { display: flex; flex-shrink: 0; flex-wrap: wrap; align-items: center; gap: 13px; }
|
|
88
|
+
.tailor-ph__stat { text-align: right; }
|
|
89
|
+
.tailor-ph__stat-value { display: block; font-size: 26px; font-weight: 500; line-height: 1; font-variant-numeric: tabular-nums; font-family: var(--tailor-brand-heading-font); }
|
|
90
|
+
.tailor-ph__stat-label { display: block; margin-top: 8px; font-size: 13px; color: var(--tailor-brand-text-muted); }
|
|
91
|
+
.tailor-ph__below { min-width: 0; }
|
|
92
|
+
|
|
93
|
+
/* TailorTabela — relatório. Medidas em --tailor-table-*, cores da marca. */
|
|
94
|
+
.tailor-table {
|
|
95
|
+
border: 1px solid var(--tailor-brand-border); background: var(--tailor-table-background, transparent);
|
|
96
|
+
color: var(--tailor-brand-text); font-family: var(--tailor-brand-body-font); border-radius: var(--tailor-brand-radius);
|
|
97
|
+
}
|
|
98
|
+
.tailor-table__scroll { overflow-x: auto; }
|
|
99
|
+
.tailor-table table { width: 100%; border-collapse: collapse; font-size: var(--tailor-table-font, 14px); }
|
|
100
|
+
.tailor-table__caption { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
101
|
+
.tailor-table th {
|
|
102
|
+
padding: var(--tailor-table-header-padding, 10px 13px); white-space: nowrap;
|
|
103
|
+
font-size: 10px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase;
|
|
104
|
+
color: var(--tailor-brand-text-muted); background: var(--tailor-table-header, color-mix(in srgb, var(--tailor-brand-text) 3%, transparent));
|
|
105
|
+
border-bottom: 1px solid var(--tailor-brand-border);
|
|
106
|
+
}
|
|
107
|
+
.tailor-table__sticky th { position: sticky; top: var(--tailor-table-top, 0); z-index: 1; background: var(--tailor-table-sticky-top, var(--tailor-white)); }
|
|
108
|
+
:where(.dark, [data-theme="dark"]) .tailor-table__sticky th { background: var(--tailor-table-sticky-top, var(--tailor-black)); }
|
|
109
|
+
.tailor-table td { padding: var(--tailor-table-padding, 10px 13px); border-top: 1px solid var(--tailor-brand-border); }
|
|
110
|
+
.tailor-table tbody tr:first-child td { border-top: 0; }
|
|
111
|
+
.tailor-table tbody tr:hover td { background: color-mix(in srgb, var(--tailor-brand-primary) 6%, transparent); }
|
|
112
|
+
.tailor-table--right { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
113
|
+
.tailor-table--center { text-align: center; }
|
|
114
|
+
.tailor-table--left { text-align: left; }
|
|
115
|
+
.tailor-table__id { white-space: nowrap; font-family: ui-monospace, "Roboto Mono", Menlo, monospace; font-size: 12px; }
|
|
116
|
+
.tailor-table__sort { display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: none; font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; cursor: pointer; }
|
|
117
|
+
.tailor-table__sort:hover { color: var(--tailor-brand-text); }
|
|
118
|
+
.tailor-table__arrow { opacity: 0.35; }
|
|
119
|
+
.tailor-table__arrow--active { opacity: 1; color: var(--tailor-brand-primary-text); }
|
|
120
|
+
.tailor-table__clickable { cursor: pointer; }
|
|
121
|
+
.tailor-table__clickable:focus-visible, .tailor-table button:focus-visible { outline: 3px solid var(--tailor-brand-primary); outline-offset: -3px; }
|
|
122
|
+
.tailor-table__state { padding: 34px 13px; text-align: center; color: var(--tailor-brand-text-muted); }
|
|
123
|
+
.tailor-table tfoot td { border-top: 1px solid var(--tailor-brand-text); font-weight: 600; }
|
|
124
|
+
.tailor-table__pagination {
|
|
125
|
+
display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px;
|
|
126
|
+
padding: 8px 13px; border-top: 1px solid var(--tailor-brand-border); font-size: 13px; color: var(--tailor-brand-text-muted);
|
|
127
|
+
}
|
|
128
|
+
.tailor-table__pages { display: flex; gap: 8px; }
|
|
129
|
+
.tailor-table__pagination button {
|
|
130
|
+
min-height: 34px; padding: 0 13px; border: 1px solid var(--tailor-brand-border); border-radius: var(--tailor-brand-radius);
|
|
131
|
+
background: transparent; color: var(--tailor-brand-text); font: inherit; cursor: pointer;
|
|
132
|
+
}
|
|
133
|
+
.tailor-table__pagination button:hover:not(:disabled) { border-color: var(--tailor-brand-text); }
|
|
134
|
+
.tailor-table__pagination button:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
135
|
+
|
|
136
|
+
/* TailorLoader — o ponto corre a espiral Fibonacci (trechos 3, 5, 8, 13, 21, 34 = 84) com rastro, pulsa e volta. */
|
|
137
|
+
.tailor-loader__rastro { stroke-dasharray: 1; stroke-dashoffset: 1; animation: tailor-rastro 2.618s linear infinite; }
|
|
138
|
+
@keyframes tailor-rastro {
|
|
139
|
+
0% { stroke-dashoffset: 1; opacity: 1; }
|
|
140
|
+
80% { stroke-dashoffset: 0; opacity: 1; }
|
|
141
|
+
100% { stroke-dashoffset: 0; opacity: 0; }
|
|
142
|
+
}
|
|
143
|
+
/* Cantos da espiral em fração do trecho de corrida (80%): 3, 8, 16, 29, 50, 84 de 84. */
|
|
144
|
+
.tailor-loader__corredor { animation: tailor-corredor 2.618s linear infinite; }
|
|
145
|
+
@keyframes tailor-corredor {
|
|
146
|
+
0% { transform: translate(0, 0); opacity: 1; }
|
|
147
|
+
2.857% { transform: translate(3px, 0); }
|
|
148
|
+
7.619% { transform: translate(3px, -5px); }
|
|
149
|
+
15.238% { transform: translate(-5px, -5px); }
|
|
150
|
+
27.619% { transform: translate(-5px, 8px); }
|
|
151
|
+
47.619% { transform: translate(16px, 8px); }
|
|
152
|
+
80% { transform: translate(16px, -26px) scale(1); opacity: 1; }
|
|
153
|
+
86% { transform: translate(16px, -26px) scale(2.2); opacity: 1; }
|
|
154
|
+
92% { transform: translate(16px, -26px) scale(0); opacity: 0; }
|
|
155
|
+
100% { transform: translate(0, 0) scale(1); opacity: 0; }
|
|
156
|
+
}
|
|
157
|
+
/* compact: o ponto dá a volta num quadrado de 13px. */
|
|
158
|
+
.tailor-loader__volta { background: var(--tailor-loader-dot, var(--tailor-brand-primary)); animation: tailor-volta 1.236s steps(1, end) infinite; }
|
|
159
|
+
@keyframes tailor-volta {
|
|
160
|
+
0% { transform: translate(0, 0); }
|
|
161
|
+
25% { transform: translate(8px, 0); }
|
|
162
|
+
50% { transform: translate(8px, 8px); }
|
|
163
|
+
75% { transform: translate(0, 8px); }
|
|
164
|
+
}
|
|
165
|
+
/* Sucesso: a malha nasce verde do ponto, o ponto estoura e o check se desenha. Uma vez só. */
|
|
166
|
+
.tailor-loader__ok-malha .tailor-malha__linha { animation: tailor-ok-malha 0.618s var(--tailor-ease-in-out) both; }
|
|
167
|
+
@keyframes tailor-ok-malha { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; opacity: 0.5; } }
|
|
168
|
+
.tailor-loader__ok-ponto { transform-box: fill-box; transform-origin: center; animation: tailor-ok-ponto 0.382s var(--tailor-ease-in-out) both; }
|
|
169
|
+
@keyframes tailor-ok-ponto { 0% { transform: scale(1); } 50% { transform: scale(3); opacity: 1; } 100% { transform: scale(0); opacity: 0; } }
|
|
170
|
+
.tailor-loader__check { stroke-dasharray: 1; stroke-dashoffset: 1; animation: tailor-check 0.382s 0.3s var(--tailor-ease-in-out) both; }
|
|
171
|
+
@keyframes tailor-check { to { stroke-dashoffset: 0; } }
|
|
172
|
+
|
|
173
|
+
/* TailorEsqueleto — barras retas e a linha da marca varrendo de cima a baixo. */
|
|
174
|
+
.tailor-skeleton { position: relative; overflow: hidden; display: grid; gap: 13px; padding: 2px 0; }
|
|
175
|
+
.tailor-skeleton__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
176
|
+
.tailor-skeleton__bar { display: block; height: 13px; background: var(--tailor-skeleton-bar, #F2F2F2); }
|
|
177
|
+
.tailor-skeleton__bar--thin { height: 8px; }
|
|
178
|
+
.tailor-skeleton__bar--thick { height: 34px; }
|
|
179
|
+
.tailor-skeleton__row { display: grid; gap: 21px; padding: 8px 0; border-bottom: 1px solid var(--tailor-brand-border); }
|
|
180
|
+
.tailor-skeleton__row--header .tailor-skeleton__bar { height: 8px; }
|
|
181
|
+
.tailor-skeleton__cards { display: grid; gap: 13px; grid-template-columns: repeat(auto-fit, minmax(144px, 1fr)); }
|
|
182
|
+
.tailor-skeleton__card { display: grid; gap: 13px; padding: 21px; border: 1px solid var(--tailor-brand-border); }
|
|
183
|
+
.tailor-skeleton::after {
|
|
184
|
+
content: ''; position: absolute; left: 0; top: 0; width: 8px; height: 8px;
|
|
185
|
+
background: var(--tailor-brand-primary);
|
|
186
|
+
animation: tailor-skeleton-dot 2.618s var(--tailor-ease-in-out) infinite;
|
|
187
|
+
}
|
|
188
|
+
/* O ponto atravessa o topo uma vez e some; o resto fica parado. */
|
|
189
|
+
@keyframes tailor-skeleton-dot {
|
|
190
|
+
0% { left: 0; opacity: 1; }
|
|
191
|
+
61.8% { left: calc(100% - 8px); opacity: 1; }
|
|
192
|
+
70%, 100% { left: calc(100% - 8px); opacity: 0; }
|
|
193
|
+
}
|
|
194
|
+
:where(.dark, [data-theme="dark"]) .tailor-skeleton { --tailor-skeleton-bar: #1F1F1F; }
|
|
195
|
+
|
|
196
|
+
/* TailorAvisos — caixa preta; a cor do tipo entra varrendo e recolhe na borda; barra do tempo. */
|
|
197
|
+
.tailor-toaster {
|
|
198
|
+
position: fixed; z-index: 1100; right: 21px; top: 21px; display: flex; flex-direction: column; gap: 8px;
|
|
199
|
+
width: min(377px, calc(100vw - 32px)); pointer-events: none;
|
|
200
|
+
}
|
|
201
|
+
@media (max-width: 640px) { .tailor-toaster { right: 16px; left: 16px; top: 16px; width: auto; } }
|
|
202
|
+
.tailor-toast {
|
|
203
|
+
--tailor-toast-color: var(--tailor-brand-primary);
|
|
204
|
+
position: relative; overflow: hidden; pointer-events: auto; display: flex; align-items: flex-start; gap: 13px;
|
|
205
|
+
padding: 16px 13px 18px 21px; background: #000; color: #fff; font-family: var(--tailor-font);
|
|
206
|
+
animation: tailor-toast-in 0.5s var(--tailor-ease-in-out) both;
|
|
207
|
+
}
|
|
208
|
+
.tailor-toast--error { --tailor-toast-color: #FF0040; }
|
|
209
|
+
.tailor-toast::before {
|
|
210
|
+
content: ''; position: absolute; left: 0; top: 0; bottom: 0; background: var(--tailor-toast-color);
|
|
211
|
+
width: 3px;
|
|
212
|
+
}
|
|
213
|
+
.tailor-toast > * { position: relative; }
|
|
214
|
+
/* O aviso abre a partir do ponto do ícone (centro do ícone de 21px: 31,5px da esquerda, 26,5px do topo). */
|
|
215
|
+
@keyframes tailor-toast-in { from { clip-path: inset(22.5px calc(100% - 35.5px) calc(100% - 30.5px) 27.5px); } to { clip-path: inset(0); } }
|
|
216
|
+
.tailor-toast__icon { width: 21px; height: 21px; flex-shrink: 0; overflow: visible; stroke: var(--tailor-toast-color); fill: var(--tailor-toast-color); }
|
|
217
|
+
.tailor-toast .tailor-loader__check { animation-delay: 0s; }
|
|
218
|
+
.tailor-toast__text { flex: 1; min-width: 0; }
|
|
219
|
+
.tailor-toast__title { margin: 0; font-size: 15px; font-weight: 500; line-height: 1.382; }
|
|
220
|
+
.tailor-toast__description { margin: 3px 0 0; font-size: 13px; line-height: 1.5; color: rgb(255 255 255 / 0.7); }
|
|
221
|
+
.tailor-toast__close {
|
|
222
|
+
margin: -5px -3px 0 0; width: 34px; height: 34px; flex-shrink: 0; border: 0; background: none; color: rgb(255 255 255 / 0.7);
|
|
223
|
+
font-size: 21px; line-height: 1; cursor: pointer;
|
|
224
|
+
}
|
|
225
|
+
.tailor-toast__close:hover { color: #fff; }
|
|
226
|
+
.tailor-toast__close:focus-visible { outline: 2px solid var(--tailor-toast-color); outline-offset: -2px; }
|
|
227
|
+
.tailor-toast__timer {
|
|
228
|
+
position: absolute !important; left: 0; right: 0; bottom: 0; height: 3px; background: var(--tailor-toast-color);
|
|
229
|
+
transform-origin: left; animation: tailor-toast-timer var(--tailor-toast-duration, 4s) linear both;
|
|
230
|
+
}
|
|
231
|
+
@keyframes tailor-toast-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }
|
|
232
|
+
|
|
233
|
+
/* TailorDigitando — o ponto da marca como cursor. */
|
|
234
|
+
.tailor-typewriter__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
235
|
+
.tailor-typewriter__cursor {
|
|
236
|
+
display: inline-block; width: 0.5em; height: 0.5em; margin-left: 0.12em; vertical-align: baseline;
|
|
237
|
+
background: var(--tailor-typewriter-cursor, var(--tailor-brand-primary));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* TailorConclusao — a cor varre o painel, o ícone e o título se escrevem, e tudo recolhe no ponto. */
|
|
241
|
+
.tailor-result {
|
|
242
|
+
--tailor-result-color: var(--tailor-brand-primary);
|
|
243
|
+
--tailor-result-text: #000;
|
|
244
|
+
position: absolute; inset: 0; z-index: 20; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
245
|
+
gap: 13px; padding: 34px 21px; text-align: center; font-family: var(--tailor-font);
|
|
246
|
+
background: var(--tailor-result-color); color: var(--tailor-result-text);
|
|
247
|
+
--tailor-typewriter-cursor: var(--tailor-result-text);
|
|
248
|
+
--tailor-result-x: 50%;
|
|
249
|
+
--tailor-result-y: 50%;
|
|
250
|
+
animation: tailor-result-in 0.5s var(--tailor-ease-in-out) both;
|
|
251
|
+
}
|
|
252
|
+
.tailor-result--error { --tailor-result-color: #FF0040; --tailor-result-text: #fff; }
|
|
253
|
+
.tailor-result--leaving { animation: tailor-result-out 0.618s var(--tailor-ease-in-out) both; }
|
|
254
|
+
/* O ponto: um quadrado de 8px no centro do botão de origem (ou do painel). */
|
|
255
|
+
@keyframes tailor-result-in {
|
|
256
|
+
from { clip-path: inset(calc(var(--tailor-result-y) - 4px) calc(100% - var(--tailor-result-x) - 4px) calc(100% - var(--tailor-result-y) - 4px) calc(var(--tailor-result-x) - 4px)); }
|
|
257
|
+
to { clip-path: inset(0); }
|
|
258
|
+
}
|
|
259
|
+
@keyframes tailor-result-out {
|
|
260
|
+
0% { clip-path: inset(0); }
|
|
261
|
+
70% { clip-path: inset(calc(var(--tailor-result-y) - 4px) calc(100% - var(--tailor-result-x) - 4px) calc(100% - var(--tailor-result-y) - 4px) calc(var(--tailor-result-x) - 4px)); opacity: 1; }
|
|
262
|
+
100% { clip-path: inset(calc(var(--tailor-result-y) - 4px) calc(100% - var(--tailor-result-x) - 4px) calc(100% - var(--tailor-result-y) - 4px) calc(var(--tailor-result-x) - 4px)); opacity: 0; }
|
|
263
|
+
}
|
|
264
|
+
.tailor-result__icon { width: 55px; height: 55px; overflow: visible; stroke: var(--tailor-result-text); }
|
|
265
|
+
.tailor-result .tailor-loader__check { animation-delay: 0.45s; }
|
|
266
|
+
.tailor-result__title { margin: 0; min-height: 1.1em; font-size: 26px; font-weight: 500; line-height: 1.1; }
|
|
267
|
+
.tailor-result__description { font-size: 15px; line-height: 1.5; opacity: 0.8; }
|
|
268
|
+
.tailor-result__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 8px; }
|
|
269
|
+
|
|
270
|
+
/* TailorField — rótulo, controle e mensagem. Erro: borda e ponto vermelhos, um empurrão curto para chamar o olho. */
|
|
271
|
+
.tailor-field { display: grid; gap: 8px; font-family: var(--tailor-font); color: var(--tailor-brand-text); }
|
|
272
|
+
.tailor-field__label { display: flex; align-items: baseline; gap: 8px; font-size: 13px; font-weight: 500; }
|
|
273
|
+
.tailor-field__required { font-size: 12px; font-weight: 400; color: var(--tailor-brand-text-muted); }
|
|
274
|
+
.tailor-field__control {
|
|
275
|
+
min-height: 40px; width: 100%; padding: 0 13px; border: 1px solid var(--tailor-field-border, #999999); border-radius: var(--tailor-brand-radius, 0);
|
|
276
|
+
background: transparent; color: inherit; font: inherit; font-size: 15px;
|
|
277
|
+
}
|
|
278
|
+
textarea.tailor-field__control { padding: 8px 13px; min-height: 89px; }
|
|
279
|
+
.tailor-field__control:focus { outline: none; border-color: var(--tailor-brand-text); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tailor-brand-primary) 60%, transparent); }
|
|
280
|
+
.tailor-field--error .tailor-field__control {
|
|
281
|
+
border-color: #FF0040; background: color-mix(in srgb, #FF0040 5%, transparent);
|
|
282
|
+
animation: tailor-field-nudge 0.382s var(--tailor-ease-out);
|
|
283
|
+
}
|
|
284
|
+
.tailor-field--error .tailor-field__control:focus { box-shadow: 0 0 0 3px color-mix(in srgb, #FF0040 35%, transparent); }
|
|
285
|
+
.tailor-field__error { display: flex; align-items: baseline; gap: 8px; margin: 0; font-size: 13px; font-weight: 500; color: #E0003A; }
|
|
286
|
+
.tailor-field__dot { flex-shrink: 0; width: 8px; height: 8px; background: #FF0040; transform: translateY(-1px); }
|
|
287
|
+
.tailor-field__hint { margin: 0; font-size: 13px; color: var(--tailor-brand-text-muted); }
|
|
288
|
+
:where(.dark, [data-theme="dark"]) .tailor-field__error { color: #FF7462; }
|
|
289
|
+
:where(.dark, [data-theme="dark"]) .tailor-field { --tailor-field-border: #666666; }
|
|
290
|
+
@keyframes tailor-field-nudge { 0%, 100% { transform: none; } 25% { transform: translateX(-5px); } 50% { transform: translateX(5px); } 75% { transform: translateX(-2px); } }
|
|
291
|
+
|
|
292
|
+
/* TailorUserMenu — o painel nasce do ponto no canto junto do avatar e recolhe nele. */
|
|
293
|
+
.tailor-user-menu__panel { animation: tailor-user-menu-in 0.382s var(--tailor-ease-out) both; }
|
|
294
|
+
.tailor-user-menu__panel--closing { animation: tailor-user-menu-out 0.236s var(--tailor-ease-in-out) both; }
|
|
295
|
+
.tailor-user-menu__panel::before {
|
|
296
|
+
content: ''; position: absolute; left: -1px; bottom: -1px; width: 8px; height: 8px; background: var(--tailor-brand-primary);
|
|
297
|
+
animation: tailor-user-menu-dot 0.618s var(--tailor-ease-in-out) both;
|
|
298
|
+
}
|
|
299
|
+
@keyframes tailor-user-menu-in { from { clip-path: inset(calc(100% - 8px) calc(100% - 8px) 0 0); } to { clip-path: inset(0); } }
|
|
300
|
+
@keyframes tailor-user-menu-out { from { clip-path: inset(0); } to { clip-path: inset(calc(100% - 8px) calc(100% - 8px) 0 0); } }
|
|
301
|
+
@keyframes tailor-user-menu-dot { 0%, 61.8% { opacity: 1; } 100% { opacity: 0; } }
|
|
302
|
+
|
|
303
|
+
/* PontoMalha e a malha da entrada: se desenham a partir do ponto e somem, em 2φ (3,236s). */
|
|
22
304
|
.tailor-malha__linha {
|
|
23
305
|
stroke-dasharray: 1;
|
|
24
306
|
stroke-dashoffset: 1;
|
|
@@ -144,6 +426,15 @@
|
|
|
144
426
|
/* Menos movimento: a informação continua, o movimento sai. */
|
|
145
427
|
@media (prefers-reduced-motion: reduce) {
|
|
146
428
|
.tailor-malha__linha { animation: none; stroke-dashoffset: 0; }
|
|
429
|
+
.tailor-loader__rastro, .tailor-loader__check, .tailor-loader__ok-malha .tailor-malha__linha { animation: none; stroke-dashoffset: 0; opacity: 1; }
|
|
430
|
+
.tailor-loader__corredor, .tailor-loader__volta { animation: none; }
|
|
431
|
+
.tailor-loader__ok-ponto { animation: none; opacity: 0; }
|
|
432
|
+
.tailor-skeleton::after { animation: none; display: none; }
|
|
433
|
+
.tailor-field--error .tailor-field__control { animation: none; }
|
|
434
|
+
.tailor-user-menu__panel, .tailor-user-menu__panel--closing, .tailor-user-menu__panel::before { animation: none; }
|
|
435
|
+
.tailor-user-menu__panel::before { display: none; }
|
|
436
|
+
.tailor-toast { animation: none; }
|
|
437
|
+
.tailor-result, .tailor-result > * { animation: none !important; }
|
|
147
438
|
.tailor-loader__reticencias::after { animation: none; content: '...'; }
|
|
148
439
|
.tailor-ponto-pisca { animation: none; }
|
|
149
440
|
.tailor-mascote__corpo, .tailor-mascote__olho, .tailor-mascote__verde { animation: none; }
|
|
@@ -2,7 +2,9 @@ import { default as React } from 'react';
|
|
|
2
2
|
export type CinematicIntroProps = {
|
|
3
3
|
/** Chamado ao fim da abertura, no "Pular" ou de imediato com menos movimento. */
|
|
4
4
|
onComplete: () => void;
|
|
5
|
+
/** Linha curta acima do slogan, com o ponto verde. Sem ela, não aparece. */
|
|
5
6
|
overline?: string;
|
|
7
|
+
/** Frase abaixo da marca (ex.: "Sua operação em outro nível"). Sem ela, não aparece. */
|
|
6
8
|
slogan?: string;
|
|
7
9
|
skipLabel?: string;
|
|
8
10
|
/** Nome do produto abaixo da marca (ex.: "Pilot"). Omitido: só a marca. */
|
|
@@ -139,7 +139,7 @@ declare const EXPRESSOES: {
|
|
|
139
139
|
export type ExpressaoDoMascote = keyof typeof EXPRESSOES;
|
|
140
140
|
/** Pose do vetor: o movimento, o olhar padrão e os enfeites de cada estado. */
|
|
141
141
|
export type PoseDoMascote = 'parado' | 'cumprimentando' | 'pensando' | 'falando' | 'esperando' | 'comemorando' | 'erro' | 'dormindo';
|
|
142
|
-
/** Desenhados com o topo da cabeça em (0, 0). Só em tinta: o verde é do agente. */
|
|
142
|
+
/** Desenhados com o topo da cabeça em (0, 0). Só em tinta (exceto o gorro): o verde é do agente. */
|
|
143
143
|
declare const ACESSORIOS: {
|
|
144
144
|
gorro: React.JSX.Element;
|
|
145
145
|
orelhas: React.JSX.Element;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A marca do cliente em variáveis CSS. Os componentes que leem `--tailor-brand-*`
|
|
4
|
+
* (`TailorPageHeader`, `TailorTable`, avisos) trocam de cor, fonte e cantos sem mudar uma
|
|
5
|
+
* linha de componente. Sem tema, valem os padrões Tailor do `style.css`.
|
|
6
|
+
*/
|
|
7
|
+
export type TailorTheme = {
|
|
8
|
+
/** Cor de destaque: o ponto do sobretítulo, a barra de foco. */
|
|
9
|
+
primary?: string;
|
|
10
|
+
/** Destaque como texto sobre o fundo (precisa de contraste; no Tailor é o verde-ink). */
|
|
11
|
+
primaryText?: string;
|
|
12
|
+
text?: string;
|
|
13
|
+
textMuted?: string;
|
|
14
|
+
border?: string;
|
|
15
|
+
/** Fundo do cabeçalho. Padrão: transparente (o cabeçalho é o começo da página). */
|
|
16
|
+
background?: string;
|
|
17
|
+
headingFont?: string;
|
|
18
|
+
bodyFont?: string;
|
|
19
|
+
headingWeight?: string | number;
|
|
20
|
+
/** `uppercase` para marcas que pedem título em caixa alta. */
|
|
21
|
+
headingCase?: 'none' | 'uppercase';
|
|
22
|
+
/** Cantos dos elementos com caixa (etiqueta, botão voltar). Tailor: 0. */
|
|
23
|
+
radius?: string;
|
|
24
|
+
};
|
|
25
|
+
/** O tema como objeto de variáveis, para `style` ou para montar um CSS do cliente. */
|
|
26
|
+
export declare function themeToVars(theme: TailorTheme): Record<string, string>;
|
|
27
|
+
export type TailorBrandProps = {
|
|
28
|
+
theme: TailorTheme;
|
|
29
|
+
/** Ajustes do tema escuro (`.dark` ou `[data-theme="dark"]` num ancestral). */
|
|
30
|
+
dark?: TailorTheme;
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Aplica o tema a tudo que estiver dentro, sem mexer no layout (`display: contents`).
|
|
35
|
+
* As variáveis vão num `<style>` com seletor próprio, e não em `style`, para o
|
|
36
|
+
* tema escuro conseguir sobrescrever as do claro.
|
|
37
|
+
*/
|
|
38
|
+
export declare function TailorBrand({ theme, dark, children }: TailorBrandProps): React.JSX.Element;
|
|
39
|
+
/**
|
|
40
|
+
* `account_info.whitelabel` do cliente (Mongo `clients`). Os campos além de
|
|
41
|
+
* `colors.primary/secondary/background/text` e `logos.*` são a proposta para
|
|
42
|
+
* completar a marca; enquanto não existirem, ficam os padrões Tailor.
|
|
43
|
+
*/
|
|
44
|
+
export type TailorWhitelabel = {
|
|
45
|
+
colors?: {
|
|
46
|
+
primary?: string;
|
|
47
|
+
secondary?: string;
|
|
48
|
+
background?: string;
|
|
49
|
+
text?: string;
|
|
50
|
+
text_muted?: string;
|
|
51
|
+
border?: string;
|
|
52
|
+
dark?: {
|
|
53
|
+
primary?: string;
|
|
54
|
+
background?: string;
|
|
55
|
+
text?: string;
|
|
56
|
+
text_muted?: string;
|
|
57
|
+
border?: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
fonts?: {
|
|
61
|
+
heading?: string;
|
|
62
|
+
body?: string;
|
|
63
|
+
heading_weight?: number | string;
|
|
64
|
+
heading_case?: 'none' | 'uppercase';
|
|
65
|
+
};
|
|
66
|
+
shape?: {
|
|
67
|
+
radius?: string;
|
|
68
|
+
};
|
|
69
|
+
logos?: {
|
|
70
|
+
rectangular_light?: string;
|
|
71
|
+
rectangular_dark?: string;
|
|
72
|
+
square_light?: string;
|
|
73
|
+
square_dark?: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
/** Razão de contraste WCAG entre duas cores hex. */
|
|
77
|
+
export declare function contrast(a: string, b: string): number;
|
|
78
|
+
/**
|
|
79
|
+
* Traduz o whitelabel do Mongo para o tema. Ignora o azul padrão do cadastro e
|
|
80
|
+
* valores que não são hex. A primária só vira cor de texto se tiver contraste
|
|
81
|
+
* AA sobre o fundo; senão o texto de destaque usa a cor de texto da marca.
|
|
82
|
+
*/
|
|
83
|
+
export declare function themeFromWhitelabel(wl?: TailorWhitelabel | null): {
|
|
84
|
+
theme: TailorTheme;
|
|
85
|
+
dark?: TailorTheme;
|
|
86
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export type TailorFieldProps = {
|
|
3
|
+
label: React.ReactNode;
|
|
4
|
+
/** Mensagem de erro. Com ela, o campo fica vermelho e a mensagem aparece abaixo, com o ponto. */
|
|
5
|
+
error?: React.ReactNode;
|
|
6
|
+
/** Ajuda abaixo do campo, quando não há erro. */
|
|
7
|
+
hint?: React.ReactNode;
|
|
8
|
+
/** Marca o campo como obrigatório (no rótulo e no `required` do controle). */
|
|
9
|
+
required?: boolean;
|
|
10
|
+
/** Um `input`, `select` ou `textarea` (ou componente que repasse as props). Recebe id, aria e a classe do campo. */
|
|
11
|
+
children: React.ReactElement;
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Rótulo, controle e mensagem num lugar só. O erro aparece de uma vez (precisa
|
|
16
|
+
* ser lido já): borda vermelha, fundo levemente tingido e a mensagem com o
|
|
17
|
+
* ponto vermelho, ligada ao controle por `aria-describedby`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function TailorField({ label, error, hint, required, children, className }: TailorFieldProps): React.JSX.Element;
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
export type TailorLoaderProps = {
|
|
3
|
-
/** Rótulo principal. Padrão:
|
|
3
|
+
/** Rótulo principal. Padrão: o texto `loading` do idioma. */
|
|
4
4
|
label?: string;
|
|
5
5
|
/** Linha de apoio abaixo do rótulo (o que está sendo carregado, ou quanto falta). */
|
|
6
|
-
detail?:
|
|
6
|
+
detail?: React.ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* Arte do parceiro ou do cliente acima do loader (`inline` e `overlay`), para a
|
|
9
|
+
* espera assinar com a marca dele. Ex.: `whitelabel.logos.rectangular_light`.
|
|
10
|
+
*/
|
|
11
|
+
logo?: string;
|
|
12
|
+
/** Arte para fundo escuro (tema escuro e `overlay`). Sem ela, vale `logo`. */
|
|
13
|
+
logoDark?: string;
|
|
14
|
+
/** Frases que o ponto escreve e apaga em ciclo, abaixo do rótulo ("Cruzando dados", "Quase lá"). */
|
|
15
|
+
messages?: string[];
|
|
16
|
+
/** Progresso real, 0 a 100. Mostra a barra e o número. Sem ele, só a animação. */
|
|
17
|
+
progress?: number;
|
|
18
|
+
/** Etapas da carga; só aparecem com `progress` (sem ele não há etapa atual). */
|
|
19
|
+
steps?: string[];
|
|
7
20
|
/**
|
|
8
21
|
* `inline`: no lugar do conteúdo. `overlay`: tela inteira, bloqueia.
|
|
9
22
|
* `compact`: uma linha (ponto + rótulo), para botões e listas.
|
|
10
|
-
* `
|
|
23
|
+
* `box`: numa caixa própria de 377px, com fundo e borda — para painéis
|
|
11
24
|
* estreitos e fundos com estampa (chat), onde o inline ficaria transparente.
|
|
12
25
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
26
|
+
/** `caixa` é o nome antigo de `box` (obsoleto). */
|
|
27
|
+
variant?: 'inline' | 'overlay' | 'compact' | 'box' | 'caixa';
|
|
28
|
+
/** Troca a corrida pelo sucesso animado (a malha nasce verde e o ponto vira check). Rótulo padrão: o texto `done` do idioma. */
|
|
29
|
+
success?: boolean;
|
|
30
|
+
/** Tira o respiro externo do `inline`/`box` (quem chama já tem espaçamento). */
|
|
31
|
+
noPadding?: boolean;
|
|
32
|
+
/** @deprecated use `noPadding`. */
|
|
15
33
|
semRespiro?: boolean;
|
|
16
34
|
className?: string;
|
|
17
35
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export type TailorPageHeaderLink = {
|
|
3
|
+
href: string;
|
|
4
|
+
className: string;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
'aria-current'?: 'page';
|
|
7
|
+
};
|
|
8
|
+
export type TailorPageHeaderProps = {
|
|
9
|
+
/** ReactNode para permitir um acento dentro do título. */
|
|
10
|
+
title: React.ReactNode;
|
|
11
|
+
/** Uma frase: o que se faz nesta tela. */
|
|
12
|
+
description?: React.ReactNode;
|
|
13
|
+
/** Seção ou contexto, com o ponto da marca. Sem ele, não aparece. */
|
|
14
|
+
eyebrow?: string;
|
|
15
|
+
/** Trilha de navegação; o último item é a página atual. */
|
|
16
|
+
breadcrumbs?: Array<{
|
|
17
|
+
label: string;
|
|
18
|
+
href?: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Link ou ação de voltar, acima de tudo. */
|
|
21
|
+
back?: {
|
|
22
|
+
label: string;
|
|
23
|
+
href?: string;
|
|
24
|
+
onClick?: () => void;
|
|
25
|
+
};
|
|
26
|
+
/** Ao lado do título: status, plano, contagem. */
|
|
27
|
+
badge?: React.ReactNode;
|
|
28
|
+
/** Números do topo, quando ajudam a decidir o que fazer aqui. */
|
|
29
|
+
stats?: Array<{
|
|
30
|
+
label: string;
|
|
31
|
+
value: React.ReactNode;
|
|
32
|
+
}>;
|
|
33
|
+
/** Botões e busca, à direita. */
|
|
34
|
+
actions?: React.ReactNode;
|
|
35
|
+
/** Abaixo, na largura toda: abas, filtros, barra de busca. */
|
|
36
|
+
children?: React.ReactNode;
|
|
37
|
+
/** Link do roteador do app (ex.: `NavLink`). Repasse todas as props. Sem ele, sai um `<a href>`. */
|
|
38
|
+
renderLink?: (props: TailorPageHeaderLink) => React.ReactNode;
|
|
39
|
+
className?: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* O cabeçalho de página. Visual só por variáveis `--tailor-brand-*` e
|
|
43
|
+
* `--tailor-header-*` (ver `TailorBrand`), para vestir a marca do cliente:
|
|
44
|
+
* cor de destaque, fontes, peso e caixa do título, cantos, fundo.
|
|
45
|
+
* Padrão Tailor: sem card nem sombra, régua de 1px embaixo, título 26 → 42.
|
|
46
|
+
*/
|
|
47
|
+
export declare function TailorPageHeader({ title, description, eyebrow, breadcrumbs, back, badge, stats, actions, children, renderLink, className, }: TailorPageHeaderProps): React.JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export type TailorResultProps = {
|
|
3
|
+
status: 'success' | 'error';
|
|
4
|
+
/** No sucesso, escrito pelo ponto letra a letra; no erro, aparece inteiro. */
|
|
5
|
+
title: string;
|
|
6
|
+
description?: React.ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* O botão que disparou a ação. O painel nasce do ponto no centro dele e
|
|
9
|
+
* recolhe de volta para lá. Sem ele, o ponto fica no centro do painel.
|
|
10
|
+
*/
|
|
11
|
+
origin?: React.RefObject<HTMLElement | null>;
|
|
12
|
+
/** ms até recolher e chamar `onClose`. Padrão: 1600 no sucesso; no erro não fecha sozinho. `false` desliga. */
|
|
13
|
+
closeAfter?: number | false;
|
|
14
|
+
/** Chamado depois que o painel recolhe no ponto: feche o modal aqui. */
|
|
15
|
+
onClose?: () => void;
|
|
16
|
+
/** Botões abaixo do texto (ex.: "Tentar de novo" no erro). */
|
|
17
|
+
actions?: React.ReactNode;
|
|
18
|
+
className?: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* O fim de um modal ou formulário. O ponto do botão cresce até cobrir o painel
|
|
22
|
+
* com a cor do resultado, o ícone se desenha e o título aparece (escrito no
|
|
23
|
+
* sucesso, de uma vez no erro, que precisa ser lido já). Depois tudo recolhe no
|
|
24
|
+
* mesmo ponto e `onClose` fecha o modal. Cobre o pai, que precisa de
|
|
25
|
+
* `position: relative`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function TailorResult({ status, title, description, origin, closeAfter, onClose, actions, className }: TailorResultProps): React.JSX.Element;
|
|
@@ -113,11 +113,6 @@ export type TailorUserMenuProps = {
|
|
|
113
113
|
/** Conteúdo livre no painel, antes das ações (tema, notificações push). */
|
|
114
114
|
children?: ReactNode;
|
|
115
115
|
};
|
|
116
|
-
/**
|
|
117
|
-
* Menu do usuário para o `rodape` da `TailorSidebar`. Botão que abre um painel
|
|
118
|
-
* (padrão disclosure: aceita conteúdo livre, como o seletor de tema). Esc e
|
|
119
|
-
* clique fora fecham; setas percorrem os itens.
|
|
120
|
-
*/
|
|
121
116
|
export declare const TailorUserMenu: React.FC<TailorUserMenuProps>;
|
|
122
117
|
export type TailorMobileBarProps = {
|
|
123
118
|
/** Abre a gaveta: ligue no `aberto` da `TailorSidebar`. */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export type TailorSkeletonProps = {
|
|
3
|
+
/** `text`: parágrafo. `block`: um retângulo (gráfico, imagem). `table`: linhas e colunas. `cards`: indicadores lado a lado. */
|
|
4
|
+
shape?: 'text' | 'block' | 'table' | 'cards';
|
|
5
|
+
/** Linhas do texto ou da tabela, ou quantidade de cartões. */
|
|
6
|
+
lines?: number;
|
|
7
|
+
/** Colunas da tabela. */
|
|
8
|
+
columns?: number;
|
|
9
|
+
/** Altura do `block`. Padrão: 144px. */
|
|
10
|
+
height?: string;
|
|
11
|
+
/** Lido pelo leitor de tela. Padrão: o texto `loading` do idioma. */
|
|
12
|
+
label?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* O lugar do conteúdo enquanto ele chega: barras cinza retas e a linha verde
|
|
17
|
+
* varrendo de cima a baixo. Sem gradiente nem cantos (manual da marca).
|
|
18
|
+
*/
|
|
19
|
+
export declare function TailorSkeleton({ shape, lines, columns, height, label, className }: TailorSkeletonProps): React.JSX.Element;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Como o valor aparece e se alinha. `percent` recebe fração (0,123 = 12,3%). */
|
|
3
|
+
export type TailorColumnType = 'text' | 'number' | 'currency' | 'percent' | 'date' | 'id';
|
|
4
|
+
export type TailorTableColumn<T> = {
|
|
5
|
+
key: string;
|
|
6
|
+
header: React.ReactNode;
|
|
7
|
+
type?: TailorColumnType;
|
|
8
|
+
/** Valor para ordenar e formatar. Padrão: `row[key]`. */
|
|
9
|
+
value?: (row: T) => unknown;
|
|
10
|
+
/** Célula própria (badge, link). Sem ela, o valor formatado pelo `type`. */
|
|
11
|
+
cell?: (row: T) => React.ReactNode;
|
|
12
|
+
align?: 'left' | 'center' | 'right';
|
|
13
|
+
sortable?: boolean;
|
|
14
|
+
width?: string;
|
|
15
|
+
/** Linha de total: um valor pronto ou uma conta sobre as linhas (todas, não só a página). */
|
|
16
|
+
total?: React.ReactNode | ((rows: T[]) => unknown);
|
|
17
|
+
};
|
|
18
|
+
export type TailorTableSort = {
|
|
19
|
+
key: string;
|
|
20
|
+
direction: 'asc' | 'desc';
|
|
21
|
+
};
|
|
22
|
+
export type TailorTableProps<T> = {
|
|
23
|
+
columns: Array<TailorTableColumn<T>>;
|
|
24
|
+
rows: T[];
|
|
25
|
+
rowKey: (row: T, index: number) => string | number;
|
|
26
|
+
/** Título acessível da tabela (vira `<caption>`, só para leitor de tela). */
|
|
27
|
+
caption: string;
|
|
28
|
+
loading?: boolean;
|
|
29
|
+
/** O que aparece sem linhas. Padrão: o texto `empty` do idioma. */
|
|
30
|
+
empty?: React.ReactNode;
|
|
31
|
+
/** Ordem controlada (servidor). Sem `onSortChange`, a tabela ordena sozinha. */
|
|
32
|
+
sort?: TailorTableSort;
|
|
33
|
+
onSortChange?: (sort: TailorTableSort) => void;
|
|
34
|
+
defaultSort?: TailorTableSort;
|
|
35
|
+
/** Paginação no navegador. */
|
|
36
|
+
pageSize?: number;
|
|
37
|
+
/** Paginação no servidor: a tabela só mostra as `rows` que recebeu. */
|
|
38
|
+
pagination?: {
|
|
39
|
+
page: number;
|
|
40
|
+
totalPages: number;
|
|
41
|
+
onPageChange: (page: number) => void;
|
|
42
|
+
totalRows?: number;
|
|
43
|
+
};
|
|
44
|
+
onRowClick?: (row: T) => void;
|
|
45
|
+
/** Cabeçalho preso ao rolar a página. */
|
|
46
|
+
stickyHeader?: boolean;
|
|
47
|
+
className?: string;
|
|
48
|
+
};
|
|
49
|
+
/** Formata pelo tipo da coluna; vazio vira traço. */
|
|
50
|
+
export declare function formatValue(v: unknown, type?: TailorColumnType, locale?: string, currency?: string): React.ReactNode;
|
|
51
|
+
/**
|
|
52
|
+
* Tabela de relatório: cabeçalho em overline, números tabulares à direita, IDs
|
|
53
|
+
* em mono, ordenação, paginação (navegador ou servidor) e linha de total.
|
|
54
|
+
* Visual por variáveis `--tailor-brand-*` e `--tailor-table-*`, como o cabeçalho.
|
|
55
|
+
*/
|
|
56
|
+
export declare function TailorTable<T>({ columns, rows, rowKey, caption, loading, empty, sort, onSortChange, defaultSort, pageSize, pagination, onRowClick, stickyHeader, className, }: TailorTableProps<T>): React.JSX.Element;
|
|
57
|
+
/** Soma de uma coluna, para usar em `total`. */
|
|
58
|
+
export declare const sumBy: <T>(value: (r: T) => number) => (rows: T[]) => number;
|