@inchurch/matcha-theme 22.38.2 → 22.38.3

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.
Files changed (93) hide show
  1. package/abstracts/_colors.scss +291 -291
  2. package/abstracts/_elevation.scss +108 -108
  3. package/abstracts/_functions.scss +321 -321
  4. package/abstracts/_media-breakpoint.scss +26 -26
  5. package/abstracts/_theme-api.scss +224 -224
  6. package/abstracts/_typography.scss +128 -128
  7. package/base/_reset.scss +487 -487
  8. package/components/app-page-header.scss +260 -260
  9. package/components/matcha-accordion.scss +297 -297
  10. package/components/matcha-audio-player.scss +31 -31
  11. package/components/matcha-autocomplete.scss +145 -145
  12. package/components/matcha-avatar.scss +426 -426
  13. package/components/matcha-badge.scss +120 -120
  14. package/components/matcha-breadcrumb.scss +231 -231
  15. package/components/matcha-button-toggle.scss +484 -484
  16. package/components/matcha-buttons.scss +973 -973
  17. package/components/matcha-calendar.scss +253 -253
  18. package/components/matcha-cards.scss +212 -212
  19. package/components/matcha-chart-card.scss +53 -53
  20. package/components/matcha-checkbox.scss +266 -266
  21. package/components/matcha-chip.scss +311 -311
  22. package/components/matcha-color-pick.scss +34 -34
  23. package/components/matcha-command-palette.scss +316 -316
  24. package/components/matcha-crop.scss +419 -419
  25. package/components/matcha-date.scss +81 -81
  26. package/components/matcha-divider.scss +100 -100
  27. package/components/matcha-draggable.scss +23 -23
  28. package/components/matcha-drawer.scss +376 -376
  29. package/components/matcha-drop-image.scss +14 -14
  30. package/components/matcha-drop-list.scss +430 -430
  31. package/components/matcha-emoji.scss +69 -69
  32. package/components/matcha-empty-state.scss +230 -230
  33. package/components/matcha-file-uploader.scss +650 -637
  34. package/components/matcha-flag.scss +103 -103
  35. package/components/matcha-footer.scss +91 -91
  36. package/components/matcha-form-field.scss +883 -883
  37. package/components/matcha-form-section.scss +100 -100
  38. package/components/matcha-header.scss +135 -135
  39. package/components/matcha-highlight.scss +164 -164
  40. package/components/matcha-hint-text.scss +90 -90
  41. package/components/matcha-horizontal-tree.scss +247 -247
  42. package/components/matcha-icon.scss +111 -111
  43. package/components/matcha-image.scss +60 -60
  44. package/components/matcha-list.scss +211 -211
  45. package/components/matcha-menu.scss +99 -99
  46. package/components/matcha-modal.scss +210 -210
  47. package/components/matcha-nav.scss +770 -770
  48. package/components/matcha-notification.scss +417 -417
  49. package/components/matcha-option.scss +170 -170
  50. package/components/matcha-page-builder.scss +4196 -4196
  51. package/components/matcha-paginator.scss +699 -699
  52. package/components/matcha-panel.scss +216 -216
  53. package/components/matcha-period.scss +174 -174
  54. package/components/matcha-profile-card.scss +50 -50
  55. package/components/matcha-progress-bar.scss +355 -355
  56. package/components/matcha-radio.scss +325 -325
  57. package/components/matcha-ripple.scss +7 -7
  58. package/components/matcha-scrollbar.scss +33 -33
  59. package/components/matcha-scrollbox-shadow.scss +120 -120
  60. package/components/matcha-section.scss +102 -102
  61. package/components/matcha-select-multiple.scss +56 -56
  62. package/components/matcha-select.scss +629 -629
  63. package/components/matcha-skeleton.scss +182 -182
  64. package/components/matcha-slide-toggle.scss +369 -369
  65. package/components/matcha-slider.scss +285 -285
  66. package/components/matcha-snack-bar.scss +259 -259
  67. package/components/matcha-spin.scss +164 -164
  68. package/components/matcha-spinner.scss +97 -97
  69. package/components/matcha-stepper.scss +376 -376
  70. package/components/matcha-table-cell.scss +107 -107
  71. package/components/matcha-table.scss +722 -722
  72. package/components/matcha-tabs.scss +642 -642
  73. package/components/matcha-text-editor.scss +111 -111
  74. package/components/matcha-time.scss +33 -33
  75. package/components/matcha-timepicker.scss +13 -13
  76. package/components/matcha-toolbar.scss +366 -366
  77. package/components/matcha-tooltip.scss +235 -235
  78. package/components/matcha-tree-view.scss +636 -636
  79. package/layout/matcha-page-layout.scss +778 -778
  80. package/main.scss +334 -334
  81. package/package.json +1 -1
  82. package/tokens/_animations-tokens.scss +37 -37
  83. package/tokens/_breakpoints-tokens.scss +35 -35
  84. package/tokens/_color-tokens.scss +288 -288
  85. package/tokens/_elevation-tokens.scss +33 -33
  86. package/tokens/_emit-tokens.scss +329 -329
  87. package/tokens/_material-palette.scss +1149 -1149
  88. package/tokens/_primitives.scss +98 -98
  89. package/tokens/_semantic-aliases.scss +120 -120
  90. package/tokens/_spacing-tokens.scss +94 -94
  91. package/tokens/_static-tokens.scss +137 -137
  92. package/tokens/_typography-tokens.scss +65 -65
  93. package/vendors/charts.scss +90 -90
@@ -1,637 +1,650 @@
1
- @use "../abstracts/media-breakpoint" as *;
2
- @use "sass:map";
3
-
4
- // =============================================================================
5
- // matcha-file-uploader.scss — File upload component (matcha/file-uploader no Figma DSV3)
6
- //
7
- // 3 estados canon (16152:254):
8
- // Estado=Dropzone (16152:209) — área vazia com icon pill + title + hint + 2 botões
9
- // Estado=Enviando (16152:227) — thumb 64×36 + nome + progress + % + cancel
10
- // Estado=Completo (16152:242) — thumb 64×36 + nome + size + delete (trash)
11
- //
12
- // Width canon: 480px (component se adapta via max-width: 480px + width: 100%)
13
- // =============================================================================
14
-
15
- @mixin matcha-file-uploader-theme($theme) {
16
-
17
- // ── Shell (modo múltiplo: moldura única em volta de dropzone + lista) ──
18
- // Recebe o mesmo tratamento visual da dropzone porque é ELE que passa a ser
19
- // a "área de upload" quando a lista mora dentro — inclusive o realce de
20
- // dragover, já que os handlers de drag vivem aqui.
21
- .matcha-file-uploader-shell {
22
- background: var(--matcha-color-surface-background, var(--matcha-color-surface-bg));
23
- border-color: var(--matcha-color-surface-border);
24
-
25
- &.matcha-file-uploader-dragover {
26
- background: var(--matcha-color-accent-alpha);
27
- border-color: var(--matcha-color-accent);
28
- }
29
-
30
- &.matcha-file-uploader-disabled {
31
- opacity: 0.5;
32
- // pointer-events + cursor default, não `not-allowed`: constraint #14 do
33
- // INTERACTION-SYSTEM (o lint de CSS avisa). A dropzone legada abaixo ainda
34
- // usa not-allowed — rastreado no backlog, fora do escopo desta mudança.
35
- pointer-events: none;
36
- cursor: default;
37
- }
38
- }
39
-
40
- // ── Dropzone ──
41
- .matcha-file-uploader-dropzone {
42
- background: var(--matcha-color-surface-background, var(--matcha-color-surface-bg));
43
- border-color: var(--matcha-color-surface-border);
44
-
45
- &.matcha-file-uploader-dragover {
46
- background: var(--matcha-color-accent-alpha);
47
- border-color: var(--matcha-color-accent);
48
- }
49
-
50
- &.matcha-file-uploader-disabled {
51
- opacity: 0.5;
52
- cursor: not-allowed;
53
- pointer-events: none;
54
- }
55
-
56
- // Dentro do shell a moldura e o fundo são do shell — a dropzone é só a
57
- // afordância de clique. Duas classes de propósito: precisa vencer o bloco
58
- // acima, que tem a mesma especificidade.
59
- &.matcha-file-uploader-dropzone--inset {
60
- background: transparent;
61
- border-color: transparent;
62
- }
63
- }
64
-
65
- .matcha-file-uploader-dropzone__icon-pill {
66
- background: var(--matcha-color-interaction-disabled-bg, #282c34);
67
- color: var(--matcha-color-text-secondary);
68
- }
69
-
70
- .matcha-file-uploader-dropzone__title {
71
- color: var(--matcha-color-text-primary);
72
- }
73
-
74
- .matcha-file-uploader-dropzone__optional {
75
- color: var(--matcha-color-interaction-disabled-fg, rgba(232, 234, 237, 0.38));
76
- }
77
-
78
- .matcha-file-uploader-dropzone__hint {
79
- color: var(--matcha-color-text-secondary);
80
- }
81
-
82
- // ── File item ──
83
- .matcha-file-uploader-file {
84
- background: var(--matcha-color-surface-card);
85
- border-color: var(--matcha-color-surface-border);
86
- }
87
-
88
- .matcha-file-uploader-file__thumb {
89
- // interaction-disabled-bg, e nao elevation-surface-3: a moldura do arquivo nao e uma
90
- // superficie elevada, e um fundo neutro atras de um icone — e o surface-3 desaparecia
91
- // sobre o card em que a linha vive.
92
- background: var(--matcha-color-interaction-disabled-bg);
93
- color: var(--matcha-color-text-muted);
94
- }
95
-
96
- .matcha-file-uploader-file__name {
97
- color: var(--matcha-color-text-primary);
98
- }
99
-
100
- .matcha-file-uploader-file__size {
101
- color: var(--matcha-color-text-secondary);
102
- }
103
-
104
- .matcha-file-uploader-file__progress {
105
- background: var(--matcha-color-surface-border);
106
- }
107
-
108
- .matcha-file-uploader-file__progress-fill {
109
- background: var(--matcha-color-accent);
110
- }
111
-
112
- .matcha-file-uploader-file__percent {
113
- color: var(--matcha-color-text-secondary);
114
- }
115
-
116
- .matcha-file-uploader-file__error-text {
117
- color: var(--matcha-color-danger);
118
- }
119
-
120
- .matcha-file-uploader-file__action {
121
- color: var(--matcha-color-text-secondary);
122
-
123
- &:hover {
124
- background: var(--matcha-color-surface-hover, transparent);
125
- color: var(--matcha-color-text-primary);
126
- }
127
-
128
- // Apagar é vermelho EM REPOUSO, não só no hover: remover arquivo é
129
- // destrutivo e irreversível, e num modo múltiplo com vários itens
130
- // empilhados a ação precisa ser distinguível antes de o ponteiro chegar
131
- // nela (em touch não há hover nenhum). `danger` é papel, não hue —
132
- // o tema pode remapear.
133
- //
134
- // `--matcha-icon-color` é obrigatório, e não redundante com o `color`: o
135
- // matcha-icon resolve o glyph por `var(--matcha-icon-color, …-icon-default)`
136
- // (matcha-icon.scss:97) e NÃO herda o `color` do pai. Era por isso que o
137
- // vermelho antigo de hover nunca chegou a pintar a lixeira.
138
- &.matcha-file-uploader-file__action--delete {
139
- color: var(--matcha-color-danger);
140
- --matcha-icon-color: var(--matcha-color-danger);
141
-
142
- // Fundo tinted em vez de surface-hover neutro, e a cor é reafirmada
143
- // porque o :hover genérico acima empurraria text-primary.
144
- &:hover {
145
- background: var(--matcha-color-danger-alpha);
146
- color: var(--matcha-color-danger);
147
- --matcha-icon-color: var(--matcha-color-danger);
148
- }
149
- }
150
- }
151
-
152
- // ── Card layout (preview preenchido) ──
153
- .matcha-file-uploader-card {
154
- border-color: var(--matcha-color-surface-border);
155
- }
156
-
157
- // contain (logo/transparência): fundo neutro atrás da imagem inteira
158
- .matcha-file-uploader-card--contain {
159
- background: var(--matcha-color-surface-card);
160
- }
161
-
162
- .matcha-file-uploader-card__action {
163
- background: var(--matcha-color-surface-card-alpha, var(--matcha-color-surface-card));
164
- color: var(--matcha-color-text-primary);
165
-
166
- &:hover {
167
- background: var(--matcha-color-surface-card);
168
- }
169
-
170
- // Mesma regra do delete na linha. Aqui só o GLYPH vira danger: o fundo
171
- // continua sendo a pastilha de surface-card, que é o que garante contraste
172
- // do ícone sobre uma imagem qualquer — trocá-lo por danger-alpha (10%)
173
- // deixaria o vermelho competir com a foto atrás.
174
- &.matcha-file-uploader-card__action--delete {
175
- color: var(--matcha-color-danger);
176
- --matcha-icon-color: var(--matcha-color-danger);
177
- }
178
- }
179
- }
180
-
181
- // ──────────────────────────────────────────────────────────────────────────────
182
- // LAYOUT — independente de tema
183
- // ──────────────────────────────────────────────────────────────────────────────
184
-
185
- // Host — full-width; consumer aplica max-width externamente se quiser cap
186
- .matcha-file-uploader {
187
- display: block;
188
- width: 100%;
189
- }
190
-
191
- // ── Empty state wrapper (dropzone + ação secundária FORA do drop) ──────────
192
- .matcha-file-uploader-empty {
193
- display: flex;
194
- flex-direction: column;
195
- gap: var(--matcha-space-150, 12px);
196
- width: 100%;
197
- }
198
-
199
- // ── Shell (modo múltiplo) ────────────────────────────────────────────────────
200
- // A "área de upload" quando a lista mora dentro: uma moldura tracejada só,
201
- // com a dropzone no topo e a lista rolável embaixo. Sem arquivo nenhum o
202
- // resultado é visualmente idêntico à dropzone solta — só a moldura mudou de
203
- // dono, do filho para o pai.
204
- .matcha-file-uploader-shell {
205
- display: flex;
206
- flex-direction: column;
207
- width: 100%;
208
- box-sizing: border-box;
209
- border: var(--matcha-border-thin, 2px) dashed transparent;
210
- border-radius: var(--matcha-radius-lg, 8px);
211
- overflow: hidden; // o radius precisa cortar a lista que rola por dentro
212
- transition: background-color var(--matcha-duration-instant, 80ms) linear,
213
- border-color var(--matcha-duration-instant, 80ms) linear;
214
-
215
- // Classe de dimensionamento posta NO HOST tem que alcancar o shell — mesmo
216
- // padrao (e mesmo racional) do matcha-grid, grid.component.scss:11-18. Sem
217
- // isto, `<matcha-file-uploader class="h-400">` esticaria o host e deixaria o
218
- // shell na altura do conteudo, e a area continuaria crescendo por dentro.
219
- //
220
- // `height: 100%` contra host de altura auto resolve pra `auto`: e no-op para
221
- // quem nao define altura, portanto value-preserving nos usos existentes.
222
- height: 100%;
223
-
224
- // O foco visível é da dropzone interna (o elemento focável), não do shell.
225
- &:focus-within {
226
- outline: none;
227
- }
228
-
229
- // Com altura definida no host, e a DROPZONE que absorve a sobra: com 1 arquivo
230
- // ela fica grande e a area nao tem buraco; conforme a lista cresce, cede
231
- // espaco ate o proprio conteudo (pill + dois textos), que o min-height auto
232
- // do flex protege de ser esmagado. Sem altura definida, nao ha sobra para
233
- // distribuir e o resultado e o mesmo de antes.
234
- .matcha-file-uploader-dropzone--inset {
235
- flex: 1 1 auto;
236
- }
237
- }
238
-
239
- // ── Lista de arquivos rolável (dentro do shell) ──────────────────────────────
240
- // max-height em custom property com fallback, igual --matcha-panel-max-height
241
- // (280px é o mesmo teto que select/autocomplete/panel já usam para "lista com
242
- // mais conteúdo abaixo"). Passar `none` devolve o crescimento ilimitado antigo.
243
- .matcha-file-uploader-list {
244
- display: flex;
245
- flex-direction: column;
246
- gap: var(--matcha-space-100, 8px);
247
- min-height: 0; // sem isso o floor de min-content impede o scroll no flex
248
- // Pede o tamanho do conteudo e ENCOLHE quando falta espaco — e ai o scroll
249
- // interno assume. E o que faz a lista caber quando o host tem altura definida
250
- // e o teto de 280px seria maior do que sobrou.
251
- flex: 0 1 auto;
252
- max-height: var(--matcha-file-uploader-list-max-height, 280px);
253
- overflow-y: auto;
254
- overflow-x: hidden; // scrollbar vertical + nowrap gera barra horizontal fantasma
255
- overscroll-behavior: contain; // o scroll não vaza pra página ao chegar no fim
256
- -webkit-overflow-scrolling: touch;
257
- padding: 0 var(--matcha-space-100, 8px) var(--matcha-space-100, 8px);
258
- }
259
-
260
- // Dentro da lista o espaçamento é do `gap` — o margin-top do item rolaria
261
- // junto e daria um respiro fantasma no topo do scroll.
262
- .matcha-file-uploader-list > .matcha-file-uploader-file,
263
- .matcha-file-uploader-list > .matcha-file-uploader-card {
264
- margin-top: 0;
265
- }
266
-
267
-
268
- // ── Dropzone box (Estado=Dropzone — Figma 16152:209) — a zona INTEIRA é o alvo
269
- .matcha-file-uploader-dropzone {
270
- display: flex;
271
- flex-direction: column;
272
- align-items: center;
273
- justify-content: center;
274
- gap: var(--matcha-space-200, 16px);
275
- padding: var(--matcha-space-400, 32px) var(--matcha-space-300, 24px);
276
- border: var(--matcha-border-thin, 2px) dashed transparent;
277
- border-radius: var(--matcha-radius-lg, 8px);
278
- cursor: pointer;
279
- transition: background-color var(--matcha-duration-instant, 80ms) linear,
280
- border-color var(--matcha-duration-instant, 80ms) linear;
281
-
282
- &:focus-visible {
283
- outline: var(--matcha-border-thin, 2px) solid var(--matcha-color-accent, #009400);
284
- outline-offset: 2px;
285
- }
286
- }
287
-
288
- // Dropzone dentro do shell: sem moldura própria (é do shell) e sem o radius
289
- // que arredondaria um canto interno. Mantém padding, cursor e foco.
290
- // Precisa vir DEPOIS do bloco base: mesma especificidade (uma classe), então é
291
- // a ordem que decide — declarado antes, o `border: 2px dashed` do base vencia e
292
- // sobravam 2px de borda transparente dentro do shell.
293
- .matcha-file-uploader-dropzone--inset {
294
- border-width: 0;
295
- border-radius: 0;
296
-
297
- // Anel de foco PARA DENTRO. A dropzone inset ocupa a largura toda do shell,
298
- // que tem `overflow: hidden` (o radius precisa cortar a lista rolável): com o
299
- // `outline-offset: 2px` do bloco base o anel cai fora da caixa e é ceifado,
300
- // deixando o alvo de teclado sem indicação visível nas laterais.
301
- &:focus-visible {
302
- outline-offset: -2px;
303
- }
304
- }
305
-
306
- // layout="card": dropzone vazio compartilha a proporção do card preenchido
307
- // (aspect-ratio aplicado inline via previewRatio) pra empty e filled terem a
308
- // MESMA altura lado a lado. Sem isso, vazio é dimensionado por padding/conteúdo
309
- // e preenchido por aspect-ratio → alturas divergentes.
310
- .matcha-file-uploader-dropzone--card {
311
- width: 100%;
312
- overflow: hidden;
313
- }
314
-
315
- // Icon pill (bg interaction/disabled-bg + padding 40h/16v · icon 24×24)
316
- .matcha-file-uploader-dropzone__icon-pill {
317
- display: inline-flex;
318
- align-items: center;
319
- justify-content: center;
320
- padding: var(--matcha-space-200, 16px) var(--matcha-space-500, 40px);
321
- border-radius: var(--matcha-radius-pill, 9999px);
322
- flex-shrink: 0;
323
-
324
- matcha-icon {
325
- --matcha-icon-size: 24px;
326
- width: 24px;
327
- height: 24px;
328
- }
329
- }
330
-
331
- // Text block — column gap 4px, center
332
- .matcha-file-uploader-dropzone__text {
333
- display: flex;
334
- flex-direction: column;
335
- align-items: center;
336
- gap: var(--matcha-space-050, 4px);
337
- text-align: center;
338
- width: 100%;
339
- }
340
-
341
- // Title row (inline: title + optional)
342
- .matcha-file-uploader-dropzone__title-row {
343
- display: inline-flex;
344
- align-items: center;
345
- justify-content: center;
346
- gap: var(--matcha-space-050, 4px);
347
- white-space: nowrap;
348
- }
349
-
350
- .matcha-file-uploader-dropzone__title {
351
- font-family: var(--matcha-font-family);
352
- font-size: var(--matcha-text-label-md, 14px);
353
- font-weight: var(--matcha-weight-medium, 500);
354
- line-height: var(--matcha-leading-label-md, 20px);
355
- }
356
-
357
- .matcha-file-uploader-dropzone__optional {
358
- font-family: var(--matcha-font-family);
359
- font-size: var(--matcha-text-label-md, 14px);
360
- font-weight: var(--matcha-weight-medium, 500);
361
- line-height: var(--matcha-leading-label-md, 20px);
362
- }
363
-
364
- .matcha-file-uploader-dropzone__hint {
365
- margin: 0;
366
- font-family: var(--matcha-font-family);
367
- font-size: var(--matcha-text-body-sm, 12px);
368
- font-weight: var(--matcha-weight-regular, 400);
369
- line-height: var(--matcha-leading-body-sm, 16px);
370
- white-space: pre-line; // suporta \n pra hint multi-linha (1280×720 px... \n Clique pra selecionar...)
371
- }
372
-
373
- // Actions row (slot secondary + select arquivo)
374
- // Hidden input
375
- .matcha-file-uploader-input {
376
- display: none;
377
- }
378
-
379
- // ── File Item — Estados Enviando (16152:227) e Completo (16152:242) ──────────
380
- .matcha-file-uploader-file {
381
- display: flex;
382
- align-items: center;
383
- gap: var(--matcha-space-200, 16px);
384
- border: var(--matcha-border-hairline, 1px) solid transparent;
385
- border-radius: var(--matcha-radius-lg, 8px);
386
- margin-top: var(--matcha-space-100, 8px);
387
- width: 100%;
388
-
389
- // Enviando: padding 20/24 (mais respiro vs. Figma original 16)
390
- &.matcha-file-uploader-file--uploading {
391
- padding: var(--matcha-space-250, 20px) var(--matcha-space-300, 24px);
392
- }
393
-
394
- // Completo/Erro: padding 20/24 (mais respiro vs. Figma original 12/16)
395
- &.matcha-file-uploader-file--completed,
396
- &.matcha-file-uploader-file--error {
397
- padding: var(--matcha-space-250, 20px) var(--matcha-space-300, 24px);
398
- }
399
- }
400
-
401
- // Thumbnail 64×36 (16:9 · radius lg)
402
- .matcha-file-uploader-file__thumb {
403
- display: flex;
404
- align-items: center;
405
- justify-content: center;
406
- width: 64px;
407
- height: 36px;
408
- border-radius: var(--matcha-radius-lg, 8px);
409
- flex-shrink: 0;
410
- overflow: hidden;
411
-
412
- img {
413
- width: 100%;
414
- height: 100%;
415
- object-fit: cover;
416
- }
417
-
418
- matcha-icon {
419
- --matcha-icon-size: 20px;
420
- width: 20px;
421
- height: 20px;
422
- }
423
- }
424
-
425
- // Info column (flex-1)
426
- .matcha-file-uploader-file__info {
427
- display: flex;
428
- flex-direction: column;
429
- flex: 1 1 0;
430
- min-width: 0;
431
- overflow: hidden;
432
-
433
- // Enviando: gap 6 (entre nome e progress bar)
434
- .matcha-file-uploader-file--uploading & {
435
- gap: var(--matcha-space-075, 6px);
436
- }
437
-
438
- // Completo: gap 4 (entre nome e size)
439
- .matcha-file-uploader-file--completed &,
440
- .matcha-file-uploader-file--error & {
441
- gap: var(--matcha-space-050, 4px);
442
- }
443
- }
444
-
445
- // File name — Manrope Medium 13/19 letter 0.2 (text/button-sm)
446
- .matcha-file-uploader-file__name {
447
- font-family: var(--matcha-font-family);
448
- font-size: var(--matcha-text-label-sm, 12px);
449
- font-weight: var(--matcha-weight-medium, 500);
450
- line-height: var(--matcha-leading-label-sm, 16px);
451
- letter-spacing: 0.2px;
452
- white-space: nowrap;
453
- overflow: hidden;
454
- text-overflow: ellipsis;
455
- }
456
-
457
- // File size — Manrope Regular 11/14 letter 0.3 (text/caption)
458
- .matcha-file-uploader-file__size {
459
- font-family: var(--matcha-font-family);
460
- font-size: var(--matcha-text-p-tiny, 11px);
461
- font-weight: var(--matcha-weight-regular, 400);
462
- line-height: var(--matcha-leading-p-tiny, 14px);
463
- letter-spacing: 0.3px;
464
- }
465
-
466
- // Error text — body-sm danger
467
- .matcha-file-uploader-file__error-text {
468
- font-family: var(--matcha-font-family);
469
- font-size: var(--matcha-text-body-sm, 12px);
470
- font-weight: var(--matcha-weight-regular, 400);
471
- line-height: var(--matcha-leading-body-sm, 16px);
472
- }
473
-
474
- // ── Progress bar (height 4px · radius sm) ─────────────────────────────────────
475
- .matcha-file-uploader-file__progress {
476
- width: 100%;
477
- height: 4px;
478
- border-radius: var(--matcha-radius-sm, 2px);
479
- overflow: hidden;
480
- }
481
-
482
- .matcha-file-uploader-file__progress-fill {
483
- height: 100%;
484
- border-radius: var(--matcha-radius-sm, 2px);
485
- transition: width var(--matcha-duration-moderate, 300ms) ease;
486
- }
487
-
488
- // Percent label — body-sm text-secondary
489
- .matcha-file-uploader-file__percent {
490
- font-family: var(--matcha-font-family);
491
- font-size: var(--matcha-text-body-sm, 12px);
492
- font-weight: var(--matcha-weight-regular, 400);
493
- line-height: var(--matcha-leading-body-sm, 16px);
494
- flex-shrink: 0;
495
- }
496
-
497
- // Cancel/Delete action button — 20×20 (icon size + bg transparent + hover)
498
- .matcha-file-uploader-file__action {
499
- display: inline-flex;
500
- align-items: center;
501
- justify-content: center;
502
- width: 20px;
503
- height: 20px;
504
- padding: 0;
505
- background: transparent;
506
- border: none;
507
- border-radius: var(--matcha-radius-sm, 2px);
508
- cursor: pointer;
509
- flex-shrink: 0;
510
- transition: background-color var(--matcha-duration-instant, 80ms) linear,
511
- color var(--matcha-duration-instant, 80ms) linear;
512
-
513
- matcha-icon {
514
- --matcha-icon-size: 20px;
515
- width: 20px;
516
- height: 20px;
517
- }
518
- // Com o uploader desabilitado a linha continua legivel, mas as acoes saem de
519
- // operacao. `pointer-events: none` + `cursor: default` em vez de
520
- // `not-allowed`: constraint #14 do INTERACTION-SYSTEM.
521
- &:disabled {
522
- opacity: var(--matcha-opacity-disabled, 0.38);
523
- pointer-events: none;
524
- cursor: default;
525
- }
526
- }
527
-
528
- // ── Card layout — preview preenchido 16:9 + ações no hover (Estado=Drop filled) ──
529
- .matcha-file-uploader-card {
530
- position: relative;
531
- width: 100%;
532
- // SEM margin-top: o card preenchido tem que alinhar o topo com o dropzone
533
- // vazio quando lado a lado (layout card). margin-top desalinhava em 8px.
534
- border: var(--matcha-border-hairline, 1px) solid transparent;
535
- border-radius: var(--matcha-radius-lg, 8px);
536
- overflow: hidden;
537
- aspect-ratio: 16 / 9; // fallback; sobrescrito por [style.aspect-ratio]="previewRatio"
538
- }
539
-
540
- .matcha-file-uploader-card__img {
541
- display: block;
542
- width: 100%;
543
- height: 100%;
544
- object-fit: cover; // sobrescrito por [style.object-fit]="previewFit"
545
- }
546
-
547
- .matcha-file-uploader-card__actions {
548
- position: absolute;
549
- top: var(--matcha-space-100, 8px);
550
- right: var(--matcha-space-100, 8px);
551
- display: flex;
552
- gap: var(--matcha-space-100, 8px);
553
- opacity: 1; // persistente — descobrível e operável em touch (canon 20252:2253)
554
- transition: opacity var(--matcha-duration-instant, 80ms) linear;
555
- }
556
-
557
- // Em dispositivos com hover, atenua no resting e destaca no hover/foco — mantém
558
- // a tela limpa sem esconder a affordance onde não há hover (touch/totem).
559
- @media (hover: hover) {
560
- .matcha-file-uploader-card__actions { opacity: 0.72; }
561
-
562
- .matcha-file-uploader-card:hover .matcha-file-uploader-card__actions,
563
- .matcha-file-uploader-card:focus-within .matcha-file-uploader-card__actions {
564
- opacity: 1;
565
- }
566
- }
567
-
568
- .matcha-file-uploader-card__action {
569
- display: inline-flex;
570
- align-items: center;
571
- justify-content: center;
572
- width: 32px;
573
- height: 32px;
574
- padding: 0;
575
- border: none;
576
- border-radius: var(--matcha-radius-pill, 9999px);
577
- cursor: pointer;
578
- backdrop-filter: blur(8px);
579
- transition: background-color var(--matcha-duration-instant, 80ms) linear;
580
-
581
- matcha-icon {
582
- --matcha-icon-size: 18px;
583
- width: 18px;
584
- height: 18px;
585
- }
586
-
587
- // Mesma regra da acao da linha: desabilitado sai de operacao sem virar
588
- // `not-allowed` (constraint #14 do INTERACTION-SYSTEM).
589
- &:disabled {
590
- opacity: var(--matcha-opacity-disabled, 0.38);
591
- pointer-events: none;
592
- cursor: default;
593
- }
594
- }
595
-
596
- // ── Cropper inline (modo crop: imagem única) ──────────────────────────────
597
- .matcha-file-uploader-cropper {
598
- display: flex;
599
- flex-direction: column;
600
- gap: var(--matcha-space-150, 12px);
601
- margin-top: var(--matcha-space-100, 8px);
602
- }
603
-
604
- .matcha-file-uploader-cropper matcha-crop {
605
- display: block;
606
- width: 100%;
607
- min-height: 280px;
608
- }
609
-
610
- .matcha-file-uploader-cropper__actions {
611
- display: flex;
612
- justify-content: flex-end;
613
- gap: var(--matcha-space-100, 8px);
614
- }
615
-
616
- // ── Responsive ───────────────────────────────────────────────────────────────
617
- @include media-breakpoint('xs') {
618
- .matcha-file-uploader-dropzone {
619
- padding: var(--matcha-space-300, 24px) var(--matcha-space-200, 16px);
620
- }
621
-
622
- .matcha-file-uploader-dropzone__icon-pill {
623
- padding: var(--matcha-space-150, 12px) var(--matcha-space-400, 32px);
624
- }
625
- }
626
-
627
- // ── Reduced motion ───────────────────────────────────────────────────────────
628
- @media (prefers-reduced-motion: reduce) {
629
- .matcha-file-uploader-shell,
630
- .matcha-file-uploader-dropzone,
631
- .matcha-file-uploader-file__progress-fill,
632
- .matcha-file-uploader-file__action,
633
- .matcha-file-uploader-card__actions,
634
- .matcha-file-uploader-card__action {
635
- transition: none;
636
- }
637
- }
1
+ @use "../abstracts/media-breakpoint" as *;
2
+ @use "sass:map";
3
+
4
+ // =============================================================================
5
+ // matcha-file-uploader.scss — File upload component (matcha/file-uploader no Figma DSV3)
6
+ //
7
+ // 3 estados canon (16152:254):
8
+ // Estado=Dropzone (16152:209) — área vazia com icon pill + title + hint + 2 botões
9
+ // Estado=Enviando (16152:227) — thumb 64×36 + nome + progress + % + cancel
10
+ // Estado=Completo (16152:242) — thumb 64×36 + nome + size + delete (trash)
11
+ //
12
+ // Width canon: 480px (component se adapta via max-width: 480px + width: 100%)
13
+ // =============================================================================
14
+
15
+ @mixin matcha-file-uploader-theme($theme) {
16
+
17
+ // ── Shell (modo múltiplo: moldura única em volta de dropzone + lista) ──
18
+ // Recebe o mesmo tratamento visual da dropzone porque é ELE que passa a ser
19
+ // a "área de upload" quando a lista mora dentro — inclusive o realce de
20
+ // dragover, já que os handlers de drag vivem aqui.
21
+ .matcha-file-uploader-shell {
22
+ background: var(--matcha-color-surface-background, var(--matcha-color-surface-bg));
23
+ border-color: var(--matcha-color-surface-border);
24
+
25
+ &.matcha-file-uploader-dragover {
26
+ background: var(--matcha-color-accent-alpha);
27
+ border-color: var(--matcha-color-accent);
28
+ }
29
+
30
+ &.matcha-file-uploader-disabled {
31
+ opacity: 0.5;
32
+ // pointer-events + cursor default, não `not-allowed`: constraint #14 do
33
+ // INTERACTION-SYSTEM (o lint de CSS avisa). A dropzone legada abaixo ainda
34
+ // usa not-allowed — rastreado no backlog, fora do escopo desta mudança.
35
+ pointer-events: none;
36
+ cursor: default;
37
+ }
38
+ }
39
+
40
+ // ── Dropzone ──
41
+ .matcha-file-uploader-dropzone {
42
+ background: var(--matcha-color-surface-background, var(--matcha-color-surface-bg));
43
+ border-color: var(--matcha-color-surface-border);
44
+
45
+ &.matcha-file-uploader-dragover {
46
+ background: var(--matcha-color-accent-alpha);
47
+ border-color: var(--matcha-color-accent);
48
+ }
49
+
50
+ &.matcha-file-uploader-disabled {
51
+ opacity: 0.5;
52
+ cursor: not-allowed;
53
+ pointer-events: none;
54
+ }
55
+
56
+ // Dentro do shell a moldura e o fundo são do shell — a dropzone é só a
57
+ // afordância de clique. Duas classes de propósito: precisa vencer o bloco
58
+ // acima, que tem a mesma especificidade.
59
+ &.matcha-file-uploader-dropzone--inset {
60
+ background: transparent;
61
+ border-color: transparent;
62
+ }
63
+ }
64
+
65
+ .matcha-file-uploader-dropzone__icon-pill {
66
+ background: var(--matcha-color-interaction-disabled-bg, #282c34);
67
+ color: var(--matcha-color-text-secondary);
68
+ }
69
+
70
+ .matcha-file-uploader-dropzone__title {
71
+ color: var(--matcha-color-text-primary);
72
+ }
73
+
74
+ .matcha-file-uploader-dropzone__optional {
75
+ color: var(--matcha-color-interaction-disabled-fg, rgba(232, 234, 237, 0.38));
76
+ }
77
+
78
+ .matcha-file-uploader-dropzone__hint {
79
+ color: var(--matcha-color-text-secondary);
80
+ }
81
+
82
+ // ── File item ──
83
+ .matcha-file-uploader-file {
84
+ background: var(--matcha-color-surface-card);
85
+ border-color: var(--matcha-color-surface-border);
86
+ }
87
+
88
+ .matcha-file-uploader-file__thumb {
89
+ // interaction-disabled-bg, e nao elevation-surface-3: a moldura do arquivo nao e uma
90
+ // superficie elevada, e um fundo neutro atras de um icone — e o surface-3 desaparecia
91
+ // sobre o card em que a linha vive.
92
+ background: var(--matcha-color-interaction-disabled-bg);
93
+ color: var(--matcha-color-text-muted);
94
+ }
95
+
96
+ .matcha-file-uploader-file__name {
97
+ color: var(--matcha-color-text-primary);
98
+ }
99
+
100
+ .matcha-file-uploader-file__size {
101
+ color: var(--matcha-color-text-secondary);
102
+ }
103
+
104
+ .matcha-file-uploader-file__progress {
105
+ background: var(--matcha-color-surface-border);
106
+ }
107
+
108
+ .matcha-file-uploader-file__progress-fill {
109
+ background: var(--matcha-color-accent);
110
+ }
111
+
112
+ .matcha-file-uploader-file__percent {
113
+ color: var(--matcha-color-text-secondary);
114
+ }
115
+
116
+ .matcha-file-uploader-file__error-text {
117
+ color: var(--matcha-color-danger);
118
+ }
119
+
120
+ .matcha-file-uploader-file__action {
121
+ color: var(--matcha-color-text-secondary);
122
+
123
+ &:hover {
124
+ background: var(--matcha-color-surface-hover, transparent);
125
+ color: var(--matcha-color-text-primary);
126
+ }
127
+
128
+ // Apagar é vermelho EM REPOUSO, não só no hover: remover arquivo é
129
+ // destrutivo e irreversível, e num modo múltiplo com vários itens
130
+ // empilhados a ação precisa ser distinguível antes de o ponteiro chegar
131
+ // nela (em touch não há hover nenhum). `danger` é papel, não hue —
132
+ // o tema pode remapear.
133
+ //
134
+ // `--matcha-icon-color` é obrigatório, e não redundante com o `color`: o
135
+ // matcha-icon resolve o glyph por `var(--matcha-icon-color, …-icon-default)`
136
+ // (matcha-icon.scss:97) e NÃO herda o `color` do pai. Era por isso que o
137
+ // vermelho antigo de hover nunca chegou a pintar a lixeira.
138
+ &.matcha-file-uploader-file__action--delete {
139
+ color: var(--matcha-color-danger);
140
+ --matcha-icon-color: var(--matcha-color-danger);
141
+
142
+ // Fundo tinted em vez de surface-hover neutro, e a cor é reafirmada
143
+ // porque o :hover genérico acima empurraria text-primary.
144
+ &:hover {
145
+ background: var(--matcha-color-danger-alpha);
146
+ color: var(--matcha-color-danger);
147
+ --matcha-icon-color: var(--matcha-color-danger);
148
+ }
149
+ }
150
+ }
151
+
152
+ // ── Card layout (preview preenchido) ──
153
+ .matcha-file-uploader-card {
154
+ border-color: var(--matcha-color-surface-border);
155
+ }
156
+
157
+ // contain (logo/transparência): fundo neutro atrás da imagem inteira
158
+ .matcha-file-uploader-card--contain {
159
+ background: var(--matcha-color-surface-card);
160
+ }
161
+
162
+ .matcha-file-uploader-card__action {
163
+ background: var(--matcha-color-surface-card-alpha, var(--matcha-color-surface-card));
164
+ color: var(--matcha-color-text-primary);
165
+
166
+ &:hover {
167
+ background: var(--matcha-color-surface-card);
168
+ }
169
+
170
+ // Mesma regra do delete na linha. Aqui só o GLYPH vira danger: o fundo
171
+ // continua sendo a pastilha de surface-card, que é o que garante contraste
172
+ // do ícone sobre uma imagem qualquer — trocá-lo por danger-alpha (10%)
173
+ // deixaria o vermelho competir com a foto atrás.
174
+ &.matcha-file-uploader-card__action--delete {
175
+ color: var(--matcha-color-danger);
176
+ --matcha-icon-color: var(--matcha-color-danger);
177
+ }
178
+ }
179
+ }
180
+
181
+ // ──────────────────────────────────────────────────────────────────────────────
182
+ // LAYOUT — independente de tema
183
+ // ──────────────────────────────────────────────────────────────────────────────
184
+
185
+ // Host — full-width; consumer aplica max-width externamente se quiser cap
186
+ .matcha-file-uploader {
187
+ display: block;
188
+ width: 100%;
189
+ }
190
+
191
+ // ── Empty state wrapper (dropzone + ação secundária FORA do drop) ──────────
192
+ .matcha-file-uploader-empty {
193
+ display: flex;
194
+ flex-direction: column;
195
+ gap: var(--matcha-space-150, 12px);
196
+ width: 100%;
197
+ }
198
+
199
+ // ── Shell (modo múltiplo) ────────────────────────────────────────────────────
200
+ // A "área de upload" quando a lista mora dentro: uma moldura tracejada só,
201
+ // com a dropzone no topo e a lista rolável embaixo. Sem arquivo nenhum o
202
+ // resultado é visualmente idêntico à dropzone solta — só a moldura mudou de
203
+ // dono, do filho para o pai.
204
+ .matcha-file-uploader-shell {
205
+ display: flex;
206
+ flex-direction: column;
207
+ width: 100%;
208
+ box-sizing: border-box;
209
+ border: var(--matcha-border-thin, 2px) dashed transparent;
210
+ border-radius: var(--matcha-radius-lg, 8px);
211
+ overflow: hidden; // o radius precisa cortar a lista que rola por dentro
212
+ transition: background-color var(--matcha-duration-instant, 80ms) linear,
213
+ border-color var(--matcha-duration-instant, 80ms) linear;
214
+
215
+ // Classe de dimensionamento posta NO HOST tem que alcancar o shell — mesmo
216
+ // padrao (e mesmo racional) do matcha-grid, grid.component.scss:11-18. Sem
217
+ // isto, `<matcha-file-uploader class="h-400">` esticaria o host e deixaria o
218
+ // shell na altura do conteudo, e a area continuaria crescendo por dentro.
219
+ //
220
+ // `height: 100%` contra host de altura auto resolve pra `auto`: e no-op para
221
+ // quem nao define altura, portanto value-preserving nos usos existentes.
222
+ height: 100%;
223
+
224
+ // O foco visível é da dropzone interna (o elemento focável), não do shell.
225
+ &:focus-within {
226
+ outline: none;
227
+ }
228
+
229
+ // Com altura definida no host, e a DROPZONE que absorve a sobra: com 1 arquivo
230
+ // ela fica grande e a area nao tem buraco; conforme a lista cresce, cede
231
+ // espaco ate o proprio conteudo (pill + dois textos), que o min-height auto
232
+ // do flex protege de ser esmagado. Sem altura definida, nao ha sobra para
233
+ // distribuir e o resultado e o mesmo de antes.
234
+ .matcha-file-uploader-dropzone--inset {
235
+ flex: 1 1 auto;
236
+ }
237
+ }
238
+
239
+ // ── Lista de arquivos rolável (dentro do shell) ──────────────────────────────
240
+ // max-height em custom property com fallback, igual --matcha-panel-max-height
241
+ // (280px é o mesmo teto que select/autocomplete/panel já usam para "lista com
242
+ // mais conteúdo abaixo"). Passar `none` devolve o crescimento ilimitado antigo.
243
+ .matcha-file-uploader-list {
244
+ display: flex;
245
+ flex-direction: column;
246
+ gap: var(--matcha-space-100, 8px);
247
+ min-height: 0; // sem isso o floor de min-content impede o scroll no flex
248
+ // Pede o tamanho do conteudo e ENCOLHE quando falta espaco — e ai o scroll
249
+ // interno assume. E o que faz a lista caber quando o host tem altura definida
250
+ // e o teto de 280px seria maior do que sobrou.
251
+ flex: 0 1 auto;
252
+ max-height: var(--matcha-file-uploader-list-max-height, 280px);
253
+ overflow-y: auto;
254
+ overflow-x: hidden; // scrollbar vertical + nowrap gera barra horizontal fantasma
255
+ overscroll-behavior: contain; // o scroll não vaza pra página ao chegar no fim
256
+ -webkit-overflow-scrolling: touch;
257
+ padding: 0 var(--matcha-space-100, 8px) var(--matcha-space-100, 8px);
258
+ }
259
+
260
+ // Dentro da lista o espaçamento é do `gap` — o margin-top do item rolaria
261
+ // junto e daria um respiro fantasma no topo do scroll.
262
+ .matcha-file-uploader-list > .matcha-file-uploader-file,
263
+ .matcha-file-uploader-list > .matcha-file-uploader-card {
264
+ margin-top: 0;
265
+ }
266
+
267
+
268
+ // ── Dropzone box (Estado=Dropzone — Figma 16152:209) — a zona INTEIRA é o alvo
269
+ .matcha-file-uploader-dropzone {
270
+ display: flex;
271
+ flex-direction: column;
272
+ align-items: center;
273
+ justify-content: center;
274
+ gap: var(--matcha-space-200, 16px);
275
+ padding: var(--matcha-space-400, 32px) var(--matcha-space-300, 24px);
276
+ border: var(--matcha-border-thin, 2px) dashed transparent;
277
+ border-radius: var(--matcha-radius-lg, 8px);
278
+ cursor: pointer;
279
+ transition: background-color var(--matcha-duration-instant, 80ms) linear,
280
+ border-color var(--matcha-duration-instant, 80ms) linear;
281
+
282
+ &:focus-visible {
283
+ outline: var(--matcha-border-thin, 2px) solid var(--matcha-color-accent, #009400);
284
+ outline-offset: 2px;
285
+ }
286
+ }
287
+
288
+ // Dropzone dentro do shell: sem moldura própria (é do shell) e sem o radius
289
+ // que arredondaria um canto interno. Mantém padding, cursor e foco.
290
+ // Precisa vir DEPOIS do bloco base: mesma especificidade (uma classe), então é
291
+ // a ordem que decide — declarado antes, o `border: 2px dashed` do base vencia e
292
+ // sobravam 2px de borda transparente dentro do shell.
293
+ .matcha-file-uploader-dropzone--inset {
294
+ border-width: 0;
295
+ border-radius: 0;
296
+
297
+ // Anel de foco PARA DENTRO. A dropzone inset ocupa a largura toda do shell,
298
+ // que tem `overflow: hidden` (o radius precisa cortar a lista rolável): com o
299
+ // `outline-offset: 2px` do bloco base o anel cai fora da caixa e é ceifado,
300
+ // deixando o alvo de teclado sem indicação visível nas laterais.
301
+ &:focus-visible {
302
+ outline-offset: -2px;
303
+ }
304
+ }
305
+
306
+ // layout="card": dropzone vazio compartilha a proporção do card preenchido
307
+ // (aspect-ratio aplicado inline via previewRatio) pra empty e filled terem a
308
+ // MESMA altura lado a lado. Sem isso, vazio é dimensionado por padding/conteúdo
309
+ // e preenchido por aspect-ratio → alturas divergentes.
310
+ .matcha-file-uploader-dropzone--card {
311
+ width: 100%;
312
+ overflow: hidden;
313
+ }
314
+
315
+ // Icon pill (bg interaction/disabled-bg + padding 40h/16v · icon 24×24)
316
+ .matcha-file-uploader-dropzone__icon-pill {
317
+ display: inline-flex;
318
+ align-items: center;
319
+ justify-content: center;
320
+ padding: var(--matcha-space-200, 16px) var(--matcha-space-500, 40px);
321
+ border-radius: var(--matcha-radius-pill, 9999px);
322
+ flex-shrink: 0;
323
+
324
+ matcha-icon {
325
+ --matcha-icon-size: 24px;
326
+ width: 24px;
327
+ height: 24px;
328
+ }
329
+ }
330
+
331
+ // Text block — column gap 4px, center
332
+ .matcha-file-uploader-dropzone__text {
333
+ display: flex;
334
+ flex-direction: column;
335
+ align-items: center;
336
+ gap: var(--matcha-space-050, 4px);
337
+ text-align: center;
338
+ width: 100%;
339
+ }
340
+
341
+ // Title row (inline: title + optional)
342
+ .matcha-file-uploader-dropzone__title-row {
343
+ display: inline-flex;
344
+ align-items: center;
345
+ justify-content: center;
346
+ gap: var(--matcha-space-050, 4px);
347
+ white-space: nowrap;
348
+ }
349
+
350
+ .matcha-file-uploader-dropzone__title {
351
+ font-family: var(--matcha-font-family);
352
+ font-size: var(--matcha-text-label-md, 14px);
353
+ font-weight: var(--matcha-weight-medium, 500);
354
+ line-height: var(--matcha-leading-label-md, 20px);
355
+ }
356
+
357
+ .matcha-file-uploader-dropzone__optional {
358
+ font-family: var(--matcha-font-family);
359
+ font-size: var(--matcha-text-label-md, 14px);
360
+ font-weight: var(--matcha-weight-medium, 500);
361
+ line-height: var(--matcha-leading-label-md, 20px);
362
+ }
363
+
364
+ .matcha-file-uploader-dropzone__hint {
365
+ margin: 0;
366
+ font-family: var(--matcha-font-family);
367
+ font-size: var(--matcha-text-body-sm, 12px);
368
+ font-weight: var(--matcha-weight-regular, 400);
369
+ line-height: var(--matcha-leading-body-sm, 16px);
370
+ white-space: pre-line; // suporta \n pra hint multi-linha (1280×720 px... \n Clique pra selecionar...)
371
+ }
372
+
373
+ // Actions row (slot secondary + select arquivo)
374
+ // Hidden input
375
+ .matcha-file-uploader-input {
376
+ display: none;
377
+ }
378
+
379
+ // ── File Item — Estados Enviando (16152:227) e Completo (16152:242) ──────────
380
+ .matcha-file-uploader-file {
381
+ display: flex;
382
+ align-items: center;
383
+ gap: var(--matcha-space-200, 16px);
384
+ border: var(--matcha-border-hairline, 1px) solid transparent;
385
+ border-radius: var(--matcha-radius-lg, 8px);
386
+ margin-top: var(--matcha-space-100, 8px);
387
+ width: 100%;
388
+
389
+ // Enviando: padding 20/24 (mais respiro vs. Figma original 16)
390
+ &.matcha-file-uploader-file--uploading {
391
+ padding: var(--matcha-space-250, 20px) var(--matcha-space-300, 24px);
392
+ }
393
+
394
+ // Completo/Erro: padding 20/24 (mais respiro vs. Figma original 12/16)
395
+ &.matcha-file-uploader-file--completed,
396
+ &.matcha-file-uploader-file--error {
397
+ padding: var(--matcha-space-250, 20px) var(--matcha-space-300, 24px);
398
+ }
399
+ }
400
+
401
+ // Thumbnail 64×36 (16:9 · radius lg)
402
+ .matcha-file-uploader-file__thumb {
403
+ display: flex;
404
+ align-items: center;
405
+ justify-content: center;
406
+ width: 64px;
407
+ height: 36px;
408
+ border-radius: var(--matcha-radius-lg, 8px);
409
+ flex-shrink: 0;
410
+ overflow: hidden;
411
+
412
+ img {
413
+ width: 100%;
414
+ height: 100%;
415
+ object-fit: cover;
416
+ }
417
+
418
+ matcha-icon {
419
+ --matcha-icon-size: 20px;
420
+ width: 20px;
421
+ height: 20px;
422
+ }
423
+ }
424
+
425
+ // Info column (flex-1)
426
+ .matcha-file-uploader-file__info {
427
+ display: flex;
428
+ flex-direction: column;
429
+ flex: 1 1 0;
430
+ min-width: 0;
431
+ overflow: hidden;
432
+
433
+ // Enviando: gap 6 (entre nome e progress bar)
434
+ .matcha-file-uploader-file--uploading & {
435
+ gap: var(--matcha-space-075, 6px);
436
+ }
437
+
438
+ // Completo: gap 4 (entre nome e size)
439
+ .matcha-file-uploader-file--completed &,
440
+ .matcha-file-uploader-file--error & {
441
+ gap: var(--matcha-space-050, 4px);
442
+ }
443
+ }
444
+
445
+ // File name — Manrope Medium 13/19 letter 0.2 (text/button-sm)
446
+ .matcha-file-uploader-file__name {
447
+ font-family: var(--matcha-font-family);
448
+ font-size: var(--matcha-text-label-sm, 12px);
449
+ font-weight: var(--matcha-weight-medium, 500);
450
+ line-height: var(--matcha-leading-label-sm, 16px);
451
+ letter-spacing: 0.2px;
452
+ white-space: nowrap;
453
+ overflow: hidden;
454
+ text-overflow: ellipsis;
455
+ }
456
+
457
+ // Nome como ancora: mesma tipografia, aparencia de link. `display: block` porque o
458
+ // ellipsis do nome depende de o elemento ser bloco, e <a> e inline por padrao.
459
+ .matcha-file-uploader-file__name--link {
460
+ display: block;
461
+ color: var(--matcha-color-accent);
462
+ text-decoration: underline;
463
+
464
+ &:hover,
465
+ &:focus-visible {
466
+ text-decoration: none;
467
+ }
468
+ }
469
+
470
+ // File size — Manrope Regular 11/14 letter 0.3 (text/caption)
471
+ .matcha-file-uploader-file__size {
472
+ font-family: var(--matcha-font-family);
473
+ font-size: var(--matcha-text-p-tiny, 11px);
474
+ font-weight: var(--matcha-weight-regular, 400);
475
+ line-height: var(--matcha-leading-p-tiny, 14px);
476
+ letter-spacing: 0.3px;
477
+ }
478
+
479
+ // Error text — body-sm danger
480
+ .matcha-file-uploader-file__error-text {
481
+ font-family: var(--matcha-font-family);
482
+ font-size: var(--matcha-text-body-sm, 12px);
483
+ font-weight: var(--matcha-weight-regular, 400);
484
+ line-height: var(--matcha-leading-body-sm, 16px);
485
+ }
486
+
487
+ // ── Progress bar (height 4px · radius sm) ─────────────────────────────────────
488
+ .matcha-file-uploader-file__progress {
489
+ width: 100%;
490
+ height: 4px;
491
+ border-radius: var(--matcha-radius-sm, 2px);
492
+ overflow: hidden;
493
+ }
494
+
495
+ .matcha-file-uploader-file__progress-fill {
496
+ height: 100%;
497
+ border-radius: var(--matcha-radius-sm, 2px);
498
+ transition: width var(--matcha-duration-moderate, 300ms) ease;
499
+ }
500
+
501
+ // Percent label — body-sm text-secondary
502
+ .matcha-file-uploader-file__percent {
503
+ font-family: var(--matcha-font-family);
504
+ font-size: var(--matcha-text-body-sm, 12px);
505
+ font-weight: var(--matcha-weight-regular, 400);
506
+ line-height: var(--matcha-leading-body-sm, 16px);
507
+ flex-shrink: 0;
508
+ }
509
+
510
+ // Cancel/Delete action button — 20×20 (icon size + bg transparent + hover)
511
+ .matcha-file-uploader-file__action {
512
+ display: inline-flex;
513
+ align-items: center;
514
+ justify-content: center;
515
+ width: 20px;
516
+ height: 20px;
517
+ padding: 0;
518
+ background: transparent;
519
+ border: none;
520
+ border-radius: var(--matcha-radius-sm, 2px);
521
+ cursor: pointer;
522
+ flex-shrink: 0;
523
+ transition: background-color var(--matcha-duration-instant, 80ms) linear,
524
+ color var(--matcha-duration-instant, 80ms) linear;
525
+
526
+ matcha-icon {
527
+ --matcha-icon-size: 20px;
528
+ width: 20px;
529
+ height: 20px;
530
+ }
531
+ // Com o uploader desabilitado a linha continua legivel, mas as acoes saem de
532
+ // operacao. `pointer-events: none` + `cursor: default` em vez de
533
+ // `not-allowed`: constraint #14 do INTERACTION-SYSTEM.
534
+ &:disabled {
535
+ opacity: var(--matcha-opacity-disabled, 0.38);
536
+ pointer-events: none;
537
+ cursor: default;
538
+ }
539
+ }
540
+
541
+ // ── Card layout — preview preenchido 16:9 + ações no hover (Estado=Drop filled) ──
542
+ .matcha-file-uploader-card {
543
+ position: relative;
544
+ width: 100%;
545
+ // SEM margin-top: o card preenchido tem que alinhar o topo com o dropzone
546
+ // vazio quando lado a lado (layout card). margin-top desalinhava em 8px.
547
+ border: var(--matcha-border-hairline, 1px) solid transparent;
548
+ border-radius: var(--matcha-radius-lg, 8px);
549
+ overflow: hidden;
550
+ aspect-ratio: 16 / 9; // fallback; sobrescrito por [style.aspect-ratio]="previewRatio"
551
+ }
552
+
553
+ .matcha-file-uploader-card__img {
554
+ display: block;
555
+ width: 100%;
556
+ height: 100%;
557
+ object-fit: cover; // sobrescrito por [style.object-fit]="previewFit"
558
+ }
559
+
560
+ .matcha-file-uploader-card__actions {
561
+ position: absolute;
562
+ top: var(--matcha-space-100, 8px);
563
+ right: var(--matcha-space-100, 8px);
564
+ display: flex;
565
+ gap: var(--matcha-space-100, 8px);
566
+ opacity: 1; // persistente — descobrível e operável em touch (canon 20252:2253)
567
+ transition: opacity var(--matcha-duration-instant, 80ms) linear;
568
+ }
569
+
570
+ // Em dispositivos com hover, atenua no resting e destaca no hover/foco — mantém
571
+ // a tela limpa sem esconder a affordance onde não há hover (touch/totem).
572
+ @media (hover: hover) {
573
+ .matcha-file-uploader-card__actions { opacity: 0.72; }
574
+
575
+ .matcha-file-uploader-card:hover .matcha-file-uploader-card__actions,
576
+ .matcha-file-uploader-card:focus-within .matcha-file-uploader-card__actions {
577
+ opacity: 1;
578
+ }
579
+ }
580
+
581
+ .matcha-file-uploader-card__action {
582
+ display: inline-flex;
583
+ align-items: center;
584
+ justify-content: center;
585
+ width: 32px;
586
+ height: 32px;
587
+ padding: 0;
588
+ border: none;
589
+ border-radius: var(--matcha-radius-pill, 9999px);
590
+ cursor: pointer;
591
+ backdrop-filter: blur(8px);
592
+ transition: background-color var(--matcha-duration-instant, 80ms) linear;
593
+
594
+ matcha-icon {
595
+ --matcha-icon-size: 18px;
596
+ width: 18px;
597
+ height: 18px;
598
+ }
599
+
600
+ // Mesma regra da acao da linha: desabilitado sai de operacao sem virar
601
+ // `not-allowed` (constraint #14 do INTERACTION-SYSTEM).
602
+ &:disabled {
603
+ opacity: var(--matcha-opacity-disabled, 0.38);
604
+ pointer-events: none;
605
+ cursor: default;
606
+ }
607
+ }
608
+
609
+ // ── Cropper inline (modo crop: imagem única) ──────────────────────────────
610
+ .matcha-file-uploader-cropper {
611
+ display: flex;
612
+ flex-direction: column;
613
+ gap: var(--matcha-space-150, 12px);
614
+ margin-top: var(--matcha-space-100, 8px);
615
+ }
616
+
617
+ .matcha-file-uploader-cropper matcha-crop {
618
+ display: block;
619
+ width: 100%;
620
+ min-height: 280px;
621
+ }
622
+
623
+ .matcha-file-uploader-cropper__actions {
624
+ display: flex;
625
+ justify-content: flex-end;
626
+ gap: var(--matcha-space-100, 8px);
627
+ }
628
+
629
+ // ── Responsive ───────────────────────────────────────────────────────────────
630
+ @include media-breakpoint('xs') {
631
+ .matcha-file-uploader-dropzone {
632
+ padding: var(--matcha-space-300, 24px) var(--matcha-space-200, 16px);
633
+ }
634
+
635
+ .matcha-file-uploader-dropzone__icon-pill {
636
+ padding: var(--matcha-space-150, 12px) var(--matcha-space-400, 32px);
637
+ }
638
+ }
639
+
640
+ // ── Reduced motion ───────────────────────────────────────────────────────────
641
+ @media (prefers-reduced-motion: reduce) {
642
+ .matcha-file-uploader-shell,
643
+ .matcha-file-uploader-dropzone,
644
+ .matcha-file-uploader-file__progress-fill,
645
+ .matcha-file-uploader-file__action,
646
+ .matcha-file-uploader-card__actions,
647
+ .matcha-file-uploader-card__action {
648
+ transition: none;
649
+ }
650
+ }