@wrrdev/fenix-ui 1.1.17 → 1.3.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.
- package/README.md +43 -4
- package/dist/components/datepicker/datepicker.js +4 -4
- package/dist/components/datepicker/datepicker.js.map +1 -1
- package/dist/components/editor/editor.js +3 -3
- package/dist/components/editor/editor.js.map +1 -1
- package/dist/components/floatlabel/floatlabel.d.ts +5 -0
- package/dist/components/floatlabel/floatlabel.d.ts.map +1 -1
- package/dist/components/floatlabel/floatlabel.js +52 -25
- package/dist/components/floatlabel/floatlabel.js.map +1 -1
- package/dist/components/input/input.d.ts +11 -0
- package/dist/components/input/input.d.ts.map +1 -1
- package/dist/components/input/input.js +11 -0
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/multiselect/multiselect.js +1 -1
- package/dist/components/multiselect/multiselect.js.map +1 -1
- package/dist/components/orderlist/orderlist.js +1 -1
- package/dist/components/orderlist/orderlist.js.map +1 -1
- package/dist/components/picklist/picklist.js +1 -1
- package/dist/components/picklist/picklist.js.map +1 -1
- package/dist/components/select/select.js +1 -1
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +14 -1
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/textarea/textarea.d.ts +7 -0
- package/dist/components/textarea/textarea.d.ts.map +1 -1
- package/dist/components/textarea/textarea.js +22 -1
- package/dist/components/textarea/textarea.js.map +1 -1
- package/dist/core/jsx.d.ts +10 -0
- package/dist/core/jsx.d.ts.map +1 -1
- package/dist/core/jsx.js.map +1 -1
- package/dist/core/tokens.d.ts +2 -0
- package/dist/core/tokens.d.ts.map +1 -1
- package/dist/core/tokens.js +9 -0
- package/dist/core/tokens.js.map +1 -1
- package/dist/docs/app.d.ts.map +1 -1
- package/dist/docs/componentes/autocomplete.doc.d.ts.map +1 -1
- package/dist/docs/componentes/datepicker.doc.d.ts.map +1 -1
- package/dist/docs/componentes/editor.doc.d.ts.map +1 -1
- package/dist/docs/componentes/floatlabel.doc.d.ts.map +1 -1
- package/dist/docs/componentes/input.doc.d.ts.map +1 -1
- package/dist/docs/componentes/multiselect.doc.d.ts.map +1 -1
- package/dist/docs/componentes/orderlist.doc.d.ts.map +1 -1
- package/dist/docs/componentes/picklist.doc.d.ts.map +1 -1
- package/dist/docs/componentes/select.doc.d.ts.map +1 -1
- package/dist/docs/componentes/table.doc.d.ts.map +1 -1
- package/dist/docs/componentes/textarea.doc.d.ts.map +1 -1
- package/dist/docs/shared.d.ts +15 -0
- package/dist/docs/shared.d.ts.map +1 -1
- package/dist/docs/shell.d.ts +10 -1
- package/dist/docs/shell.d.ts.map +1 -1
- package/dist/docs/theme-reference.d.ts +53 -0
- package/dist/docs/theme-reference.d.ts.map +1 -0
- package/dist/docs/types.d.ts +2 -0
- package/dist/docs/types.d.ts.map +1 -1
- package/dist/fenix-ui.umd.min.js +200 -169
- package/dist/fenix-ui4.esm.js +119 -38
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/package.json +1 -1
|
@@ -44,6 +44,8 @@ export declare class FxFloatlabel extends FxElement {
|
|
|
44
44
|
private _labelClickBound;
|
|
45
45
|
/** Observa o light DOM para readotar uma <label> recriada pelo framework. */
|
|
46
46
|
private _labelObserver?;
|
|
47
|
+
/** Timer do `focusout`: confirma o estado no próximo tick (foco já movido). */
|
|
48
|
+
private _syncTimer;
|
|
47
49
|
protected connectedCallback(): void;
|
|
48
50
|
protected render(): void;
|
|
49
51
|
protected disconnectedCallback(): void;
|
|
@@ -52,6 +54,9 @@ export declare class FxFloatlabel extends FxElement {
|
|
|
52
54
|
/** Label = filho direto <label>. */
|
|
53
55
|
private findLabel;
|
|
54
56
|
private attachEvents;
|
|
57
|
+
/** Reavalia o estado no próximo tick (o foco já saiu do campo). */
|
|
58
|
+
private scheduleSync;
|
|
59
|
+
private cancelScheduledSync;
|
|
55
60
|
/** Observa mudanças de atributos do controle (value, open, values...). */
|
|
56
61
|
private syncControlReference;
|
|
57
62
|
private syncState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floatlabel.d.ts","sourceRoot":"","sources":["../../../src/components/floatlabel/floatlabel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAa,SAAQ,SAAS;IAC1C,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,EAEvB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,EAEvB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAEtB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAE7C;
|
|
1
|
+
{"version":3,"file":"floatlabel.d.ts","sourceRoot":"","sources":["../../../src/components/floatlabel/floatlabel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAa,SAAQ,SAAS;IAC1C,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,EAEvB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,EAEvB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAEtB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAE7C;IAKD,OAAgB,MAAM,SAmJpB;IACF,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,cAAc,CAAS;IAC/B,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,gBAAgB,CAAS;IACjC,6EAA6E;IAC7E,OAAO,CAAC,cAAc,CAAC,CAAmB;IAC1C,+EAA+E;IAC/E,OAAO,CAAC,UAAU,CAA4C;cAE3C,iBAAiB,IAAI,IAAI;IAS5C,SAAS,CAAC,MAAM,IAAI,IAAI;cAmDL,oBAAoB,IAAI,IAAI;IAQ/C,oEAAoE;IACpE,OAAO,CAAC,WAAW;IAQnB,oCAAoC;IACpC,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,YAAY;IA2BpB,mEAAmE;IACnE,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,mBAAmB;IAO3B,0EAA0E;IAC1E,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,SAAS;IA4CjB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,SAAS;IAgBjB,OAAO,CAAC,SAAS;IAmBjB,OAAO,CAAC,UAAU;IA8BlB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,eAAe;CAIvB;AAED,wBAAgB,kBAAkB,IAAI,OAAO,YAAY,CAExD"}
|
|
@@ -98,6 +98,7 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
|
|
|
98
98
|
}
|
|
99
99
|
disconnectedCallback() {
|
|
100
100
|
super.disconnectedCallback();
|
|
101
|
+
this.cancelScheduledSync();
|
|
101
102
|
this.observer?.disconnect();
|
|
102
103
|
this._labelObserver?.disconnect();
|
|
103
104
|
this._labelObserver = void 0;
|
|
@@ -127,14 +128,28 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
|
|
|
127
128
|
}
|
|
128
129
|
if (this.listenersReady) return;
|
|
129
130
|
this.listenersReady = true;
|
|
130
|
-
this.addEventListener("focusin", () =>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
);
|
|
131
|
+
this.addEventListener("focusin", () => {
|
|
132
|
+
this.cancelScheduledSync();
|
|
133
|
+
this.syncState();
|
|
134
|
+
});
|
|
135
|
+
this.addEventListener("focusout", () => this.scheduleSync());
|
|
135
136
|
this.addEventListener("input", () => this.syncState());
|
|
136
137
|
this.addEventListener("change", () => this.syncState());
|
|
137
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
|
+
}
|
|
138
153
|
/** Observa mudanças de atributos do controle (value, open, values...). */
|
|
139
154
|
syncControlReference(control = this.targetControl) {
|
|
140
155
|
if (control !== this.targetControl) {
|
|
@@ -167,18 +182,16 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
|
|
|
167
182
|
const c = this.targetControl;
|
|
168
183
|
const attrValue = c.getAttribute ? (c.getAttribute("value") ?? "").trim() : "";
|
|
169
184
|
const attrValues = c.getAttribute ? (c.getAttribute("values") ?? "").trim() : "";
|
|
185
|
+
const isOpen = Boolean(c.hasAttribute?.("open"));
|
|
170
186
|
const hasValue = Boolean(
|
|
171
|
-
typeof c.value === "string" && c.value.trim() !== "" || Array.isArray(c.values) && c.values.length > 0 || attrValue !== "" || attrValues !== "" || c.hasAttribute &&
|
|
187
|
+
typeof c.value === "string" && c.value.trim() !== "" || Array.isArray(c.values) && c.values.length > 0 || attrValue !== "" || attrValues !== "" || c.hasAttribute && c.hasAttribute("start")
|
|
172
188
|
);
|
|
173
189
|
const focused = this.isFocusedInside();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.removeAttribute("active");
|
|
180
|
-
this.removeAttribute("focus");
|
|
181
|
-
}
|
|
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");
|
|
182
195
|
}
|
|
183
196
|
/**
|
|
184
197
|
* variant="in" + ícone à esquerda dentro do campo (fx-input icon):
|
|
@@ -256,6 +269,10 @@ _FxFloatlabel.styles = css`
|
|
|
256
269
|
font-family: var(--fx-font-family);
|
|
257
270
|
font-size: var(--fx-font-size);
|
|
258
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);
|
|
259
276
|
}
|
|
260
277
|
/* Full width: o host estica até o pai; o controle interno com
|
|
261
278
|
atributo full (ou largura herdada) acompanha. */
|
|
@@ -287,7 +304,7 @@ _FxFloatlabel.styles = css`
|
|
|
287
304
|
transform: translateY(-50%);
|
|
288
305
|
font-size: calc(var(--fx-font-size, 14px) - 3px);
|
|
289
306
|
font-weight: 600;
|
|
290
|
-
color: var(--
|
|
307
|
+
color: var(--_flabel-color);
|
|
291
308
|
background-color: var(--fx-surface-background, #fff);
|
|
292
309
|
padding: 0 4px;
|
|
293
310
|
line-height: 1;
|
|
@@ -305,8 +322,11 @@ _FxFloatlabel.styles = css`
|
|
|
305
322
|
color var(--fx-motion-duration-normal, 180ms)
|
|
306
323
|
var(--fx-motion-easing, ease-in-out);
|
|
307
324
|
}
|
|
308
|
-
|
|
309
|
-
|
|
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);
|
|
310
330
|
}
|
|
311
331
|
|
|
312
332
|
/* ---- variant="in": label como placeholder dentro, sobe ao focar/valor ---- */
|
|
@@ -325,7 +345,8 @@ _FxFloatlabel.styles = css`
|
|
|
325
345
|
font-weight: 600;
|
|
326
346
|
background-color: var(--fx-surface-background, #fff);
|
|
327
347
|
padding: 0 5px;
|
|
328
|
-
color:
|
|
348
|
+
/* Sem color aqui: a especificidade desta regra
|
|
349
|
+
(variant + active) venceria :host([error])/:host([focus]). */
|
|
329
350
|
}
|
|
330
351
|
|
|
331
352
|
/* ---- variant="in" com ícone dentro do campo (fx-input icon):
|
|
@@ -356,16 +377,22 @@ _FxFloatlabel.styles = css`
|
|
|
356
377
|
font-size: calc(var(--fx-font-size, 14px) - 3px);
|
|
357
378
|
font-weight: 600;
|
|
358
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;
|
|
359
384
|
}
|
|
360
385
|
|
|
361
|
-
/* ---- estados de erro / sucesso ----
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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);
|
|
365
392
|
}
|
|
366
|
-
:host([success])
|
|
367
|
-
:host([valid])
|
|
368
|
-
color: var(--fx-color-success, #10b981);
|
|
393
|
+
:host([success]),
|
|
394
|
+
:host([valid]) {
|
|
395
|
+
--_flabel-color: var(--fx-color-success, #10b981);
|
|
369
396
|
}
|
|
370
397
|
|
|
371
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\", \"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' 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\t/** Observa o light DOM para readotar uma <label> recriada pelo framework. */\n\tprivate _labelObserver?: MutationObserver;\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.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\", () => 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":"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"}
|
|
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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"}
|
|
@@ -12,6 +12,17 @@ import type { FxInputType } from '../../core/jsx';
|
|
|
12
12
|
* size, placeholder, disabled, readonly, min, max, step, autofocus,
|
|
13
13
|
* clearable, full (largura 100% acompanhando o elemento pai), icon (nome do
|
|
14
14
|
* glifo Fenix Icons ou emoji/texto livre) e icon-pos (left|right, padrão left).
|
|
15
|
+
*
|
|
16
|
+
* Autofill: o <input> real vive no Shadow DOM, então `autocomplete` precisa ser
|
|
17
|
+
* REPASSADO ao campo interno — sem isso o navegador aplica o default `on` e
|
|
18
|
+
* preenche o campo com credenciais salvas (heurística "primeiro campo de texto
|
|
19
|
+
* = usuário"). O default aqui é `off` (mesma escolha do <fx-autocomplete>) e o
|
|
20
|
+
* atributo declarado no host vence. Quando o autofill for desejado, use os
|
|
21
|
+
* tokens semânticos: autocomplete="username", "current-password",
|
|
22
|
+
* "new-password", "one-time-code", "email", "street-address", … O Chrome
|
|
23
|
+
* IGNORA `off` em type="password" — para login use "current-password" (e
|
|
24
|
+
* "new-password" no cadastro/troca de senha). `name` e `id` também são
|
|
25
|
+
* repassados ao campo interno (melhoram a classificação do navegador).
|
|
15
26
|
* Largura: o default (260px; sm 220px / lg 300px) fica no HOST via tokens
|
|
16
27
|
* --fx-input-width(-sm|-lg) — CSS externo, classes e style inline no elemento
|
|
17
28
|
* vencem o default sem precisar de atributo.
|
|
@@ -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;AA4BlD
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,OAAQ,SAAQ,aAAa;IACxC,OAAgB,MAAM,SAuIpB;IAMF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAWjD;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;IA0GjC,yEAAyE;cACtD,mBAAmB,IAAI,IAAI;CAO/C;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
|
|
@@ -39,6 +39,12 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
39
39
|
"step",
|
|
40
40
|
"clearable",
|
|
41
41
|
"full",
|
|
42
|
+
// `autocomplete` é observado porque ele vive no campo INTERNO (autofill
|
|
43
|
+
// do navegador): mudar o atributo no host precisa refletir no <input>.
|
|
44
|
+
// `name`/`id` idem — os dois descem para o campo interno.
|
|
45
|
+
"autocomplete",
|
|
46
|
+
"name",
|
|
47
|
+
"id",
|
|
42
48
|
"icon",
|
|
43
49
|
"icon-pos",
|
|
44
50
|
"error",
|
|
@@ -102,6 +108,9 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
102
108
|
const min = this.getAttr("min");
|
|
103
109
|
const max = this.getAttr("max");
|
|
104
110
|
const step = this.getAttr("step");
|
|
111
|
+
const autocomplete = this.getAttr("autocomplete", "off");
|
|
112
|
+
const name = this.getAttr("name");
|
|
113
|
+
const id = this.getAttr("id");
|
|
105
114
|
const icon = this.getAttr("icon");
|
|
106
115
|
const iconPos = this.getAttr("icon-pos", "left") === "right" ? "right" : "left";
|
|
107
116
|
const slottedIcon = this.querySelector('[slot="icon"]');
|
|
@@ -112,6 +121,8 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
112
121
|
${useIconBox ? `<div class="field-icon icon-${iconPos}">` : ""}
|
|
113
122
|
${useIconBox && iconPos === "left" ? iconContent : ""}
|
|
114
123
|
<input class="field" part="input" type="${esc(type)}"
|
|
124
|
+
${name ? `name="${esc(name)}"` : ""} ${id ? `id="${esc(id)}"` : ""}
|
|
125
|
+
autocomplete="${esc(autocomplete)}"
|
|
115
126
|
${placeholder ? `placeholder="${esc(placeholder)}"` : ""}
|
|
116
127
|
${min ? `min="${esc(min)}"` : ""} ${max ? `max="${esc(max)}"` : ""} ${step ? `step="${esc(step)}"` : ""}
|
|
117
128
|
/>
|
|
@@ -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 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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"}
|
|
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 *\n * Autofill: o <input> real vive no Shadow DOM, então `autocomplete` precisa ser\n * REPASSADO ao campo interno — sem isso o navegador aplica o default `on` e\n * preenche o campo com credenciais salvas (heurística \"primeiro campo de texto\n * = usuário\"). O default aqui é `off` (mesma escolha do <fx-autocomplete>) e o\n * atributo declarado no host vence. Quando o autofill for desejado, use os\n * tokens semânticos: autocomplete=\"username\", \"current-password\",\n * \"new-password\", \"one-time-code\", \"email\", \"street-address\", … O Chrome\n * IGNORA `off` em type=\"password\" — para login use \"current-password\" (e\n * \"new-password\" no cadastro/troca de senha). `name` e `id` também são\n * repassados ao campo interno (melhoram a classificação do navegador).\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 // `autocomplete` é observado porque ele vive no campo INTERNO (autofill\n // do navegador): mudar o atributo no host precisa refletir no <input>.\n // `name`/`id` idem — os dois descem para o campo interno.\n 'autocomplete', 'name', 'id',\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 // Autofill: quem o navegador enxerga é o <input> interno, então o atributo\n // do HOST é repassado; sem ele, `off` (evita o preenchimento heurístico de\n // credenciais — ver o JSDoc da classe).\n const autocomplete = this.getAttr('autocomplete', 'off');\n // `name`/`id` também descem (classificação do navegador). O `name` do host\n // continua sendo o que entra no FormData via ElementInternals.\n const name = this.getAttr('name');\n const id = this.getAttr('id');\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 ${name ? `name=\"${esc(name)}\"` : ''} ${id ? `id=\"${esc(id)}\"` : ''}\n autocomplete=\"${esc(autocomplete)}\"\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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"}
|
|
@@ -188,7 +188,7 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
188
188
|
<span class="count">${chips.length} selecionado${chips.length === 1 ? "" : "s"}</span>
|
|
189
189
|
${this.hasAttr("clearable") && chips.length ? '<button type="button" class="clear-all">Limpar tudo</button>' : ""}
|
|
190
190
|
</div>
|
|
191
|
-
${this.hasAttr("searchable") ? `<input class="search" part="search" type="text" placeholder="${esc(this.getAttr("search-placeholder", "Pesquisar…"))}">` : ""}
|
|
191
|
+
${this.hasAttr("searchable") ? `<input class="search" part="search" type="text" autocomplete="off" placeholder="${esc(this.getAttr("search-placeholder", "Pesquisar…"))}">` : ""}
|
|
192
192
|
<ul class="list">
|
|
193
193
|
${filtered.map((o) => `
|
|
194
194
|
<li><button type="button" class="opt" role="option" data-value="${esc(o.value)}"
|