@inchurch/matcha-theme 22.36.1 → 22.37.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.
@@ -461,3 +461,24 @@ matcha-button-toggle {
461
461
  }
462
462
  }
463
463
  }
464
+
465
+ // =============================================================================
466
+ // Readonly — seleção legível, clique inerte
467
+ // =============================================================================
468
+ // NENHUMA troca de cor aqui, e é isso que separa `readonly` de `disabled`: o bloco
469
+ // `&[disabled]` acima aplica os mixins `disabled-*-button` nos botões inativos, apagando
470
+ // justamente o contraste que diz qual opção está selecionada. Numa tela cujo propósito é LER
471
+ // esse estado, apagá-lo é o oposto do que se quer.
472
+ //
473
+ // O que muda é só a promessa de interação: o ponteiro deixa de sugerir clique. A inércia em si
474
+ // mora no `onSelect` do componente, não aqui — CSS não deve ser a trava.
475
+ matcha-button-toggle[readonly] {
476
+
477
+ .button-group {
478
+
479
+ button[matcha-button],
480
+ a[matcha-button] {
481
+ cursor: default;
482
+ }
483
+ }
484
+ }
@@ -13,16 +13,19 @@
13
13
  }
14
14
 
15
15
  // ── Hover (off) — bg sutil surface alpha (Figma matcha/state-layer) ─
16
- &:not([disabled]):hover .matcha-checkbox-container {
16
+ // `[readonly]` entra nas guardas de hover/active junto de `[disabled]`: nao vai
17
+ // acontecer nada no clique, entao prometer interacao e mentir. Mas NAO entra nas regras
18
+ // de COR abaixo — e justamente por isso que readonly existe: o estado continua legivel.
19
+ &:not([disabled]):not([readonly]):hover .matcha-checkbox-container {
17
20
  background: var(--matcha-color-surface-hover, rgba(0, 0, 0, 0.04));
18
21
  box-shadow: var(--matcha-shadow-1);
19
22
  }
20
23
 
21
24
  // ── Hover/Focus quando checked/indeterminate — accent-alpha bg ─────
22
- &:not([disabled]):hover :is([checked], [indeterminate]) ~ .matcha-checkbox-container,
25
+ &:not([disabled]):not([readonly]):hover :is([checked], [indeterminate]) ~ .matcha-checkbox-container,
23
26
  // fallback global (estado checked/indeterminate sem condicional)
24
- &:not([disabled]):hover .matcha-checkbox-container:has(.matcha-checkbox-icon--checked),
25
- &:not([disabled]):hover .matcha-checkbox-container:has(.matcha-checkbox-icon--indeterminate) {
27
+ &:not([disabled]):not([readonly]):hover .matcha-checkbox-container:has(.matcha-checkbox-icon--checked),
28
+ &:not([disabled]):not([readonly]):hover .matcha-checkbox-container:has(.matcha-checkbox-icon--indeterminate) {
26
29
  background: var(--matcha-color-accent-alpha, rgba(0, 148, 0, 0.1));
27
30
  }
28
31
 
@@ -67,6 +70,14 @@
67
70
  &[disabled] .matcha-checkbox-label {
68
71
  color: var(--matcha-color-disabled-fg);
69
72
  }
73
+
74
+ // ── Readonly — cursor de leitura, e NENHUMA troca de cor ───────────
75
+ // O ponteiro deixa de ser `pointer` porque nao ha o que acionar; o glyph e o label
76
+ // ficam com a cor normal, que e o ponto de usar readonly em vez de disabled.
77
+ &[readonly] .matcha-checkbox,
78
+ &[readonly] .matcha-checkbox-container {
79
+ cursor: default;
80
+ }
70
81
  }
71
82
  }
72
83
 
@@ -107,7 +118,7 @@ $matcha-checkbox-touch: 36px;
107
118
  }
108
119
 
109
120
  // Active overlay — só quando não disabled
110
- matcha-checkbox:not([disabled]) .matcha-checkbox-container:active::before {
121
+ matcha-checkbox:not([disabled]):not([readonly]) .matcha-checkbox-container:active::before {
111
122
  opacity: var(--matcha-state-active);
112
123
  }
113
124
 
@@ -14,6 +14,29 @@
14
14
 
15
15
  @mixin matcha-file-uploader-theme($theme) {
16
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
+
17
40
  // ── Dropzone ──
18
41
  .matcha-file-uploader-dropzone {
19
42
  background: var(--matcha-color-surface-background, var(--matcha-color-surface-bg));
@@ -29,6 +52,14 @@
29
52
  cursor: not-allowed;
30
53
  pointer-events: none;
31
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
+ }
32
63
  }
33
64
 
34
65
  .matcha-file-uploader-dropzone__icon-pill {
@@ -94,8 +125,27 @@
94
125
  color: var(--matcha-color-text-primary);
95
126
  }
96
127
 
97
- &.matcha-file-uploader-file__action--delete:hover {
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 {
98
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
+ }
99
149
  }
100
150
  }
101
151
 
@@ -116,6 +166,15 @@
116
166
  &:hover {
117
167
  background: var(--matcha-color-surface-card);
118
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
+ }
119
178
  }
120
179
  }
121
180
 
@@ -137,6 +196,75 @@
137
196
  width: 100%;
138
197
  }
139
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
+
140
268
  // ── Dropzone box (Estado=Dropzone — Figma 16152:209) — a zona INTEIRA é o alvo
141
269
  .matcha-file-uploader-dropzone {
142
270
  display: flex;
@@ -157,6 +285,24 @@
157
285
  }
158
286
  }
159
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
+
160
306
  // layout="card": dropzone vazio compartilha a proporção do card preenchido
161
307
  // (aspect-ratio aplicado inline via previewRatio) pra empty e filled terem a
162
308
  // MESMA altura lado a lado. Sem isso, vazio é dimensionado por padding/conteúdo
@@ -369,6 +515,14 @@
369
515
  width: 20px;
370
516
  height: 20px;
371
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
+ }
372
526
  }
373
527
 
374
528
  // ── Card layout — preview preenchido 16:9 + ações no hover (Estado=Drop filled) ──
@@ -429,6 +583,14 @@
429
583
  width: 18px;
430
584
  height: 18px;
431
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
+ }
432
594
  }
433
595
 
434
596
  // ── Cropper inline (modo crop: imagem única) ──────────────────────────────
@@ -464,6 +626,7 @@
464
626
 
465
627
  // ── Reduced motion ───────────────────────────────────────────────────────────
466
628
  @media (prefers-reduced-motion: reduce) {
629
+ .matcha-file-uploader-shell,
467
630
  .matcha-file-uploader-dropzone,
468
631
  .matcha-file-uploader-file__progress-fill,
469
632
  .matcha-file-uploader-file__action,
@@ -424,6 +424,33 @@
424
424
  --matcha-table-cell-bg: var(--matcha-table-row-bg-hover-accent);
425
425
  }
426
426
 
427
+ // O ALVO é a CÉLULA inteira, não só o texto do nome.
428
+ //
429
+ // A âncora envolve o nome, e em título curto isso vira um alvo de poucos pixels —
430
+ // reclamação de quem usa, medida em séries. O `::after` esticado resolve sem trocar
431
+ // a âncora por handler de clique: o href continua existindo, então ctrl+clique,
432
+ // clique do meio e "abrir em nova aba" seguem funcionando, e o foco de teclado
433
+ // continua sendo o do link.
434
+ //
435
+ // De quebra conserta o hover: a regra acima só pinta a linha quando o mouse está
436
+ // sobre o TEXTO. Com a área esticada, pinta ao entrar na célula — que é o que faz
437
+ // a linha parecer clicável.
438
+ //
439
+ // Escopado em `.matcha-table td` de propósito: `a[matcha-cell-link]` também aparece
440
+ // em card de listagem, fora de tabela, e lá o link deve seguir do tamanho do texto.
441
+ //
442
+ // O custo é a seleção de texto DENTRO desta célula. No resto da linha continua
443
+ // funcionando, que é a diferença para a linha toda clicável.
444
+ td:has(a[matcha-cell-link]) {
445
+ position: relative;
446
+ }
447
+
448
+ td a[matcha-cell-link]::after {
449
+ content: '';
450
+ position: absolute;
451
+ inset: 0;
452
+ }
453
+
427
454
  tbody tr.is-selected td {
428
455
  --matcha-table-cell-bg: var(--matcha-table-row-bg-selected);
429
456
  }
@@ -46,6 +46,17 @@
46
46
  }
47
47
  }
48
48
 
49
+ // Texto do tooltip — `pre-line` respeita as quebras (`\n`) que o directive insere
50
+ // entre os itens quando `matchaTooltip` recebe um array (1 informação por linha);
51
+ // linhas longas ainda quebram normalmente. O alinhamento vem da custom property
52
+ // `--matcha-tooltip-text-align` que o directive seta a partir de `matchaTooltipAlign`
53
+ // (default center) — só é perceptível com múltiplas linhas.
54
+ .matcha-tooltip-text {
55
+ white-space: pre-line;
56
+ overflow-wrap: anywhere;
57
+ text-align: var(--matcha-tooltip-text-align, center);
58
+ }
59
+
49
60
  // ── Arrow (mesma cor do bg) — 13.856×6 — ::after triangular ────────────────
50
61
  // Tamanho via border-style: solid trick. base 13.856 ≈ 2×7px borders → border-width
51
62
  // horizontal ~7, vertical 6. Mantemos arredondado pelo Figma (13.856 × 6).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inchurch/matcha-theme",
3
- "version": "22.36.1",
3
+ "version": "22.37.0",
4
4
  "description": "Themes for matcha-design-system",
5
5
  "main": "main.scss",
6
6
  "scripts": {