@wrrdev/fenix-ui 1.1.17 → 1.2.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.
@@ -73,6 +73,14 @@ const lightTokens = {
73
73
  /** Brilho dos estados `success`/`valid` — acompanha `focus-ring` igualmente. */
74
74
  "success-ring": "0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent)"
75
75
  },
76
+ table: {
77
+ /** Cor do corpo do fx-table; `var()` mantém claro/escuro automáticos. */
78
+ text: "var(--fx-text-default)",
79
+ /** Cabeçalho acompanha o corpo por padrão. */
80
+ "header-text": "var(--fx-table-text)",
81
+ /** O UA aplica bold no <th>; aqui o valor fica explícito e tokenizado. */
82
+ "header-weight": "bold"
83
+ },
76
84
  z: { base: "auto", dropdown: "1000", modal: "1100", toast: "1200" }
77
85
  };
78
86
  const darkTokens = {
@@ -99,6 +107,7 @@ const darkTokens = {
99
107
  },
100
108
  motion: { ...lightTokens.motion },
101
109
  effect: { ...lightTokens.effect },
110
+ table: { ...lightTokens.table },
102
111
  z: { ...lightTokens.z }
103
112
  };
104
113
  const defaultTokens = lightTokens;
@@ -4658,6 +4667,13 @@ _FxTable.styles = css`
4658
4667
  border-bottom: 2px solid var(--fx-border-default);
4659
4668
  white-space: nowrap;
4660
4669
  user-select: none;
4670
+ /* Cor/peso do cabeçalho por token de TABELA. Sem isso o th herda
4671
+ --fx-text-default do host e o peso vem do UA (bold). */
4672
+ color: var(
4673
+ --fx-table-header-text,
4674
+ var(--fx-table-text, var(--fx-text-default))
4675
+ );
4676
+ font-weight: var(--fx-table-header-weight, bold);
4661
4677
  }
4662
4678
  th.sortable {
4663
4679
  cursor: pointer;
@@ -4702,6 +4718,12 @@ _FxTable.styles = css`
4702
4718
  td {
4703
4719
  padding: var(--fx-space-sm) var(--fx-space-md);
4704
4720
  border-bottom: 1px solid var(--fx-border-default);
4721
+ /* Texto do corpo: token próprio para não depender de
4722
+ --fx-text-default (que afeta TODOS os textos do app) e para
4723
+ não herdar o font-weight do body da página — era isso que
4724
+ fazia a célula parecer mais clara que o cabeçalho. */
4725
+ color: var(--fx-table-text, var(--fx-text-default));
4726
+ font-weight: var(--fx-table-text-weight, inherit);
4705
4727
  }
4706
4728
  tbody tr {
4707
4729
  cursor: pointer;
@@ -4978,6 +5000,7 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
4978
5000
  }
4979
5001
  disconnectedCallback() {
4980
5002
  super.disconnectedCallback();
5003
+ this.cancelScheduledSync();
4981
5004
  this.observer?.disconnect();
4982
5005
  this._labelObserver?.disconnect();
4983
5006
  this._labelObserver = void 0;
@@ -5007,14 +5030,28 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
5007
5030
  }
5008
5031
  if (this.listenersReady) return;
5009
5032
  this.listenersReady = true;
5010
- this.addEventListener("focusin", () => this.syncState());
5011
- this.addEventListener(
5012
- "focusout",
5013
- () => setTimeout(() => this.syncState(), 60)
5014
- );
5033
+ this.addEventListener("focusin", () => {
5034
+ this.cancelScheduledSync();
5035
+ this.syncState();
5036
+ });
5037
+ this.addEventListener("focusout", () => this.scheduleSync());
5015
5038
  this.addEventListener("input", () => this.syncState());
5016
5039
  this.addEventListener("change", () => this.syncState());
5017
5040
  }
5041
+ /** Reavalia o estado no próximo tick (o foco já saiu do campo). */
5042
+ scheduleSync() {
5043
+ this.cancelScheduledSync();
5044
+ this._syncTimer = setTimeout(() => {
5045
+ this._syncTimer = void 0;
5046
+ this.syncState();
5047
+ }, 0);
5048
+ }
5049
+ cancelScheduledSync() {
5050
+ if (this._syncTimer !== void 0) {
5051
+ clearTimeout(this._syncTimer);
5052
+ this._syncTimer = void 0;
5053
+ }
5054
+ }
5018
5055
  /** Observa mudanças de atributos do controle (value, open, values...). */
5019
5056
  syncControlReference(control = this.targetControl) {
5020
5057
  if (control !== this.targetControl) {
@@ -5047,18 +5084,16 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
5047
5084
  const c = this.targetControl;
5048
5085
  const attrValue = c.getAttribute ? (c.getAttribute("value") ?? "").trim() : "";
5049
5086
  const attrValues = c.getAttribute ? (c.getAttribute("values") ?? "").trim() : "";
5087
+ const isOpen = Boolean(c.hasAttribute?.("open"));
5050
5088
  const hasValue = Boolean(
5051
- typeof c.value === "string" && c.value.trim() !== "" || Array.isArray(c.values) && c.values.length > 0 || attrValue !== "" || attrValues !== "" || c.hasAttribute && (c.hasAttribute("open") || c.hasAttribute("start"))
5089
+ typeof c.value === "string" && c.value.trim() !== "" || Array.isArray(c.values) && c.values.length > 0 || attrValue !== "" || attrValues !== "" || c.hasAttribute && c.hasAttribute("start")
5052
5090
  );
5053
5091
  const focused = this.isFocusedInside();
5054
- if (hasValue || focused) {
5055
- this.setAttribute("active", "");
5056
- if (focused) this.setAttribute("focus", "");
5057
- else this.removeAttribute("focus");
5058
- } else {
5059
- this.removeAttribute("active");
5060
- this.removeAttribute("focus");
5061
- }
5092
+ const highlighted = focused || isOpen;
5093
+ if (hasValue || highlighted) this.setAttribute("active", "");
5094
+ else this.removeAttribute("active");
5095
+ if (highlighted) this.setAttribute("focus", "");
5096
+ else this.removeAttribute("focus");
5062
5097
  }
5063
5098
  /**
5064
5099
  * variant="in" + ícone à esquerda dentro do campo (fx-input icon):
@@ -5136,6 +5171,10 @@ _FxFloatlabel.styles = css`
5136
5171
  font-family: var(--fx-font-family);
5137
5172
  font-size: var(--fx-font-size);
5138
5173
  vertical-align: middle;
5174
+ /* Cor da label em UM único lugar. active (valor OU foco OU
5175
+ dropdown) só posiciona; focus (foco OU dropdown) só destaca —
5176
+ assim a label acompanha a borda, que só fica primária em foco. */
5177
+ --_flabel-color: var(--fx-text-muted, #94a3b8);
5139
5178
  }
5140
5179
  /* Full width: o host estica até o pai; o controle interno com
5141
5180
  atributo full (ou largura herdada) acompanha. */
@@ -5167,7 +5206,7 @@ _FxFloatlabel.styles = css`
5167
5206
  transform: translateY(-50%);
5168
5207
  font-size: calc(var(--fx-font-size, 14px) - 3px);
5169
5208
  font-weight: 600;
5170
- color: var(--fx-text-muted, #94a3b8);
5209
+ color: var(--_flabel-color);
5171
5210
  background-color: var(--fx-surface-background, #fff);
5172
5211
  padding: 0 4px;
5173
5212
  line-height: 1;
@@ -5185,8 +5224,11 @@ _FxFloatlabel.styles = css`
5185
5224
  color var(--fx-motion-duration-normal, 180ms)
5186
5225
  var(--fx-motion-easing, ease-in-out);
5187
5226
  }
5188
- :host([active]) .flabel {
5189
- color: var(--fx-color-primary, #4f46e5);
5227
+ /* Destacada = foco (ou dropdown aberto). NÃO usar [active]: ele
5228
+ também é verdadeiro com valor e sem foco — era o que deixava a
5229
+ label primária com a borda neutra. */
5230
+ :host([focus]) {
5231
+ --_flabel-color: var(--fx-color-primary, #4f46e5);
5190
5232
  }
5191
5233
 
5192
5234
  /* ---- variant="in": label como placeholder dentro, sobe ao focar/valor ---- */
@@ -5205,7 +5247,8 @@ _FxFloatlabel.styles = css`
5205
5247
  font-weight: 600;
5206
5248
  background-color: var(--fx-surface-background, #fff);
5207
5249
  padding: 0 5px;
5208
- color: var(--fx-color-primary, #4f46e5);
5250
+ /* Sem color aqui: a especificidade desta regra
5251
+ (variant + active) venceria :host([error])/:host([focus]). */
5209
5252
  }
5210
5253
 
5211
5254
  /* ---- variant="in" com ícone dentro do campo (fx-input icon):
@@ -5236,16 +5279,22 @@ _FxFloatlabel.styles = css`
5236
5279
  font-size: calc(var(--fx-font-size, 14px) - 3px);
5237
5280
  font-weight: 600;
5238
5281
  white-space: normal;
5282
+ /* Fora do campo, a label pode receber clique: o listener do
5283
+ render() foca o controle (nas variantes on/in o
5284
+ pointer-events: none é intencional, para não bloquear a borda). */
5285
+ pointer-events: auto;
5239
5286
  }
5240
5287
 
5241
- /* ---- estados de erro / sucesso ---- */
5242
- :host([error]) .flabel,
5243
- :host([invalid]) .flabel {
5244
- color: var(--fx-color-danger, #dc2626);
5288
+ /* ---- estados de erro / sucesso ----
5289
+ Declarados DEPOIS de :host([focus]) e com a mesma especificidade:
5290
+ a validação vence o foco pela ordem, sem depender de acidente. */
5291
+ :host([error]),
5292
+ :host([invalid]) {
5293
+ --_flabel-color: var(--fx-color-danger, #dc2626);
5245
5294
  }
5246
- :host([success]) .flabel,
5247
- :host([valid]) .flabel {
5248
- color: var(--fx-color-success, #10b981);
5295
+ :host([success]),
5296
+ :host([valid]) {
5297
+ --_flabel-color: var(--fx-color-success, #10b981);
5249
5298
  }
5250
5299
 
5251
5300
  /* ---- mensagem de erro sob o campo (fonte menor) ---- */