@inchurch/matcha-theme 22.38.2 → 22.38.4

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,722 +1,722 @@
1
- @use "sass:map";
2
-
3
- // =============================================================================
4
- // matcha-table — `<table matcha-table>` directive (1:1 Figma DSV3 15641:275)
5
- //
6
- // API canon:
7
- // <table matcha-table size="comfortable" striped hover bordered stickyHeader
8
- // [stickyStart]="N" [stickyEnd]="N" [loading]="isLoading">
9
- // <thead><tr><th>...</th></tr></thead>
10
- // <tbody><tr><td>...</td></tr></tbody>
11
- // </table>
12
- //
13
- // Companion directives (a11y automática):
14
- // [matcha-table-sortable] em <th> — aria-sort + tabindex + keyboard
15
- // [rowClick] em <tr> — AÇÃO na linha (abrir modal, selecionar). Só o
16
- // binding já ativa tudo (tabindex + Enter/Space
17
- // + hover accent na linha inteira)
18
- //
19
- // Navegação (leva pra outra página) NÃO é [rowClick]:
20
- // <a matcha-cell-link [routerLink]> dentro do <td> — âncora de verdade, então
21
- // ctrl+clique/clique do meio/botão direito funcionam e o resto da linha segue
22
- // selecionável. Ponteiro e tom accent aparecem só sobre a âncora, pintando a linha
23
- // inteira. Os dois no mesmo <tr> é proibido pelo canon (dois hovers competindo).
24
- //
25
- // Helpers CSS-only:
26
- // td.numeric → tabular-nums
27
- // td.truncate → ellipsis (text overflow)
28
- // td[data-align] → alinhamento (data-alinhamento / td.text-{center,right} deprecated)
29
- // tr.equal/.plus/.minus → variant semantic accent (cor via CSS var)
30
- // tr.child-item → indent + accent esmaecido
31
- // tr.is-selected → highlight
32
- // =============================================================================
33
-
34
- @mixin matcha-table-modern($theme) {
35
-
36
- // ═══════════════════════════════════════════════════════════════════════
37
- // CSS vars do componente — única fonte de verdade. ZERO literal hardcoded
38
- // fora deste bloco. Consumer override qualquer um no escopo do <table>.
39
- // ═══════════════════════════════════════════════════════════════════════
40
- .matcha-table {
41
- // ── Dimensions ────────────────────────────────────────────────────────
42
- // Calibrado pra density moderna (Stripe/Linear/Notion). Comfortable era 52
43
- // antes — apertado em telas wide. 58px equilibra densidade × respiro.
44
- --matcha-table-cell-height-compact: var(--matcha-space-500, 40px);
45
- --matcha-table-cell-height-comfortable: 58px;
46
- --matcha-table-cell-height-spacious: var(--matcha-space-800, 64px);
47
- // Padding uniforme L/R — assimétrico cria sensação de "primeira coluna
48
- // afastada do resto". 16px ambos lados é canon moderno.
49
- --matcha-table-cell-padding-x-left: var(--matcha-space-200, 16px);
50
- --matcha-table-cell-padding-x-right: var(--matcha-space-200, 16px);
51
- --matcha-table-cell-min-width: var(--matcha-space-1500, 120px);
52
- --matcha-table-paginator-spacing: var(--matcha-space-300, 24px);
53
-
54
- // ── Typography (modern density Stripe/Linear) ─────────────────────────
55
- --matcha-table-font-size: 13px;
56
- --matcha-table-font-line-height: 18px;
57
- --matcha-table-header-font-size: var(--matcha-text-body-sm, 12px);
58
- --matcha-table-header-font-line-height: var(--matcha-leading-body-sm, 16px);
59
- --matcha-table-header-letter-spacing: 0.04em;
60
-
61
- // ── Borders / radius ──────────────────────────────────────────────────
62
- --matcha-table-border-color: var(--matcha-color-interaction-disabled-border);
63
- --matcha-table-border-header: var(--matcha-border-thin, 2px);
64
- --matcha-table-border-row: var(--matcha-border-hairline, 1px);
65
- --matcha-table-bordered-radius: var(--matcha-radius-md, 4px);
66
- // Escala de card (não a mesma de --matcha-table-bordered-radius, que é só
67
- // pro inset border fino) — roundedTop arredonda o CONTAINER inteiro.
68
- --matcha-table-rounded-top-radius: var(--matcha-radius-xl, 16px);
69
-
70
- // ── Surface colors ────────────────────────────────────────────────────
71
- --matcha-table-cell-bg: var(--matcha-color-surface-card);
72
- --matcha-table-row-bg-selected: var(--matcha-color-accent-alpha-12,
73
- rgba(var(--matcha-color-accent), 0.12));
74
- --matcha-table-row-bg-striped: var(--matcha-color-surface-card-alt,
75
- rgba(var(--matcha-color-fg), 0.02));
76
- // Hover NÃO usa surface-table-row-hover token (tá próximo demais de surface-card
77
- // em dark e fica imperceptível). Calibrado pra delta visível mesmo sobre stripe.
78
- // CSS custom property values são TEXTO LITERAL pro Sass — fn calls Sass
79
- // dentro NÃO são avaliadas. Precisa interpolação `#{}` pra forçar Sass a
80
- // evaluar `var(--matcha-color-fg)` ANTES do output. Sem isso, o valor sai
81
- // literal no bundle (ex: `rgba(var(--matcha-color-fg), 0.12)`) e o browser
82
- // descarta como invalid CSS (= chevron invisível, hover sem cor, etc).
83
- --matcha-table-row-bg-hover: rgba(var(--matcha-color-fg), 0.12);
84
- --matcha-table-row-bg-hover-striped: rgba(var(--matcha-color-fg), 0.18);
85
- --matcha-table-header-bg-hover: rgba(var(--matcha-color-fg), 0.10);
86
- // Hover da linha que NAVEGA: tom accent, disparado só quando o ponteiro está
87
- // sobre a âncora da célula (ver "Row navegável" adiante). Fora dela a linha usa
88
- // o hover neutro acima — é esse contraste que diz ao usuário onde clicar.
89
- --matcha-table-row-bg-hover-accent: var(--matcha-color-accent-surface-hover);
90
-
91
- // ── Sort indicator colors ─────────────────────────────────────────────
92
- // Inactive usa alpha 0.45 no foreground — claramente VISÍVEL como "ali tem
93
- // sort", sem competir com texto do header. Hover/ativo passa pra accent full.
94
- --matcha-table-sort-icon-color: rgba(var(--matcha-color-fg), 0.45);
95
- --matcha-table-sort-icon-color-active: var(--matcha-color-accent);
96
- --matcha-table-sort-icon-size: var(--matcha-space-250, 20px);
97
-
98
- // ── Empty cell placeholder ────────────────────────────────────────────
99
- --matcha-table-empty-cell-text: var(--matcha-color-text-muted,
100
- rgba(var(--matcha-color-fg), 0.5));
101
-
102
- // ── Variant accent (semantic row) ─────────────────────────────────────
103
- --matcha-table-variant-accent-width: var(--matcha-space-050, 4px);
104
- --matcha-table-row-accent-child-opacity: 0.5;
105
- --matcha-table-selected-accent-width: var(--matcha-border-thin, 2px);
106
- --matcha-table-selected-accent-color: var(--matcha-color-accent);
107
-
108
- // ── Column width hints (utility classes col-*) ────────────────────────
109
- --matcha-table-col-checkbox-width: var(--matcha-space-600, 48px);
110
- --matcha-table-col-id-width: var(--matcha-space-1000, 80px);
111
- --matcha-table-col-narrow-width: var(--matcha-space-1500, 120px);
112
- --matcha-table-col-medium-width: var(--matcha-space-2500, 200px);
113
- // Pino da coluna de Ações (matcha-cell-action) — mesma escala de
114
- // --matcha-table-col-id-width (80px), token próprio pra não acoplar
115
- // semanticamente coisas diferentes que hoje só coincidem em valor.
116
- --matcha-table-col-actions-width: var(--matcha-space-1000, 80px);
117
-
118
- // ── States / animations ───────────────────────────────────────────────
119
- --matcha-table-disabled-opacity: var(--matcha-state-disabled-opacity, 0.5);
120
- --matcha-table-loading-pulse-duration: var(--matcha-duration-slow, 1500ms);
121
- --matcha-table-loading-pulse-opacity-min: 0.5;
122
- --matcha-table-transition-duration: var(--matcha-duration-instant, 80ms);
123
- --matcha-table-focus-ring-width: var(--matcha-state-focus-ring-width, 2px);
124
- --matcha-table-focus-ring-color: var(--matcha-color-accent);
125
-
126
- // ── Empty state row (matcha-table-empty directive) ────────────────────
127
- --matcha-table-empty-padding-y: var(--matcha-space-500, 40px);
128
- --matcha-table-empty-padding-x: var(--matcha-space-300, 24px);
129
-
130
- // ── Resize handle (matcha-table-resizable directive) ──────────────────
131
- --matcha-table-resize-handle-width: var(--matcha-space-100, 8px);
132
- --matcha-table-resize-indicator-width: var(--matcha-border-hairline, 1px);
133
- --matcha-table-resize-indicator-height: 50%;
134
- --matcha-table-resize-indicator-top: 25%;
135
- }
136
-
137
- // ── Base — display table nativo + typography via vars ─────────────────
138
- .matcha-table {
139
- display: table;
140
- width: 100%;
141
- // separate (não collapse) + spacing 0 → visualmente idêntico (zero gap
142
- // entre cells), mas `border-collapse: collapse` é conhecido por quebrar
143
- // `position: sticky` em <th>/<td> em vários browsers — especialmente
144
- // quando a cell precisa ficar sticky nos DOIS eixos ao mesmo tempo (linha
145
- // + coluna, o canto stickyHeader × stickyStart/stickyEnd). Só existe
146
- // border-bottom no arquivo (nenhum border-top) — sem risco de borda dupla.
147
- border-collapse: separate;
148
- border-spacing: 0;
149
- font-family: var(--matcha-font-family);
150
- font-size: var(--matcha-table-font-size);
151
- line-height: var(--matcha-table-font-line-height);
152
- font-weight: var(--matcha-weight-regular, 400);
153
- color: var(--matcha-color-text-secondary,
154
- color-mix(in srgb, var(--matcha-color-fg) 70%, transparent));
155
-
156
- // ── Header cells (text/primary, border-bottom 2px, height 64) ────────
157
- thead th {
158
- box-sizing: border-box;
159
- height: var(--matcha-table-cell-height-comfortable);
160
- min-width: var(--matcha-table-cell-min-width);
161
- padding: 0
162
- var(--matcha-table-cell-padding-x-right)
163
- 0
164
- var(--matcha-table-cell-padding-x-left);
165
- background: var(--matcha-table-cell-bg);
166
- color: var(--matcha-color-text-primary,
167
- var(--matcha-color-fg));
168
- // Header menor que body — hierarquia "metadado" vs "conteúdo".
169
- font-size: var(--matcha-table-header-font-size);
170
- line-height: var(--matcha-table-header-font-line-height);
171
- font-weight: var(--matcha-weight-semibold, 600);
172
- letter-spacing: var(--matcha-table-header-letter-spacing);
173
- text-align: left;
174
- vertical-align: middle;
175
- border-bottom: var(--matcha-table-border-header) solid
176
- var(--matcha-table-border-color);
177
-
178
- // Alinhamento — data-align é o canon DSV3; data-alinhamento + utility
179
- // classes seguem aceitos (deprecated, remoção em ciclo futuro)
180
- &[data-align="center"], &[data-alinhamento="center"], &.text-center { text-align: center; }
181
- &[data-align="right"], &[data-alinhamento="right"], &.text-right { text-align: right; }
182
-
183
- // ── Sortable hint ([matcha-table-sortable] directive seta os attrs) ──
184
- &[data-sortable] {
185
- cursor: pointer;
186
- user-select: none;
187
- // SEMPRE reserva espaço pro chevron (mesmo inativo) — chevron entra/sai
188
- // só via opacity. Sem padding condicional → texto NÃO desloca no hover.
189
- padding-right: calc(var(--matcha-table-cell-padding-x-right) +
190
- var(--matcha-table-sort-icon-size));
191
- position: relative;
192
-
193
- // Hover NÃO muda bg do header — bg-change era o que fazia o user
194
- // perceber "movimento" quando o chevron entrava ao mesmo tempo.
195
- // Manter header estático no hover, só chevron aparece com opacity.
196
- // Cursor pointer + chevron fade já comunicam interatividade.
197
-
198
- // Sort indicators — SVG 1:1 com `matcha/icon/arrow` (figma-icons.ts).
199
- // Estado inativo (none) = INVISÍVEL (opacity 0). Aparece no hover do header
200
- // com opacity faded, full quando ativo (asc/desc). Pattern canon Stripe/Linear:
201
- // sortable em todas as colunas, mas indicador só na que tá ordenando.
202
- &::after {
203
- content: '';
204
- position: absolute;
205
- right: var(--matcha-table-cell-padding-x-right);
206
- top: 50%;
207
- transform: translateY(-50%);
208
- width: var(--matcha-table-sort-icon-size);
209
- height: var(--matcha-table-sort-icon-size);
210
- background: var(--matcha-table-sort-icon-color);
211
- transition: background-color var(--matcha-table-transition-duration) linear;
212
- mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 7.82499L6.1 12.725C5.9 12.925 5.66667 13.0208 5.4 13.0125C5.13333 13.0042 4.9 12.9 4.7 12.7C4.51667 12.5 4.42083 12.2667 4.4125 12C4.40417 11.7333 4.5 11.5 4.7 11.3L11.3 4.69999C11.4 4.59999 11.5083 4.52915 11.625 4.48749C11.7417 4.44582 11.8667 4.42499 12 4.42499C12.1333 4.42499 12.2583 4.44582 12.375 4.48749C12.4917 4.52915 12.6 4.59999 12.7 4.69999L19.3 11.3C19.4833 11.4833 19.575 11.7125 19.575 11.9875C19.575 12.2625 19.4833 12.5 19.3 12.7C19.1 12.9 18.8625 13 18.5875 13C18.3125 13 18.075 12.9 17.875 12.7L13 7.82499V19C13 19.2833 12.9042 19.5208 12.7125 19.7125C12.5208 19.9042 12.2833 20 12 20C11.7167 20 11.4792 19.9042 11.2875 19.7125C11.0958 19.5208 11 19.2833 11 19V7.82499Z'/></svg>")
213
- center/contain no-repeat;
214
- }
215
-
216
- // Hover no header — chevron escurece pra foreground full (já visível
217
- // muted antes via alpha 0.45). Reforça que tá interativo.
218
- &:hover::after {
219
- background: var(--matcha-color-text-primary,
220
- var(--matcha-color-fg));
221
- }
222
-
223
- // Padding-right SEMPRE reserva espaço pro chevron. Antes ajustava
224
- // condicionalmente (sem padding extra no inativo) — causava text
225
- // "dançar" 20px ao hover quando padding crescia pra acomodar arrow.
226
- // Mantém estável: chevron entra via opacity, sem reflow do texto.
227
-
228
- &[data-sort-direction="asc"]::after {
229
- mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 7.82499L6.1 12.725C5.9 12.925 5.66667 13.0208 5.4 13.0125C5.13333 13.0042 4.9 12.9 4.7 12.7C4.51667 12.5 4.42083 12.2667 4.4125 12C4.40417 11.7333 4.5 11.5 4.7 11.3L11.3 4.69999C11.4 4.59999 11.5083 4.52915 11.625 4.48749C11.7417 4.44582 11.8667 4.42499 12 4.42499C12.1333 4.42499 12.2583 4.44582 12.375 4.48749C12.4917 4.52915 12.6 4.59999 12.7 4.69999L19.3 11.3C19.4833 11.4833 19.575 11.7125 19.575 11.9875C19.575 12.2625 19.4833 12.5 19.3 12.7C19.1 12.9 18.8625 13 18.5875 13C18.3125 13 18.075 12.9 17.875 12.7L13 7.82499V19C13 19.2833 12.9042 19.5208 12.7125 19.7125C12.5208 19.9042 12.2833 20 12 20C11.7167 20 11.4792 19.9042 11.2875 19.7125C11.0958 19.5208 11 19.2833 11 19V7.82499Z'/></svg>")
230
- center/contain no-repeat;
231
- background: var(--matcha-table-sort-icon-color-active);
232
- }
233
-
234
- &[data-sort-direction="desc"]::after {
235
- mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 16.175V5C11 4.71667 11.0958 4.47917 11.2875 4.2875C11.4792 4.09583 11.7167 4 12 4C12.2833 4 12.5208 4.09583 12.7125 4.2875C12.9042 4.47917 13 4.71667 13 5V16.175L17.9 11.275C18.1 11.075 18.3333 10.9792 18.6 10.9875C18.8667 10.9958 19.1 11.1 19.3 11.3C19.4833 11.5 19.5792 11.7333 19.5875 12C19.5958 12.2667 19.5 12.5 19.3 12.7L12.7 19.3C12.6 19.4 12.4917 19.4708 12.375 19.5125C12.2583 19.5542 12.1333 19.575 12 19.575C11.8667 19.575 11.7417 19.5542 11.625 19.5125C11.5083 19.4708 11.4 19.4 11.3 19.3L4.7 12.7C4.51667 12.5167 4.425 12.2875 4.425 12.0125C4.425 11.7375 4.51667 11.5 4.7 11.3C4.9 11.1 5.1375 11 5.4125 11C5.6875 11 5.925 11.1 6.125 11.3L11 16.175Z'/></svg>")
236
- center/contain no-repeat;
237
- background: var(--matcha-table-sort-icon-color-active);
238
- }
239
-
240
- // Header right-aligned com sort — chevron vem ANTES do texto (canon).
241
- &[data-align="right"], &[data-alinhamento="right"], &.text-right {
242
- padding-right: var(--matcha-table-cell-padding-x-right);
243
- padding-left: calc(var(--matcha-table-cell-padding-x-left) +
244
- var(--matcha-table-sort-icon-size));
245
-
246
- &::after {
247
- right: auto;
248
- left: var(--matcha-table-cell-padding-x-left);
249
- }
250
- }
251
-
252
- // Focus ring quando keyboard-navigated (tabindex via directive).
253
- &:focus-visible {
254
- outline: var(--matcha-table-focus-ring-width) solid
255
- var(--matcha-table-focus-ring-color);
256
- outline-offset: calc(-1 * var(--matcha-table-focus-ring-width));
257
- }
258
- }
259
- }
260
-
261
- // ── Body cells ────────────────────────────────────────────────────────
262
- tbody td {
263
- box-sizing: border-box;
264
- height: var(--matcha-table-cell-height-comfortable);
265
- min-width: var(--matcha-table-cell-min-width);
266
- padding: 0
267
- var(--matcha-table-cell-padding-x-right)
268
- 0
269
- var(--matcha-table-cell-padding-x-left);
270
- background: var(--matcha-table-cell-bg);
271
- color: var(--matcha-color-text-secondary,
272
- color-mix(in srgb, var(--matcha-color-fg) 70%, transparent));
273
- vertical-align: middle;
274
- border-bottom: var(--matcha-table-border-row) solid
275
- var(--matcha-table-border-color);
276
- transition: background-color var(--matcha-table-transition-duration) linear;
277
-
278
- &[data-align="center"], &[data-alinhamento="center"], &.text-center { text-align: center; }
279
- &[data-align="right"], &[data-alinhamento="right"], &.text-right { text-align: right; }
280
-
281
- // Helper: tabular-nums (alinha decimais)
282
- &[data-numeric], &.numeric {
283
- font-variant-numeric: tabular-nums;
284
- font-feature-settings: 'tnum';
285
- }
286
-
287
- // Helper: ellipsis (texto longo)
288
- &[data-truncate], &.truncate {
289
- max-width: 0;
290
- white-space: nowrap;
291
- overflow: hidden;
292
- text-overflow: ellipsis;
293
- }
294
-
295
- // Helper: cells vazias preenchidas com "—" muted (canon moderno).
296
- &:empty::after {
297
- content: '—';
298
- color: var(--matcha-table-empty-cell-text);
299
- }
300
- }
301
-
302
- // ── Column width hints (aplicar em <th> — <td> herda pela coluna) ─────
303
- thead th,
304
- tbody td {
305
- // min-width igual ao width — sem isso, a regra base thead th/tbody td
306
- // (min-width:120px) vence e a coluna nunca encolhe pro valor pretendido
307
- // (mesmo bug achado e corrigido em .col-actions).
308
- &.col-checkbox { width: var(--matcha-table-col-checkbox-width); min-width: var(--matcha-table-col-checkbox-width); text-align: center; }
309
- &.col-id { width: var(--matcha-table-col-id-width); }
310
- &.col-narrow { width: var(--matcha-table-col-narrow-width); }
311
- &.col-medium { width: var(--matcha-table-col-medium-width); }
312
- // Actions / icon-buttons — pinada em --matcha-table-col-actions-width (80px).
313
- // Children inline-flex, centralizados (título "Ações" no header também
314
- // centraliza — text-align: center vale pro conteúdo das duas linhas).
315
- //
316
- // Importante: `.matcha-button` tem `display: flex` (block-level) que faria
317
- // os icon-buttons empilharem verticalmente. Override via selector descendant
318
- // com specificity maior pra forçar inline-flex no contexto de actions cell.
319
- &.col-actions {
320
- width: var(--matcha-table-col-actions-width);
321
- // min-width base da tbody td/thead th é 120px (--matcha-table-cell-min-width)
322
- // — maior que os 80px daqui, e min-width sempre vence sobre width no box
323
- // model. Sem este override, a coluna ficava presa em 120px (achado real).
324
- min-width: var(--matcha-table-col-actions-width);
325
- white-space: nowrap;
326
- text-align: center;
327
-
328
- > .matcha-button,
329
- > button[matcha-icon-button],
330
- > * {
331
- display: inline-flex;
332
- vertical-align: middle;
333
- }
334
- > * + * {
335
- margin-left: var(--matcha-space-050, 4px);
336
- }
337
- }
338
- // Greedy = espaço residual (default, classe explícita pra documentação).
339
- &.col-greedy { width: auto; }
340
- }
341
-
342
- // ── Paginator alinhado após table (convention) ────────────────────────
343
- // Sem margin-top — paginator gruda no border-bottom da última row.
344
- // Spacing visual vem dos paddings internos do próprio matcha-paginator.
345
- .matcha-table-paginator,
346
- & + matcha-paginator {
347
- display: block;
348
- margin-top: 0;
349
- }
350
-
351
- // ── Row clickable ([rowClick] directive — só o binding já ativa tudo) ─
352
- tbody tr.matcha-row-clickable {
353
- cursor: pointer;
354
-
355
- &:hover td {
356
- --matcha-table-cell-bg: var(--matcha-table-row-bg-hover);
357
- }
358
-
359
- &:focus-visible {
360
- outline: var(--matcha-table-focus-ring-width) solid
361
- var(--matcha-table-focus-ring-color);
362
- outline-offset: calc(-1 * var(--matcha-table-focus-ring-width));
363
- }
364
- }
365
-
366
- // ── Modifiers via atributo no host (Angular @HostBinding, ver
367
- // table.component.ts) ──────────────────────────────────────────────────
368
- // Cada modifier tem 2 seletores alternativos: `&[x]` (forma legado — o
369
- // host JÁ É `.matcha-table`, self) e `matcha-table[x] &` (forma elemento
370
- // — o atributo mora no host `<matcha-table>`, mas o alvo real é o filho
371
- // `.matcha-table` dentro do wrapper; CSS alcança via descendente, não
372
- // precisa o atributo estar duplicado no filho).
373
- // Striped usa :where() pra dropar specificity do :nth-child — hover vence.
374
- &[striped] tbody tr:where(:nth-child(even)) td,
375
- matcha-table[striped] & tbody tr:where(:nth-child(even)) td {
376
- --matcha-table-cell-bg: var(--matcha-table-row-bg-striped);
377
- }
378
-
379
- &[hover] tbody tr:hover td,
380
- matcha-table[hover] & tbody tr:hover td {
381
- --matcha-table-cell-bg: var(--matcha-table-row-bg-hover);
382
- }
383
-
384
- // Stripe-aware hover: row PAR ganha alpha mais forte pra preservar stripe.
385
- &[striped] tbody tr:where(:nth-child(even)).matcha-row-clickable:hover td,
386
- matcha-table[striped] & tbody tr:where(:nth-child(even)).matcha-row-clickable:hover td,
387
- &[striped][hover] tbody tr:where(:nth-child(even)):hover td,
388
- matcha-table[striped][hover] & tbody tr:where(:nth-child(even)):hover td {
389
- --matcha-table-cell-bg: var(--matcha-table-row-bg-hover-striped);
390
- }
391
-
392
- // Hover accent de linha clicável: a diretiva [rowClick] já sobrescreve
393
- // --matcha-table-row-bg-hover(-striped) inline no próprio <tr> (ver
394
- // table-clickable-row.directive.ts) — nenhuma regra de tema necessária
395
- // aqui, o estilo inline já vence qualquer especificidade.
396
-
397
- // ── Row navegável (`<a matcha-cell-link>` na célula) ───────────────────
398
- // A linha que LEVA PRA OUTRA PÁGINA não é clicável inteira: quem navega é uma
399
- // âncora de verdade na célula, pra que ctrl+clique, clique do meio e o menu do
400
- // botão direito funcionem — e pra que o resto da linha continue selecionável
401
- // com o mouse (com a linha toda clicável, não dava pra copiar texto da tabela).
402
- //
403
- // O feedback visual é o inverso do rowClick: ponteiro e tom accent aparecem só
404
- // quando o mouse está sobre a âncora, e aí a linha INTEIRA pinta. Fora dela vale
405
- // o hover neutro de sempre.
406
- //
407
- // Pinta via --matcha-table-cell-bg em TODOS os td da linha (mesmo caminho do
408
- // hover normal), não com background-color no <tr>: em tabela striped o fundo
409
- // mora no td, e um background no tr ficaria por baixo dele — o resultado seria
410
- // uma célula colorida e o resto não.
411
- //
412
- // Depois das regras de striped de propósito: mesma especificidade, e ordem de
413
- // origem decide. `:has()` já é usado em ~10 componentes do tema.
414
- // O estilo BASE da âncora é global (base/_reset.scss) — ela também aparece em
415
- // card de listagem, fora de qualquer tabela. Aqui fica só o que é da tabela: o
416
- // realce da linha inteira.
417
- a[matcha-cell-link]:focus-visible {
418
- outline: var(--matcha-table-focus-ring-width) solid
419
- var(--matcha-table-focus-ring-color);
420
- }
421
-
422
- tbody tr:has(a[matcha-cell-link]:hover) td,
423
- tbody tr:has(a[matcha-cell-link]:focus-visible) td {
424
- --matcha-table-cell-bg: var(--matcha-table-row-bg-hover-accent);
425
- }
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
-
454
- tbody tr.is-selected td {
455
- --matcha-table-cell-bg: var(--matcha-table-row-bg-selected);
456
- }
457
- // Selected row ganha border-left accent — affordance canon Linear/Notion.
458
- // No <tr> → não some com stickyStart. Usa ::after pra coexistir com
459
- // ::before do variant accent sem conflito.
460
- tbody tr.is-selected {
461
- position: relative;
462
-
463
- &::after {
464
- content: '';
465
- position: absolute;
466
- top: 0;
467
- left: 0;
468
- width: var(--matcha-table-selected-accent-width);
469
- height: 100%;
470
- background: var(--matcha-table-selected-accent-color);
471
- pointer-events: none;
472
- z-index: 2; // acima do variant accent (z-index 1)
473
- }
474
- }
475
-
476
- // Bordered — box-shadow inset em vez de border real (não clipa shadows
477
- // externos das sticky cols, que projetam pra fora da <table>).
478
- // Conhecido: em alguns casos (sticky-header/colunas fixas, forma
479
- // elemento) o background opaco das cells pode encobrir parte deste
480
- // traço — não resolvido nesta rodada (fora do escopo desta tarefa); se
481
- // precisar de borda garantida num caso desses, use a utility class de
482
- // borda (ex.: `b-1`) direto no consumo do componente.
483
- &[bordered] {
484
- box-shadow: inset 0 0 0 var(--matcha-table-border-row)
485
- var(--matcha-table-border-color);
486
- border-radius: var(--matcha-table-bordered-radius);
487
- }
488
-
489
- // Sticky header (vertical scroll). Forma legado: o host (a própria table) é
490
- // o container de scroll. Default do componente é overflow-y:hidden (só
491
- // horizontal é fallback passivo — dois scrolls na tela atrapalham mobile);
492
- // stickyHeader é opt-in EXPLÍCITO, reabilita overflow-y:auto de propósito
493
- // + altura máxima (sem isso não há o que rolar, o header não tem contra o
494
- // que grudar). Altura configurável via var (default 70vh).
495
- &[sticky-header],
496
- matcha-table[sticky-header] & {
497
- max-height: var(--matcha-table-sticky-header-max-height, 70vh);
498
- overflow-y: auto;
499
- }
500
- &[sticky-header] thead th,
501
- matcha-table[sticky-header] & thead th {
502
- position: sticky;
503
- top: 0;
504
- z-index: 1;
505
- }
506
-
507
- // Size variants (data-size cascateia altura)
508
- &[data-size="compact"] thead th,
509
- &[data-size="compact"] tbody td { height: var(--matcha-table-cell-height-compact); }
510
- &[data-size="spacious"] thead th,
511
- &[data-size="spacious"] tbody td { height: var(--matcha-table-cell-height-spacious); }
512
-
513
- // ── Sticky columns (start/end) — canon scroll horizontal ──────────────
514
- // Performance: nada de box-shadow com blur nem background-image (gradient)
515
- // aqui — são caros de repaint/compositar MULTIPLICADOS por linha (uma
516
- // tabela de 40+ linhas com 2 colunas sticky = 80 elementos), e causavam
517
- // queda de FPS real no scroll (medido em produção). Custo mínimo:
518
- // background-color sólido (fixo, não acompanha hover/tint — troca-se
519
- // fidelidade visual por performance) + border 1px sólida no lugar da
520
- // sombra de scroll-edge (borda é essencialmente grátis pro compositor,
521
- // sombra com blur não é).
522
- //
523
- // :not([sticky-header])/:not(matcha-table[sticky-header] &) — combinar
524
- // sticky column COM sticky header exige que a cell de CANTO (cabeçalho da
525
- // coluna fixa) fique sticky nos dois eixos (top E left/right) ao mesmo
526
- // tempo — inconsistente entre browsers em cells de <table> (documentado
527
- // no JSDoc do componente). Em vez de deixar o canto quebrado, desliga a
528
- // coluna fixa inteira quando stickyHeader está ativo — só o header fixo
529
- // (mais universalmente útil) permanece. `sticky-header` agora é atributo
530
- // no host — o 2º `:not()` cobre o caso em que o host é o wrapper (forma
531
- // elemento, `sticky-header` não está no `.matcha-table` em si, está no
532
- // `<matcha-table>` ancestral). `data-sticky-start`/`-end` continuam só no
533
- // nó real (mirrorAttributesToRealTable), não precisam de alternativa.
534
- @for $i from 1 through 4 {
535
- &[data-sticky-start="#{$i}"]:not([sticky-header]):not(matcha-table[sticky-header] &) {
536
- thead th:nth-child(-n+#{$i}),
537
- tbody tr td:nth-child(-n+#{$i}) {
538
- position: sticky;
539
- z-index: 2;
540
- background-color: var(--matcha-color-surface-card);
541
- }
542
- // Última cell do grupo sticky-start marca o scroll-edge com borda fina.
543
- thead th:nth-child(#{$i}),
544
- tbody tr td:nth-child(#{$i}) {
545
- border-right: var(--matcha-border-hairline, 1px) solid var(--matcha-color-surface-border);
546
- }
547
- }
548
-
549
- &[data-sticky-end="#{$i}"]:not([sticky-header]):not(matcha-table[sticky-header] &) {
550
- thead th:nth-last-child(-n+#{$i}),
551
- tbody tr td:nth-last-child(-n+#{$i}) {
552
- position: sticky;
553
- z-index: 2;
554
- background-color: var(--matcha-color-surface-card);
555
- }
556
- thead th:nth-last-child(#{$i}),
557
- tbody tr td:nth-last-child(#{$i}) {
558
- border-left: var(--matcha-border-hairline, 1px) solid var(--matcha-color-surface-border);
559
- }
560
- }
561
- }
562
-
563
- &[data-sticky-start]:not([sticky-header]):not(matcha-table[sticky-header] &) thead th:first-child,
564
- &[data-sticky-start]:not([sticky-header]):not(matcha-table[sticky-header] &) tbody tr td:first-child { left: 0; }
565
- &[data-sticky-end]:not([sticky-header]):not(matcha-table[sticky-header] &) thead th:last-child,
566
- &[data-sticky-end]:not([sticky-header]):not(matcha-table[sticky-header] &) tbody tr td:last-child { right: 0; }
567
-
568
- // ── Variant accent (semantic row coloring) ────────────────────────────
569
- // Defaults canon Figma DSV3: equal=info, plus=success, minus=danger.
570
- // Accent vive no <tr> → NÃO some com stickyStart.
571
- // `position: relative` em <tr> suportado em Chrome 80+, FF 87+, Safari 14+.
572
- tr.equal { --matcha-table-row-accent-color: var(--matcha-color-info); }
573
- tr.plus { --matcha-table-row-accent-color: var(--matcha-color-success); }
574
- tr.minus { --matcha-table-row-accent-color: var(--matcha-color-danger); }
575
-
576
- tbody tr[data-variant],
577
- tbody tr.equal,
578
- tbody tr.plus,
579
- tbody tr.minus {
580
- position: relative;
581
-
582
- &::before {
583
- content: '';
584
- position: absolute;
585
- top: 0;
586
- left: 0;
587
- width: var(--matcha-table-variant-accent-width);
588
- height: 100%;
589
- background: var(--matcha-table-row-accent-color, transparent);
590
- pointer-events: none;
591
- z-index: 1; // acima do bg da cell, abaixo do sticky shadow
592
- }
593
- }
594
-
595
- // Child row (indent + accent esmaecido) — pattern tree/hierarquia.
596
- tbody tr.child-item::before {
597
- opacity: var(--matcha-table-row-accent-child-opacity);
598
- }
599
- tbody tr.child-item td:first-child {
600
- padding-left: calc(var(--matcha-table-cell-padding-x-left) +
601
- var(--matcha-space-200, 16px));
602
- }
603
-
604
- // ── Loading state global — pulse + aria-busy ─────────────────────────
605
- &[loading] tbody tr,
606
- matcha-table[loading] & tbody tr {
607
- animation: matcha-table-loading-pulse
608
- var(--matcha-table-loading-pulse-duration)
609
- ease-in-out infinite;
610
- pointer-events: none;
611
- }
612
-
613
- // ── Disabled state — row/cell ─────────────────────────────────────────
614
- // Pattern canon Material/Polaris. A11y: também aplicar aria-disabled.
615
- tbody tr[disabled], tbody tr.is-disabled,
616
- tbody td[disabled], tbody td.is-disabled {
617
- opacity: var(--matcha-table-disabled-opacity);
618
- pointer-events: none;
619
- cursor: not-allowed;
620
- }
621
-
622
- // ── Per-row loading (matcha-table-loading directive) ─────────────────
623
- // Pulse só naquela row — optimistic save/delete sem bloquear a tabela.
624
- tbody tr.is-loading {
625
- animation: matcha-table-loading-pulse
626
- var(--matcha-table-loading-pulse-duration)
627
- ease-in-out infinite;
628
- pointer-events: none;
629
- }
630
-
631
- // ── Empty state row (matcha-table-empty directive) ────────────────────
632
- // Container pra <matcha-empty-state>. Directive seta colspan automático.
633
- tbody tr.is-empty td {
634
- padding: var(--matcha-table-empty-padding-y) var(--matcha-table-empty-padding-x);
635
- text-align: center;
636
- background: var(--matcha-table-cell-bg);
637
- }
638
-
639
- // ── Resizable column (matcha-table-resizable directive) ───────────────
640
- // Handle invisível na borda direita. Hover mostra cursor + barra discreta.
641
- thead th.is-resizable {
642
- position: relative;
643
-
644
- &::before {
645
- content: '';
646
- position: absolute;
647
- top: var(--matcha-table-resize-indicator-top);
648
- right: 0;
649
- width: var(--matcha-table-resize-indicator-width);
650
- height: var(--matcha-table-resize-indicator-height);
651
- background: var(--matcha-table-border-color);
652
- opacity: 0;
653
- transition: opacity var(--matcha-table-transition-duration) linear;
654
- }
655
- &:hover::before { opacity: 1; }
656
- }
657
- thead th.is-resizable::after {
658
- // Override do ::after do sort-indicator: handle de resize ocupa a borda.
659
- // Col sortable + resizable é raro — consumer escolhe uma.
660
- content: '';
661
- position: absolute;
662
- top: 0;
663
- right: calc(-1 * var(--matcha-table-resize-handle-width) / 2);
664
- width: var(--matcha-table-resize-handle-width);
665
- height: 100%;
666
- cursor: col-resize;
667
- z-index: 3;
668
- background: transparent;
669
- mask: none;
670
- }
671
- }
672
-
673
- // Sticky header — forma ELEMENTO (`<matcha-table>`, ver table.component.ts).
674
- // Aqui quem rola de verdade é `.matcha-table__scroll` (o wrapper), não a
675
- // `<table class="matcha-table">` interna — a altura máxima precisa ir no
676
- // wrapper. `sticky-header` mora no HOST `<matcha-table>` (@HostBinding,
677
- // não mais na `<table>` filha) — como o host é ANCESTRAL do wrapper, um
678
- // descendente simples já alcança, sem precisar de `:has()`.
679
- matcha-table[sticky-header] > .matcha-table__scroll {
680
- max-height: var(--matcha-table-sticky-header-max-height, 70vh);
681
- overflow-y: auto;
682
- }
683
-
684
- // roundedTop — regra TOP-LEVEL (fora do bloco .matcha-table acima): alcança
685
- // o host na forma legado (self, `.matcha-table[rounded-top]`, o próprio
686
- // `<table>` é a superfície visual) e o wrapper de scroll na forma elemento
687
- // (descendente a partir do host `<matcha-table rounded-top>` — só o
688
- // wrapper precisa do radius, ele já clipa a `<table>` interna via
689
- // overflow non-visible, a `<table>` em si não precisa do próprio radius).
690
- //
691
- // Fallback inline no 2º var() é OBRIGATÓRIO: --matcha-table-rounded-top-radius
692
- // é declarada dentro do bloco `.matcha-table { }` acima — na forma elemento
693
- // `.matcha-table__scroll` é PAI de `.matcha-table` (o wrapper envolve a
694
- // table real), não descendente. CSS custom property só desce a árvore, não
695
- // sobe — sem o fallback aqui a var nunca resolve nesse elemento (bug
696
- // silencioso: sem erro, só o border-radius nunca aparece). O fallback ainda
697
- // respeita um override do consumer, se ele setar a var em algum ancestral
698
- // real do wrapper (ex.: no próprio host `<matcha-table>` ou em `:root`).
699
- .matcha-table[rounded-top] {
700
- border-top-left-radius: var(--matcha-table-rounded-top-radius);
701
- border-top-right-radius: var(--matcha-table-rounded-top-radius);
702
- }
703
- matcha-table[rounded-top] .matcha-table__scroll {
704
- border-top-left-radius: var(--matcha-table-rounded-top-radius, var(--matcha-radius-xl, 16px));
705
- border-top-right-radius: var(--matcha-table-rounded-top-radius, var(--matcha-radius-xl, 16px));
706
- }
707
-
708
- @keyframes matcha-table-loading-pulse {
709
- 0%, 100% { opacity: 1; }
710
- 50% { opacity: var(--matcha-table-loading-pulse-opacity-min); }
711
- }
712
-
713
- // ── Reduced motion ────────────────────────────────────────────────────
714
- @media (prefers-reduced-motion: reduce) {
715
- .matcha-table tbody td,
716
- .matcha-table[loading] tbody tr,
717
- matcha-table[loading] .matcha-table tbody tr {
718
- transition: none;
719
- animation: none;
720
- }
721
- }
722
- }
1
+ @use "sass:map";
2
+
3
+ // =============================================================================
4
+ // matcha-table — `<table matcha-table>` directive (1:1 Figma DSV3 15641:275)
5
+ //
6
+ // API canon:
7
+ // <table matcha-table size="comfortable" striped hover bordered stickyHeader
8
+ // [stickyStart]="N" [stickyEnd]="N" [loading]="isLoading">
9
+ // <thead><tr><th>...</th></tr></thead>
10
+ // <tbody><tr><td>...</td></tr></tbody>
11
+ // </table>
12
+ //
13
+ // Companion directives (a11y automática):
14
+ // [matcha-table-sortable] em <th> — aria-sort + tabindex + keyboard
15
+ // [rowClick] em <tr> — AÇÃO na linha (abrir modal, selecionar). Só o
16
+ // binding já ativa tudo (tabindex + Enter/Space
17
+ // + hover accent na linha inteira)
18
+ //
19
+ // Navegação (leva pra outra página) NÃO é [rowClick]:
20
+ // <a matcha-cell-link [routerLink]> dentro do <td> — âncora de verdade, então
21
+ // ctrl+clique/clique do meio/botão direito funcionam e o resto da linha segue
22
+ // selecionável. Ponteiro e tom accent aparecem só sobre a âncora, pintando a linha
23
+ // inteira. Os dois no mesmo <tr> é proibido pelo canon (dois hovers competindo).
24
+ //
25
+ // Helpers CSS-only:
26
+ // td.numeric → tabular-nums
27
+ // td.truncate → ellipsis (text overflow)
28
+ // td[data-align] → alinhamento (data-alinhamento / td.text-{center,right} deprecated)
29
+ // tr.equal/.plus/.minus → variant semantic accent (cor via CSS var)
30
+ // tr.child-item → indent + accent esmaecido
31
+ // tr.is-selected → highlight
32
+ // =============================================================================
33
+
34
+ @mixin matcha-table-modern($theme) {
35
+
36
+ // ═══════════════════════════════════════════════════════════════════════
37
+ // CSS vars do componente — única fonte de verdade. ZERO literal hardcoded
38
+ // fora deste bloco. Consumer override qualquer um no escopo do <table>.
39
+ // ═══════════════════════════════════════════════════════════════════════
40
+ .matcha-table {
41
+ // ── Dimensions ────────────────────────────────────────────────────────
42
+ // Calibrado pra density moderna (Stripe/Linear/Notion). Comfortable era 52
43
+ // antes — apertado em telas wide. 58px equilibra densidade × respiro.
44
+ --matcha-table-cell-height-compact: var(--matcha-space-500, 40px);
45
+ --matcha-table-cell-height-comfortable: 58px;
46
+ --matcha-table-cell-height-spacious: var(--matcha-space-800, 64px);
47
+ // Padding uniforme L/R — assimétrico cria sensação de "primeira coluna
48
+ // afastada do resto". 16px ambos lados é canon moderno.
49
+ --matcha-table-cell-padding-x-left: var(--matcha-space-200, 16px);
50
+ --matcha-table-cell-padding-x-right: var(--matcha-space-200, 16px);
51
+ --matcha-table-cell-min-width: var(--matcha-space-1500, 120px);
52
+ --matcha-table-paginator-spacing: var(--matcha-space-300, 24px);
53
+
54
+ // ── Typography (modern density Stripe/Linear) ─────────────────────────
55
+ --matcha-table-font-size: 13px;
56
+ --matcha-table-font-line-height: 18px;
57
+ --matcha-table-header-font-size: var(--matcha-text-body-sm, 12px);
58
+ --matcha-table-header-font-line-height: var(--matcha-leading-body-sm, 16px);
59
+ --matcha-table-header-letter-spacing: 0.04em;
60
+
61
+ // ── Borders / radius ──────────────────────────────────────────────────
62
+ --matcha-table-border-color: var(--matcha-color-interaction-disabled-border);
63
+ --matcha-table-border-header: var(--matcha-border-thin, 2px);
64
+ --matcha-table-border-row: var(--matcha-border-hairline, 1px);
65
+ --matcha-table-bordered-radius: var(--matcha-radius-md, 4px);
66
+ // Escala de card (não a mesma de --matcha-table-bordered-radius, que é só
67
+ // pro inset border fino) — roundedTop arredonda o CONTAINER inteiro.
68
+ --matcha-table-rounded-top-radius: var(--matcha-radius-xl, 16px);
69
+
70
+ // ── Surface colors ────────────────────────────────────────────────────
71
+ --matcha-table-cell-bg: var(--matcha-color-surface-card);
72
+ --matcha-table-row-bg-selected: var(--matcha-color-accent-alpha-12,
73
+ rgba(var(--matcha-color-accent), 0.12));
74
+ --matcha-table-row-bg-striped: var(--matcha-color-surface-card-alt,
75
+ rgba(var(--matcha-color-fg), 0.02));
76
+ // Hover NÃO usa surface-table-row-hover token (tá próximo demais de surface-card
77
+ // em dark e fica imperceptível). Calibrado pra delta visível mesmo sobre stripe.
78
+ // CSS custom property values são TEXTO LITERAL pro Sass — fn calls Sass
79
+ // dentro NÃO são avaliadas. Precisa interpolação `#{}` pra forçar Sass a
80
+ // evaluar `var(--matcha-color-fg)` ANTES do output. Sem isso, o valor sai
81
+ // literal no bundle (ex: `rgba(var(--matcha-color-fg), 0.12)`) e o browser
82
+ // descarta como invalid CSS (= chevron invisível, hover sem cor, etc).
83
+ --matcha-table-row-bg-hover: rgba(var(--matcha-color-fg), 0.12);
84
+ --matcha-table-row-bg-hover-striped: rgba(var(--matcha-color-fg), 0.18);
85
+ --matcha-table-header-bg-hover: rgba(var(--matcha-color-fg), 0.10);
86
+ // Hover da linha que NAVEGA: tom accent, disparado só quando o ponteiro está
87
+ // sobre a âncora da célula (ver "Row navegável" adiante). Fora dela a linha usa
88
+ // o hover neutro acima — é esse contraste que diz ao usuário onde clicar.
89
+ --matcha-table-row-bg-hover-accent: var(--matcha-color-accent-surface-hover);
90
+
91
+ // ── Sort indicator colors ─────────────────────────────────────────────
92
+ // Inactive usa alpha 0.45 no foreground — claramente VISÍVEL como "ali tem
93
+ // sort", sem competir com texto do header. Hover/ativo passa pra accent full.
94
+ --matcha-table-sort-icon-color: rgba(var(--matcha-color-fg), 0.45);
95
+ --matcha-table-sort-icon-color-active: var(--matcha-color-accent);
96
+ --matcha-table-sort-icon-size: var(--matcha-space-250, 20px);
97
+
98
+ // ── Empty cell placeholder ────────────────────────────────────────────
99
+ --matcha-table-empty-cell-text: var(--matcha-color-text-muted,
100
+ rgba(var(--matcha-color-fg), 0.5));
101
+
102
+ // ── Variant accent (semantic row) ─────────────────────────────────────
103
+ --matcha-table-variant-accent-width: var(--matcha-space-050, 4px);
104
+ --matcha-table-row-accent-child-opacity: 0.5;
105
+ --matcha-table-selected-accent-width: var(--matcha-border-thin, 2px);
106
+ --matcha-table-selected-accent-color: var(--matcha-color-accent);
107
+
108
+ // ── Column width hints (utility classes col-*) ────────────────────────
109
+ --matcha-table-col-checkbox-width: var(--matcha-space-600, 48px);
110
+ --matcha-table-col-id-width: var(--matcha-space-1000, 80px);
111
+ --matcha-table-col-narrow-width: var(--matcha-space-1500, 120px);
112
+ --matcha-table-col-medium-width: var(--matcha-space-2500, 200px);
113
+ // Pino da coluna de Ações (matcha-cell-action) — mesma escala de
114
+ // --matcha-table-col-id-width (80px), token próprio pra não acoplar
115
+ // semanticamente coisas diferentes que hoje só coincidem em valor.
116
+ --matcha-table-col-actions-width: var(--matcha-space-1000, 80px);
117
+
118
+ // ── States / animations ───────────────────────────────────────────────
119
+ --matcha-table-disabled-opacity: var(--matcha-state-disabled-opacity, 0.5);
120
+ --matcha-table-loading-pulse-duration: var(--matcha-duration-slow, 1500ms);
121
+ --matcha-table-loading-pulse-opacity-min: 0.5;
122
+ --matcha-table-transition-duration: var(--matcha-duration-instant, 80ms);
123
+ --matcha-table-focus-ring-width: var(--matcha-state-focus-ring-width, 2px);
124
+ --matcha-table-focus-ring-color: var(--matcha-color-accent);
125
+
126
+ // ── Empty state row (matcha-table-empty directive) ────────────────────
127
+ --matcha-table-empty-padding-y: var(--matcha-space-500, 40px);
128
+ --matcha-table-empty-padding-x: var(--matcha-space-300, 24px);
129
+
130
+ // ── Resize handle (matcha-table-resizable directive) ──────────────────
131
+ --matcha-table-resize-handle-width: var(--matcha-space-100, 8px);
132
+ --matcha-table-resize-indicator-width: var(--matcha-border-hairline, 1px);
133
+ --matcha-table-resize-indicator-height: 50%;
134
+ --matcha-table-resize-indicator-top: 25%;
135
+ }
136
+
137
+ // ── Base — display table nativo + typography via vars ─────────────────
138
+ .matcha-table {
139
+ display: table;
140
+ width: 100%;
141
+ // separate (não collapse) + spacing 0 → visualmente idêntico (zero gap
142
+ // entre cells), mas `border-collapse: collapse` é conhecido por quebrar
143
+ // `position: sticky` em <th>/<td> em vários browsers — especialmente
144
+ // quando a cell precisa ficar sticky nos DOIS eixos ao mesmo tempo (linha
145
+ // + coluna, o canto stickyHeader × stickyStart/stickyEnd). Só existe
146
+ // border-bottom no arquivo (nenhum border-top) — sem risco de borda dupla.
147
+ border-collapse: separate;
148
+ border-spacing: 0;
149
+ font-family: var(--matcha-font-family);
150
+ font-size: var(--matcha-table-font-size);
151
+ line-height: var(--matcha-table-font-line-height);
152
+ font-weight: var(--matcha-weight-regular, 400);
153
+ color: var(--matcha-color-text-secondary,
154
+ color-mix(in srgb, var(--matcha-color-fg) 70%, transparent));
155
+
156
+ // ── Header cells (text/primary, border-bottom 2px, height 64) ────────
157
+ thead th {
158
+ box-sizing: border-box;
159
+ height: var(--matcha-table-cell-height-comfortable);
160
+ min-width: var(--matcha-table-cell-min-width);
161
+ padding: 0
162
+ var(--matcha-table-cell-padding-x-right)
163
+ 0
164
+ var(--matcha-table-cell-padding-x-left);
165
+ background: var(--matcha-table-cell-bg);
166
+ color: var(--matcha-color-text-primary,
167
+ var(--matcha-color-fg));
168
+ // Header menor que body — hierarquia "metadado" vs "conteúdo".
169
+ font-size: var(--matcha-table-header-font-size);
170
+ line-height: var(--matcha-table-header-font-line-height);
171
+ font-weight: var(--matcha-weight-semibold, 600);
172
+ letter-spacing: var(--matcha-table-header-letter-spacing);
173
+ text-align: left;
174
+ vertical-align: middle;
175
+ border-bottom: var(--matcha-table-border-header) solid
176
+ var(--matcha-table-border-color);
177
+
178
+ // Alinhamento — data-align é o canon DSV3; data-alinhamento + utility
179
+ // classes seguem aceitos (deprecated, remoção em ciclo futuro)
180
+ &[data-align="center"], &[data-alinhamento="center"], &.text-center { text-align: center; }
181
+ &[data-align="right"], &[data-alinhamento="right"], &.text-right { text-align: right; }
182
+
183
+ // ── Sortable hint ([matcha-table-sortable] directive seta os attrs) ──
184
+ &[data-sortable] {
185
+ cursor: pointer;
186
+ user-select: none;
187
+ // SEMPRE reserva espaço pro chevron (mesmo inativo) — chevron entra/sai
188
+ // só via opacity. Sem padding condicional → texto NÃO desloca no hover.
189
+ padding-right: calc(var(--matcha-table-cell-padding-x-right) +
190
+ var(--matcha-table-sort-icon-size));
191
+ position: relative;
192
+
193
+ // Hover NÃO muda bg do header — bg-change era o que fazia o user
194
+ // perceber "movimento" quando o chevron entrava ao mesmo tempo.
195
+ // Manter header estático no hover, só chevron aparece com opacity.
196
+ // Cursor pointer + chevron fade já comunicam interatividade.
197
+
198
+ // Sort indicators — SVG 1:1 com `matcha/icon/arrow` (figma-icons.ts).
199
+ // Estado inativo (none) = INVISÍVEL (opacity 0). Aparece no hover do header
200
+ // com opacity faded, full quando ativo (asc/desc). Pattern canon Stripe/Linear:
201
+ // sortable em todas as colunas, mas indicador só na que tá ordenando.
202
+ &::after {
203
+ content: '';
204
+ position: absolute;
205
+ right: var(--matcha-table-cell-padding-x-right);
206
+ top: 50%;
207
+ transform: translateY(-50%);
208
+ width: var(--matcha-table-sort-icon-size);
209
+ height: var(--matcha-table-sort-icon-size);
210
+ background: var(--matcha-table-sort-icon-color);
211
+ transition: background-color var(--matcha-table-transition-duration) linear;
212
+ mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 7.82499L6.1 12.725C5.9 12.925 5.66667 13.0208 5.4 13.0125C5.13333 13.0042 4.9 12.9 4.7 12.7C4.51667 12.5 4.42083 12.2667 4.4125 12C4.40417 11.7333 4.5 11.5 4.7 11.3L11.3 4.69999C11.4 4.59999 11.5083 4.52915 11.625 4.48749C11.7417 4.44582 11.8667 4.42499 12 4.42499C12.1333 4.42499 12.2583 4.44582 12.375 4.48749C12.4917 4.52915 12.6 4.59999 12.7 4.69999L19.3 11.3C19.4833 11.4833 19.575 11.7125 19.575 11.9875C19.575 12.2625 19.4833 12.5 19.3 12.7C19.1 12.9 18.8625 13 18.5875 13C18.3125 13 18.075 12.9 17.875 12.7L13 7.82499V19C13 19.2833 12.9042 19.5208 12.7125 19.7125C12.5208 19.9042 12.2833 20 12 20C11.7167 20 11.4792 19.9042 11.2875 19.7125C11.0958 19.5208 11 19.2833 11 19V7.82499Z'/></svg>")
213
+ center/contain no-repeat;
214
+ }
215
+
216
+ // Hover no header — chevron escurece pra foreground full (já visível
217
+ // muted antes via alpha 0.45). Reforça que tá interativo.
218
+ &:hover::after {
219
+ background: var(--matcha-color-text-primary,
220
+ var(--matcha-color-fg));
221
+ }
222
+
223
+ // Padding-right SEMPRE reserva espaço pro chevron. Antes ajustava
224
+ // condicionalmente (sem padding extra no inativo) — causava text
225
+ // "dançar" 20px ao hover quando padding crescia pra acomodar arrow.
226
+ // Mantém estável: chevron entra via opacity, sem reflow do texto.
227
+
228
+ &[data-sort-direction="asc"]::after {
229
+ mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 7.82499L6.1 12.725C5.9 12.925 5.66667 13.0208 5.4 13.0125C5.13333 13.0042 4.9 12.9 4.7 12.7C4.51667 12.5 4.42083 12.2667 4.4125 12C4.40417 11.7333 4.5 11.5 4.7 11.3L11.3 4.69999C11.4 4.59999 11.5083 4.52915 11.625 4.48749C11.7417 4.44582 11.8667 4.42499 12 4.42499C12.1333 4.42499 12.2583 4.44582 12.375 4.48749C12.4917 4.52915 12.6 4.59999 12.7 4.69999L19.3 11.3C19.4833 11.4833 19.575 11.7125 19.575 11.9875C19.575 12.2625 19.4833 12.5 19.3 12.7C19.1 12.9 18.8625 13 18.5875 13C18.3125 13 18.075 12.9 17.875 12.7L13 7.82499V19C13 19.2833 12.9042 19.5208 12.7125 19.7125C12.5208 19.9042 12.2833 20 12 20C11.7167 20 11.4792 19.9042 11.2875 19.7125C11.0958 19.5208 11 19.2833 11 19V7.82499Z'/></svg>")
230
+ center/contain no-repeat;
231
+ background: var(--matcha-table-sort-icon-color-active);
232
+ }
233
+
234
+ &[data-sort-direction="desc"]::after {
235
+ mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 16.175V5C11 4.71667 11.0958 4.47917 11.2875 4.2875C11.4792 4.09583 11.7167 4 12 4C12.2833 4 12.5208 4.09583 12.7125 4.2875C12.9042 4.47917 13 4.71667 13 5V16.175L17.9 11.275C18.1 11.075 18.3333 10.9792 18.6 10.9875C18.8667 10.9958 19.1 11.1 19.3 11.3C19.4833 11.5 19.5792 11.7333 19.5875 12C19.5958 12.2667 19.5 12.5 19.3 12.7L12.7 19.3C12.6 19.4 12.4917 19.4708 12.375 19.5125C12.2583 19.5542 12.1333 19.575 12 19.575C11.8667 19.575 11.7417 19.5542 11.625 19.5125C11.5083 19.4708 11.4 19.4 11.3 19.3L4.7 12.7C4.51667 12.5167 4.425 12.2875 4.425 12.0125C4.425 11.7375 4.51667 11.5 4.7 11.3C4.9 11.1 5.1375 11 5.4125 11C5.6875 11 5.925 11.1 6.125 11.3L11 16.175Z'/></svg>")
236
+ center/contain no-repeat;
237
+ background: var(--matcha-table-sort-icon-color-active);
238
+ }
239
+
240
+ // Header right-aligned com sort — chevron vem ANTES do texto (canon).
241
+ &[data-align="right"], &[data-alinhamento="right"], &.text-right {
242
+ padding-right: var(--matcha-table-cell-padding-x-right);
243
+ padding-left: calc(var(--matcha-table-cell-padding-x-left) +
244
+ var(--matcha-table-sort-icon-size));
245
+
246
+ &::after {
247
+ right: auto;
248
+ left: var(--matcha-table-cell-padding-x-left);
249
+ }
250
+ }
251
+
252
+ // Focus ring quando keyboard-navigated (tabindex via directive).
253
+ &:focus-visible {
254
+ outline: var(--matcha-table-focus-ring-width) solid
255
+ var(--matcha-table-focus-ring-color);
256
+ outline-offset: calc(-1 * var(--matcha-table-focus-ring-width));
257
+ }
258
+ }
259
+ }
260
+
261
+ // ── Body cells ────────────────────────────────────────────────────────
262
+ tbody td {
263
+ box-sizing: border-box;
264
+ height: var(--matcha-table-cell-height-comfortable);
265
+ min-width: var(--matcha-table-cell-min-width);
266
+ padding: 0
267
+ var(--matcha-table-cell-padding-x-right)
268
+ 0
269
+ var(--matcha-table-cell-padding-x-left);
270
+ background: var(--matcha-table-cell-bg);
271
+ color: var(--matcha-color-text-secondary,
272
+ color-mix(in srgb, var(--matcha-color-fg) 70%, transparent));
273
+ vertical-align: middle;
274
+ border-bottom: var(--matcha-table-border-row) solid
275
+ var(--matcha-table-border-color);
276
+ transition: background-color var(--matcha-table-transition-duration) linear;
277
+
278
+ &[data-align="center"], &[data-alinhamento="center"], &.text-center { text-align: center; }
279
+ &[data-align="right"], &[data-alinhamento="right"], &.text-right { text-align: right; }
280
+
281
+ // Helper: tabular-nums (alinha decimais)
282
+ &[data-numeric], &.numeric {
283
+ font-variant-numeric: tabular-nums;
284
+ font-feature-settings: 'tnum';
285
+ }
286
+
287
+ // Helper: ellipsis (texto longo)
288
+ &[data-truncate], &.truncate {
289
+ max-width: 0;
290
+ white-space: nowrap;
291
+ overflow: hidden;
292
+ text-overflow: ellipsis;
293
+ }
294
+
295
+ // Helper: cells vazias preenchidas com "—" muted (canon moderno).
296
+ &:empty::after {
297
+ content: '—';
298
+ color: var(--matcha-table-empty-cell-text);
299
+ }
300
+ }
301
+
302
+ // ── Column width hints (aplicar em <th> — <td> herda pela coluna) ─────
303
+ thead th,
304
+ tbody td {
305
+ // min-width igual ao width — sem isso, a regra base thead th/tbody td
306
+ // (min-width:120px) vence e a coluna nunca encolhe pro valor pretendido
307
+ // (mesmo bug achado e corrigido em .col-actions).
308
+ &.col-checkbox { width: var(--matcha-table-col-checkbox-width); min-width: var(--matcha-table-col-checkbox-width); text-align: center; }
309
+ &.col-id { width: var(--matcha-table-col-id-width); }
310
+ &.col-narrow { width: var(--matcha-table-col-narrow-width); }
311
+ &.col-medium { width: var(--matcha-table-col-medium-width); }
312
+ // Actions / icon-buttons — pinada em --matcha-table-col-actions-width (80px).
313
+ // Children inline-flex, centralizados (título "Ações" no header também
314
+ // centraliza — text-align: center vale pro conteúdo das duas linhas).
315
+ //
316
+ // Importante: `.matcha-button` tem `display: flex` (block-level) que faria
317
+ // os icon-buttons empilharem verticalmente. Override via selector descendant
318
+ // com specificity maior pra forçar inline-flex no contexto de actions cell.
319
+ &.col-actions {
320
+ width: var(--matcha-table-col-actions-width);
321
+ // min-width base da tbody td/thead th é 120px (--matcha-table-cell-min-width)
322
+ // — maior que os 80px daqui, e min-width sempre vence sobre width no box
323
+ // model. Sem este override, a coluna ficava presa em 120px (achado real).
324
+ min-width: var(--matcha-table-col-actions-width);
325
+ white-space: nowrap;
326
+ text-align: center;
327
+
328
+ > .matcha-button,
329
+ > button[matcha-icon-button],
330
+ > * {
331
+ display: inline-flex;
332
+ vertical-align: middle;
333
+ }
334
+ > * + * {
335
+ margin-left: var(--matcha-space-050, 4px);
336
+ }
337
+ }
338
+ // Greedy = espaço residual (default, classe explícita pra documentação).
339
+ &.col-greedy { width: auto; }
340
+ }
341
+
342
+ // ── Paginator alinhado após table (convention) ────────────────────────
343
+ // Sem margin-top — paginator gruda no border-bottom da última row.
344
+ // Spacing visual vem dos paddings internos do próprio matcha-paginator.
345
+ .matcha-table-paginator,
346
+ & + matcha-paginator {
347
+ display: block;
348
+ margin-top: 0;
349
+ }
350
+
351
+ // ── Row clickable ([rowClick] directive — só o binding já ativa tudo) ─
352
+ tbody tr.matcha-row-clickable {
353
+ cursor: pointer;
354
+
355
+ &:hover td {
356
+ --matcha-table-cell-bg: var(--matcha-table-row-bg-hover);
357
+ }
358
+
359
+ &:focus-visible {
360
+ outline: var(--matcha-table-focus-ring-width) solid
361
+ var(--matcha-table-focus-ring-color);
362
+ outline-offset: calc(-1 * var(--matcha-table-focus-ring-width));
363
+ }
364
+ }
365
+
366
+ // ── Modifiers via atributo no host (Angular @HostBinding, ver
367
+ // table.component.ts) ──────────────────────────────────────────────────
368
+ // Cada modifier tem 2 seletores alternativos: `&[x]` (forma legado — o
369
+ // host JÁ É `.matcha-table`, self) e `matcha-table[x] &` (forma elemento
370
+ // — o atributo mora no host `<matcha-table>`, mas o alvo real é o filho
371
+ // `.matcha-table` dentro do wrapper; CSS alcança via descendente, não
372
+ // precisa o atributo estar duplicado no filho).
373
+ // Striped usa :where() pra dropar specificity do :nth-child — hover vence.
374
+ &[striped] tbody tr:where(:nth-child(even)) td,
375
+ matcha-table[striped] & tbody tr:where(:nth-child(even)) td {
376
+ --matcha-table-cell-bg: var(--matcha-table-row-bg-striped);
377
+ }
378
+
379
+ &[hover] tbody tr:hover td,
380
+ matcha-table[hover] & tbody tr:hover td {
381
+ --matcha-table-cell-bg: var(--matcha-table-row-bg-hover);
382
+ }
383
+
384
+ // Stripe-aware hover: row PAR ganha alpha mais forte pra preservar stripe.
385
+ &[striped] tbody tr:where(:nth-child(even)).matcha-row-clickable:hover td,
386
+ matcha-table[striped] & tbody tr:where(:nth-child(even)).matcha-row-clickable:hover td,
387
+ &[striped][hover] tbody tr:where(:nth-child(even)):hover td,
388
+ matcha-table[striped][hover] & tbody tr:where(:nth-child(even)):hover td {
389
+ --matcha-table-cell-bg: var(--matcha-table-row-bg-hover-striped);
390
+ }
391
+
392
+ // Hover accent de linha clicável: a diretiva [rowClick] já sobrescreve
393
+ // --matcha-table-row-bg-hover(-striped) inline no próprio <tr> (ver
394
+ // table-clickable-row.directive.ts) — nenhuma regra de tema necessária
395
+ // aqui, o estilo inline já vence qualquer especificidade.
396
+
397
+ // ── Row navegável (`<a matcha-cell-link>` na célula) ───────────────────
398
+ // A linha que LEVA PRA OUTRA PÁGINA não é clicável inteira: quem navega é uma
399
+ // âncora de verdade na célula, pra que ctrl+clique, clique do meio e o menu do
400
+ // botão direito funcionem — e pra que o resto da linha continue selecionável
401
+ // com o mouse (com a linha toda clicável, não dava pra copiar texto da tabela).
402
+ //
403
+ // O feedback visual é o inverso do rowClick: ponteiro e tom accent aparecem só
404
+ // quando o mouse está sobre a âncora, e aí a linha INTEIRA pinta. Fora dela vale
405
+ // o hover neutro de sempre.
406
+ //
407
+ // Pinta via --matcha-table-cell-bg em TODOS os td da linha (mesmo caminho do
408
+ // hover normal), não com background-color no <tr>: em tabela striped o fundo
409
+ // mora no td, e um background no tr ficaria por baixo dele — o resultado seria
410
+ // uma célula colorida e o resto não.
411
+ //
412
+ // Depois das regras de striped de propósito: mesma especificidade, e ordem de
413
+ // origem decide. `:has()` já é usado em ~10 componentes do tema.
414
+ // O estilo BASE da âncora é global (base/_reset.scss) — ela também aparece em
415
+ // card de listagem, fora de qualquer tabela. Aqui fica só o que é da tabela: o
416
+ // realce da linha inteira.
417
+ a[matcha-cell-link]:focus-visible {
418
+ outline: var(--matcha-table-focus-ring-width) solid
419
+ var(--matcha-table-focus-ring-color);
420
+ }
421
+
422
+ tbody tr:has(a[matcha-cell-link]:hover) td,
423
+ tbody tr:has(a[matcha-cell-link]:focus-visible) td {
424
+ --matcha-table-cell-bg: var(--matcha-table-row-bg-hover-accent);
425
+ }
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
+
454
+ tbody tr.is-selected td {
455
+ --matcha-table-cell-bg: var(--matcha-table-row-bg-selected);
456
+ }
457
+ // Selected row ganha border-left accent — affordance canon Linear/Notion.
458
+ // No <tr> → não some com stickyStart. Usa ::after pra coexistir com
459
+ // ::before do variant accent sem conflito.
460
+ tbody tr.is-selected {
461
+ position: relative;
462
+
463
+ &::after {
464
+ content: '';
465
+ position: absolute;
466
+ top: 0;
467
+ left: 0;
468
+ width: var(--matcha-table-selected-accent-width);
469
+ height: 100%;
470
+ background: var(--matcha-table-selected-accent-color);
471
+ pointer-events: none;
472
+ z-index: 2; // acima do variant accent (z-index 1)
473
+ }
474
+ }
475
+
476
+ // Bordered — box-shadow inset em vez de border real (não clipa shadows
477
+ // externos das sticky cols, que projetam pra fora da <table>).
478
+ // Conhecido: em alguns casos (sticky-header/colunas fixas, forma
479
+ // elemento) o background opaco das cells pode encobrir parte deste
480
+ // traço — não resolvido nesta rodada (fora do escopo desta tarefa); se
481
+ // precisar de borda garantida num caso desses, use a utility class de
482
+ // borda (ex.: `b-1`) direto no consumo do componente.
483
+ &[bordered] {
484
+ box-shadow: inset 0 0 0 var(--matcha-table-border-row)
485
+ var(--matcha-table-border-color);
486
+ border-radius: var(--matcha-table-bordered-radius);
487
+ }
488
+
489
+ // Sticky header (vertical scroll). Forma legado: o host (a própria table) é
490
+ // o container de scroll. Default do componente é overflow-y:hidden (só
491
+ // horizontal é fallback passivo — dois scrolls na tela atrapalham mobile);
492
+ // stickyHeader é opt-in EXPLÍCITO, reabilita overflow-y:auto de propósito
493
+ // + altura máxima (sem isso não há o que rolar, o header não tem contra o
494
+ // que grudar). Altura configurável via var (default 70vh).
495
+ &[sticky-header],
496
+ matcha-table[sticky-header] & {
497
+ max-height: var(--matcha-table-sticky-header-max-height, 70vh);
498
+ overflow-y: auto;
499
+ }
500
+ &[sticky-header] thead th,
501
+ matcha-table[sticky-header] & thead th {
502
+ position: sticky;
503
+ top: 0;
504
+ z-index: 1;
505
+ }
506
+
507
+ // Size variants (data-size cascateia altura)
508
+ &[data-size="compact"] thead th,
509
+ &[data-size="compact"] tbody td { height: var(--matcha-table-cell-height-compact); }
510
+ &[data-size="spacious"] thead th,
511
+ &[data-size="spacious"] tbody td { height: var(--matcha-table-cell-height-spacious); }
512
+
513
+ // ── Sticky columns (start/end) — canon scroll horizontal ──────────────
514
+ // Performance: nada de box-shadow com blur nem background-image (gradient)
515
+ // aqui — são caros de repaint/compositar MULTIPLICADOS por linha (uma
516
+ // tabela de 40+ linhas com 2 colunas sticky = 80 elementos), e causavam
517
+ // queda de FPS real no scroll (medido em produção). Custo mínimo:
518
+ // background-color sólido (fixo, não acompanha hover/tint — troca-se
519
+ // fidelidade visual por performance) + border 1px sólida no lugar da
520
+ // sombra de scroll-edge (borda é essencialmente grátis pro compositor,
521
+ // sombra com blur não é).
522
+ //
523
+ // :not([sticky-header])/:not(matcha-table[sticky-header] &) — combinar
524
+ // sticky column COM sticky header exige que a cell de CANTO (cabeçalho da
525
+ // coluna fixa) fique sticky nos dois eixos (top E left/right) ao mesmo
526
+ // tempo — inconsistente entre browsers em cells de <table> (documentado
527
+ // no JSDoc do componente). Em vez de deixar o canto quebrado, desliga a
528
+ // coluna fixa inteira quando stickyHeader está ativo — só o header fixo
529
+ // (mais universalmente útil) permanece. `sticky-header` agora é atributo
530
+ // no host — o 2º `:not()` cobre o caso em que o host é o wrapper (forma
531
+ // elemento, `sticky-header` não está no `.matcha-table` em si, está no
532
+ // `<matcha-table>` ancestral). `data-sticky-start`/`-end` continuam só no
533
+ // nó real (mirrorAttributesToRealTable), não precisam de alternativa.
534
+ @for $i from 1 through 4 {
535
+ &[data-sticky-start="#{$i}"]:not([sticky-header]):not(matcha-table[sticky-header] &) {
536
+ thead th:nth-child(-n+#{$i}),
537
+ tbody tr td:nth-child(-n+#{$i}) {
538
+ position: sticky;
539
+ z-index: 2;
540
+ background-color: var(--matcha-color-surface-card);
541
+ }
542
+ // Última cell do grupo sticky-start marca o scroll-edge com borda fina.
543
+ thead th:nth-child(#{$i}),
544
+ tbody tr td:nth-child(#{$i}) {
545
+ border-right: var(--matcha-border-hairline, 1px) solid var(--matcha-color-surface-border);
546
+ }
547
+ }
548
+
549
+ &[data-sticky-end="#{$i}"]:not([sticky-header]):not(matcha-table[sticky-header] &) {
550
+ thead th:nth-last-child(-n+#{$i}),
551
+ tbody tr td:nth-last-child(-n+#{$i}) {
552
+ position: sticky;
553
+ z-index: 2;
554
+ background-color: var(--matcha-color-surface-card);
555
+ }
556
+ thead th:nth-last-child(#{$i}),
557
+ tbody tr td:nth-last-child(#{$i}) {
558
+ border-left: var(--matcha-border-hairline, 1px) solid var(--matcha-color-surface-border);
559
+ }
560
+ }
561
+ }
562
+
563
+ &[data-sticky-start]:not([sticky-header]):not(matcha-table[sticky-header] &) thead th:first-child,
564
+ &[data-sticky-start]:not([sticky-header]):not(matcha-table[sticky-header] &) tbody tr td:first-child { left: 0; }
565
+ &[data-sticky-end]:not([sticky-header]):not(matcha-table[sticky-header] &) thead th:last-child,
566
+ &[data-sticky-end]:not([sticky-header]):not(matcha-table[sticky-header] &) tbody tr td:last-child { right: 0; }
567
+
568
+ // ── Variant accent (semantic row coloring) ────────────────────────────
569
+ // Defaults canon Figma DSV3: equal=info, plus=success, minus=danger.
570
+ // Accent vive no <tr> → NÃO some com stickyStart.
571
+ // `position: relative` em <tr> suportado em Chrome 80+, FF 87+, Safari 14+.
572
+ tr.equal { --matcha-table-row-accent-color: var(--matcha-color-info); }
573
+ tr.plus { --matcha-table-row-accent-color: var(--matcha-color-success); }
574
+ tr.minus { --matcha-table-row-accent-color: var(--matcha-color-danger); }
575
+
576
+ tbody tr[data-variant],
577
+ tbody tr.equal,
578
+ tbody tr.plus,
579
+ tbody tr.minus {
580
+ position: relative;
581
+
582
+ &::before {
583
+ content: '';
584
+ position: absolute;
585
+ top: 0;
586
+ left: 0;
587
+ width: var(--matcha-table-variant-accent-width);
588
+ height: 100%;
589
+ background: var(--matcha-table-row-accent-color, transparent);
590
+ pointer-events: none;
591
+ z-index: 1; // acima do bg da cell, abaixo do sticky shadow
592
+ }
593
+ }
594
+
595
+ // Child row (indent + accent esmaecido) — pattern tree/hierarquia.
596
+ tbody tr.child-item::before {
597
+ opacity: var(--matcha-table-row-accent-child-opacity);
598
+ }
599
+ tbody tr.child-item td:first-child {
600
+ padding-left: calc(var(--matcha-table-cell-padding-x-left) +
601
+ var(--matcha-space-200, 16px));
602
+ }
603
+
604
+ // ── Loading state global — pulse + aria-busy ─────────────────────────
605
+ &[loading] tbody tr,
606
+ matcha-table[loading] & tbody tr {
607
+ animation: matcha-table-loading-pulse
608
+ var(--matcha-table-loading-pulse-duration)
609
+ ease-in-out infinite;
610
+ pointer-events: none;
611
+ }
612
+
613
+ // ── Disabled state — row/cell ─────────────────────────────────────────
614
+ // Pattern canon Material/Polaris. A11y: também aplicar aria-disabled.
615
+ tbody tr[disabled], tbody tr.is-disabled,
616
+ tbody td[disabled], tbody td.is-disabled {
617
+ opacity: var(--matcha-table-disabled-opacity);
618
+ pointer-events: none;
619
+ cursor: not-allowed;
620
+ }
621
+
622
+ // ── Per-row loading (matcha-table-loading directive) ─────────────────
623
+ // Pulse só naquela row — optimistic save/delete sem bloquear a tabela.
624
+ tbody tr.is-loading {
625
+ animation: matcha-table-loading-pulse
626
+ var(--matcha-table-loading-pulse-duration)
627
+ ease-in-out infinite;
628
+ pointer-events: none;
629
+ }
630
+
631
+ // ── Empty state row (matcha-table-empty directive) ────────────────────
632
+ // Container pra <matcha-empty-state>. Directive seta colspan automático.
633
+ tbody tr.is-empty td {
634
+ padding: var(--matcha-table-empty-padding-y) var(--matcha-table-empty-padding-x);
635
+ text-align: center;
636
+ background: var(--matcha-table-cell-bg);
637
+ }
638
+
639
+ // ── Resizable column (matcha-table-resizable directive) ───────────────
640
+ // Handle invisível na borda direita. Hover mostra cursor + barra discreta.
641
+ thead th.is-resizable {
642
+ position: relative;
643
+
644
+ &::before {
645
+ content: '';
646
+ position: absolute;
647
+ top: var(--matcha-table-resize-indicator-top);
648
+ right: 0;
649
+ width: var(--matcha-table-resize-indicator-width);
650
+ height: var(--matcha-table-resize-indicator-height);
651
+ background: var(--matcha-table-border-color);
652
+ opacity: 0;
653
+ transition: opacity var(--matcha-table-transition-duration) linear;
654
+ }
655
+ &:hover::before { opacity: 1; }
656
+ }
657
+ thead th.is-resizable::after {
658
+ // Override do ::after do sort-indicator: handle de resize ocupa a borda.
659
+ // Col sortable + resizable é raro — consumer escolhe uma.
660
+ content: '';
661
+ position: absolute;
662
+ top: 0;
663
+ right: calc(-1 * var(--matcha-table-resize-handle-width) / 2);
664
+ width: var(--matcha-table-resize-handle-width);
665
+ height: 100%;
666
+ cursor: col-resize;
667
+ z-index: 3;
668
+ background: transparent;
669
+ mask: none;
670
+ }
671
+ }
672
+
673
+ // Sticky header — forma ELEMENTO (`<matcha-table>`, ver table.component.ts).
674
+ // Aqui quem rola de verdade é `.matcha-table__scroll` (o wrapper), não a
675
+ // `<table class="matcha-table">` interna — a altura máxima precisa ir no
676
+ // wrapper. `sticky-header` mora no HOST `<matcha-table>` (@HostBinding,
677
+ // não mais na `<table>` filha) — como o host é ANCESTRAL do wrapper, um
678
+ // descendente simples já alcança, sem precisar de `:has()`.
679
+ matcha-table[sticky-header] > .matcha-table__scroll {
680
+ max-height: var(--matcha-table-sticky-header-max-height, 70vh);
681
+ overflow-y: auto;
682
+ }
683
+
684
+ // roundedTop — regra TOP-LEVEL (fora do bloco .matcha-table acima): alcança
685
+ // o host na forma legado (self, `.matcha-table[rounded-top]`, o próprio
686
+ // `<table>` é a superfície visual) e o wrapper de scroll na forma elemento
687
+ // (descendente a partir do host `<matcha-table rounded-top>` — só o
688
+ // wrapper precisa do radius, ele já clipa a `<table>` interna via
689
+ // overflow non-visible, a `<table>` em si não precisa do próprio radius).
690
+ //
691
+ // Fallback inline no 2º var() é OBRIGATÓRIO: --matcha-table-rounded-top-radius
692
+ // é declarada dentro do bloco `.matcha-table { }` acima — na forma elemento
693
+ // `.matcha-table__scroll` é PAI de `.matcha-table` (o wrapper envolve a
694
+ // table real), não descendente. CSS custom property só desce a árvore, não
695
+ // sobe — sem o fallback aqui a var nunca resolve nesse elemento (bug
696
+ // silencioso: sem erro, só o border-radius nunca aparece). O fallback ainda
697
+ // respeita um override do consumer, se ele setar a var em algum ancestral
698
+ // real do wrapper (ex.: no próprio host `<matcha-table>` ou em `:root`).
699
+ .matcha-table[rounded-top] {
700
+ border-top-left-radius: var(--matcha-table-rounded-top-radius);
701
+ border-top-right-radius: var(--matcha-table-rounded-top-radius);
702
+ }
703
+ matcha-table[rounded-top] .matcha-table__scroll {
704
+ border-top-left-radius: var(--matcha-table-rounded-top-radius, var(--matcha-radius-xl, 16px));
705
+ border-top-right-radius: var(--matcha-table-rounded-top-radius, var(--matcha-radius-xl, 16px));
706
+ }
707
+
708
+ @keyframes matcha-table-loading-pulse {
709
+ 0%, 100% { opacity: 1; }
710
+ 50% { opacity: var(--matcha-table-loading-pulse-opacity-min); }
711
+ }
712
+
713
+ // ── Reduced motion ────────────────────────────────────────────────────
714
+ @media (prefers-reduced-motion: reduce) {
715
+ .matcha-table tbody td,
716
+ .matcha-table[loading] tbody tr,
717
+ matcha-table[loading] .matcha-table tbody tr {
718
+ transition: none;
719
+ animation: none;
720
+ }
721
+ }
722
+ }