@wrrdev/fenix-ui 1.1.16 → 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.
Files changed (111) hide show
  1. package/README.md +12 -3
  2. package/dist/components/autocomplete/autocomplete.d.ts +3 -1
  3. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  4. package/dist/components/autocomplete/autocomplete.js +7 -1
  5. package/dist/components/autocomplete/autocomplete.js.map +1 -1
  6. package/dist/components/avatar/avatar.d.ts +1 -0
  7. package/dist/components/avatar/avatar.d.ts.map +1 -1
  8. package/dist/components/avatar/avatar.js +3 -0
  9. package/dist/components/avatar/avatar.js.map +1 -1
  10. package/dist/components/badge/badge.d.ts +4 -0
  11. package/dist/components/badge/badge.d.ts.map +1 -1
  12. package/dist/components/badge/badge.js +12 -0
  13. package/dist/components/badge/badge.js.map +1 -1
  14. package/dist/components/button/button.d.ts +2 -0
  15. package/dist/components/button/button.d.ts.map +1 -1
  16. package/dist/components/button/button.js +6 -0
  17. package/dist/components/button/button.js.map +1 -1
  18. package/dist/components/card/card.d.ts +2 -0
  19. package/dist/components/card/card.d.ts.map +1 -1
  20. package/dist/components/card/card.js +6 -0
  21. package/dist/components/card/card.js.map +1 -1
  22. package/dist/components/checkbox/checkbox.d.ts +5 -1
  23. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  24. package/dist/components/checkbox/checkbox.js +7 -8
  25. package/dist/components/checkbox/checkbox.js.map +1 -1
  26. package/dist/components/datepicker/datepicker.d.ts +17 -2
  27. package/dist/components/datepicker/datepicker.d.ts.map +1 -1
  28. package/dist/components/datepicker/datepicker.js +113 -42
  29. package/dist/components/datepicker/datepicker.js.map +1 -1
  30. package/dist/components/dialog/dialog.d.ts +4 -1
  31. package/dist/components/dialog/dialog.d.ts.map +1 -1
  32. package/dist/components/dialog/dialog.js +41 -18
  33. package/dist/components/dialog/dialog.js.map +1 -1
  34. package/dist/components/drawer/drawer.d.ts +5 -1
  35. package/dist/components/drawer/drawer.d.ts.map +1 -1
  36. package/dist/components/drawer/drawer.js +39 -17
  37. package/dist/components/drawer/drawer.js.map +1 -1
  38. package/dist/components/fileupload/fileupload.d.ts +13 -1
  39. package/dist/components/fileupload/fileupload.d.ts.map +1 -1
  40. package/dist/components/fileupload/fileupload.js +29 -1
  41. package/dist/components/fileupload/fileupload.js.map +1 -1
  42. package/dist/components/floatlabel/floatlabel.d.ts +20 -0
  43. package/dist/components/floatlabel/floatlabel.d.ts.map +1 -1
  44. package/dist/components/floatlabel/floatlabel.js +99 -26
  45. package/dist/components/floatlabel/floatlabel.js.map +1 -1
  46. package/dist/components/icon/icon.d.ts +4 -0
  47. package/dist/components/icon/icon.d.ts.map +1 -1
  48. package/dist/components/icon/icon.js +12 -0
  49. package/dist/components/icon/icon.js.map +1 -1
  50. package/dist/components/input/input.d.ts +9 -2
  51. package/dist/components/input/input.d.ts.map +1 -1
  52. package/dist/components/input/input.js +56 -8
  53. package/dist/components/input/input.js.map +1 -1
  54. package/dist/components/multiselect/multiselect.d.ts +8 -1
  55. package/dist/components/multiselect/multiselect.d.ts.map +1 -1
  56. package/dist/components/multiselect/multiselect.js +61 -9
  57. package/dist/components/multiselect/multiselect.js.map +1 -1
  58. package/dist/components/orderlist/orderlist.d.ts +7 -0
  59. package/dist/components/orderlist/orderlist.d.ts.map +1 -1
  60. package/dist/components/orderlist/orderlist.js +34 -2
  61. package/dist/components/orderlist/orderlist.js.map +1 -1
  62. package/dist/components/radio/radio.d.ts +6 -0
  63. package/dist/components/radio/radio.d.ts.map +1 -1
  64. package/dist/components/radio/radio.js +12 -6
  65. package/dist/components/radio/radio.js.map +1 -1
  66. package/dist/components/select/select.d.ts +19 -1
  67. package/dist/components/select/select.d.ts.map +1 -1
  68. package/dist/components/select/select.js +116 -10
  69. package/dist/components/select/select.js.map +1 -1
  70. package/dist/components/switch/switch.d.ts +4 -1
  71. package/dist/components/switch/switch.d.ts.map +1 -1
  72. package/dist/components/switch/switch.js +2 -1
  73. package/dist/components/switch/switch.js.map +1 -1
  74. package/dist/components/table/table.d.ts +27 -0
  75. package/dist/components/table/table.d.ts.map +1 -1
  76. package/dist/components/table/table.js +119 -21
  77. package/dist/components/table/table.js.map +1 -1
  78. package/dist/components/tabs/tabs.d.ts +6 -0
  79. package/dist/components/tabs/tabs.d.ts.map +1 -1
  80. package/dist/components/tabs/tabs.js +34 -0
  81. package/dist/components/tabs/tabs.js.map +1 -1
  82. package/dist/components/textarea/textarea.d.ts +5 -1
  83. package/dist/components/textarea/textarea.d.ts.map +1 -1
  84. package/dist/components/textarea/textarea.js +25 -6
  85. package/dist/components/textarea/textarea.js.map +1 -1
  86. package/dist/core/jsx.d.ts +11 -5
  87. package/dist/core/jsx.d.ts.map +1 -1
  88. package/dist/core/jsx.js.map +1 -1
  89. package/dist/core/modal.d.ts +31 -0
  90. package/dist/core/modal.d.ts.map +1 -0
  91. package/dist/core/modal.js +61 -0
  92. package/dist/core/modal.js.map +1 -0
  93. package/dist/core/tokens.d.ts +2 -0
  94. package/dist/core/tokens.d.ts.map +1 -1
  95. package/dist/core/tokens.js +9 -0
  96. package/dist/core/tokens.js.map +1 -1
  97. package/dist/docs/app.d.ts.map +1 -1
  98. package/dist/docs/componentes/floatlabel.doc.d.ts.map +1 -1
  99. package/dist/docs/componentes/input.doc.d.ts.map +1 -1
  100. package/dist/docs/componentes/orderlist.doc.d.ts.map +1 -1
  101. package/dist/docs/componentes/table.doc.d.ts.map +1 -1
  102. package/dist/docs/shell.d.ts +10 -1
  103. package/dist/docs/shell.d.ts.map +1 -1
  104. package/dist/docs/theme-reference.d.ts +53 -0
  105. package/dist/docs/theme-reference.d.ts.map +1 -0
  106. package/dist/docs/types.d.ts +2 -0
  107. package/dist/docs/types.d.ts.map +1 -1
  108. package/dist/fenix-ui.umd.min.js +270 -233
  109. package/dist/fenix-ui4.esm.js +890 -176
  110. package/dist/fenix-ui4.esm.js.map +1 -1
  111. package/package.json +1 -1
@@ -11,7 +11,51 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
11
11
  this._labelClickBound = false;
12
12
  }
13
13
  static get observedAttributes() {
14
- return ["variant", "error", "invalid", "success", "valid", "full", "icon", "icon-pos"];
14
+ return ["variant", "error", "invalid", "success", "valid", "full", "icon", "icon-pos", "error-text"];
15
+ }
16
+ get error() {
17
+ return this.hasAttr("error");
18
+ }
19
+ set error(value) {
20
+ this.toggleAttr("error", Boolean(value));
21
+ }
22
+ get invalid() {
23
+ return this.hasAttr("invalid");
24
+ }
25
+ set invalid(value) {
26
+ this.toggleAttr("invalid", Boolean(value));
27
+ }
28
+ get success() {
29
+ return this.hasAttr("success");
30
+ }
31
+ set success(value) {
32
+ this.toggleAttr("success", Boolean(value));
33
+ }
34
+ get valid() {
35
+ return this.hasAttr("valid");
36
+ }
37
+ set valid(value) {
38
+ this.toggleAttr("valid", Boolean(value));
39
+ }
40
+ get full() {
41
+ return this.hasAttr("full");
42
+ }
43
+ set full(value) {
44
+ this.toggleAttr("full", Boolean(value));
45
+ }
46
+ get errorText() {
47
+ return this.getAttr("error-text");
48
+ }
49
+ set errorText(value) {
50
+ this.setAttribute("error-text", value == null ? "" : String(value));
51
+ }
52
+ connectedCallback() {
53
+ super.connectedCallback();
54
+ this._labelObserver = new MutationObserver(() => {
55
+ const label = this.findLabel();
56
+ if (label && label !== this._label) this.render();
57
+ });
58
+ this._labelObserver.observe(this, { childList: true });
15
59
  }
16
60
  render() {
17
61
  const control = this.findControl();
@@ -54,7 +98,10 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
54
98
  }
55
99
  disconnectedCallback() {
56
100
  super.disconnectedCallback();
101
+ this.cancelScheduledSync();
57
102
  this.observer?.disconnect();
103
+ this._labelObserver?.disconnect();
104
+ this._labelObserver = void 0;
58
105
  }
59
106
  /** Campo de controle = qualquer filho direto que não seja label. */
60
107
  findControl() {
@@ -81,14 +128,28 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
81
128
  }
82
129
  if (this.listenersReady) return;
83
130
  this.listenersReady = true;
84
- this.addEventListener("focusin", () => this.syncState());
85
- this.addEventListener(
86
- "focusout",
87
- () => setTimeout(() => this.syncState(), 60)
88
- );
131
+ this.addEventListener("focusin", () => {
132
+ this.cancelScheduledSync();
133
+ this.syncState();
134
+ });
135
+ this.addEventListener("focusout", () => this.scheduleSync());
89
136
  this.addEventListener("input", () => this.syncState());
90
137
  this.addEventListener("change", () => this.syncState());
91
138
  }
139
+ /** Reavalia o estado no próximo tick (o foco já saiu do campo). */
140
+ scheduleSync() {
141
+ this.cancelScheduledSync();
142
+ this._syncTimer = setTimeout(() => {
143
+ this._syncTimer = void 0;
144
+ this.syncState();
145
+ }, 0);
146
+ }
147
+ cancelScheduledSync() {
148
+ if (this._syncTimer !== void 0) {
149
+ clearTimeout(this._syncTimer);
150
+ this._syncTimer = void 0;
151
+ }
152
+ }
92
153
  /** Observa mudanças de atributos do controle (value, open, values...). */
93
154
  syncControlReference(control = this.targetControl) {
94
155
  if (control !== this.targetControl) {
@@ -121,18 +182,16 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
121
182
  const c = this.targetControl;
122
183
  const attrValue = c.getAttribute ? (c.getAttribute("value") ?? "").trim() : "";
123
184
  const attrValues = c.getAttribute ? (c.getAttribute("values") ?? "").trim() : "";
185
+ const isOpen = Boolean(c.hasAttribute?.("open"));
124
186
  const hasValue = Boolean(
125
- 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"))
187
+ typeof c.value === "string" && c.value.trim() !== "" || Array.isArray(c.values) && c.values.length > 0 || attrValue !== "" || attrValues !== "" || c.hasAttribute && c.hasAttribute("start")
126
188
  );
127
189
  const focused = this.isFocusedInside();
128
- if (hasValue || focused) {
129
- this.setAttribute("active", "");
130
- if (focused) this.setAttribute("focus", "");
131
- else this.removeAttribute("focus");
132
- } else {
133
- this.removeAttribute("active");
134
- this.removeAttribute("focus");
135
- }
190
+ const highlighted = focused || isOpen;
191
+ if (hasValue || highlighted) this.setAttribute("active", "");
192
+ else this.removeAttribute("active");
193
+ if (highlighted) this.setAttribute("focus", "");
194
+ else this.removeAttribute("focus");
136
195
  }
137
196
  /**
138
197
  * variant="in" + ícone à esquerda dentro do campo (fx-input icon):
@@ -210,6 +269,10 @@ _FxFloatlabel.styles = css`
210
269
  font-family: var(--fx-font-family);
211
270
  font-size: var(--fx-font-size);
212
271
  vertical-align: middle;
272
+ /* Cor da label em UM único lugar. active (valor OU foco OU
273
+ dropdown) só posiciona; focus (foco OU dropdown) só destaca —
274
+ assim a label acompanha a borda, que só fica primária em foco. */
275
+ --_flabel-color: var(--fx-text-muted, #94a3b8);
213
276
  }
214
277
  /* Full width: o host estica até o pai; o controle interno com
215
278
  atributo full (ou largura herdada) acompanha. */
@@ -241,7 +304,7 @@ _FxFloatlabel.styles = css`
241
304
  transform: translateY(-50%);
242
305
  font-size: calc(var(--fx-font-size, 14px) - 3px);
243
306
  font-weight: 600;
244
- color: var(--fx-text-muted, #94a3b8);
307
+ color: var(--_flabel-color);
245
308
  background-color: var(--fx-surface-background, #fff);
246
309
  padding: 0 4px;
247
310
  line-height: 1;
@@ -259,8 +322,11 @@ _FxFloatlabel.styles = css`
259
322
  color var(--fx-motion-duration-normal, 180ms)
260
323
  var(--fx-motion-easing, ease-in-out);
261
324
  }
262
- :host([active]) .flabel {
263
- color: var(--fx-color-primary, #4f46e5);
325
+ /* Destacada = foco (ou dropdown aberto). NÃO usar [active]: ele
326
+ também é verdadeiro com valor e sem foco — era o que deixava a
327
+ label primária com a borda neutra. */
328
+ :host([focus]) {
329
+ --_flabel-color: var(--fx-color-primary, #4f46e5);
264
330
  }
265
331
 
266
332
  /* ---- variant="in": label como placeholder dentro, sobe ao focar/valor ---- */
@@ -279,7 +345,8 @@ _FxFloatlabel.styles = css`
279
345
  font-weight: 600;
280
346
  background-color: var(--fx-surface-background, #fff);
281
347
  padding: 0 5px;
282
- color: var(--fx-color-primary, #4f46e5);
348
+ /* Sem color aqui: a especificidade desta regra
349
+ (variant + active) venceria :host([error])/:host([focus]). */
283
350
  }
284
351
 
285
352
  /* ---- variant="in" com ícone dentro do campo (fx-input icon):
@@ -310,16 +377,22 @@ _FxFloatlabel.styles = css`
310
377
  font-size: calc(var(--fx-font-size, 14px) - 3px);
311
378
  font-weight: 600;
312
379
  white-space: normal;
380
+ /* Fora do campo, a label pode receber clique: o listener do
381
+ render() foca o controle (nas variantes on/in o
382
+ pointer-events: none é intencional, para não bloquear a borda). */
383
+ pointer-events: auto;
313
384
  }
314
385
 
315
- /* ---- estados de erro / sucesso ---- */
316
- :host([error]) .flabel,
317
- :host([invalid]) .flabel {
318
- color: var(--fx-color-danger, #dc2626);
386
+ /* ---- estados de erro / sucesso ----
387
+ Declarados DEPOIS de :host([focus]) e com a mesma especificidade:
388
+ a validação vence o foco pela ordem, sem depender de acidente. */
389
+ :host([error]),
390
+ :host([invalid]) {
391
+ --_flabel-color: var(--fx-color-danger, #dc2626);
319
392
  }
320
- :host([success]) .flabel,
321
- :host([valid]) .flabel {
322
- color: var(--fx-color-success, #10b981);
393
+ :host([success]),
394
+ :host([valid]) {
395
+ --_flabel-color: var(--fx-color-success, #10b981);
323
396
  }
324
397
 
325
398
  /* ---- mensagem de erro sob o campo (fonte menor) ---- */
@@ -1 +1 @@
1
- {"version":3,"file":"floatlabel.js","sources":["../../../src/components/floatlabel/floatlabel.ts"],"sourcesContent":["import { FxElement } from \"../../core/base\";\nimport { css } from \"../../core/css\";\nimport { defineElement } from \"../../core/define\";\n\n/**\n * Rótulo flutuante .\n *\n * Envolve um campo de formulário (fx-input, fx-select, fx-multiselect,\n * fx-datepicker, ...) + um <label>. A label se comporta como placeholder\n * quando o campo está vazio e \"sobe\" quando o campo é focado, contém valor\n * ou abre dropdown.\n *\n * - variant=\"on\" (padrão): label sobre a borda superior\n * - variant=\"in\" : label como placeholder interno, sobe ao focar/valor\n * - variant=\"over\" : label estática acima do campo\n *\n * - error / invalid: label + borda em vermelho, mensagem menor abaixo.\n * - success / valid: label + borda em verde.\n *\n * A <label> do light DOM é MOVIDA para dentro do Shadow DOM (como `.flabel`)\n * na primeira renderização — a partir daí NÃO existe cópia no light DOM.\n * Ocultá-la com `hidden` (solução anterior) era frágil: o consumidor, o\n * framework ou uma folha de estilo podiam remover o atributo e a label\n * original reaparecia embaixo do campo, quebrando o floatlabel. Mover o\n * elemento é estrutural: não há nada removível lá fora.\n */\nexport class FxFloatlabel extends FxElement {\n\tstatic override get observedAttributes(): string[] {\n\t\treturn [\"variant\", \"error\", \"invalid\", \"success\", \"valid\", \"full\", \"icon\", \"icon-pos\"];\n\t}\n\t// 'active' NÃO entra em observedAttributes: togglá-lo apenas atualiza CSS\n\t// via :host([active]), sem re-render destrutivo (mantém o foco do campo).\n\n\tstatic override styles = css`\n\t\t:host {\n\t\t\tdisplay: inline-block;\n\t\t\tposition: relative;\n\t\t\tfont-family: var(--fx-font-family);\n\t\t\tfont-size: var(--fx-font-size);\n\t\t\tvertical-align: middle;\n\t\t}\n\t\t/* Full width: o host estica até o pai; o controle interno com\n\t\t atributo full (ou largura herdada) acompanha. */\n\t\t:host([full]) {\n\t\t\tdisplay: block;\n\t\t\twidth: 100%;\n\t\t}\n\t\t.wrapper {\n\t\t\tposition: relative;\n\t\t\tdisplay: inline-block;\n\t\t\twidth: 100%;\n\t\t}\n\t\t/* Caixa do campo: referência de posicionamento da label flutuante\n (isolada da mensagem de erro, que cresce o wrapper). */\n\t\t.field-box {\n\t\t\tposition: relative;\n\t\t\tdisplay: inline-block;\n\t\t\twidth: 100%;\n\t\t}\n\t\t:host([variant=\"in\"]) .field-box {\n\t\t\tpadding-top: var(--fx-space-xs, 6px);\n\t\t}\n\n\t\t/* ---- label base (variant=\"on\", padrão): sobre a borda superior ---- */\n\t\t.flabel {\n\t\t\tposition: absolute;\n\t\t\tleft: var(--fx-space-md, 12px);\n\t\t\ttop: 0;\n\t\t\ttransform: translateY(-50%);\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 600;\n\t\t\tcolor: var(--fx-text-muted, #94a3b8);\n\t\t\tbackground-color: var(--fx-surface-background, #fff);\n\t\t\tpadding: 0 4px;\n\t\t\tline-height: 1;\n\t\t\tborder-radius: 2px;\n\t\t\tpointer-events: none;\n\t\t\tz-index: 2;\n\t\t\twhite-space: nowrap;\n\t\t\ttransition:\n\t\t\t\ttop var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out),\n\t\t\t\ttransform var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out),\n\t\t\t\tfont-size var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out),\n\t\t\t\tcolor var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out);\n\t\t}\n\t\t:host([active]) .flabel {\n\t\t\tcolor: var(--fx-color-primary, #4f46e5);\n\t\t}\n\n\t\t/* ---- variant=\"in\": label como placeholder dentro, sobe ao focar/valor ---- */\n\t\t:host([variant=\"in\"]) .flabel {\n\t\t\ttop: 50%;\n\t\t\ttransform: translateY(-50%);\n\t\t\tfont-size: var(--fx-font-size, 14px);\n\t\t\tfont-weight: var(--fx-font-weight, 400);\n\t\t\tbackground: transparent;\n\t\t\tpadding: 0;\n\t\t}\n\t\t:host([variant=\"in\"][active]) .flabel {\n\t\t\ttop: 0;\n\t\t\ttransform: translateY(-50%);\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 600;\n\t\t\tbackground-color: var(--fx-surface-background, #fff);\n\t\t\tpadding: 0 5px;\n\t\t\tcolor: var(--fx-color-primary, #4f46e5);\n\t\t}\n\n\t\t/* ---- variant=\"in\" com ícone dentro do campo (fx-input icon):\n\t\t enquanto a label funciona como placeholder (sem foco/valor), ela\n\t\t começa DEPOIS do glifo (mesma métrica de padding do fx-input).\n\t\t Ao subir (active), volta ao offset padrão da borda. ---- */\n\t\t:host([variant=\"in\"]) .flabel.icon-indent {\n\t\t\tleft: calc(\n\t\t\t\tvar(--fx-space-md, 12px) +\n\t\t\t\tvar(--fx-input-icon-size, calc(var(--fx-font-size, 14px) + 6px)) +\n\t\t\t\tvar(--fx-space-sm, 8px)\n\t\t\t);\n\t\t}\n\t\t:host([variant=\"in\"][active]) .flabel.icon-indent {\n\t\t\tleft: var(--fx-space-md, 12px);\n\t\t}\n\n\t\t/* ---- variant=\"over\": label estática ACIMA do campo (fora, em fluxo) ---- */\n\t\t:host([variant=\"over\"]) {\n\t\t\tdisplay: block;\n\t\t}\n\t\t:host([variant=\"over\"]) .flabel {\n\t\t\tposition: static;\n\t\t\tdisplay: block;\n\t\t\tmargin-bottom: var(--fx-space-xs, 4px);\n\t\t\tbackground: transparent;\n\t\t\tpadding: 0;\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 600;\n\t\t\twhite-space: normal;\n\t\t}\n\n\t\t/* ---- estados de erro / sucesso ---- */\n\t\t:host([error]) .flabel,\n\t\t:host([invalid]) .flabel {\n\t\t\tcolor: var(--fx-color-danger, #dc2626);\n\t\t}\n\t\t:host([success]) .flabel,\n\t\t:host([valid]) .flabel {\n\t\t\tcolor: var(--fx-color-success, #10b981);\n\t\t}\n\n\t\t/* ---- mensagem de erro sob o campo (fonte menor) ---- */\n\t\t.error-message {\n\t\t\tdisplay: none;\n\t\t\tmargin-top: var(--fx-space-xs, 4px);\n\t\t\tmargin-left: var(--fx-space-md, 12px);\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 500;\n\t\t\tcolor: var(--fx-color-danger, #dc2626);\n\t\t\tline-height: 1.2;\n\t\t}\n\t\t:host([error]) .error-message,\n\t\t:host([invalid]) .error-message {\n\t\t\tdisplay: block;\n\t\t}\n\t`;\n\tprivate targetControl: HTMLElement | null = null;\n\tprivate observer: MutationObserver | null = null;\n\tprivate listenersReady = false;\n\t/** <label> adotada do light DOM — vive no Shadow DOM após o 1º render. */\n\tprivate _label: HTMLElement | null = null;\n\tprivate _labelClickBound = false;\n\n\tprotected render(): void {\n\t\tconst control = this.findControl();\n\t\t// Adota a <label> do light DOM (na 1ª renderização, ou se o consumidor\n\t\t// trocar por outra depois). Uma vez movida, o render seguinte não a\n\t\t// encontra no light DOM — a referência `_label` é reutilizada.\n\t\tconst lightLabel = this.findLabel();\n\t\tif (lightLabel) this._label = lightLabel;\n\t\tconst label = this._label;\n\t\tconst isOver = this.getAttribute(\"variant\") === \"over\";\n\n\t\tthis.setTemplate(`\n <div class=\"wrapper\" part=\"wrapper\">\n <div class=\"field-box\" part=\"field\">\n <slot></slot>\n </div>\n <div class=\"error-message\" part=\"error\"></div>\n </div>\n `);\n\n\t\t// A label original é INSERIDA no Shadow DOM (move o próprio elemento —\n\t\t// não é uma cópia). `setTemplate` reconstrói o template a cada render e\n\t\t// a label desanexada é reinserida no novo wrapper, com estado intacto.\n\t\tif (label) {\n\t\t\tlabel.classList.add(\"flabel\");\n\t\t\tlabel.setAttribute(\"part\", \"label\");\n\t\t\tconst wrapper = this.root.querySelector(\".wrapper\");\n\t\t\tconst fieldBox = this.root.querySelector(\".field-box\");\n\t\t\tif (wrapper && fieldBox) {\n\t\t\t\tif (isOver) wrapper.insertBefore(label, fieldBox);\n\t\t\t\telse fieldBox.appendChild(label);\n\t\t\t}\n\t\t\t// `for` não cruza a fronteira do shadow (o id do controle vive no\n\t\t\t// light DOM): o clique na label foca o campo manualmente.\n\t\t\tif (!this._labelClickBound) {\n\t\t\t\tthis._labelClickBound = true;\n\t\t\t\tlabel.addEventListener(\"click\", (e) => {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\t(this.targetControl as HTMLElement | null)?.focus?.();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.attachEvents();\n\t\tthis.syncControlReference(control);\n\t\tthis.syncState();\n\t\tthis.applyError();\n\t\tthis.applyFull();\n\t\tthis.applyIcon();\n\t\tthis.syncIconIndent();\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tsuper.disconnectedCallback();\n\t\tthis.observer?.disconnect();\n\t}\n\n\t/** Campo de controle = qualquer filho direto que não seja label. */\n\tprivate findControl(): HTMLElement | null {\n\t\treturn (\n\t\t\t(Array.from(this.children).find(\n\t\t\t\t(n) => n.tagName.toLowerCase() !== \"label\",\n\t\t\t) as HTMLElement) ?? null\n\t\t);\n\t}\n\n\t/** Label = filho direto <label>. */\n\tprivate findLabel(): HTMLElement | null {\n\t\treturn (\n\t\t\t(Array.from(this.children).find(\n\t\t\t\t(n) => n.tagName.toLowerCase() === \"label\",\n\t\t\t) as HTMLElement) ?? null\n\t\t);\n\t}\n\n\tprivate attachEvents(): void {\n\t\tconst slot = this.root.querySelector(\"slot\");\n\t\tif (slot) {\n\t\t\tslot.addEventListener(\"slotchange\", () => {\n\t\t\t\t// Se o usuário trocar o controle/label em tempo de execução, re-sincroniza.\n\t\t\t\tconst control = this.findControl();\n\t\t\t\tthis.syncControlReference(control);\n\t\t\t\tthis.syncState();\n\t\t\t\tthis.applyError();\n\t\t\t\tthis.applyFull();\n\t\t\t});\n\t\t}\n\n\t\tif (this.listenersReady) return;\n\t\tthis.listenersReady = true;\n\n\t\tthis.addEventListener(\"focusin\", () => this.syncState());\n\t\tthis.addEventListener(\"focusout\", () =>\n\t\t\tsetTimeout(() => this.syncState(), 60),\n\t\t);\n\t\tthis.addEventListener(\"input\", () => this.syncState());\n\t\tthis.addEventListener(\"change\", () => this.syncState());\n\t}\n\n\t/** Observa mudanças de atributos do controle (value, open, values...). */\n\tprivate syncControlReference(\n\t\tcontrol: HTMLElement | null = this.targetControl,\n\t): void {\n\t\tif (control !== this.targetControl) {\n\t\t\tthis.observer?.disconnect();\n\t\t\tthis.observer = null;\n\t\t\tthis.targetControl = control;\n\t\t\tif (control) {\n\t\t\t\tthis.observer = new MutationObserver(() => {\n\t\t\t\t\tthis.syncState();\n\t\t\t\t\tthis.syncIconIndent();\n\t\t\t\t});\n\t\t\t\tthis.observer.observe(control, {\n\t\t\t\t\tattributes: true,\n\t\t\t\t\tattributeFilter: [\"value\", \"values\", \"start\", \"end\", \"open\", \"icon\", \"icon-pos\"],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate syncState(): void {\n\t\tif (this.targetControl && !this.targetControl.isConnected) {\n\t\t\tthis.targetControl = null;\n\t\t\tthis.observer?.disconnect();\n\t\t\tthis.observer = null;\n\t\t}\n\n\t\tif (!this.targetControl) {\n\t\t\tthis.removeAttribute(\"active\");\n\t\t\tthis.removeAttribute(\"focus\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst c = this.targetControl as any;\n\t\tconst attrValue = c.getAttribute\n\t\t\t? (c.getAttribute(\"value\") ?? \"\").trim()\n\t\t\t: \"\";\n\t\tconst attrValues = c.getAttribute\n\t\t\t? (c.getAttribute(\"values\") ?? \"\").trim()\n\t\t\t: \"\";\n\t\tconst hasValue = Boolean(\n\t\t\t(typeof c.value === \"string\" && c.value.trim() !== \"\") ||\n\t\t\t(Array.isArray(c.values) && c.values.length > 0) ||\n\t\t\tattrValue !== \"\" ||\n\t\t\tattrValues !== \"\" ||\n\t\t\t(c.hasAttribute &&\n\t\t\t\t(c.hasAttribute(\"open\") || c.hasAttribute(\"start\"))),\n\t\t);\n\n\t\tconst focused = this.isFocusedInside();\n\t\tif (hasValue || focused) {\n\t\t\tthis.setAttribute(\"active\", \"\");\n\t\t\tif (focused) this.setAttribute(\"focus\", \"\");\n\t\t\telse this.removeAttribute(\"focus\");\n\t\t} else {\n\t\t\tthis.removeAttribute(\"active\");\n\t\t\tthis.removeAttribute(\"focus\");\n\t\t}\n\t}\n\n\t/**\n\t * variant=\"in\" + ícone à esquerda dentro do campo (fx-input icon):\n\t * enquanto a label é placeholder ela precisa começar depois do glifo.\n\t * Detecta via atributos icon/icon-pos (ou slot icon) do controle.\n\t */\n\tprivate syncIconIndent(): void {\n\t\tconst label = this.root.querySelector(\".flabel\");\n\t\tif (!label) return;\n\t\tconst c = this.targetControl;\n\t\tconst hasLeftIcon = Boolean(\n\t\t\tc &&\n\t\t\t(c.hasAttribute(\"icon\") || c.querySelector(':scope > [slot=\"icon\"]')) &&\n\t\t\tc.getAttribute(\"icon-pos\") !== \"right\",\n\t\t);\n\t\tlabel.classList.toggle(\"icon-indent\", hasLeftIcon);\n\t}\n\n\tprivate applyFull(): void {\n\t\t// Espelha o full no controle interno: o CSS :host([full]) do floatlabel\n\t\t// estica apenas o HOST — sem isso o campo interno (ex.: fx-input com\n\t\t// width 260px fixa) não acompanharia o container.\n\t\tif (\n\t\t\tthis.targetControl &&\n\t\t\ttypeof this.targetControl.setAttribute === \"function\"\n\t\t) {\n\t\t\tif (this.hasAttr(\"full\")) {\n\t\t\t\tthis.targetControl.setAttribute(\"full\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.targetControl.removeAttribute(\"full\");\n\t\t\t}\n\t\t\t}\n\t}\n\n\tprivate applyIcon(): void {\n\t\tif (\n\t\t\tthis.targetControl &&\n\t\t\ttypeof this.targetControl.setAttribute === \"function\"\n\t\t) {\n\t\t\t// Só propaga icon/icon-pos se o FLOATLABEL possuir o atributo.\n\t\t\t// Se o ícone foi pondo diretamente no controle (ex.: <fx-input icon=\"lock\">),\n\t\t\t// não remove — deixa o controle manter o seu próprio ícone.\n\t\t\tif (this.hasAttr(\"icon\")) {\n\t\t\t\tconst icon = this.getAttr(\"icon\");\n\t\t\t\tconst iconPos = this.getAttr(\"icon-pos\");\n\t\t\t\tif (icon !== \"\") this.targetControl.setAttribute(\"icon\", icon);\n\t\t\t\telse this.targetControl.setAttribute(\"icon\", \"\");\n\t\t\t\tif (iconPos && iconPos !== \"left\") this.targetControl.setAttribute(\"icon-pos\", iconPos);\n\t\t\t\telse this.targetControl.removeAttribute(\"icon-pos\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate applyError(): void {\n\t\tconst hasError = this.hasAttr(\"error\") || this.hasAttr(\"invalid\");\n\t\tconst errMsg = this.root.querySelector(\n\t\t\t\".error-message\",\n\t\t) as HTMLElement | null;\n\t\tif (errMsg) {\n\t\t\terrMsg.textContent = hasError\n\t\t\t\t? this.getAttribute(\"error-text\")?.trim() || \"Valor inválido\"\n\t\t\t\t: \"\";\n\t\t}\n\n\t\t// Espelha o estado no controle interno para que a borda também fique vermelha/verde.\n\t\tif (\n\t\t\tthis.targetControl &&\n\t\t\ttypeof this.targetControl.setAttribute === \"function\"\n\t\t) {\n\t\t\tconst t = this.targetControl;\n\t\t\tif (hasError) {\n\t\t\t\tt.setAttribute(\"error\", \"\");\n\t\t\t\tt.removeAttribute(\"success\");\n\t\t\t} else if (this.hasAttr(\"success\") || this.hasAttr(\"valid\")) {\n\t\t\t\tt.setAttribute(\"success\", \"\");\n\t\t\t\tt.removeAttribute(\"error\");\n\t\t\t} else {\n\t\t\t\tt.removeAttribute(\"error\");\n\t\t\t\tt.removeAttribute(\"success\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate isWithin(activeEl: Element): boolean {\n\t\tlet current: Element | null = activeEl;\n\t\twhile (current && current !== this) {\n\t\t\tcurrent = current.parentElement;\n\t\t}\n\t\treturn current === this;\n\t}\n\n\tprivate isFocusedInside(): boolean {\n\t\tconst activeEl = (document as Document).activeElement;\n\t\treturn Boolean(activeEl && this.isWithin(activeEl));\n\t}\n}\n\nexport function defineFxFloatlabel(): typeof FxFloatlabel {\n\treturn defineElement(\"fx-floatlabel\", FxFloatlabel);\n}\n"],"names":[],"mappings":";;;AA0BO,MAAM,gBAAN,MAAM,sBAAqB,UAAU;AAAA,EAArC,cAAA;AAAA,UAAA,GAAA,SAAA;AA6IN,SAAQ,gBAAoC;AAC5C,SAAQ,WAAoC;AAC5C,SAAQ,iBAAiB;AAEzB,SAAQ,SAA6B;AACrC,SAAQ,mBAAmB;AAAA,EAAA;AAAA,EAjJ3B,WAAoB,qBAA+B;AAClD,WAAO,CAAC,WAAW,SAAS,WAAW,WAAW,SAAS,QAAQ,QAAQ,UAAU;AAAA,EACtF;AAAA,EAiJU,SAAe;AACxB,UAAM,UAAU,KAAK,YAAA;AAIrB,UAAM,aAAa,KAAK,UAAA;AACxB,QAAI,iBAAiB,SAAS;AAC9B,UAAM,QAAQ,KAAK;AACnB,UAAM,SAAS,KAAK,aAAa,SAAS,MAAM;AAEhD,SAAK,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAOd;AAKH,QAAI,OAAO;AACV,YAAM,UAAU,IAAI,QAAQ;AAC5B,YAAM,aAAa,QAAQ,OAAO;AAClC,YAAM,UAAU,KAAK,KAAK,cAAc,UAAU;AAClD,YAAM,WAAW,KAAK,KAAK,cAAc,YAAY;AACrD,UAAI,WAAW,UAAU;AACxB,YAAI,OAAQ,SAAQ,aAAa,OAAO,QAAQ;AAAA,YAC3C,UAAS,YAAY,KAAK;AAAA,MAChC;AAGA,UAAI,CAAC,KAAK,kBAAkB;AAC3B,aAAK,mBAAmB;AACxB,cAAM,iBAAiB,SAAS,CAAC,MAAM;AACtC,YAAE,eAAA;AACD,eAAK,eAAsC,QAAA;AAAA,QAC7C,CAAC;AAAA,MACF;AAAA,IACD;AAEA,SAAK,aAAA;AACL,SAAK,qBAAqB,OAAO;AACjC,SAAK,UAAA;AACL,SAAK,WAAA;AACL,SAAK,UAAA;AACL,SAAK,UAAA;AACL,SAAK,eAAA;AAAA,EACN;AAAA,EAEmB,uBAA6B;AAC/C,UAAM,qBAAA;AACN,SAAK,UAAU,WAAA;AAAA,EAChB;AAAA;AAAA,EAGQ,cAAkC;AACzC,WACE,MAAM,KAAK,KAAK,QAAQ,EAAE;AAAA,MAC1B,CAAC,MAAM,EAAE,QAAQ,kBAAkB;AAAA,IAAA,KACf;AAAA,EAEvB;AAAA;AAAA,EAGQ,YAAgC;AACvC,WACE,MAAM,KAAK,KAAK,QAAQ,EAAE;AAAA,MAC1B,CAAC,MAAM,EAAE,QAAQ,kBAAkB;AAAA,IAAA,KACf;AAAA,EAEvB;AAAA,EAEQ,eAAqB;AAC5B,UAAM,OAAO,KAAK,KAAK,cAAc,MAAM;AAC3C,QAAI,MAAM;AACT,WAAK,iBAAiB,cAAc,MAAM;AAEzC,cAAM,UAAU,KAAK,YAAA;AACrB,aAAK,qBAAqB,OAAO;AACjC,aAAK,UAAA;AACL,aAAK,WAAA;AACL,aAAK,UAAA;AAAA,MACN,CAAC;AAAA,IACF;AAEA,QAAI,KAAK,eAAgB;AACzB,SAAK,iBAAiB;AAEtB,SAAK,iBAAiB,WAAW,MAAM,KAAK,WAAW;AACvD,SAAK;AAAA,MAAiB;AAAA,MAAY,MACjC,WAAW,MAAM,KAAK,UAAA,GAAa,EAAE;AAAA,IAAA;AAEtC,SAAK,iBAAiB,SAAS,MAAM,KAAK,WAAW;AACrD,SAAK,iBAAiB,UAAU,MAAM,KAAK,WAAW;AAAA,EACvD;AAAA;AAAA,EAGQ,qBACP,UAA8B,KAAK,eAC5B;AACP,QAAI,YAAY,KAAK,eAAe;AACnC,WAAK,UAAU,WAAA;AACf,WAAK,WAAW;AAChB,WAAK,gBAAgB;AACrB,UAAI,SAAS;AACZ,aAAK,WAAW,IAAI,iBAAiB,MAAM;AAC1C,eAAK,UAAA;AACL,eAAK,eAAA;AAAA,QACN,CAAC;AACD,aAAK,SAAS,QAAQ,SAAS;AAAA,UAC9B,YAAY;AAAA,UACZ,iBAAiB,CAAC,SAAS,UAAU,SAAS,OAAO,QAAQ,QAAQ,UAAU;AAAA,QAAA,CAC/E;AAAA,MACF;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,YAAkB;AACzB,QAAI,KAAK,iBAAiB,CAAC,KAAK,cAAc,aAAa;AAC1D,WAAK,gBAAgB;AACrB,WAAK,UAAU,WAAA;AACf,WAAK,WAAW;AAAA,IACjB;AAEA,QAAI,CAAC,KAAK,eAAe;AACxB,WAAK,gBAAgB,QAAQ;AAC7B,WAAK,gBAAgB,OAAO;AAC5B;AAAA,IACD;AAEA,UAAM,IAAI,KAAK;AACf,UAAM,YAAY,EAAE,gBAChB,EAAE,aAAa,OAAO,KAAK,IAAI,KAAA,IAChC;AACH,UAAM,aAAa,EAAE,gBACjB,EAAE,aAAa,QAAQ,KAAK,IAAI,KAAA,IACjC;AACH,UAAM,WAAW;AAAA,MACf,OAAO,EAAE,UAAU,YAAY,EAAE,MAAM,KAAA,MAAW,MAClD,MAAM,QAAQ,EAAE,MAAM,KAAK,EAAE,OAAO,SAAS,KAC9C,cAAc,MACd,eAAe,MACd,EAAE,iBACD,EAAE,aAAa,MAAM,KAAK,EAAE,aAAa,OAAO;AAAA,IAAA;AAGnD,UAAM,UAAU,KAAK,gBAAA;AACrB,QAAI,YAAY,SAAS;AACxB,WAAK,aAAa,UAAU,EAAE;AAC9B,UAAI,QAAS,MAAK,aAAa,SAAS,EAAE;AAAA,UACrC,MAAK,gBAAgB,OAAO;AAAA,IAClC,OAAO;AACN,WAAK,gBAAgB,QAAQ;AAC7B,WAAK,gBAAgB,OAAO;AAAA,IAC7B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAuB;AAC9B,UAAM,QAAQ,KAAK,KAAK,cAAc,SAAS;AAC/C,QAAI,CAAC,MAAO;AACZ,UAAM,IAAI,KAAK;AACf,UAAM,cAAc;AAAA,MACnB,MACC,EAAE,aAAa,MAAM,KAAK,EAAE,cAAc,wBAAwB,MACnE,EAAE,aAAa,UAAU,MAAM;AAAA,IAAA;AAEhC,UAAM,UAAU,OAAO,eAAe,WAAW;AAAA,EAClD;AAAA,EAEQ,YAAkB;AAIzB,QACC,KAAK,iBACL,OAAO,KAAK,cAAc,iBAAiB,YAC1C;AACD,UAAI,KAAK,QAAQ,MAAM,GAAG;AACzB,aAAK,cAAc,aAAa,QAAQ,EAAE;AAAA,MAC3C,OAAO;AACN,aAAK,cAAc,gBAAgB,MAAM;AAAA,MAC1C;AAAA,IACA;AAAA,EACF;AAAA,EAEQ,YAAkB;AACzB,QACC,KAAK,iBACL,OAAO,KAAK,cAAc,iBAAiB,YAC1C;AAID,UAAI,KAAK,QAAQ,MAAM,GAAG;AACzB,cAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,cAAM,UAAU,KAAK,QAAQ,UAAU;AACvC,YAAI,SAAS,GAAI,MAAK,cAAc,aAAa,QAAQ,IAAI;AAAA,YACxD,MAAK,cAAc,aAAa,QAAQ,EAAE;AAC/C,YAAI,WAAW,YAAY,aAAa,cAAc,aAAa,YAAY,OAAO;AAAA,YACjF,MAAK,cAAc,gBAAgB,UAAU;AAAA,MACnD;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,aAAmB;AAC1B,UAAM,WAAW,KAAK,QAAQ,OAAO,KAAK,KAAK,QAAQ,SAAS;AAChE,UAAM,SAAS,KAAK,KAAK;AAAA,MACxB;AAAA,IAAA;AAED,QAAI,QAAQ;AACX,aAAO,cAAc,WAClB,KAAK,aAAa,YAAY,GAAG,UAAU,mBAC3C;AAAA,IACJ;AAGA,QACC,KAAK,iBACL,OAAO,KAAK,cAAc,iBAAiB,YAC1C;AACD,YAAM,IAAI,KAAK;AACf,UAAI,UAAU;AACb,UAAE,aAAa,SAAS,EAAE;AAC1B,UAAE,gBAAgB,SAAS;AAAA,MAC5B,WAAW,KAAK,QAAQ,SAAS,KAAK,KAAK,QAAQ,OAAO,GAAG;AAC5D,UAAE,aAAa,WAAW,EAAE;AAC5B,UAAE,gBAAgB,OAAO;AAAA,MAC1B,OAAO;AACN,UAAE,gBAAgB,OAAO;AACzB,UAAE,gBAAgB,SAAS;AAAA,MAC5B;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,SAAS,UAA4B;AAC5C,QAAI,UAA0B;AAC9B,WAAO,WAAW,YAAY,MAAM;AACnC,gBAAU,QAAQ;AAAA,IACnB;AACA,WAAO,YAAY;AAAA,EACpB;AAAA,EAEQ,kBAA2B;AAClC,UAAM,WAAY,SAAsB;AACxC,WAAO,QAAQ,YAAY,KAAK,SAAS,QAAQ,CAAC;AAAA,EACnD;AACD;AA1YC,cAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAPnB,IAAM,eAAN;AAmZA,SAAS,qBAA0C;AACzD,SAAO,cAAc,iBAAiB,YAAY;AACnD;"}
1
+ {"version":3,"file":"floatlabel.js","sources":["../../../src/components/floatlabel/floatlabel.ts"],"sourcesContent":["import { FxElement } from \"../../core/base\";\nimport { css } from \"../../core/css\";\nimport { defineElement } from \"../../core/define\";\n\n/**\n * Rótulo flutuante .\n *\n * Envolve um campo de formulário (fx-input, fx-select, fx-multiselect,\n * fx-datepicker, ...) + um <label>. A label se comporta como placeholder\n * quando o campo está vazio e \"sobe\" quando o campo é focado, contém valor\n * ou abre dropdown.\n *\n * - variant=\"on\" (padrão): label sobre a borda superior\n * - variant=\"in\" : label como placeholder interno, sobe ao focar/valor\n * - variant=\"over\" : label estática acima do campo\n *\n * - error / invalid: label + borda em vermelho, mensagem menor abaixo.\n * - success / valid: label + borda em verde.\n *\n * A <label> do light DOM é MOVIDA para dentro do Shadow DOM (como `.flabel`)\n * na primeira renderização — a partir daí NÃO existe cópia no light DOM.\n * Ocultá-la com `hidden` (solução anterior) era frágil: o consumidor, o\n * framework ou uma folha de estilo podiam remover o atributo e a label\n * original reaparecia embaixo do campo, quebrando o floatlabel. Mover o\n * elemento é estrutural: não há nada removível lá fora.\n */\nexport class FxFloatlabel extends FxElement {\n\tstatic override get observedAttributes(): string[] {\n\t\treturn [\"variant\", \"error\", \"invalid\", \"success\", \"valid\", \"full\", \"icon\", \"icon-pos\", \"error-text\"];\n\t}\n\n\tget error(): boolean {\n\t\treturn this.hasAttr(\"error\");\n\t}\n\tset error(value: boolean) {\n\t\tthis.toggleAttr(\"error\", Boolean(value));\n\t}\n\n\tget invalid(): boolean {\n\t\treturn this.hasAttr(\"invalid\");\n\t}\n\tset invalid(value: boolean) {\n\t\tthis.toggleAttr(\"invalid\", Boolean(value));\n\t}\n\n\tget success(): boolean {\n\t\treturn this.hasAttr(\"success\");\n\t}\n\tset success(value: boolean) {\n\t\tthis.toggleAttr(\"success\", Boolean(value));\n\t}\n\n\tget valid(): boolean {\n\t\treturn this.hasAttr(\"valid\");\n\t}\n\tset valid(value: boolean) {\n\t\tthis.toggleAttr(\"valid\", Boolean(value));\n\t}\n\n\tget full(): boolean {\n\t\treturn this.hasAttr(\"full\");\n\t}\n\tset full(value: boolean) {\n\t\tthis.toggleAttr(\"full\", Boolean(value));\n\t}\n\n\tget errorText(): string {\n\t\treturn this.getAttr(\"error-text\");\n\t}\n\tset errorText(value: string | null | undefined) {\n\t\tthis.setAttribute(\"error-text\", value == null ? \"\" : String(value));\n\t}\n\t// 'active' e 'focus' NÃO entram em observedAttributes: togglá-los apenas\n\t// atualiza CSS via :host([...]), sem re-render destrutivo (mantém o foco\n\t// do campo). 'active' levanta a label; 'focus' é o que a destaca.\n\n\tstatic override styles = css`\n\t\t:host {\n\t\t\tdisplay: inline-block;\n\t\t\tposition: relative;\n\t\t\tfont-family: var(--fx-font-family);\n\t\t\tfont-size: var(--fx-font-size);\n\t\t\tvertical-align: middle;\n\t\t\t/* Cor da label em UM único lugar. active (valor OU foco OU\n\t\t\t dropdown) só posiciona; focus (foco OU dropdown) só destaca —\n\t\t\t assim a label acompanha a borda, que só fica primária em foco. */\n\t\t\t--_flabel-color: var(--fx-text-muted, #94a3b8);\n\t\t}\n\t\t/* Full width: o host estica até o pai; o controle interno com\n\t\t atributo full (ou largura herdada) acompanha. */\n\t\t:host([full]) {\n\t\t\tdisplay: block;\n\t\t\twidth: 100%;\n\t\t}\n\t\t.wrapper {\n\t\t\tposition: relative;\n\t\t\tdisplay: inline-block;\n\t\t\twidth: 100%;\n\t\t}\n\t\t/* Caixa do campo: referência de posicionamento da label flutuante\n (isolada da mensagem de erro, que cresce o wrapper). */\n\t\t.field-box {\n\t\t\tposition: relative;\n\t\t\tdisplay: inline-block;\n\t\t\twidth: 100%;\n\t\t}\n\t\t:host([variant=\"in\"]) .field-box {\n\t\t\tpadding-top: var(--fx-space-xs, 6px);\n\t\t}\n\n\t\t/* ---- label base (variant=\"on\", padrão): sobre a borda superior ---- */\n\t\t.flabel {\n\t\t\tposition: absolute;\n\t\t\tleft: var(--fx-space-md, 12px);\n\t\t\ttop: 0;\n\t\t\ttransform: translateY(-50%);\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 600;\n\t\t\tcolor: var(--_flabel-color);\n\t\t\tbackground-color: var(--fx-surface-background, #fff);\n\t\t\tpadding: 0 4px;\n\t\t\tline-height: 1;\n\t\t\tborder-radius: 2px;\n\t\t\tpointer-events: none;\n\t\t\tz-index: 2;\n\t\t\twhite-space: nowrap;\n\t\t\ttransition:\n\t\t\t\ttop var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out),\n\t\t\t\ttransform var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out),\n\t\t\t\tfont-size var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out),\n\t\t\t\tcolor var(--fx-motion-duration-normal, 180ms)\n\t\t\t\t\tvar(--fx-motion-easing, ease-in-out);\n\t\t}\n\t\t/* Destacada = foco (ou dropdown aberto). NÃO usar [active]: ele\n\t\t também é verdadeiro com valor e sem foco — era o que deixava a\n\t\t label primária com a borda neutra. */\n\t\t:host([focus]) {\n\t\t\t--_flabel-color: var(--fx-color-primary, #4f46e5);\n\t\t}\n\n\t\t/* ---- variant=\"in\": label como placeholder dentro, sobe ao focar/valor ---- */\n\t\t:host([variant=\"in\"]) .flabel {\n\t\t\ttop: 50%;\n\t\t\ttransform: translateY(-50%);\n\t\t\tfont-size: var(--fx-font-size, 14px);\n\t\t\tfont-weight: var(--fx-font-weight, 400);\n\t\t\tbackground: transparent;\n\t\t\tpadding: 0;\n\t\t}\n\t\t:host([variant=\"in\"][active]) .flabel {\n\t\t\ttop: 0;\n\t\t\ttransform: translateY(-50%);\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 600;\n\t\t\tbackground-color: var(--fx-surface-background, #fff);\n\t\t\tpadding: 0 5px;\n\t\t\t/* Sem color aqui: a especificidade desta regra\n\t\t\t (variant + active) venceria :host([error])/:host([focus]). */\n\t\t}\n\n\t\t/* ---- variant=\"in\" com ícone dentro do campo (fx-input icon):\n\t\t enquanto a label funciona como placeholder (sem foco/valor), ela\n\t\t começa DEPOIS do glifo (mesma métrica de padding do fx-input).\n\t\t Ao subir (active), volta ao offset padrão da borda. ---- */\n\t\t:host([variant=\"in\"]) .flabel.icon-indent {\n\t\t\tleft: calc(\n\t\t\t\tvar(--fx-space-md, 12px) +\n\t\t\t\tvar(--fx-input-icon-size, calc(var(--fx-font-size, 14px) + 6px)) +\n\t\t\t\tvar(--fx-space-sm, 8px)\n\t\t\t);\n\t\t}\n\t\t:host([variant=\"in\"][active]) .flabel.icon-indent {\n\t\t\tleft: var(--fx-space-md, 12px);\n\t\t}\n\n\t\t/* ---- variant=\"over\": label estática ACIMA do campo (fora, em fluxo) ---- */\n\t\t:host([variant=\"over\"]) {\n\t\t\tdisplay: block;\n\t\t}\n\t\t:host([variant=\"over\"]) .flabel {\n\t\t\tposition: static;\n\t\t\tdisplay: block;\n\t\t\tmargin-bottom: var(--fx-space-xs, 4px);\n\t\t\tbackground: transparent;\n\t\t\tpadding: 0;\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 600;\n\t\t\twhite-space: normal;\n\t\t\t/* Fora do campo, a label pode receber clique: o listener do\n\t\t\t render() foca o controle (nas variantes on/in o\n\t\t\t pointer-events: none é intencional, para não bloquear a borda). */\n\t\t\tpointer-events: auto;\n\t\t}\n\n\t\t/* ---- estados de erro / sucesso ----\n\t\t Declarados DEPOIS de :host([focus]) e com a mesma especificidade:\n\t\t a validação vence o foco pela ordem, sem depender de acidente. */\n\t\t:host([error]),\n\t\t:host([invalid]) {\n\t\t\t--_flabel-color: var(--fx-color-danger, #dc2626);\n\t\t}\n\t\t:host([success]),\n\t\t:host([valid]) {\n\t\t\t--_flabel-color: var(--fx-color-success, #10b981);\n\t\t}\n\n\t\t/* ---- mensagem de erro sob o campo (fonte menor) ---- */\n\t\t.error-message {\n\t\t\tdisplay: none;\n\t\t\tmargin-top: var(--fx-space-xs, 4px);\n\t\t\tmargin-left: var(--fx-space-md, 12px);\n\t\t\tfont-size: calc(var(--fx-font-size, 14px) - 3px);\n\t\t\tfont-weight: 500;\n\t\t\tcolor: var(--fx-color-danger, #dc2626);\n\t\t\tline-height: 1.2;\n\t\t}\n\t\t:host([error]) .error-message,\n\t\t:host([invalid]) .error-message {\n\t\t\tdisplay: block;\n\t\t}\n\t`;\n\tprivate targetControl: HTMLElement | null = null;\n\tprivate observer: MutationObserver | null = null;\n\tprivate listenersReady = false;\n\t/** <label> adotada do light DOM — vive no Shadow DOM após o 1º render. */\n\tprivate _label: HTMLElement | null = null;\n\tprivate _labelClickBound = false;\n\t/** Observa o light DOM para readotar uma <label> recriada pelo framework. */\n\tprivate _labelObserver?: MutationObserver;\n\t/** Timer do `focusout`: confirma o estado no próximo tick (foco já movido). */\n\tprivate _syncTimer: ReturnType<typeof setTimeout> | undefined;\n\n\tprotected override connectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tthis._labelObserver = new MutationObserver(() => {\n\t\t\tconst label = this.findLabel();\n\t\t\tif (label && label !== this._label) this.render();\n\t\t});\n\t\tthis._labelObserver.observe(this, { childList: true });\n\t}\n\n\tprotected render(): void {\n\t\tconst control = this.findControl();\n\t\t// Adota a <label> do light DOM (na 1ª renderização, ou se o consumidor\n\t\t// trocar por outra depois). Uma vez movida, o render seguinte não a\n\t\t// encontra no light DOM — a referência `_label` é reutilizada.\n\t\tconst lightLabel = this.findLabel();\n\t\tif (lightLabel) this._label = lightLabel;\n\t\tconst label = this._label;\n\t\tconst isOver = this.getAttribute(\"variant\") === \"over\";\n\n\t\tthis.setTemplate(`\n <div class=\"wrapper\" part=\"wrapper\">\n <div class=\"field-box\" part=\"field\">\n <slot></slot>\n </div>\n <div class=\"error-message\" part=\"error\"></div>\n </div>\n `);\n\n\t\t// A label original é INSERIDA no Shadow DOM (move o próprio elemento —\n\t\t// não é uma cópia). `setTemplate` reconstrói o template a cada render e\n\t\t// a label desanexada é reinserida no novo wrapper, com estado intacto.\n\t\tif (label) {\n\t\t\tlabel.classList.add(\"flabel\");\n\t\t\tlabel.setAttribute(\"part\", \"label\");\n\t\t\tconst wrapper = this.root.querySelector(\".wrapper\");\n\t\t\tconst fieldBox = this.root.querySelector(\".field-box\");\n\t\t\tif (wrapper && fieldBox) {\n\t\t\t\tif (isOver) wrapper.insertBefore(label, fieldBox);\n\t\t\t\telse fieldBox.appendChild(label);\n\t\t\t}\n\t\t\t// `for` não cruza a fronteira do shadow (o id do controle vive no\n\t\t\t// light DOM): o clique na label foca o campo manualmente.\n\t\t\tif (!this._labelClickBound) {\n\t\t\t\tthis._labelClickBound = true;\n\t\t\t\tlabel.addEventListener(\"click\", (e) => {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\t(this.targetControl as HTMLElement | null)?.focus?.();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.attachEvents();\n\t\tthis.syncControlReference(control);\n\t\tthis.syncState();\n\t\tthis.applyError();\n\t\tthis.applyFull();\n\t\tthis.applyIcon();\n\t\tthis.syncIconIndent();\n\t}\n\n\tprotected override disconnectedCallback(): void {\n\t\tsuper.disconnectedCallback();\n\t\tthis.cancelScheduledSync();\n\t\tthis.observer?.disconnect();\n\t\tthis._labelObserver?.disconnect();\n\t\tthis._labelObserver = undefined;\n\t}\n\n\t/** Campo de controle = qualquer filho direto que não seja label. */\n\tprivate findControl(): HTMLElement | null {\n\t\treturn (\n\t\t\t(Array.from(this.children).find(\n\t\t\t\t(n) => n.tagName.toLowerCase() !== \"label\",\n\t\t\t) as HTMLElement) ?? null\n\t\t);\n\t}\n\n\t/** Label = filho direto <label>. */\n\tprivate findLabel(): HTMLElement | null {\n\t\treturn (\n\t\t\t(Array.from(this.children).find(\n\t\t\t\t(n) => n.tagName.toLowerCase() === \"label\",\n\t\t\t) as HTMLElement) ?? null\n\t\t);\n\t}\n\n\tprivate attachEvents(): void {\n\t\tconst slot = this.root.querySelector(\"slot\");\n\t\tif (slot) {\n\t\t\tslot.addEventListener(\"slotchange\", () => {\n\t\t\t\t// Se o usuário trocar o controle/label em tempo de execução, re-sincroniza.\n\t\t\t\tconst control = this.findControl();\n\t\t\t\tthis.syncControlReference(control);\n\t\t\t\tthis.syncState();\n\t\t\t\tthis.applyError();\n\t\t\t\tthis.applyFull();\n\t\t\t});\n\t\t}\n\n\t\tif (this.listenersReady) return;\n\t\tthis.listenersReady = true;\n\n\t\tthis.addEventListener(\"focusin\", () => {\n\t\t\t// Um foco novo invalida o `focusout` pendente: sem isso o\n\t\t\t// sync atrasado poderia reaplicar o estado anterior.\n\t\t\tthis.cancelScheduledSync();\n\t\t\tthis.syncState();\n\t\t});\n\t\tthis.addEventListener(\"focusout\", () => this.scheduleSync());\n\t\tthis.addEventListener(\"input\", () => this.syncState());\n\t\tthis.addEventListener(\"change\", () => this.syncState());\n\t}\n\n\t/** Reavalia o estado no próximo tick (o foco já saiu do campo). */\n\tprivate scheduleSync(): void {\n\t\tthis.cancelScheduledSync();\n\t\tthis._syncTimer = setTimeout(() => {\n\t\t\tthis._syncTimer = undefined;\n\t\t\tthis.syncState();\n\t\t}, 0);\n\t}\n\n\tprivate cancelScheduledSync(): void {\n\t\tif (this._syncTimer !== undefined) {\n\t\t\tclearTimeout(this._syncTimer);\n\t\t\tthis._syncTimer = undefined;\n\t\t}\n\t}\n\n\t/** Observa mudanças de atributos do controle (value, open, values...). */\n\tprivate syncControlReference(\n\t\tcontrol: HTMLElement | null = this.targetControl,\n\t): void {\n\t\tif (control !== this.targetControl) {\n\t\t\tthis.observer?.disconnect();\n\t\t\tthis.observer = null;\n\t\t\tthis.targetControl = control;\n\t\t\tif (control) {\n\t\t\t\tthis.observer = new MutationObserver(() => {\n\t\t\t\t\tthis.syncState();\n\t\t\t\t\tthis.syncIconIndent();\n\t\t\t\t});\n\t\t\t\tthis.observer.observe(control, {\n\t\t\t\t\tattributes: true,\n\t\t\t\t\tattributeFilter: [\"value\", \"values\", \"start\", \"end\", \"open\", \"icon\", \"icon-pos\"],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate syncState(): void {\n\t\tif (this.targetControl && !this.targetControl.isConnected) {\n\t\t\tthis.targetControl = null;\n\t\t\tthis.observer?.disconnect();\n\t\t\tthis.observer = null;\n\t\t}\n\n\t\tif (!this.targetControl) {\n\t\t\tthis.removeAttribute(\"active\");\n\t\t\tthis.removeAttribute(\"focus\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst c = this.targetControl as any;\n\t\tconst attrValue = c.getAttribute\n\t\t\t? (c.getAttribute(\"value\") ?? \"\").trim()\n\t\t\t: \"\";\n\t\tconst attrValues = c.getAttribute\n\t\t\t? (c.getAttribute(\"values\") ?? \"\").trim()\n\t\t\t: \"\";\n\t\t// `open` = dropdown/calendário aberto; `start` = data escolhida em\n\t\t// componentes de range (é VALOR, não \"aberto\").\n\t\tconst isOpen = Boolean(c.hasAttribute?.(\"open\"));\n\t\tconst hasValue = Boolean(\n\t\t\t(typeof c.value === \"string\" && c.value.trim() !== \"\") ||\n\t\t\t(Array.isArray(c.values) && c.values.length > 0) ||\n\t\t\tattrValue !== \"\" ||\n\t\t\tattrValues !== \"\" ||\n\t\t\t(c.hasAttribute && c.hasAttribute(\"start\")),\n\t\t);\n\n\t\tconst focused = this.isFocusedInside();\n\t\t// `active` só levanta a label (valor, foco ou dropdown aberto);\n\t\t// `focus` é o que a destaca no CSS — e inclui o dropdown aberto\n\t\t// para o destaque não depender de onde o foco está.\n\t\tconst highlighted = focused || isOpen;\n\n\t\tif (hasValue || highlighted) this.setAttribute(\"active\", \"\");\n\t\telse this.removeAttribute(\"active\");\n\n\t\tif (highlighted) this.setAttribute(\"focus\", \"\");\n\t\telse this.removeAttribute(\"focus\");\n\t}\n\n\t/**\n\t * variant=\"in\" + ícone à esquerda dentro do campo (fx-input icon):\n\t * enquanto a label é placeholder ela precisa começar depois do glifo.\n\t * Detecta via atributos icon/icon-pos (ou slot icon) do controle.\n\t */\n\tprivate syncIconIndent(): void {\n\t\tconst label = this.root.querySelector(\".flabel\");\n\t\tif (!label) return;\n\t\tconst c = this.targetControl;\n\t\tconst hasLeftIcon = Boolean(\n\t\t\tc &&\n\t\t\t(c.hasAttribute(\"icon\") || c.querySelector(':scope > [slot=\"icon\"]')) &&\n\t\t\tc.getAttribute(\"icon-pos\") !== \"right\",\n\t\t);\n\t\tlabel.classList.toggle(\"icon-indent\", hasLeftIcon);\n\t}\n\n\tprivate applyFull(): void {\n\t\t// Espelha o full no controle interno: o CSS :host([full]) do floatlabel\n\t\t// estica apenas o HOST — sem isso o campo interno (ex.: fx-input com\n\t\t// width 260px fixa) não acompanharia o container.\n\t\tif (\n\t\t\tthis.targetControl &&\n\t\t\ttypeof this.targetControl.setAttribute === \"function\"\n\t\t) {\n\t\t\tif (this.hasAttr(\"full\")) {\n\t\t\t\tthis.targetControl.setAttribute(\"full\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.targetControl.removeAttribute(\"full\");\n\t\t\t}\n\t\t\t}\n\t}\n\n\tprivate applyIcon(): void {\n\t\tif (\n\t\t\tthis.targetControl &&\n\t\t\ttypeof this.targetControl.setAttribute === \"function\"\n\t\t) {\n\t\t\t// Só propaga icon/icon-pos se o FLOATLABEL possuir o atributo.\n\t\t\t// Se o ícone foi pondo diretamente no controle (ex.: <fx-input icon=\"lock\">),\n\t\t\t// não remove — deixa o controle manter o seu próprio ícone.\n\t\t\tif (this.hasAttr(\"icon\")) {\n\t\t\t\tconst icon = this.getAttr(\"icon\");\n\t\t\t\tconst iconPos = this.getAttr(\"icon-pos\");\n\t\t\t\tif (icon !== \"\") this.targetControl.setAttribute(\"icon\", icon);\n\t\t\t\telse this.targetControl.setAttribute(\"icon\", \"\");\n\t\t\t\tif (iconPos && iconPos !== \"left\") this.targetControl.setAttribute(\"icon-pos\", iconPos);\n\t\t\t\telse this.targetControl.removeAttribute(\"icon-pos\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate applyError(): void {\n\t\tconst hasError = this.hasAttr(\"error\") || this.hasAttr(\"invalid\");\n\t\tconst errMsg = this.root.querySelector(\n\t\t\t\".error-message\",\n\t\t) as HTMLElement | null;\n\t\tif (errMsg) {\n\t\t\terrMsg.textContent = hasError\n\t\t\t\t? this.getAttribute(\"error-text\")?.trim() || \"Valor inválido\"\n\t\t\t\t: \"\";\n\t\t}\n\n\t\t// Espelha o estado no controle interno para que a borda também fique vermelha/verde.\n\t\tif (\n\t\t\tthis.targetControl &&\n\t\t\ttypeof this.targetControl.setAttribute === \"function\"\n\t\t) {\n\t\t\tconst t = this.targetControl;\n\t\t\tif (hasError) {\n\t\t\t\tt.setAttribute(\"error\", \"\");\n\t\t\t\tt.removeAttribute(\"success\");\n\t\t\t} else if (this.hasAttr(\"success\") || this.hasAttr(\"valid\")) {\n\t\t\t\tt.setAttribute(\"success\", \"\");\n\t\t\t\tt.removeAttribute(\"error\");\n\t\t\t} else {\n\t\t\t\tt.removeAttribute(\"error\");\n\t\t\t\tt.removeAttribute(\"success\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate isWithin(activeEl: Element): boolean {\n\t\tlet current: Element | null = activeEl;\n\t\twhile (current && current !== this) {\n\t\t\tcurrent = current.parentElement;\n\t\t}\n\t\treturn current === this;\n\t}\n\n\tprivate isFocusedInside(): boolean {\n\t\tconst activeEl = (document as Document).activeElement;\n\t\treturn Boolean(activeEl && this.isWithin(activeEl));\n\t}\n}\n\nexport function defineFxFloatlabel(): typeof FxFloatlabel {\n\treturn defineElement(\"fx-floatlabel\", FxFloatlabel);\n}\n"],"names":[],"mappings":"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"}
@@ -33,6 +33,10 @@ export declare class FxIcon extends FxElement {
33
33
  /** Nome acessível; vazio = ícone decorativo (aria-hidden). */
34
34
  get label(): string;
35
35
  set label(value: string);
36
+ get fill(): boolean;
37
+ set fill(value: boolean);
38
+ get bold(): boolean;
39
+ set bold(value: boolean);
36
40
  protected connectedCallback(): void;
37
41
  protected render(): void;
38
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/components/icon/icon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,SAoBpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,2DAA2D;IAC3D,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAGrB;IAED,8DAA8D;IAC9D,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;cAEkB,iBAAiB,IAAI,IAAI;cAMzB,MAAM,IAAI,IAAI;CAclC;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
1
+ {"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/components/icon/icon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,SAoBpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,2DAA2D;IAC3D,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAGrB;IAED,8DAA8D;IAC9D,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAEtB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAEtB;cAEkB,iBAAiB,IAAI,IAAI;cAMzB,MAAM,IAAI,IAAI;CAclC;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
@@ -31,6 +31,18 @@ const _FxIcon = class _FxIcon extends FxElement {
31
31
  if (value) this.setAttribute("label", value);
32
32
  else this.removeAttribute("label");
33
33
  }
34
+ get fill() {
35
+ return this.hasAttr("fill");
36
+ }
37
+ set fill(value) {
38
+ this.toggleAttr("fill", Boolean(value));
39
+ }
40
+ get bold() {
41
+ return this.hasAttr("bold");
42
+ }
43
+ set bold(value) {
44
+ this.toggleAttr("bold", Boolean(value));
45
+ }
34
46
  connectedCallback() {
35
47
  loadFenixIconsFont();
36
48
  super.connectedCallback();
@@ -1 +1 @@
1
- {"version":3,"file":"icon.js","sources":["../../../src/components/icon/icon.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, isFenixIconName } from '../../icons/base-css';\nimport { loadFenixIconsFont } from '../../icons/font';\n\n/**\n * <fx-icon> — Glifo da Fenix Icons com AUTOCOMPLETE no editor.\n *\n * Por que existe: a forma antiga (`<i class=\"fx-icon fx-icon-home\">`) é CSS\n * puro — o TypeScript não valida nem sugere nada enquanto você digita, e o\n * auto-import só injeta o import. Com o elemento, o atributo `name` é tipado\n * como `FenixIconName`, então Volar/vue-tsc, React e TSX listam os +4.000\n * glifos válidos (e o hover mostra a assinatura do atributo).\n *\n * Atributos:\n * - name → nome do glifo (ex.: `home`); texto livre/emoji também funciona\n * - size → sm | md | lg | xl (default: 1em, herda o texto ao redor)\n * - label → nome acessível; sem label o ícone é aria-hidden (decorativo)\n * - fill → variante preenchida\n * - bold → traço mais pesado\n *\n * O glifo sai por LIGADURA (o nome vira o conteúdo de texto com a\n * font-family de ícones) — por isso funciona dentro do Shadow DOM, onde as\n * classes `fx-icon-<nome>` do documento não atravessam a fronteira.\n *\n * Tamanho via CSS: --fx-icon-size (-sm/-md/-lg/-xl).\n */\nexport class FxIcon extends FxElement {\n static override styles = css`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n vertical-align: middle;\n line-height: 1;\n color: currentColor;\n font-size: var(--fx-icon-size, 1em);\n }\n :host([size='sm']) { font-size: var(--fx-icon-size-sm, 16px); }\n :host([size='md']) { font-size: var(--fx-icon-size-md, 20px); }\n :host([size='lg']) { font-size: var(--fx-icon-size-lg, 24px); }\n :host([size='xl']) { font-size: var(--fx-icon-size-xl, 32px); }\n :host([hidden]) { display: none; }\n .icon { display: inline-block; }\n /* Variantes da fonte (FILL/wght): vencem a classe base .fx-icon. */\n :host([fill]) .icon { font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; }\n :host([bold]) .icon { font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 24; }\n ${FENIX_ICON_BASE_CSS}\n `;\n\n static override get observedAttributes(): string[] {\n return ['name', 'size', 'label', 'fill', 'bold'];\n }\n\n /** Nome do glifo (ou texto livre/emoji). */\n get name(): string {\n return this.getAttr('name');\n }\n set name(value: string) {\n this.setAttribute('name', value);\n }\n\n /** Tamanho: '' (herda 1em) | 'sm' | 'md' | 'lg' | 'xl'. */\n get size(): string {\n return this.getAttr('size');\n }\n set size(value: string) {\n if (value) this.setAttribute('size', value);\n else this.removeAttribute('size');\n }\n\n /** Nome acessível; vazio = ícone decorativo (aria-hidden). */\n get label(): string {\n return this.getAttr('label');\n }\n set label(value: string) {\n if (value) this.setAttribute('label', value);\n else this.removeAttribute('label');\n }\n\n protected override connectedCallback(): void {\n // O glifo depende do @font-face: garante o CSS leve (sem as 4k classes).\n loadFenixIconsFont();\n super.connectedCallback();\n }\n\n protected override render(): void {\n const name = this.getAttr('name').trim();\n const label = this.getAttr('label').trim();\n const a11y = label ? `role=\"img\" aria-label=\"${esc(label)}\"` : 'aria-hidden=\"true\"';\n if (!name) {\n this.setTemplate(`<span class=\"icon\" part=\"icon\" ${a11y}></span>`);\n return;\n }\n // Nome válido → ligadura (glifo). Emoji/texto livre → texto puro.\n const inner = isFenixIconName(name)\n ? `<i class=\"icon fx-icon\" part=\"icon\" ${a11y}>${esc(name)}</i>`\n : `<span class=\"icon\" part=\"icon\" ${a11y}>${esc(name)}</span>`;\n this.setTemplate(inner);\n }\n}\n\nexport function defineFxIcon(): typeof FxIcon {\n return defineElement('fx-icon', FxIcon);\n}\n"],"names":[],"mappings":";;;;;;AA6BO,MAAM,UAAN,MAAM,gBAAe,UAAU;AAAA,EAuBpC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,QAAQ,QAAQ,SAAS,QAAQ,MAAM;AAAA,EACjD;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,SAAK,aAAa,QAAQ,KAAK;AAAA,EACjC;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,QAAI,MAAO,MAAK,aAAa,QAAQ,KAAK;AAAA,QACrC,MAAK,gBAAgB,MAAM;AAAA,EAClC;AAAA;AAAA,EAGA,IAAI,QAAgB;AAClB,WAAO,KAAK,QAAQ,OAAO;AAAA,EAC7B;AAAA,EACA,IAAI,MAAM,OAAe;AACvB,QAAI,MAAO,MAAK,aAAa,SAAS,KAAK;AAAA,QACtC,MAAK,gBAAgB,OAAO;AAAA,EACnC;AAAA,EAEmB,oBAA0B;AAE3C,uBAAA;AACA,UAAM,kBAAA;AAAA,EACR;AAAA,EAEmB,SAAe;AAChC,UAAM,OAAO,KAAK,QAAQ,MAAM,EAAE,KAAA;AAClC,UAAM,QAAQ,KAAK,QAAQ,OAAO,EAAE,KAAA;AACpC,UAAM,OAAO,QAAQ,0BAA0B,IAAI,KAAK,CAAC,MAAM;AAC/D,QAAI,CAAC,MAAM;AACT,WAAK,YAAY,kCAAkC,IAAI,UAAU;AACjE;AAAA,IACF;AAEA,UAAM,QAAQ,gBAAgB,IAAI,IAC9B,uCAAuC,IAAI,IAAI,IAAI,IAAI,CAAC,SACxD,kCAAkC,IAAI,IAAI,IAAI,IAAI,CAAC;AACvD,SAAK,YAAY,KAAK;AAAA,EACxB;AACF;AAxEE,QAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAmBrB,mBAAmB;AAAA;AApBlB,IAAM,SAAN;AA2EA,SAAS,eAA8B;AAC5C,SAAO,cAAc,WAAW,MAAM;AACxC;"}
1
+ {"version":3,"file":"icon.js","sources":["../../../src/components/icon/icon.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, isFenixIconName } from '../../icons/base-css';\nimport { loadFenixIconsFont } from '../../icons/font';\n\n/**\n * <fx-icon> — Glifo da Fenix Icons com AUTOCOMPLETE no editor.\n *\n * Por que existe: a forma antiga (`<i class=\"fx-icon fx-icon-home\">`) é CSS\n * puro — o TypeScript não valida nem sugere nada enquanto você digita, e o\n * auto-import só injeta o import. Com o elemento, o atributo `name` é tipado\n * como `FenixIconName`, então Volar/vue-tsc, React e TSX listam os +4.000\n * glifos válidos (e o hover mostra a assinatura do atributo).\n *\n * Atributos:\n * - name → nome do glifo (ex.: `home`); texto livre/emoji também funciona\n * - size → sm | md | lg | xl (default: 1em, herda o texto ao redor)\n * - label → nome acessível; sem label o ícone é aria-hidden (decorativo)\n * - fill → variante preenchida\n * - bold → traço mais pesado\n *\n * O glifo sai por LIGADURA (o nome vira o conteúdo de texto com a\n * font-family de ícones) — por isso funciona dentro do Shadow DOM, onde as\n * classes `fx-icon-<nome>` do documento não atravessam a fronteira.\n *\n * Tamanho via CSS: --fx-icon-size (-sm/-md/-lg/-xl).\n */\nexport class FxIcon extends FxElement {\n static override styles = css`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n vertical-align: middle;\n line-height: 1;\n color: currentColor;\n font-size: var(--fx-icon-size, 1em);\n }\n :host([size='sm']) { font-size: var(--fx-icon-size-sm, 16px); }\n :host([size='md']) { font-size: var(--fx-icon-size-md, 20px); }\n :host([size='lg']) { font-size: var(--fx-icon-size-lg, 24px); }\n :host([size='xl']) { font-size: var(--fx-icon-size-xl, 32px); }\n :host([hidden]) { display: none; }\n .icon { display: inline-block; }\n /* Variantes da fonte (FILL/wght): vencem a classe base .fx-icon. */\n :host([fill]) .icon { font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; }\n :host([bold]) .icon { font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 24; }\n ${FENIX_ICON_BASE_CSS}\n `;\n\n static override get observedAttributes(): string[] {\n return ['name', 'size', 'label', 'fill', 'bold'];\n }\n\n /** Nome do glifo (ou texto livre/emoji). */\n get name(): string {\n return this.getAttr('name');\n }\n set name(value: string) {\n this.setAttribute('name', value);\n }\n\n /** Tamanho: '' (herda 1em) | 'sm' | 'md' | 'lg' | 'xl'. */\n get size(): string {\n return this.getAttr('size');\n }\n set size(value: string) {\n if (value) this.setAttribute('size', value);\n else this.removeAttribute('size');\n }\n\n /** Nome acessível; vazio = ícone decorativo (aria-hidden). */\n get label(): string {\n return this.getAttr('label');\n }\n set label(value: string) {\n if (value) this.setAttribute('label', value);\n else this.removeAttribute('label');\n }\n\n get fill(): boolean {\n return this.hasAttr('fill');\n }\n set fill(value: boolean) {\n this.toggleAttr('fill', Boolean(value));\n }\n\n get bold(): boolean {\n return this.hasAttr('bold');\n }\n set bold(value: boolean) {\n this.toggleAttr('bold', Boolean(value));\n }\n\n protected override connectedCallback(): void {\n // O glifo depende do @font-face: garante o CSS leve (sem as 4k classes).\n loadFenixIconsFont();\n super.connectedCallback();\n }\n\n protected override render(): void {\n const name = this.getAttr('name').trim();\n const label = this.getAttr('label').trim();\n const a11y = label ? `role=\"img\" aria-label=\"${esc(label)}\"` : 'aria-hidden=\"true\"';\n if (!name) {\n this.setTemplate(`<span class=\"icon\" part=\"icon\" ${a11y}></span>`);\n return;\n }\n // Nome válido → ligadura (glifo). Emoji/texto livre → texto puro.\n const inner = isFenixIconName(name)\n ? `<i class=\"icon fx-icon\" part=\"icon\" ${a11y}>${esc(name)}</i>`\n : `<span class=\"icon\" part=\"icon\" ${a11y}>${esc(name)}</span>`;\n this.setTemplate(inner);\n }\n}\n\nexport function defineFxIcon(): typeof FxIcon {\n return defineElement('fx-icon', FxIcon);\n}\n"],"names":[],"mappings":";;;;;;AA6BO,MAAM,UAAN,MAAM,gBAAe,UAAU;AAAA,EAuBpC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,QAAQ,QAAQ,SAAS,QAAQ,MAAM;AAAA,EACjD;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,SAAK,aAAa,QAAQ,KAAK;AAAA,EACjC;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,QAAI,MAAO,MAAK,aAAa,QAAQ,KAAK;AAAA,QACrC,MAAK,gBAAgB,MAAM;AAAA,EAClC;AAAA;AAAA,EAGA,IAAI,QAAgB;AAClB,WAAO,KAAK,QAAQ,OAAO;AAAA,EAC7B;AAAA,EACA,IAAI,MAAM,OAAe;AACvB,QAAI,MAAO,MAAK,aAAa,SAAS,KAAK;AAAA,QACtC,MAAK,gBAAgB,OAAO;AAAA,EACnC;AAAA,EAEA,IAAI,OAAgB;AAClB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EACA,IAAI,KAAK,OAAgB;AACvB,SAAK,WAAW,QAAQ,QAAQ,KAAK,CAAC;AAAA,EACxC;AAAA,EAEA,IAAI,OAAgB;AAClB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EACA,IAAI,KAAK,OAAgB;AACvB,SAAK,WAAW,QAAQ,QAAQ,KAAK,CAAC;AAAA,EACxC;AAAA,EAEmB,oBAA0B;AAE3C,uBAAA;AACA,UAAM,kBAAA;AAAA,EACR;AAAA,EAEmB,SAAe;AAChC,UAAM,OAAO,KAAK,QAAQ,MAAM,EAAE,KAAA;AAClC,UAAM,QAAQ,KAAK,QAAQ,OAAO,EAAE,KAAA;AACpC,UAAM,OAAO,QAAQ,0BAA0B,IAAI,KAAK,CAAC,MAAM;AAC/D,QAAI,CAAC,MAAM;AACT,WAAK,YAAY,kCAAkC,IAAI,UAAU;AACjE;AAAA,IACF;AAEA,UAAM,QAAQ,gBAAgB,IAAI,IAC9B,uCAAuC,IAAI,IAAI,IAAI,IAAI,CAAC,SACxD,kCAAkC,IAAI,IAAI,IAAI,IAAI,CAAC;AACvD,SAAK,YAAY,KAAK;AAAA,EACxB;AACF;AAtFE,QAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAmBrB,mBAAmB;AAAA;AApBlB,IAAM,SAAN;AAyFA,SAAS,eAA8B;AAC5C,SAAO,cAAc,WAAW,MAAM;AACxC;"}
@@ -7,7 +7,8 @@ import type { FxInputType } from '../../core/jsx';
7
7
  * (via atributo `name`), recebe reset/disable do form, é alvo de
8
8
  * `<label for>`/`autofocus` e o Enter submete o form.
9
9
  *
10
- * Atributos: type (text|number|password), value, name,
10
+ * Atributos: type (text|number|password|email|tel|url|search|date|time|
11
+ * datetime-local|month|week|color), value, name,
11
12
  * size, placeholder, disabled, readonly, min, max, step, autofocus,
12
13
  * clearable, full (largura 100% acompanhando o elemento pai), icon (nome do
13
14
  * glifo Fenix Icons ou emoji/texto livre) e icon-pos (left|right, padrão left).
@@ -28,9 +29,15 @@ export declare class FxInput extends FxFormElement {
28
29
  get size(): string;
29
30
  set size(value: string);
30
31
  get value(): string;
31
- set value(value: string);
32
+ set value(value: string | number | null | undefined);
32
33
  get disabled(): boolean;
33
34
  set disabled(value: boolean);
35
+ get readonly(): boolean;
36
+ set readonly(value: boolean);
37
+ get clearable(): boolean;
38
+ set clearable(value: boolean);
39
+ get full(): boolean;
40
+ set full(value: boolean);
34
41
  protected render(): void;
35
42
  /** Empurra o atributo `value` para o campo interno sem re-renderizar. */
36
43
  protected syncInternalControl(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/input/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAKxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAclD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,OAAQ,SAAQ,aAAa;IACxC,OAAgB,MAAM,SAuIpB;IAMF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAMjD;IAED,+CAA+C;IAC/C,IAAa,IAAI,IAAI,WAAW,CAE/B;IACD,IAAa,IAAI,CAAC,KAAK,EAAE,WAAW,EAEnC;IAED,wCAAwC;IACxC,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAMtB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;cAEkB,MAAM,IAAI,IAAI;IAiFjC,yEAAyE;cACtD,mBAAmB,IAAI,IAAI;CAO/C;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/input/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAKxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA4BlD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,OAAQ,SAAQ,aAAa;IACxC,OAAgB,MAAM,SAuIpB;IAMF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAOjD;IAED,+CAA+C;IAC/C,IAAa,IAAI,IAAI,WAAW,CAE/B;IACD,IAAa,IAAI,CAAC,KAAK,EAAE,WAAW,EAEnC;IAED,wCAAwC;IACxC,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAMlD;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,OAAO,EAE3B;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAEtB;cAEkB,MAAM,IAAI,IAAI;IAgGjC,yEAAyE;cACtD,mBAAmB,IAAI,IAAI;CAO/C;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
@@ -3,7 +3,21 @@ import { css } from "../../core/css.js";
3
3
  import { defineElement } from "../../core/define.js";
4
4
  import { esc } from "../../core/sanitize.js";
5
5
  import { FENIX_ICON_BASE_CSS, fenixIconHtml } from "../../icons/base-css.js";
6
- const INPUT_TYPES = /* @__PURE__ */ new Set(["text", "number", "password"]);
6
+ const INPUT_TYPES = /* @__PURE__ */ new Set([
7
+ "text",
8
+ "number",
9
+ "password",
10
+ "email",
11
+ "tel",
12
+ "url",
13
+ "search",
14
+ "date",
15
+ "time",
16
+ "datetime-local",
17
+ "month",
18
+ "week",
19
+ "color"
20
+ ]);
7
21
  function normalizeInputType(value) {
8
22
  const type = (value || "text").toLowerCase();
9
23
  return INPUT_TYPES.has(type) ? type : "text";
@@ -23,6 +37,8 @@ const _FxInput = class _FxInput extends FxFormElement {
23
37
  "min",
24
38
  "max",
25
39
  "step",
40
+ "clearable",
41
+ "full",
26
42
  "icon",
27
43
  "icon-pos",
28
44
  "error",
@@ -50,7 +66,7 @@ const _FxInput = class _FxInput extends FxFormElement {
50
66
  return this.getAttr("value");
51
67
  }
52
68
  set value(value) {
53
- this.setAttribute("value", value);
69
+ this.setAttribute("value", value == null ? "" : String(value));
54
70
  this.syncInternalControl();
55
71
  this.updateFormValue();
56
72
  }
@@ -60,6 +76,24 @@ const _FxInput = class _FxInput extends FxFormElement {
60
76
  set disabled(value) {
61
77
  this.toggleAttr("disabled", Boolean(value));
62
78
  }
79
+ get readonly() {
80
+ return this.hasAttr("readonly");
81
+ }
82
+ set readonly(value) {
83
+ this.toggleAttr("readonly", Boolean(value));
84
+ }
85
+ get clearable() {
86
+ return this.hasAttr("clearable");
87
+ }
88
+ set clearable(value) {
89
+ this.toggleAttr("clearable", Boolean(value));
90
+ }
91
+ get full() {
92
+ return this.hasAttr("full");
93
+ }
94
+ set full(value) {
95
+ this.toggleAttr("full", Boolean(value));
96
+ }
63
97
  render() {
64
98
  const type = this.type;
65
99
  const placeholder = this.getAttr("placeholder");
@@ -90,20 +124,27 @@ const _FxInput = class _FxInput extends FxFormElement {
90
124
  field.value = this.getAttr("value");
91
125
  if (disabled) field.setAttribute("disabled", "");
92
126
  if (readonly) field.setAttribute("readonly", "");
93
- const emit = (event) => {
127
+ const notifyInput = () => {
94
128
  this.value = field.value;
95
129
  this.emitValueEvents();
130
+ };
131
+ const notifyChange = () => {
132
+ this.value = field.value;
96
133
  this.dispatchEvent(
97
- new CustomEvent(event, { bubbles: true, composed: true, detail: { value: field.value } })
134
+ new CustomEvent("change", {
135
+ bubbles: true,
136
+ composed: true,
137
+ detail: { value: field.value }
138
+ })
98
139
  );
99
140
  };
100
141
  field.addEventListener("input", (e) => {
101
142
  e.stopPropagation();
102
- emit("input");
143
+ notifyInput();
103
144
  });
104
145
  field.addEventListener("change", (e) => {
105
146
  e.stopPropagation();
106
- emit("change");
147
+ notifyChange();
107
148
  });
108
149
  field.addEventListener("keydown", (e) => {
109
150
  if (e.key !== "Enter" || e.isComposing) return;
@@ -119,8 +160,15 @@ const _FxInput = class _FxInput extends FxFormElement {
119
160
  clearBtn?.addEventListener("click", () => {
120
161
  field.value = "";
121
162
  syncClear();
122
- emit("input");
123
- emit("change");
163
+ this.value = "";
164
+ this.emitValueEvents();
165
+ this.dispatchEvent(
166
+ new CustomEvent("change", {
167
+ bubbles: true,
168
+ composed: true,
169
+ detail: { value: "" }
170
+ })
171
+ );
124
172
  field.focus();
125
173
  });
126
174
  field.addEventListener("input", syncClear);
@@ -1 +1 @@
1
- {"version":3,"file":"input.js","sources":["../../../src/components/input/input.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\nimport type { FxInputType } from '../../core/jsx';\n\n/**\n * Tipos suportados pelo <fx-input>: text, number e password.\n * Qualquer outro tipo (email, search, date, file, range, checkbox, radio…)\n * cai em `text` em runtime.\n */\nconst INPUT_TYPES = new Set(['text', 'number', 'password']);\n\nfunction normalizeInputType(value: string): string {\n const type = (value || 'text').toLowerCase();\n return INPUT_TYPES.has(type) ? type : 'text';\n}\n\n/**\n * <fx-input> — Campo de texto estilizado com os tokens do tema.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`), recebe reset/disable do form, é alvo de\n * `<label for>`/`autofocus` e o Enter submete o form.\n *\n * Atributos: type (text|number|password), value, name,\n * size, placeholder, disabled, readonly, min, max, step, autofocus,\n * clearable, full (largura 100% acompanhando o elemento pai), icon (nome do\n * glifo Fenix Icons ou emoji/texto livre) e icon-pos (left|right, padrão left).\n * Largura: o default (260px; sm 220px / lg 300px) fica no HOST via tokens\n * --fx-input-width(-sm|-lg) — CSS externo, classes e style inline no elemento\n * vencem o default sem precisar de atributo.\n * Slots: `icon` (ícone customizado no lugar do atributo).\n * Eventos: `input`, `change` e `valueChange` (composed, detail: { value }) —\n * `input` aciona o v-model do Vue e `valueChange` o [(value)] do Angular.\n */\nexport class FxInput extends FxFormElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n /* Largura padrão no HOST (e não no .field interno): assim CSS externo,\n classes do framework (ex.: w-full) e style inline no elemento vencem\n este default naturalmente — regras do documento têm precedência sobre\n :host. Default global trocável via token: --fx-input-width (-sm/-lg). */\n width: var(--fx-input-width, 260px);\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n .field {\n font-family: inherit;\n font-size: inherit;\n /* Altura determinística: com line-height fixo, o padding vertical cabe no\n token --fx-size-* e a caixa fica EXATAMENTE no tamanho padrão\n (sm 32 / md 40 / lg 48), igual para todos os campos. */\n line-height: var(--fx-font-line-height);\n min-height: var(--fx-size-md);\n font-weight: var(--fx-font-weight);\n color: var(--fx-text-default);\n background-color: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n padding: var(--fx-space-sm) var(--fx-space-lg);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n box-sizing: border-box;\n transition:\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .field::placeholder { color: var(--fx-text-muted); opacity: 1; }\n .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .field:disabled,\n .field[readonly] {\n opacity: 0.55;\n cursor: not-allowed;\n background-color: var(--fx-surface-surface-hover);\n }\n /* Validação: borda sempre; o brilho (anel) segue o token do preset\n --fx-effect-error-ring, que acompanha effect.focus-ring — anel\n desligado ⇒ erro só com a borda vermelha, ligado ⇒ soma o brilho. */\n :host([error]) .field,\n :host([invalid]) .field,\n :host([error]) .field:focus-visible,\n :host([invalid]) .field:focus-visible {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([success]) .field,\n :host([valid]) .field,\n :host([success]) .field:focus-visible,\n :host([valid]) .field:focus-visible {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([size='sm']) { width: var(--fx-input-width-sm, 220px); }\n :host([size='sm']) .field { padding: var(--fx-space-xs) var(--fx-space-md); font-size: var(--fx-font-size); min-height: var(--fx-size-sm); }\n :host([size='lg']) { width: var(--fx-input-width-lg, 300px); }\n :host([size='lg']) .field { padding: var(--fx-space-sm) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); min-height: var(--fx-size-lg); }\n /* Full width: o host estica até o pai e o campo interno acompanha\n (usar com um container de largura controlada, ex. w-full no Vue). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { width: 100%; }\n /* Clearable */\n :host([clearable]) { position: relative; display: inline-flex; }\n :host([clearable][full]) { display: block; }\n .wrap { position: relative; display: inline-flex; align-items: center; width: 100%; }\n :host([full]) .wrap { display: flex; width: 100%; }\n :host([clearable]) .field { padding-right: var(--fx-space-xl); }\n :host([clearable][full]) .field { flex: 1 1 auto; }\n /* Ícones (attr icon + slot icon) — DENTRO do campo, sobre o padding\n (padrão Material/PrimeVue). Tamanho ajustável via --fx-input-icon-size. */\n .field-icon {\n position: relative;\n display: inline-flex;\n box-sizing: border-box;\n width: 100%;\n }\n .field-icon .field { flex: 1 1 auto; min-width: 0; }\n :host([full]) .field-icon { display: flex; width: 100%; }\n .field-icon .fx-icon,\n .field-icon ::slotted([slot='icon']) {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px));\n line-height: 1;\n color: var(--fx-text-muted);\n pointer-events: none;\n z-index: 1;\n }\n .field-icon.icon-left .fx-icon,\n .field-icon.icon-left ::slotted([slot='icon']) { left: var(--fx-space-md); }\n .field-icon.icon-right .fx-icon,\n .field-icon.icon-right ::slotted([slot='icon']) { right: var(--fx-space-md); }\n /* padding = posição do ícone + largura + folga extra para o texto\n não colar no glifo. */\n .field-icon.icon-left .field {\n padding-left: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n .field-icon.icon-right .field {\n padding-right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n /* icon-pos right + clearable: desloca o × para não sobrepor o ícone. */\n .field-icon.icon-right + .clear {\n right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-lg));\n }\n .clear {\n position: absolute;\n right: var(--fx-space-xs);\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n cursor: pointer;\n padding: 0 var(--fx-space-xs);\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n /* Base dos glifos Fenix Icons (o @font-face vem de @wrrdev/fenix-ui/icons). */\n ${FENIX_ICON_BASE_CSS}\n `;\n\n // `value` fica FORA da observação: refleti-lo a cada tecla não pode\n // re-renderizar o template, senão o campo perde o foco ao digitar.\n // `error`/`success` (+ aliases `invalid`/`valid`) PRECISAM ser observados:\n // é assim que o toggle no playground / setAttribute no submit re-renderiza.\n static override get observedAttributes(): string[] {\n return [\n 'type', 'size', 'placeholder',\n 'disabled', 'readonly', 'min', 'max', 'step',\n 'icon', 'icon-pos', 'error', 'invalid', 'success', 'valid',\n ];\n }\n\n /** Tipo do input interno. Padrão: `'text'`. */\n override get type(): FxInputType {\n return normalizeInputType(this.getAttr('type', 'text')) as FxInputType;\n }\n override set type(value: FxInputType) {\n this.setAttribute('type', normalizeInputType(value));\n }\n\n /** Tamanho do campo. Padrão: `'md'`. */\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(value: string) {\n this.setAttribute('size', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(value: string) {\n this.setAttribute('value', value);\n // `value` NÃO é observado (re-render a cada tecla perderia o foco): o\n // campo interno é sincronizado aqui, sem re-render, preservando foco/cursor.\n this.syncInternalControl();\n this.updateFormValue();\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n protected override render(): void {\n const type = this.type;\n const placeholder = this.getAttr('placeholder');\n const readonly = this.hasAttr('readonly');\n const disabled = this.hasAttr('disabled');\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n const step = this.getAttr('step');\n const icon = this.getAttr('icon');\n const iconPos = this.getAttr('icon-pos', 'left') === 'right' ? 'right' : 'left';\n // Slot `icon` vence o atributo `icon`; sem nenhum dos dois, sem ícone.\n const slottedIcon = this.querySelector<HTMLElement>('[slot=\"icon\"]');\n const iconContent = slottedIcon\n ? '<slot name=\"icon\" part=\"icon\"></slot>'\n : (icon ? fenixIconHtml(icon) : '');\n const useIconBox = Boolean(iconContent);\n\n this.setTemplate(`\n ${this.hasAttr('clearable') ? '<div class=\"wrap\" part=\"wrap\">' : ''}\n ${useIconBox ? `<div class=\"field-icon icon-${iconPos}\">` : ''}\n ${useIconBox && iconPos === 'left' ? iconContent : ''}\n <input class=\"field\" part=\"input\" type=\"${esc(type)}\"\n ${placeholder ? `placeholder=\"${esc(placeholder)}\"` : ''}\n ${min ? `min=\"${esc(min)}\"` : ''} ${max ? `max=\"${esc(max)}\"` : ''} ${step ? `step=\"${esc(step)}\"` : ''}\n />\n ${useIconBox && iconPos === 'right' ? iconContent : ''}\n ${useIconBox ? '</div>' : ''}\n ${this.hasAttr('clearable') ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\" tabindex=\"-1\">×</button></div>' : ''}\n `);\n\n const field = this.root.querySelector<HTMLInputElement>('.field');\n if (!field) return;\n field.value = this.getAttr('value');\n if (disabled) field.setAttribute('disabled', '');\n if (readonly) field.setAttribute('readonly', '');\n\n const emit = (event: string): void => {\n this.value = field.value;\n // input (v-model do Vue) + valueChange ([(value)] do Angular).\n this.emitValueEvents();\n this.dispatchEvent(\n new CustomEvent(event, { bubbles: true, composed: true, detail: { value: field.value } }),\n );\n };\n // Bloqueia o evento nativo (composed, sem detail) para que só o\n // CustomEvent com detail chegue aos consumidores.\n field.addEventListener('input', (e) => { e.stopPropagation(); emit('input'); });\n field.addEventListener('change', (e) => { e.stopPropagation(); emit('change'); });\n\n // Enter submete o form: o <input> interno vive no Shadow DOM e NÃO se\n // associa ao <form> do light DOM (a fronteira do shadow corta a associação\n // por árvore) — por isso a submissão implícita é acionada manualmente.\n // Ouvintes de submit no form (ex.: submit.prevent de frameworks) seguem\n // recebendo o evento normalmente.\n field.addEventListener('keydown', (e) => {\n if (e.key !== 'Enter' || e.isComposing) return;\n if (!this.form) return;\n e.preventDefault();\n this.form.requestSubmit();\n });\n\n // Clearable: botão × que limpa o campo e emite input/change vazios.\n const clearBtn = this.root.querySelector<HTMLButtonElement>('.clear');\n const syncClear = (): void => {\n clearBtn?.toggleAttribute('hidden', field.value === '');\n };\n syncClear();\n clearBtn?.addEventListener('click', () => {\n field.value = '';\n syncClear();\n emit('input');\n emit('change');\n field.focus();\n });\n field.addEventListener('input', syncClear);\n\n // Publica name/value no form (ElementInternals) a cada render — cobre o\n // estado inicial e mudanças de atributos observados.\n this.updateFormValue();\n }\n\n /** Empurra o atributo `value` para o campo interno sem re-renderizar. */\n protected override syncInternalControl(): void {\n const field = this.root.querySelector<HTMLInputElement>('.field');\n const value = this.getAttr('value');\n const type = this.type;\n if (field && field.type !== type) field.type = type;\n if (field && field.value !== value) field.value = value;\n }\n}\n\nexport function defineFxInput(): typeof FxInput {\n return defineElement('fx-input', FxInput);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"input.js","sources":["../../../src/components/input/input.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\nimport type { FxInputType } from '../../core/jsx';\n\n/**\n * Tipos nativos aceitos pelo <fx-input>. Os tipos com semântica própria\n * (file, checkbox, radio, submit, reset, button, image) continuam fora:\n * eles não fazem sentido no componente de texto.\n */\nconst INPUT_TYPES = new Set([\n 'text',\n 'number',\n 'password',\n 'email',\n 'tel',\n 'url',\n 'search',\n 'date',\n 'time',\n 'datetime-local',\n 'month',\n 'week',\n 'color',\n]);\n\nfunction normalizeInputType(value: string): string {\n const type = (value || 'text').toLowerCase();\n return INPUT_TYPES.has(type) ? type : 'text';\n}\n\n/**\n * <fx-input> — Campo de texto estilizado com os tokens do tema.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`), recebe reset/disable do form, é alvo de\n * `<label for>`/`autofocus` e o Enter submete o form.\n *\n * Atributos: type (text|number|password|email|tel|url|search|date|time|\n * datetime-local|month|week|color), value, name,\n * size, placeholder, disabled, readonly, min, max, step, autofocus,\n * clearable, full (largura 100% acompanhando o elemento pai), icon (nome do\n * glifo Fenix Icons ou emoji/texto livre) e icon-pos (left|right, padrão left).\n * Largura: o default (260px; sm 220px / lg 300px) fica no HOST via tokens\n * --fx-input-width(-sm|-lg) — CSS externo, classes e style inline no elemento\n * vencem o default sem precisar de atributo.\n * Slots: `icon` (ícone customizado no lugar do atributo).\n * Eventos: `input`, `change` e `valueChange` (composed, detail: { value }) —\n * `input` aciona o v-model do Vue e `valueChange` o [(value)] do Angular.\n */\nexport class FxInput extends FxFormElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n /* Largura padrão no HOST (e não no .field interno): assim CSS externo,\n classes do framework (ex.: w-full) e style inline no elemento vencem\n este default naturalmente — regras do documento têm precedência sobre\n :host. Default global trocável via token: --fx-input-width (-sm/-lg). */\n width: var(--fx-input-width, 260px);\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n .field {\n font-family: inherit;\n font-size: inherit;\n /* Altura determinística: com line-height fixo, o padding vertical cabe no\n token --fx-size-* e a caixa fica EXATAMENTE no tamanho padrão\n (sm 32 / md 40 / lg 48), igual para todos os campos. */\n line-height: var(--fx-font-line-height);\n min-height: var(--fx-size-md);\n font-weight: var(--fx-font-weight);\n color: var(--fx-text-default);\n background-color: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n padding: var(--fx-space-sm) var(--fx-space-lg);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n box-sizing: border-box;\n transition:\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .field::placeholder { color: var(--fx-text-muted); opacity: 1; }\n .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .field:disabled,\n .field[readonly] {\n opacity: 0.55;\n cursor: not-allowed;\n background-color: var(--fx-surface-surface-hover);\n }\n /* Validação: borda sempre; o brilho (anel) segue o token do preset\n --fx-effect-error-ring, que acompanha effect.focus-ring — anel\n desligado ⇒ erro só com a borda vermelha, ligado ⇒ soma o brilho. */\n :host([error]) .field,\n :host([invalid]) .field,\n :host([error]) .field:focus-visible,\n :host([invalid]) .field:focus-visible {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([success]) .field,\n :host([valid]) .field,\n :host([success]) .field:focus-visible,\n :host([valid]) .field:focus-visible {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([size='sm']) { width: var(--fx-input-width-sm, 220px); }\n :host([size='sm']) .field { padding: var(--fx-space-xs) var(--fx-space-md); font-size: var(--fx-font-size); min-height: var(--fx-size-sm); }\n :host([size='lg']) { width: var(--fx-input-width-lg, 300px); }\n :host([size='lg']) .field { padding: var(--fx-space-sm) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); min-height: var(--fx-size-lg); }\n /* Full width: o host estica até o pai e o campo interno acompanha\n (usar com um container de largura controlada, ex. w-full no Vue). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { width: 100%; }\n /* Clearable */\n :host([clearable]) { position: relative; display: inline-flex; }\n :host([clearable][full]) { display: block; }\n .wrap { position: relative; display: inline-flex; align-items: center; width: 100%; }\n :host([full]) .wrap { display: flex; width: 100%; }\n :host([clearable]) .field { padding-right: var(--fx-space-xl); }\n :host([clearable][full]) .field { flex: 1 1 auto; }\n /* Ícones (attr icon + slot icon) — DENTRO do campo, sobre o padding\n (padrão Material/PrimeVue). Tamanho ajustável via --fx-input-icon-size. */\n .field-icon {\n position: relative;\n display: inline-flex;\n box-sizing: border-box;\n width: 100%;\n }\n .field-icon .field { flex: 1 1 auto; min-width: 0; }\n :host([full]) .field-icon { display: flex; width: 100%; }\n .field-icon .fx-icon,\n .field-icon ::slotted([slot='icon']) {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px));\n line-height: 1;\n color: var(--fx-text-muted);\n pointer-events: none;\n z-index: 1;\n }\n .field-icon.icon-left .fx-icon,\n .field-icon.icon-left ::slotted([slot='icon']) { left: var(--fx-space-md); }\n .field-icon.icon-right .fx-icon,\n .field-icon.icon-right ::slotted([slot='icon']) { right: var(--fx-space-md); }\n /* padding = posição do ícone + largura + folga extra para o texto\n não colar no glifo. */\n .field-icon.icon-left .field {\n padding-left: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n .field-icon.icon-right .field {\n padding-right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n /* icon-pos right + clearable: desloca o × para não sobrepor o ícone. */\n .field-icon.icon-right + .clear {\n right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-lg));\n }\n .clear {\n position: absolute;\n right: var(--fx-space-xs);\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n cursor: pointer;\n padding: 0 var(--fx-space-xs);\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n /* Base dos glifos Fenix Icons (o @font-face vem de @wrrdev/fenix-ui/icons). */\n ${FENIX_ICON_BASE_CSS}\n `;\n\n // `value` fica FORA da observação: refleti-lo a cada tecla não pode\n // re-renderizar o template, senão o campo perde o foco ao digitar.\n // `error`/`success` (+ aliases `invalid`/`valid`) PRECISAM ser observados:\n // é assim que o toggle no playground / setAttribute no submit re-renderiza.\n static override get observedAttributes(): string[] {\n return [\n 'type', 'size', 'placeholder',\n 'disabled', 'readonly', 'min', 'max', 'step',\n 'clearable', 'full',\n 'icon', 'icon-pos', 'error', 'invalid', 'success', 'valid',\n ];\n }\n\n /** Tipo do input interno. Padrão: `'text'`. */\n override get type(): FxInputType {\n return normalizeInputType(this.getAttr('type', 'text')) as FxInputType;\n }\n override set type(value: FxInputType) {\n this.setAttribute('type', normalizeInputType(value));\n }\n\n /** Tamanho do campo. Padrão: `'md'`. */\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(value: string) {\n this.setAttribute('size', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(value: string | number | null | undefined) {\n this.setAttribute('value', value == null ? '' : String(value));\n // `value` NÃO é observado (re-render a cada tecla perderia o foco): o\n // campo interno é sincronizado aqui, sem re-render, preservando foco/cursor.\n this.syncInternalControl();\n this.updateFormValue();\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n get readonly(): boolean {\n return this.hasAttr('readonly');\n }\n set readonly(value: boolean) {\n this.toggleAttr('readonly', Boolean(value));\n }\n\n get clearable(): boolean {\n return this.hasAttr('clearable');\n }\n set clearable(value: boolean) {\n this.toggleAttr('clearable', Boolean(value));\n }\n\n get full(): boolean {\n return this.hasAttr('full');\n }\n set full(value: boolean) {\n this.toggleAttr('full', Boolean(value));\n }\n\n protected override render(): void {\n const type = this.type;\n const placeholder = this.getAttr('placeholder');\n const readonly = this.hasAttr('readonly');\n const disabled = this.hasAttr('disabled');\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n const step = this.getAttr('step');\n const icon = this.getAttr('icon');\n const iconPos = this.getAttr('icon-pos', 'left') === 'right' ? 'right' : 'left';\n // Slot `icon` vence o atributo `icon`; sem nenhum dos dois, sem ícone.\n const slottedIcon = this.querySelector<HTMLElement>('[slot=\"icon\"]');\n const iconContent = slottedIcon\n ? '<slot name=\"icon\" part=\"icon\"></slot>'\n : (icon ? fenixIconHtml(icon) : '');\n const useIconBox = Boolean(iconContent);\n\n this.setTemplate(`\n ${this.hasAttr('clearable') ? '<div class=\"wrap\" part=\"wrap\">' : ''}\n ${useIconBox ? `<div class=\"field-icon icon-${iconPos}\">` : ''}\n ${useIconBox && iconPos === 'left' ? iconContent : ''}\n <input class=\"field\" part=\"input\" type=\"${esc(type)}\"\n ${placeholder ? `placeholder=\"${esc(placeholder)}\"` : ''}\n ${min ? `min=\"${esc(min)}\"` : ''} ${max ? `max=\"${esc(max)}\"` : ''} ${step ? `step=\"${esc(step)}\"` : ''}\n />\n ${useIconBox && iconPos === 'right' ? iconContent : ''}\n ${useIconBox ? '</div>' : ''}\n ${this.hasAttr('clearable') ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\" tabindex=\"-1\">×</button></div>' : ''}\n `);\n\n const field = this.root.querySelector<HTMLInputElement>('.field');\n if (!field) return;\n field.value = this.getAttr('value');\n if (disabled) field.setAttribute('disabled', '');\n if (readonly) field.setAttribute('readonly', '');\n\n // Cada interação emite UM `input` (v-model) + `valueChange` (Angular).\n // O `change` só existe no evento nativo de mudança/limpar.\n const notifyInput = (): void => {\n this.value = field.value;\n this.emitValueEvents();\n };\n const notifyChange = (): void => {\n this.value = field.value;\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { value: field.value },\n }),\n );\n };\n // Bloqueia o evento nativo (composed, sem detail) para que só o\n // CustomEvent com detail chegue aos consumidores.\n field.addEventListener('input', (e) => { e.stopPropagation(); notifyInput(); });\n field.addEventListener('change', (e) => { e.stopPropagation(); notifyChange(); });\n\n // Enter submete o form: o <input> interno vive no Shadow DOM e NÃO se\n // associa ao <form> do light DOM (a fronteira do shadow corta a associação\n // por árvore) — por isso a submissão implícita é acionada manualmente.\n // Ouvintes de submit no form (ex.: submit.prevent de frameworks) seguem\n // recebendo o evento normalmente.\n field.addEventListener('keydown', (e) => {\n if (e.key !== 'Enter' || e.isComposing) return;\n if (!this.form) return;\n e.preventDefault();\n this.form.requestSubmit();\n });\n\n // Clearable: botão × que limpa o campo e emite input/change vazios.\n const clearBtn = this.root.querySelector<HTMLButtonElement>('.clear');\n const syncClear = (): void => {\n clearBtn?.toggleAttribute('hidden', field.value === '');\n };\n syncClear();\n clearBtn?.addEventListener('click', () => {\n field.value = '';\n syncClear();\n this.value = '';\n this.emitValueEvents();\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { value: '' },\n }),\n );\n field.focus();\n });\n field.addEventListener('input', syncClear);\n\n // Publica name/value no form (ElementInternals) a cada render — cobre o\n // estado inicial e mudanças de atributos observados.\n this.updateFormValue();\n }\n\n /** Empurra o atributo `value` para o campo interno sem re-renderizar. */\n protected override syncInternalControl(): void {\n const field = this.root.querySelector<HTMLInputElement>('.field');\n const value = this.getAttr('value');\n const type = this.type;\n if (field && field.type !== type) field.type = type;\n if (field && field.value !== value) field.value = value;\n }\n}\n\nexport function defineFxInput(): typeof FxInput {\n return defineElement('fx-input', FxInput);\n}\n"],"names":[],"mappings":"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"}