@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.
- package/README.md +12 -3
- package/dist/components/autocomplete/autocomplete.d.ts +3 -1
- package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
- package/dist/components/autocomplete/autocomplete.js +7 -1
- package/dist/components/autocomplete/autocomplete.js.map +1 -1
- package/dist/components/avatar/avatar.d.ts +1 -0
- package/dist/components/avatar/avatar.d.ts.map +1 -1
- package/dist/components/avatar/avatar.js +3 -0
- package/dist/components/avatar/avatar.js.map +1 -1
- package/dist/components/badge/badge.d.ts +4 -0
- package/dist/components/badge/badge.d.ts.map +1 -1
- package/dist/components/badge/badge.js +12 -0
- package/dist/components/badge/badge.js.map +1 -1
- package/dist/components/button/button.d.ts +2 -0
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +6 -0
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/card/card.d.ts +2 -0
- package/dist/components/card/card.d.ts.map +1 -1
- package/dist/components/card/card.js +6 -0
- package/dist/components/card/card.js.map +1 -1
- package/dist/components/checkbox/checkbox.d.ts +5 -1
- package/dist/components/checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox/checkbox.js +7 -8
- package/dist/components/checkbox/checkbox.js.map +1 -1
- package/dist/components/datepicker/datepicker.d.ts +17 -2
- package/dist/components/datepicker/datepicker.d.ts.map +1 -1
- package/dist/components/datepicker/datepicker.js +113 -42
- package/dist/components/datepicker/datepicker.js.map +1 -1
- package/dist/components/dialog/dialog.d.ts +4 -1
- package/dist/components/dialog/dialog.d.ts.map +1 -1
- package/dist/components/dialog/dialog.js +41 -18
- package/dist/components/dialog/dialog.js.map +1 -1
- package/dist/components/drawer/drawer.d.ts +5 -1
- package/dist/components/drawer/drawer.d.ts.map +1 -1
- package/dist/components/drawer/drawer.js +39 -17
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/fileupload/fileupload.d.ts +13 -1
- package/dist/components/fileupload/fileupload.d.ts.map +1 -1
- package/dist/components/fileupload/fileupload.js +29 -1
- package/dist/components/fileupload/fileupload.js.map +1 -1
- package/dist/components/floatlabel/floatlabel.d.ts +20 -0
- package/dist/components/floatlabel/floatlabel.d.ts.map +1 -1
- package/dist/components/floatlabel/floatlabel.js +99 -26
- package/dist/components/floatlabel/floatlabel.js.map +1 -1
- package/dist/components/icon/icon.d.ts +4 -0
- package/dist/components/icon/icon.d.ts.map +1 -1
- package/dist/components/icon/icon.js +12 -0
- package/dist/components/icon/icon.js.map +1 -1
- package/dist/components/input/input.d.ts +9 -2
- package/dist/components/input/input.d.ts.map +1 -1
- package/dist/components/input/input.js +56 -8
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/multiselect/multiselect.d.ts +8 -1
- package/dist/components/multiselect/multiselect.d.ts.map +1 -1
- package/dist/components/multiselect/multiselect.js +61 -9
- package/dist/components/multiselect/multiselect.js.map +1 -1
- package/dist/components/orderlist/orderlist.d.ts +7 -0
- package/dist/components/orderlist/orderlist.d.ts.map +1 -1
- package/dist/components/orderlist/orderlist.js +34 -2
- package/dist/components/orderlist/orderlist.js.map +1 -1
- package/dist/components/radio/radio.d.ts +6 -0
- package/dist/components/radio/radio.d.ts.map +1 -1
- package/dist/components/radio/radio.js +12 -6
- package/dist/components/radio/radio.js.map +1 -1
- package/dist/components/select/select.d.ts +19 -1
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +116 -10
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/switch/switch.d.ts +4 -1
- package/dist/components/switch/switch.d.ts.map +1 -1
- package/dist/components/switch/switch.js +2 -1
- package/dist/components/switch/switch.js.map +1 -1
- package/dist/components/table/table.d.ts +27 -0
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +119 -21
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tabs/tabs.d.ts +6 -0
- package/dist/components/tabs/tabs.d.ts.map +1 -1
- package/dist/components/tabs/tabs.js +34 -0
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/components/textarea/textarea.d.ts +5 -1
- package/dist/components/textarea/textarea.d.ts.map +1 -1
- package/dist/components/textarea/textarea.js +25 -6
- package/dist/components/textarea/textarea.js.map +1 -1
- package/dist/core/jsx.d.ts +11 -5
- package/dist/core/jsx.d.ts.map +1 -1
- package/dist/core/jsx.js.map +1 -1
- package/dist/core/modal.d.ts +31 -0
- package/dist/core/modal.d.ts.map +1 -0
- package/dist/core/modal.js +61 -0
- package/dist/core/modal.js.map +1 -0
- 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/floatlabel.doc.d.ts.map +1 -1
- package/dist/docs/componentes/input.doc.d.ts.map +1 -1
- package/dist/docs/componentes/orderlist.doc.d.ts.map +1 -1
- package/dist/docs/componentes/table.doc.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 +270 -233
- package/dist/fenix-ui4.esm.js +890 -176
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/package.json +1 -1
|
@@ -30,6 +30,8 @@ import { FxFormElement } from '../../core/form-element';
|
|
|
30
30
|
export declare class FxDatepicker extends FxFormElement {
|
|
31
31
|
static styles: string;
|
|
32
32
|
static get observedAttributes(): string[];
|
|
33
|
+
/** Evita renders em cascata quando vários atributos mudam juntos. */
|
|
34
|
+
private _suppressRender;
|
|
33
35
|
protected attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
34
36
|
/** No FormData do form, conforme o modo (single/range/multiple). */
|
|
35
37
|
protected getFormValue(): string | FormData | null;
|
|
@@ -41,12 +43,25 @@ export declare class FxDatepicker extends FxFormElement {
|
|
|
41
43
|
get mode(): string;
|
|
42
44
|
set mode(value: string);
|
|
43
45
|
get value(): string;
|
|
44
|
-
set value(v: string);
|
|
46
|
+
set value(v: string | number | null | undefined);
|
|
47
|
+
get start(): string;
|
|
48
|
+
set start(v: string | null | undefined);
|
|
49
|
+
get end(): string;
|
|
50
|
+
set end(v: string | null | undefined);
|
|
45
51
|
/** Datas do modo múltiplo (CSV no atributo). */
|
|
46
52
|
get values(): string[];
|
|
47
|
-
set values(list: string[]);
|
|
53
|
+
set values(list: string[] | null | undefined);
|
|
48
54
|
get disabled(): boolean;
|
|
49
55
|
set disabled(value: boolean);
|
|
56
|
+
/** `:show-time` procura a propriedade LITERAL no Vue (sem camelização). */
|
|
57
|
+
get 'show-time'(): boolean;
|
|
58
|
+
set 'show-time'(value: boolean);
|
|
59
|
+
get 'free-text'(): boolean;
|
|
60
|
+
set 'free-text'(value: boolean);
|
|
61
|
+
get clearable(): boolean;
|
|
62
|
+
set clearable(value: boolean);
|
|
63
|
+
get full(): boolean;
|
|
64
|
+
set full(value: boolean);
|
|
50
65
|
protected connectedCallback(): void;
|
|
51
66
|
protected disconnectedCallback(): void;
|
|
52
67
|
/** form.reset(): restaura value/start/end/values iniciais (re-render via atributos observados). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../src/components/datepicker/datepicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAKxD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,YAAa,SAAQ,aAAa;IAC7C,OAAgB,MAAM,SAwJpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;
|
|
1
|
+
{"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../src/components/datepicker/datepicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAKxD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,YAAa,SAAQ,aAAa;IAC7C,OAAgB,MAAM,SAwJpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,qEAAqE;IACrE,OAAO,CAAC,eAAe,CAAS;cAEb,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAYjH,oEAAoE;cACjD,YAAY,IAAI,MAAM,GAAG,QAAQ,GAAG,IAAI;IAoB3D,OAAO,CAAC,WAAW,CAAC,CAAqB;IACzC,6CAA6C;IAC7C,OAAO,CAAC,IAAI,CAAwB;IACpC,iEAAiE;IACjE,OAAO,CAAC,aAAa,CAAC,CAAgE;IAEtF,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EAE9C;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAErC;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IACD,IAAI,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAEnC;IAED,gDAAgD;IAChD,IAAI,MAAM,IAAI,MAAM,EAAE,CAGrB;IACD,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EAG3C;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,2EAA2E;IAC3E,IAAI,WAAW,IAAI,OAAO,CAEzB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,OAAO,EAE7B;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,OAAO,EAE7B;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,iBAAiB,IAAI,IAAI;cAmBzB,oBAAoB,IAAI,IAAI;IAK/C,mGAAmG;IAC1F,iBAAiB,IAAI,IAAI;IAelC,wFAAwF;IACxF,OAAO,CAAC,UAAU;IAyBlB,8CAA8C;IAC9C,OAAO,CAAC,WAAW;IAoBnB,iDAAiD;IACjD,OAAO,CAAC,QAAQ;IAKhB,sDAAsD;IACtD,OAAO,CAAC,iBAAiB;IAQzB,+DAA+D;IAC/D,OAAO,CAAC,QAAQ;cAOG,MAAM,IAAI,IAAI;IAwKjC,OAAO,CAAC,UAAU;IASlB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAgClB,8DAA8D;IAC9D,OAAO,CAAC,WAAW;CA4CpB;AAED,wBAAgB,kBAAkB,IAAI,OAAO,YAAY,CAExD"}
|
|
@@ -5,13 +5,16 @@ import { esc } from "../../core/sanitize.js";
|
|
|
5
5
|
const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
6
6
|
constructor() {
|
|
7
7
|
super(...arguments);
|
|
8
|
+
this._suppressRender = false;
|
|
8
9
|
this.time = { h: 0, m: 0, s: 0 };
|
|
9
10
|
}
|
|
10
11
|
static get observedAttributes() {
|
|
11
|
-
return ["value", "start", "end", "values", "mode", "min", "max", "placeholder", "disabled", "format", "show-time", "free-text", "error", "invalid", "success", "valid"];
|
|
12
|
+
return ["value", "start", "end", "values", "mode", "min", "max", "placeholder", "disabled", "format", "show-time", "free-text", "clearable", "full", "error", "invalid", "success", "valid"];
|
|
12
13
|
}
|
|
13
14
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
14
|
-
|
|
15
|
+
if (!this._suppressRender) {
|
|
16
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
17
|
+
}
|
|
15
18
|
if (name === "value" || name === "start" || name === "end" || name === "values") {
|
|
16
19
|
this.updateFormValue();
|
|
17
20
|
}
|
|
@@ -47,7 +50,19 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
47
50
|
return this.getAttr("value");
|
|
48
51
|
}
|
|
49
52
|
set value(v) {
|
|
50
|
-
this.setAttribute("value", v);
|
|
53
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
54
|
+
}
|
|
55
|
+
get start() {
|
|
56
|
+
return this.getAttr("start");
|
|
57
|
+
}
|
|
58
|
+
set start(v) {
|
|
59
|
+
this.setAttribute("start", v == null ? "" : String(v));
|
|
60
|
+
}
|
|
61
|
+
get end() {
|
|
62
|
+
return this.getAttr("end");
|
|
63
|
+
}
|
|
64
|
+
set end(v) {
|
|
65
|
+
this.setAttribute("end", v == null ? "" : String(v));
|
|
51
66
|
}
|
|
52
67
|
/** Datas do modo múltiplo (CSV no atributo). */
|
|
53
68
|
get values() {
|
|
@@ -55,7 +70,8 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
55
70
|
return csv ? csv.split(",").map((v) => v.trim()).filter(Boolean) : [];
|
|
56
71
|
}
|
|
57
72
|
set values(list) {
|
|
58
|
-
|
|
73
|
+
const values = Array.isArray(list) ? list.map(String) : [];
|
|
74
|
+
this.setAttribute("values", values.join(","));
|
|
59
75
|
}
|
|
60
76
|
get disabled() {
|
|
61
77
|
return this.hasAttr("disabled");
|
|
@@ -63,6 +79,31 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
63
79
|
set disabled(value) {
|
|
64
80
|
this.toggleAttr("disabled", Boolean(value));
|
|
65
81
|
}
|
|
82
|
+
/** `:show-time` procura a propriedade LITERAL no Vue (sem camelização). */
|
|
83
|
+
get "show-time"() {
|
|
84
|
+
return this.hasAttr("show-time");
|
|
85
|
+
}
|
|
86
|
+
set "show-time"(value) {
|
|
87
|
+
this.toggleAttr("show-time", Boolean(value));
|
|
88
|
+
}
|
|
89
|
+
get "free-text"() {
|
|
90
|
+
return this.hasAttr("free-text");
|
|
91
|
+
}
|
|
92
|
+
set "free-text"(value) {
|
|
93
|
+
this.toggleAttr("free-text", Boolean(value));
|
|
94
|
+
}
|
|
95
|
+
get clearable() {
|
|
96
|
+
return this.hasAttr("clearable");
|
|
97
|
+
}
|
|
98
|
+
set clearable(value) {
|
|
99
|
+
this.toggleAttr("clearable", Boolean(value));
|
|
100
|
+
}
|
|
101
|
+
get full() {
|
|
102
|
+
return this.hasAttr("full");
|
|
103
|
+
}
|
|
104
|
+
set full(value) {
|
|
105
|
+
this.toggleAttr("full", Boolean(value));
|
|
106
|
+
}
|
|
66
107
|
connectedCallback() {
|
|
67
108
|
if (!this._initialState) {
|
|
68
109
|
this._initialState = {
|
|
@@ -87,11 +128,17 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
87
128
|
/** form.reset(): restaura value/start/end/values iniciais (re-render via atributos observados). */
|
|
88
129
|
formResetCallback() {
|
|
89
130
|
const s = this._initialState ?? { value: "", start: "", end: "", values: "" };
|
|
90
|
-
this.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
131
|
+
this._suppressRender = true;
|
|
132
|
+
try {
|
|
133
|
+
this.setAttribute("value", s.value);
|
|
134
|
+
this.setAttribute("start", s.start);
|
|
135
|
+
this.setAttribute("end", s.end);
|
|
136
|
+
this.setAttribute("values", s.values);
|
|
137
|
+
} finally {
|
|
138
|
+
this._suppressRender = false;
|
|
139
|
+
}
|
|
94
140
|
this.updateFormValue();
|
|
141
|
+
if (this.isConnected) this.render();
|
|
95
142
|
}
|
|
96
143
|
/** Formata uma data ISO ("YYYY-MM-DD" ou com "THH:mm:ss") segundo o atributo format. */
|
|
97
144
|
formatDate(iso) {
|
|
@@ -199,10 +246,15 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
199
246
|
});
|
|
200
247
|
this.root.querySelector(".clear")?.addEventListener("click", (e) => {
|
|
201
248
|
e.stopPropagation();
|
|
202
|
-
this.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
249
|
+
this._suppressRender = true;
|
|
250
|
+
try {
|
|
251
|
+
this.removeAttribute("value");
|
|
252
|
+
this.removeAttribute("start");
|
|
253
|
+
this.removeAttribute("end");
|
|
254
|
+
this.removeAttribute("values");
|
|
255
|
+
} finally {
|
|
256
|
+
this._suppressRender = false;
|
|
257
|
+
}
|
|
206
258
|
this.removeAttribute("open");
|
|
207
259
|
this.render();
|
|
208
260
|
this.emitChange({});
|
|
@@ -210,19 +262,24 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
210
262
|
const cal = this.root.querySelector(".pop fx-calendar");
|
|
211
263
|
cal?.addEventListener("change", (e) => {
|
|
212
264
|
const d = e.detail ?? {};
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
265
|
+
this._suppressRender = true;
|
|
266
|
+
try {
|
|
267
|
+
if (d.start !== void 0 || d.end !== void 0) {
|
|
268
|
+
if (d.start) this.setAttribute("start", this.withTime(d.start));
|
|
269
|
+
else this.removeAttribute("start");
|
|
270
|
+
if (d.end) this.setAttribute("end", this.withTime(d.end));
|
|
271
|
+
else this.removeAttribute("end");
|
|
272
|
+
}
|
|
273
|
+
if (d.values !== void 0) {
|
|
274
|
+
this.values = d.values.map((v) => this.withTime(v));
|
|
275
|
+
}
|
|
276
|
+
if (d.value !== void 0) {
|
|
277
|
+
if (d.value) this.setAttribute("value", this.withTime(d.value));
|
|
278
|
+
else this.removeAttribute("value");
|
|
279
|
+
if (this.mode === "single") this.removeAttribute("open");
|
|
280
|
+
}
|
|
281
|
+
} finally {
|
|
282
|
+
this._suppressRender = false;
|
|
226
283
|
}
|
|
227
284
|
this.render();
|
|
228
285
|
this.emitChange(d);
|
|
@@ -249,11 +306,16 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
249
306
|
s: clamp(s?.value, 59)
|
|
250
307
|
};
|
|
251
308
|
const reattach = (iso) => iso.split("T")[0] ? `${iso.split("T")[0]}T${this.timeText()}` : iso;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
this.
|
|
309
|
+
this._suppressRender = true;
|
|
310
|
+
try {
|
|
311
|
+
if (this.value) this.setAttribute("value", reattach(this.value));
|
|
312
|
+
if (this.getAttr("start")) this.setAttribute("start", reattach(this.getAttr("start")));
|
|
313
|
+
if (this.getAttr("end")) this.setAttribute("end", reattach(this.getAttr("end")));
|
|
314
|
+
if (this.values.length) {
|
|
315
|
+
this.values = this.values.map((v) => reattach(v));
|
|
316
|
+
}
|
|
317
|
+
} finally {
|
|
318
|
+
this._suppressRender = false;
|
|
257
319
|
}
|
|
258
320
|
const display2 = this.root.querySelector(".display");
|
|
259
321
|
if (display2) display2.value = this.displayText();
|
|
@@ -309,23 +371,32 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
309
371
|
setTimeout(() => input.classList.remove("invalid"), 1500);
|
|
310
372
|
return;
|
|
311
373
|
}
|
|
374
|
+
this._suppressRender = true;
|
|
375
|
+
try {
|
|
376
|
+
if (this.mode === "single") {
|
|
377
|
+
this.setAttribute("value", this.withTime(parsed));
|
|
378
|
+
} else if (this.mode === "range") {
|
|
379
|
+
if (!this.getAttr("start") || this.getAttr("start") && this.getAttr("end")) {
|
|
380
|
+
this.setAttribute("start", this.withTime(parsed));
|
|
381
|
+
this.removeAttribute("end");
|
|
382
|
+
} else {
|
|
383
|
+
const [a, b] = [this.getAttr("start").split("T")[0], parsed].sort();
|
|
384
|
+
this.setAttribute("start", this.withTime(a));
|
|
385
|
+
this.setAttribute("end", this.withTime(b));
|
|
386
|
+
}
|
|
387
|
+
} else {
|
|
388
|
+
const list = this.values.filter((v) => v.split("T")[0] !== parsed);
|
|
389
|
+
list.push(this.withTime(parsed));
|
|
390
|
+
this.values = list;
|
|
391
|
+
}
|
|
392
|
+
} finally {
|
|
393
|
+
this._suppressRender = false;
|
|
394
|
+
}
|
|
312
395
|
if (this.mode === "single") {
|
|
313
|
-
this.setAttribute("value", this.withTime(parsed));
|
|
314
396
|
this.emitChange({ value: this.value });
|
|
315
397
|
} else if (this.mode === "range") {
|
|
316
|
-
if (!this.getAttr("start") || this.getAttr("start") && this.getAttr("end")) {
|
|
317
|
-
this.setAttribute("start", this.withTime(parsed));
|
|
318
|
-
this.removeAttribute("end");
|
|
319
|
-
} else {
|
|
320
|
-
const [a, b] = [this.getAttr("start").split("T")[0], parsed].sort();
|
|
321
|
-
this.setAttribute("start", this.withTime(a));
|
|
322
|
-
this.setAttribute("end", this.withTime(b));
|
|
323
|
-
}
|
|
324
398
|
this.emitChange({ start: this.getAttr("start"), end: this.getAttr("end") });
|
|
325
399
|
} else {
|
|
326
|
-
const list = this.values.filter((v) => v.split("T")[0] !== parsed);
|
|
327
|
-
list.push(this.withTime(parsed));
|
|
328
|
-
this.values = list;
|
|
329
400
|
this.emitChange({ values: this.values });
|
|
330
401
|
}
|
|
331
402
|
this.render();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datepicker.js","sources":["../../../src/components/datepicker/datepicker.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-datepicker> — Campo de data com calendário suspenso.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`): single → `name`; range → `name-start`/`name-end`;\n * multiple → uma entrada `name` por data.\n *\n * Um input com ícone de calendário no final; ao clicar, abre o\n * <fx-calendar> em um popover. As datas selecionadas aparecem no input.\n *\n * Atributos:\n * - mode=\"single\" | \"range\" | \"multiple\" (padrão: single)\n * - value=\"YYYY-MM-DD\" → single\n * - start/end → range\n * - values=\"a,b,c\" → múltiplas datas (CSV)\n * - min/max, placeholder, disabled, clearable, size (sm|md|lg)\n * - format=\"dd/mm/yyyy\" → máscara de exibição (tokens: dd mm yyyy, HH MM SS)\n * - show-time → inclui seleção de hora:minuto:segundo no popover;\n * o valor passa a ser \"YYYY-MM-DDTHH:mm:ss\"\n *\n * Evento: `change` (composed):\n * - single: { value }\n * - range: { value, start, end }\n * - multiple: { values: string[] }\n *\n * Além do `change`, emite `input`/`valueChange` com o MESMO payload\n * (v-model do Vue e [(value)] do Angular).\n */\nexport class FxDatepicker extends FxFormElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n position: relative;\n }\n .field {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs);\n width: 100%;\n box-sizing: border-box;\n /* Base = tamanho padrão (md): 40px */\n min-height: var(--fx-size-md, 40px);\n min-width: 150px;\n padding: var(--fx-space-xs) var(--fx-space-md);\n /* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */\n line-height: var(--fx-font-line-height);\n background-color: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n cursor: pointer;\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 :host([size='sm']) .field { min-height: var(--fx-size-sm); min-width: 180px; }\n :host([size='md']) .field { min-height: var(--fx-size-md); }\n :host([size='lg']) .field { min-height: var(--fx-size-lg); min-width: 220px; font-size: calc(var(--fx-font-size) + 4px); }\n /* Range e multiple exibem mais de uma data: campo mais largo */\n :host([mode='range']) .field { min-width: 250px; }\n :host([mode='multiple']) .field { min-width: 220px; }\n :host([mode='range'][size='lg']) .field { min-width: 360px; }\n :host([mode='multiple'][size='lg']) .field { min-width: 320px; }\n :host([show-time]) .field { min-width: 250px; }\n /* Full width: o host estica até o pai (os min-width dos modos são\n descartados para permitir layouts estreitos sob controle do pai). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { min-width: 0; }\n /* Validação — borda e brilho JÁ EM REPOUSO (igual ao fx-input); o brilho\n segue o token --fx-effect-error-ring, que acompanha effect.focus-ring. */\n :host([error]) .field,\n :host([invalid]) .field {\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([error]) .field:focus-within,\n :host([invalid]) .field:focus-within,\n :host([error][open]) .field,\n :host([invalid][open]) .field {\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 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([success]) .field:focus-within,\n :host([valid]) .field:focus-within,\n :host([success][open]) .field,\n :host([valid][open]) .field {\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 .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-within {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .display {\n flex: 1;\n border: none;\n background: none;\n font: inherit;\n color: var(--fx-text-default);\n outline: none;\n cursor: pointer;\n text-align: left;\n padding: 0;\n min-width: 0;\n }\n .display::placeholder { color: var(--fx-text-muted); }\n .cal-icon { color: var(--fx-text-muted); display: inline-flex; pointer-events: none; }\n .clear {\n border: none;\n background: none;\n color: var(--fx-text-muted);\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n padding: 0 2px;\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n .pop {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\n display: none;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n box-shadow: var(--fx-shadow-lg);\n overflow: hidden;\n }\n :host([up]) .pop {\n top: auto;\n bottom: calc(100% + 4px);\n }\n :host([open]) .pop { display: block; }\n .time-row {\n display: flex;\n align-items: center;\n gap: var(--fx-space-xs);\n padding: var(--fx-space-sm) var(--fx-space-md);\n border-top: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n border-radius: 0 0 var(--fx-radius-md) var(--fx-radius-md);\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-text-default);\n }\n .time-row label { color: var(--fx-text-muted); }\n .time-row input {\n width: 38px;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n padding: 2px 2px;\n outline: none;\n text-align: center;\n -moz-appearance: textfield;\n appearance: textfield;\n }\n .time-row input::-webkit-outer-spin-button,\n .time-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }\n .time-row .sep { color: var(--fx-text-muted); font-weight: bold; padding: 0 1px; }\n .time-row input:focus {\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([disabled]) .field { opacity: 0.55; cursor: not-allowed; background: var(--fx-surface-surface-hover); }\n :host([disabled]) .display { cursor: not-allowed; }\n :host([free-text]) .display { cursor: text; }\n :host([free-text]) .display.invalid { border-bottom: 2px solid var(--fx-color-danger); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['value', 'start', 'end', 'values', 'mode', 'min', 'max', 'placeholder', 'disabled', 'format', 'show-time', 'free-text', 'error', 'invalid', 'success', 'valid'];\n }\n\n protected override attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n super.attributeChangedCallback(name, oldValue, newValue);\n // Mantém o FormData do form sincronizado sem tocar no render.\n if (name === 'value' || name === 'start' || name === 'end' || name === 'values') {\n this.updateFormValue();\n }\n }\n\n /** No FormData do form, conforme o modo (single/range/multiple). */\n protected override getFormValue(): string | FormData | null {\n if (this.mode === 'range') {\n const start = this.getAttr('start');\n const end = this.getAttr('end');\n if (!this.name || (!start && !end)) return null;\n const fd = new FormData();\n if (start) fd.append(`${this.name}-start`, start);\n if (end) fd.append(`${this.name}-end`, end);\n return fd;\n }\n if (this.mode === 'multiple') {\n const values = this.values;\n if (!this.name || values.length === 0) return null;\n const fd = new FormData();\n values.forEach((v) => fd.append(this.name, v));\n return fd;\n }\n return this.getAttr('value') || null;\n }\n\n private docListener?: (e: Event) => void;\n /** Hora/minuto/segundo do modo show-time. */\n private time = { h: 0, m: 0, s: 0 };\n /** Estado inicial (value/start/end/values) para form.reset(). */\n private _initialState?: { value: string; start: string; end: string; values: string };\n\n get mode(): string {\n const m = this.getAttr('mode', 'single');\n return ['single', 'range', 'multiple'].includes(m) ? m : 'single';\n }\n set mode(value: string) {\n this.setAttribute('mode', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(v: string) {\n this.setAttribute('value', v);\n }\n\n /** Datas do modo múltiplo (CSV no atributo). */\n get values(): string[] {\n const csv = this.getAttr('values');\n return csv ? csv.split(',').map((v) => v.trim()).filter(Boolean) : [];\n }\n set values(list: string[]) {\n this.setAttribute('values', list.join(','));\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 connectedCallback(): void {\n if (!this._initialState) {\n this._initialState = {\n value: this.getAttr('value'),\n start: this.getAttr('start'),\n end: this.getAttr('end'),\n values: this.getAttr('values'),\n };\n }\n super.connectedCallback();\n // Fecha o popover ao clicar fora.\n this.docListener = (e: Event) => {\n if (!this.hasAttr('open')) return;\n if (e.composedPath().includes(this)) return;\n this.removeAttribute('open');\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n /** form.reset(): restaura value/start/end/values iniciais (re-render via atributos observados). */\n override formResetCallback(): void {\n const s = this._initialState ?? { value: '', start: '', end: '', values: '' };\n this.setAttribute('value', s.value);\n this.setAttribute('start', s.start);\n this.setAttribute('end', s.end);\n this.setAttribute('values', s.values);\n this.updateFormValue();\n }\n\n /** Formata uma data ISO (\"YYYY-MM-DD\" ou com \"THH:mm:ss\") segundo o atributo format. */\n private formatDate(iso: string): string {\n if (!iso) return '';\n const fmt = this.getAttr('format');\n // Sem máscara: exibe apenas a parte da data (a hora entra via timeText\n // quando show-time está ativo — nunca duplicada).\n if (!fmt) return iso.split('T')[0]!;\n const [datePart, timePart] = iso.split('T');\n const [y = '', m = '', d = ''] = datePart.split('-');\n const [H = '00', M = '00', S = '00'] = (timePart ?? '').split(':');\n // Placeholders para evitar colisão entre mm (mês) e MM (minuto).\n return fmt\n .replace(/yyyy/gi, '\\u0001')\n .replace(/MM/g, '\\u0005')\n .replace(/mm/g, '\\u0002')\n .replace(/dd/gi, '\\u0003')\n .replace(/HH/g, '\\u0004')\n .replace(/SS/gi, '\\u0006')\n .replace(/\\u0001/g, y)\n .replace(/\\u0002/g, m)\n .replace(/\\u0003/g, d)\n .replace(/\\u0004/g, H)\n .replace(/\\u0005/g, M)\n .replace(/\\u0006/g, S);\n }\n\n /** Texto exibido no input conforme o modo. */\n private displayText(): string {\n if (this.mode === 'range') {\n const s = this.getAttr('start');\n const e = this.getAttr('end');\n const arrow = this.hasAttr('show-time') ? ' ' : ' → ';\n if (!s && !e) return '';\n if (this.hasAttr('show-time')) {\n const f = (iso?: string): string => (iso ? `${this.formatDate(iso)} ${this.timeText()}` : iso ? `${this.formatDate(iso)} …` : '');\n return s && e ? `${f(s)} → ${f(e)}` : f(s);\n }\n void arrow;\n return s && e ? `${this.formatDate(s)} → ${this.formatDate(e)}` : s ? `${this.formatDate(s)} → …` : '';\n }\n if (this.mode === 'multiple') {\n return this.values.map((v) => this.formatDate(v)).join(', ');\n }\n const base = this.formatDate(this.value);\n return base && this.hasAttr('show-time') ? `${base} ${this.timeText()}` : base;\n }\n\n /** \"HH:MM:SS\" do estado interno de show-time. */\n private timeText(): string {\n const p = (n: number): string => String(n).padStart(2, '0');\n return `${p(this.time.h)}:${p(this.time.m)}:${p(this.time.s)}`;\n }\n\n /** Extrai hora de um valor ISO inicial, se houver. */\n private loadTimeFromValue(): void {\n const src = this.value || this.getAttr('start') || '';\n const t = src.split('T')[1];\n if (!t) return;\n const [h, m, s] = t.split(':').map((n) => parseInt(n, 10));\n this.time = { h: h || 0, m: m || 0, s: s || 0 };\n }\n\n /** Anexa o horário interno ao ISO puro vindo do calendário. */\n private withTime(iso: string): string {\n if (!iso) return iso;\n return this.hasAttr('show-time')\n ? `${iso}T${this.timeText()}`\n : iso;\n }\n\n protected override render(): void {\n const disabled = this.disabled;\n const clearable = this.hasAttr('clearable');\n const text = this.displayText();\n const hasValue = text.length > 0;\n const showTime = this.hasAttr('show-time');\n this.loadTimeFromValue();\n\n // Atributos repassados ao calendário interno, conforme o modo.\n let calAttrs = `min=\"${esc(this.getAttr('min'))}\" max=\"${esc(this.getAttr('max'))}\"`;\n if (this.mode === 'range') {\n calAttrs += ` range start=\"${esc(this.getAttr('start'))}\" end=\"${esc(this.getAttr('end'))}\"`;\n } else if (this.mode === 'multiple') {\n // Repassa apenas as partes de data (sem hora) para o calendário pintar.\n const dates = this.values.map((v) => v.split('T')[0]).join(',');\n calAttrs += ` mode=\"multiple\" values=\"${esc(dates)}\"`;\n } else {\n const v = this.value.split('T')[0] ?? '';\n calAttrs += ` value=\"${esc(v)}\"`;\n }\n\n this.setTemplate(`\n <div class=\"field\" part=\"field\">\n <input class=\"display\" part=\"display\" type=\"text\" ${this.hasAttr('free-text') && !disabled ? '' : 'readonly'}\n placeholder=\"${esc(this.getAttr('placeholder', 'dd/mm/aaaa'))}\"\n value=\"${esc(text)}\" ${disabled ? 'disabled' : ''}>\n <button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\"\n ${clearable && hasValue ? '' : 'hidden'}>\n ×\n </button>\n <span class=\"cal-icon\" part=\"icon\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M16 2v4M8 2v4M3 10h18\"/>\n </svg>\n </span>\n </div>\n <div class=\"pop\" part=\"popover\">\n <fx-calendar ${calAttrs}></fx-calendar>\n ${\n showTime\n ? `<div class=\"time-row\" part=\"time\">\n <input class=\"t-h\" type=\"number\" min=\"0\" max=\"23\" value=\"${this.time.h}\" aria-label=\"Hora\"><span class=\"sep\">:</span>\n <input class=\"t-m\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.m}\" aria-label=\"Minuto\"><span class=\"sep\">:</span>\n <input class=\"t-s\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.s}\" aria-label=\"Segundo\">\n </div>`\n : ''\n }\n </div>\n `);\n\n const field = this.root.querySelector('.field');\n if (!field) return;\n if (disabled) return;\n\n field.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.clear')) return;\n // Com free-text, clicar no próprio input serve para digitar — não abre.\n if (this.hasAttr('free-text') && (e.target as HTMLElement).classList.contains('display')) return;\n this.toggleAttribute('open');\n // Flip vertical: se não cabe abaixo, abre para cima.\n if (this.hasAttr('open')) {\n const r = this.getBoundingClientRect();\n this.toggleAttribute('up', r.bottom + 340 > window.innerHeight && r.top > 340);\n } else {\n this.removeAttribute('up');\n }\n });\n\n // Limpar: zera tudo conforme o modo.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.removeAttribute('value');\n this.removeAttribute('start');\n this.removeAttribute('end');\n this.removeAttribute('values');\n this.removeAttribute('open');\n this.render();\n this.emitChange({});\n });\n\n // Sincroniza seleção do calendário de volta para os atributos.\n const cal = this.root.querySelector<HTMLElement>('.pop fx-calendar');\n cal?.addEventListener('change', (e) => {\n const d = (e as CustomEvent).detail ?? {};\n if (d.start !== undefined || d.end !== undefined) {\n if (d.start) this.setAttribute('start', this.withTime(d.start));\n else this.removeAttribute('start');\n if (d.end) this.setAttribute('end', this.withTime(d.end));\n else this.removeAttribute('end');\n }\n if (d.values !== undefined) {\n this.values = d.values.map((v: string) => this.withTime(v));\n }\n if (d.value !== undefined) {\n if (d.value) this.setAttribute('value', this.withTime(d.value));\n else this.removeAttribute('value');\n // No modo single, fecha ao escolher.\n if (this.mode === 'single') this.removeAttribute('open');\n }\n this.render();\n this.emitChange(d);\n });\n\n // free-text: valida ao sair do campo ou pressionar Enter.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display && this.hasAttr('free-text') && !disabled) {\n display.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.commitTyped(display);\n }\n });\n display.addEventListener('blur', () => this.commitTyped(display));\n }\n\n // show-time: captura alterações de hora/minuto/segundo.\n if (showTime) {\n const applyTime = (): void => {\n const h = this.root.querySelector<HTMLInputElement>('.t-h');\n const m = this.root.querySelector<HTMLInputElement>('.t-m');\n const s = this.root.querySelector<HTMLInputElement>('.t-s');\n const clamp = (v: string | undefined, max: number): number =>\n Math.min(max, Math.max(0, parseInt(v ?? '0', 10) || 0));\n this.time = {\n h: clamp(h?.value, 23),\n m: clamp(m?.value, 59),\n s: clamp(s?.value, 59),\n };\n // Reanexa o horário aos valores já selecionados.\n const reattach = (iso: string): string => iso.split('T')[0] ? `${iso.split('T')[0]}T${this.timeText()}` : iso;\n if (this.value) this.setAttribute('value', reattach(this.value));\n if (this.getAttr('start')) this.setAttribute('start', reattach(this.getAttr('start')));\n if (this.getAttr('end')) this.setAttribute('end', reattach(this.getAttr('end')));\n if (this.values.length) {\n this.values = this.values.map((v) => reattach(v));\n }\n // Atualiza apenas o texto do input, sem re-render completo.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display) display.value = this.displayText();\n this.emitChange({ value: this.value });\n };\n ['.t-h', '.t-m', '.t-s'].forEach((sel) => {\n this.root.querySelector(sel)?.addEventListener('input', applyTime);\n });\n }\n }\n\n private emitChange(detail: Record<string, unknown>): void {\n // input (v-model do Vue) + valueChange ([(value)] do Angular) com o\n // mesmo payload do change (single: { value } · range: { start, end }).\n this.emitValueEvents(detail);\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail }),\n );\n }\n\n /**\n * Interpreta o texto digitado livremente e devolve ISO \"YYYY-MM-DD\"\n * (ou null se inválido). Aceita a máscara do atributo `format`\n * (tokens dd/mm/yyyy) ou ISO direto.\n */\n private parseTyped(raw: string): string | null {\n const text = raw.trim();\n if (!text) return null;\n const fmt = this.getAttr('format', 'dd/mm/yyyy').toLowerCase();\n\n // Ordem dos tokens conforme a máscara informada.\n const order = ['dd', 'mm', 'yyyy'].filter((t) => fmt.includes(t));\n if (order.length < 3) return null;\n\n const parts = text.split(new RegExp(`[^0-9]+`)).filter(Boolean);\n if (parts.length !== 3) return null;\n\n const map: Record<string, string> = {};\n order.forEach((t, i) => (map[t] = parts[i]!));\n const iso = `${map['yyyy']}-${(map['mm'] ?? '').padStart(2, '0')}-${(map['dd'] ?? '').padStart(2, '0')}`;\n\n // Validação de data real.\n const d = new Date(`${iso}T12:00:00`);\n if (Number.isNaN(d.getTime())) return null;\n const [y, m, day] = iso.split('-').map((n) => parseInt(n, 10));\n if (d.getFullYear() !== y || d.getMonth() + 1 !== m || d.getDate() !== day) return null;\n\n // Limites min/max (apenas a parte da data).\n const cmp = (a: string, b: string): number => a.split('T')[0]!.localeCompare(b.split('T')[0]!);\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n if (min && cmp(iso, min) < 0) return null;\n if (max && cmp(iso, max) > 0) return null;\n\n return iso;\n }\n\n /** Aplica o texto digitado no modo free-text (blur/Enter). */\n private commitTyped(input: HTMLInputElement): void {\n const parsed = this.parseTyped(input.value);\n if (!parsed) {\n // Inválido: sinaliza, reverte o texto e emite evento.\n input.classList.add('invalid');\n this.dispatchEvent(\n new CustomEvent('invalid', { bubbles: true, composed: true, detail: { text: input.value } }),\n );\n input.value = this.displayText();\n setTimeout(() => input.classList.remove('invalid'), 1500);\n return;\n }\n if (this.mode === 'single') {\n this.setAttribute('value', this.withTime(parsed));\n this.emitChange({ value: this.value });\n } else if (this.mode === 'range') {\n // Primeira digitação vira start; com start preenchido, vira end.\n if (!this.getAttr('start') || (this.getAttr('start') && this.getAttr('end'))) {\n this.setAttribute('start', this.withTime(parsed));\n this.removeAttribute('end');\n } else {\n const [a, b] = [this.getAttr('start')!.split('T')[0]!, parsed].sort();\n this.setAttribute('start', this.withTime(a));\n this.setAttribute('end', this.withTime(b));\n }\n this.emitChange({ start: this.getAttr('start'), end: this.getAttr('end') });\n } else {\n const list = this.values.filter((v) => v.split('T')[0] !== parsed);\n list.push(this.withTime(parsed));\n this.values = list;\n this.emitChange({ values: this.values });\n }\n this.render();\n }\n}\n\nexport function defineFxDatepicker(): typeof FxDatepicker {\n return defineElement('fx-datepicker', FxDatepicker);\n}\n\n"],"names":["display"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"datepicker.js","sources":["../../../src/components/datepicker/datepicker.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-datepicker> — Campo de data com calendário suspenso.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`): single → `name`; range → `name-start`/`name-end`;\n * multiple → uma entrada `name` por data.\n *\n * Um input com ícone de calendário no final; ao clicar, abre o\n * <fx-calendar> em um popover. As datas selecionadas aparecem no input.\n *\n * Atributos:\n * - mode=\"single\" | \"range\" | \"multiple\" (padrão: single)\n * - value=\"YYYY-MM-DD\" → single\n * - start/end → range\n * - values=\"a,b,c\" → múltiplas datas (CSV)\n * - min/max, placeholder, disabled, clearable, size (sm|md|lg)\n * - format=\"dd/mm/yyyy\" → máscara de exibição (tokens: dd mm yyyy, HH MM SS)\n * - show-time → inclui seleção de hora:minuto:segundo no popover;\n * o valor passa a ser \"YYYY-MM-DDTHH:mm:ss\"\n *\n * Evento: `change` (composed):\n * - single: { value }\n * - range: { value, start, end }\n * - multiple: { values: string[] }\n *\n * Além do `change`, emite `input`/`valueChange` com o MESMO payload\n * (v-model do Vue e [(value)] do Angular).\n */\nexport class FxDatepicker extends FxFormElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n position: relative;\n }\n .field {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs);\n width: 100%;\n box-sizing: border-box;\n /* Base = tamanho padrão (md): 40px */\n min-height: var(--fx-size-md, 40px);\n min-width: 150px;\n padding: var(--fx-space-xs) var(--fx-space-md);\n /* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */\n line-height: var(--fx-font-line-height);\n background-color: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n cursor: pointer;\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 :host([size='sm']) .field { min-height: var(--fx-size-sm); min-width: 180px; }\n :host([size='md']) .field { min-height: var(--fx-size-md); }\n :host([size='lg']) .field { min-height: var(--fx-size-lg); min-width: 220px; font-size: calc(var(--fx-font-size) + 4px); }\n /* Range e multiple exibem mais de uma data: campo mais largo */\n :host([mode='range']) .field { min-width: 250px; }\n :host([mode='multiple']) .field { min-width: 220px; }\n :host([mode='range'][size='lg']) .field { min-width: 360px; }\n :host([mode='multiple'][size='lg']) .field { min-width: 320px; }\n :host([show-time]) .field { min-width: 250px; }\n /* Full width: o host estica até o pai (os min-width dos modos são\n descartados para permitir layouts estreitos sob controle do pai). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { min-width: 0; }\n /* Validação — borda e brilho JÁ EM REPOUSO (igual ao fx-input); o brilho\n segue o token --fx-effect-error-ring, que acompanha effect.focus-ring. */\n :host([error]) .field,\n :host([invalid]) .field {\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([error]) .field:focus-within,\n :host([invalid]) .field:focus-within,\n :host([error][open]) .field,\n :host([invalid][open]) .field {\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 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([success]) .field:focus-within,\n :host([valid]) .field:focus-within,\n :host([success][open]) .field,\n :host([valid][open]) .field {\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 .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-within {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .display {\n flex: 1;\n border: none;\n background: none;\n font: inherit;\n color: var(--fx-text-default);\n outline: none;\n cursor: pointer;\n text-align: left;\n padding: 0;\n min-width: 0;\n }\n .display::placeholder { color: var(--fx-text-muted); }\n .cal-icon { color: var(--fx-text-muted); display: inline-flex; pointer-events: none; }\n .clear {\n border: none;\n background: none;\n color: var(--fx-text-muted);\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n padding: 0 2px;\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n .pop {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\n display: none;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n box-shadow: var(--fx-shadow-lg);\n overflow: hidden;\n }\n :host([up]) .pop {\n top: auto;\n bottom: calc(100% + 4px);\n }\n :host([open]) .pop { display: block; }\n .time-row {\n display: flex;\n align-items: center;\n gap: var(--fx-space-xs);\n padding: var(--fx-space-sm) var(--fx-space-md);\n border-top: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n border-radius: 0 0 var(--fx-radius-md) var(--fx-radius-md);\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-text-default);\n }\n .time-row label { color: var(--fx-text-muted); }\n .time-row input {\n width: 38px;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n padding: 2px 2px;\n outline: none;\n text-align: center;\n -moz-appearance: textfield;\n appearance: textfield;\n }\n .time-row input::-webkit-outer-spin-button,\n .time-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }\n .time-row .sep { color: var(--fx-text-muted); font-weight: bold; padding: 0 1px; }\n .time-row input:focus {\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([disabled]) .field { opacity: 0.55; cursor: not-allowed; background: var(--fx-surface-surface-hover); }\n :host([disabled]) .display { cursor: not-allowed; }\n :host([free-text]) .display { cursor: text; }\n :host([free-text]) .display.invalid { border-bottom: 2px solid var(--fx-color-danger); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['value', 'start', 'end', 'values', 'mode', 'min', 'max', 'placeholder', 'disabled', 'format', 'show-time', 'free-text', 'clearable', 'full', 'error', 'invalid', 'success', 'valid'];\n }\n\n /** Evita renders em cascata quando vários atributos mudam juntos. */\n private _suppressRender = false;\n\n protected override attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n // Durante agrupamentos internos (applyTime/calendar/clear) não recria o\n // shadow DOM: o foco do input de hora/minuto/segundo ficaria perdido.\n if (!this._suppressRender) {\n super.attributeChangedCallback(name, oldValue, newValue);\n }\n // Mantém o FormData do form sincronizado sem tocar no render.\n if (name === 'value' || name === 'start' || name === 'end' || name === 'values') {\n this.updateFormValue();\n }\n }\n\n /** No FormData do form, conforme o modo (single/range/multiple). */\n protected override getFormValue(): string | FormData | null {\n if (this.mode === 'range') {\n const start = this.getAttr('start');\n const end = this.getAttr('end');\n if (!this.name || (!start && !end)) return null;\n const fd = new FormData();\n if (start) fd.append(`${this.name}-start`, start);\n if (end) fd.append(`${this.name}-end`, end);\n return fd;\n }\n if (this.mode === 'multiple') {\n const values = this.values;\n if (!this.name || values.length === 0) return null;\n const fd = new FormData();\n values.forEach((v) => fd.append(this.name, v));\n return fd;\n }\n return this.getAttr('value') || null;\n }\n\n private docListener?: (e: Event) => void;\n /** Hora/minuto/segundo do modo show-time. */\n private time = { h: 0, m: 0, s: 0 };\n /** Estado inicial (value/start/end/values) para form.reset(). */\n private _initialState?: { value: string; start: string; end: string; values: string };\n\n get mode(): string {\n const m = this.getAttr('mode', 'single');\n return ['single', 'range', 'multiple'].includes(m) ? m : 'single';\n }\n set mode(value: string) {\n this.setAttribute('mode', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(v: string | number | null | undefined) {\n this.setAttribute('value', v == null ? '' : String(v));\n }\n\n get start(): string {\n return this.getAttr('start');\n }\n set start(v: string | null | undefined) {\n this.setAttribute('start', v == null ? '' : String(v));\n }\n\n get end(): string {\n return this.getAttr('end');\n }\n set end(v: string | null | undefined) {\n this.setAttribute('end', v == null ? '' : String(v));\n }\n\n /** Datas do modo múltiplo (CSV no atributo). */\n get values(): string[] {\n const csv = this.getAttr('values');\n return csv ? csv.split(',').map((v) => v.trim()).filter(Boolean) : [];\n }\n set values(list: string[] | null | undefined) {\n const values = Array.isArray(list) ? list.map(String) : [];\n this.setAttribute('values', values.join(','));\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 /** `:show-time` procura a propriedade LITERAL no Vue (sem camelização). */\n get 'show-time'(): boolean {\n return this.hasAttr('show-time');\n }\n set 'show-time'(value: boolean) {\n this.toggleAttr('show-time', Boolean(value));\n }\n\n get 'free-text'(): boolean {\n return this.hasAttr('free-text');\n }\n set 'free-text'(value: boolean) {\n this.toggleAttr('free-text', 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 connectedCallback(): void {\n if (!this._initialState) {\n this._initialState = {\n value: this.getAttr('value'),\n start: this.getAttr('start'),\n end: this.getAttr('end'),\n values: this.getAttr('values'),\n };\n }\n super.connectedCallback();\n // Fecha o popover ao clicar fora.\n this.docListener = (e: Event) => {\n if (!this.hasAttr('open')) return;\n if (e.composedPath().includes(this)) return;\n this.removeAttribute('open');\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n /** form.reset(): restaura value/start/end/values iniciais (re-render via atributos observados). */\n override formResetCallback(): void {\n const s = this._initialState ?? { value: '', start: '', end: '', values: '' };\n this._suppressRender = true;\n try {\n this.setAttribute('value', s.value);\n this.setAttribute('start', s.start);\n this.setAttribute('end', s.end);\n this.setAttribute('values', s.values);\n } finally {\n this._suppressRender = false;\n }\n this.updateFormValue();\n if (this.isConnected) this.render();\n }\n\n /** Formata uma data ISO (\"YYYY-MM-DD\" ou com \"THH:mm:ss\") segundo o atributo format. */\n private formatDate(iso: string): string {\n if (!iso) return '';\n const fmt = this.getAttr('format');\n // Sem máscara: exibe apenas a parte da data (a hora entra via timeText\n // quando show-time está ativo — nunca duplicada).\n if (!fmt) return iso.split('T')[0]!;\n const [datePart, timePart] = iso.split('T');\n const [y = '', m = '', d = ''] = datePart.split('-');\n const [H = '00', M = '00', S = '00'] = (timePart ?? '').split(':');\n // Placeholders para evitar colisão entre mm (mês) e MM (minuto).\n return fmt\n .replace(/yyyy/gi, '\\u0001')\n .replace(/MM/g, '\\u0005')\n .replace(/mm/g, '\\u0002')\n .replace(/dd/gi, '\\u0003')\n .replace(/HH/g, '\\u0004')\n .replace(/SS/gi, '\\u0006')\n .replace(/\\u0001/g, y)\n .replace(/\\u0002/g, m)\n .replace(/\\u0003/g, d)\n .replace(/\\u0004/g, H)\n .replace(/\\u0005/g, M)\n .replace(/\\u0006/g, S);\n }\n\n /** Texto exibido no input conforme o modo. */\n private displayText(): string {\n if (this.mode === 'range') {\n const s = this.getAttr('start');\n const e = this.getAttr('end');\n const arrow = this.hasAttr('show-time') ? ' ' : ' → ';\n if (!s && !e) return '';\n if (this.hasAttr('show-time')) {\n const f = (iso?: string): string => (iso ? `${this.formatDate(iso)} ${this.timeText()}` : iso ? `${this.formatDate(iso)} …` : '');\n return s && e ? `${f(s)} → ${f(e)}` : f(s);\n }\n void arrow;\n return s && e ? `${this.formatDate(s)} → ${this.formatDate(e)}` : s ? `${this.formatDate(s)} → …` : '';\n }\n if (this.mode === 'multiple') {\n return this.values.map((v) => this.formatDate(v)).join(', ');\n }\n const base = this.formatDate(this.value);\n return base && this.hasAttr('show-time') ? `${base} ${this.timeText()}` : base;\n }\n\n /** \"HH:MM:SS\" do estado interno de show-time. */\n private timeText(): string {\n const p = (n: number): string => String(n).padStart(2, '0');\n return `${p(this.time.h)}:${p(this.time.m)}:${p(this.time.s)}`;\n }\n\n /** Extrai hora de um valor ISO inicial, se houver. */\n private loadTimeFromValue(): void {\n const src = this.value || this.getAttr('start') || '';\n const t = src.split('T')[1];\n if (!t) return;\n const [h, m, s] = t.split(':').map((n) => parseInt(n, 10));\n this.time = { h: h || 0, m: m || 0, s: s || 0 };\n }\n\n /** Anexa o horário interno ao ISO puro vindo do calendário. */\n private withTime(iso: string): string {\n if (!iso) return iso;\n return this.hasAttr('show-time')\n ? `${iso}T${this.timeText()}`\n : iso;\n }\n\n protected override render(): void {\n const disabled = this.disabled;\n const clearable = this.hasAttr('clearable');\n const text = this.displayText();\n const hasValue = text.length > 0;\n const showTime = this.hasAttr('show-time');\n this.loadTimeFromValue();\n\n // Atributos repassados ao calendário interno, conforme o modo.\n let calAttrs = `min=\"${esc(this.getAttr('min'))}\" max=\"${esc(this.getAttr('max'))}\"`;\n if (this.mode === 'range') {\n calAttrs += ` range start=\"${esc(this.getAttr('start'))}\" end=\"${esc(this.getAttr('end'))}\"`;\n } else if (this.mode === 'multiple') {\n // Repassa apenas as partes de data (sem hora) para o calendário pintar.\n const dates = this.values.map((v) => v.split('T')[0]).join(',');\n calAttrs += ` mode=\"multiple\" values=\"${esc(dates)}\"`;\n } else {\n const v = this.value.split('T')[0] ?? '';\n calAttrs += ` value=\"${esc(v)}\"`;\n }\n\n this.setTemplate(`\n <div class=\"field\" part=\"field\">\n <input class=\"display\" part=\"display\" type=\"text\" ${this.hasAttr('free-text') && !disabled ? '' : 'readonly'}\n placeholder=\"${esc(this.getAttr('placeholder', 'dd/mm/aaaa'))}\"\n value=\"${esc(text)}\" ${disabled ? 'disabled' : ''}>\n <button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\"\n ${clearable && hasValue ? '' : 'hidden'}>\n ×\n </button>\n <span class=\"cal-icon\" part=\"icon\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M16 2v4M8 2v4M3 10h18\"/>\n </svg>\n </span>\n </div>\n <div class=\"pop\" part=\"popover\">\n <fx-calendar ${calAttrs}></fx-calendar>\n ${\n showTime\n ? `<div class=\"time-row\" part=\"time\">\n <input class=\"t-h\" type=\"number\" min=\"0\" max=\"23\" value=\"${this.time.h}\" aria-label=\"Hora\"><span class=\"sep\">:</span>\n <input class=\"t-m\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.m}\" aria-label=\"Minuto\"><span class=\"sep\">:</span>\n <input class=\"t-s\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.s}\" aria-label=\"Segundo\">\n </div>`\n : ''\n }\n </div>\n `);\n\n const field = this.root.querySelector('.field');\n if (!field) return;\n if (disabled) return;\n\n field.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.clear')) return;\n // Com free-text, clicar no próprio input serve para digitar — não abre.\n if (this.hasAttr('free-text') && (e.target as HTMLElement).classList.contains('display')) return;\n this.toggleAttribute('open');\n // Flip vertical: se não cabe abaixo, abre para cima.\n if (this.hasAttr('open')) {\n const r = this.getBoundingClientRect();\n this.toggleAttribute('up', r.bottom + 340 > window.innerHeight && r.top > 340);\n } else {\n this.removeAttribute('up');\n }\n });\n\n // Limpar: zera tudo conforme o modo (um único render no final).\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this._suppressRender = true;\n try {\n this.removeAttribute('value');\n this.removeAttribute('start');\n this.removeAttribute('end');\n this.removeAttribute('values');\n } finally {\n this._suppressRender = false;\n }\n this.removeAttribute('open');\n this.render();\n this.emitChange({});\n });\n\n // Sincroniza seleção do calendário de volta para os atributos.\n const cal = this.root.querySelector<HTMLElement>('.pop fx-calendar');\n cal?.addEventListener('change', (e) => {\n const d = (e as CustomEvent).detail ?? {};\n // Agrupa os setAttribute em um único render (o calendário dispara\n // value/start/end/values de uma vez).\n this._suppressRender = true;\n try {\n if (d.start !== undefined || d.end !== undefined) {\n if (d.start) this.setAttribute('start', this.withTime(d.start));\n else this.removeAttribute('start');\n if (d.end) this.setAttribute('end', this.withTime(d.end));\n else this.removeAttribute('end');\n }\n if (d.values !== undefined) {\n this.values = d.values.map((v: string) => this.withTime(v));\n }\n if (d.value !== undefined) {\n if (d.value) this.setAttribute('value', this.withTime(d.value));\n else this.removeAttribute('value');\n // No modo single, fecha ao escolher.\n if (this.mode === 'single') this.removeAttribute('open');\n }\n } finally {\n this._suppressRender = false;\n }\n this.render();\n this.emitChange(d);\n });\n\n // free-text: valida ao sair do campo ou pressionar Enter.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display && this.hasAttr('free-text') && !disabled) {\n display.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.commitTyped(display);\n }\n });\n display.addEventListener('blur', () => this.commitTyped(display));\n }\n\n // show-time: captura alterações de hora/minuto/segundo.\n if (showTime) {\n const applyTime = (): void => {\n const h = this.root.querySelector<HTMLInputElement>('.t-h');\n const m = this.root.querySelector<HTMLInputElement>('.t-m');\n const s = this.root.querySelector<HTMLInputElement>('.t-s');\n const clamp = (v: string | undefined, max: number): number =>\n Math.min(max, Math.max(0, parseInt(v ?? '0', 10) || 0));\n this.time = {\n h: clamp(h?.value, 23),\n m: clamp(m?.value, 59),\n s: clamp(s?.value, 59),\n };\n // Reanexa o horário aos valores já selecionados.\n const reattach = (iso: string): string => iso.split('T')[0] ? `${iso.split('T')[0]}T${this.timeText()}` : iso;\n // Sem supressão, cada setAttribute acima re-renderizaria e o input\n // de hora/minuto/segundo perderia o foco a cada dígito.\n this._suppressRender = true;\n try {\n if (this.value) this.setAttribute('value', reattach(this.value));\n if (this.getAttr('start')) this.setAttribute('start', reattach(this.getAttr('start')));\n if (this.getAttr('end')) this.setAttribute('end', reattach(this.getAttr('end')));\n if (this.values.length) {\n this.values = this.values.map((v) => reattach(v));\n }\n } finally {\n this._suppressRender = false;\n }\n // Atualiza apenas o texto do input, sem re-render completo.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display) display.value = this.displayText();\n this.emitChange({ value: this.value });\n };\n ['.t-h', '.t-m', '.t-s'].forEach((sel) => {\n this.root.querySelector(sel)?.addEventListener('input', applyTime);\n });\n }\n }\n\n private emitChange(detail: Record<string, unknown>): void {\n // input (v-model do Vue) + valueChange ([(value)] do Angular) com o\n // mesmo payload do change (single: { value } · range: { start, end }).\n this.emitValueEvents(detail);\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail }),\n );\n }\n\n /**\n * Interpreta o texto digitado livremente e devolve ISO \"YYYY-MM-DD\"\n * (ou null se inválido). Aceita a máscara do atributo `format`\n * (tokens dd/mm/yyyy) ou ISO direto.\n */\n private parseTyped(raw: string): string | null {\n const text = raw.trim();\n if (!text) return null;\n const fmt = this.getAttr('format', 'dd/mm/yyyy').toLowerCase();\n\n // Ordem dos tokens conforme a máscara informada.\n const order = ['dd', 'mm', 'yyyy'].filter((t) => fmt.includes(t));\n if (order.length < 3) return null;\n\n const parts = text.split(new RegExp(`[^0-9]+`)).filter(Boolean);\n if (parts.length !== 3) return null;\n\n const map: Record<string, string> = {};\n order.forEach((t, i) => (map[t] = parts[i]!));\n const iso = `${map['yyyy']}-${(map['mm'] ?? '').padStart(2, '0')}-${(map['dd'] ?? '').padStart(2, '0')}`;\n\n // Validação de data real.\n const d = new Date(`${iso}T12:00:00`);\n if (Number.isNaN(d.getTime())) return null;\n const [y, m, day] = iso.split('-').map((n) => parseInt(n, 10));\n if (d.getFullYear() !== y || d.getMonth() + 1 !== m || d.getDate() !== day) return null;\n\n // Limites min/max (apenas a parte da data).\n const cmp = (a: string, b: string): number => a.split('T')[0]!.localeCompare(b.split('T')[0]!);\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n if (min && cmp(iso, min) < 0) return null;\n if (max && cmp(iso, max) > 0) return null;\n\n return iso;\n }\n\n /** Aplica o texto digitado no modo free-text (blur/Enter). */\n private commitTyped(input: HTMLInputElement): void {\n const parsed = this.parseTyped(input.value);\n if (!parsed) {\n // Inválido: sinaliza, reverte o texto e emite evento.\n input.classList.add('invalid');\n this.dispatchEvent(\n new CustomEvent('invalid', { bubbles: true, composed: true, detail: { text: input.value } }),\n );\n input.value = this.displayText();\n setTimeout(() => input.classList.remove('invalid'), 1500);\n return;\n }\n this._suppressRender = true;\n try {\n if (this.mode === 'single') {\n this.setAttribute('value', this.withTime(parsed));\n } else if (this.mode === 'range') {\n // Primeira digitação vira start; com start preenchido, vira end.\n if (!this.getAttr('start') || (this.getAttr('start') && this.getAttr('end'))) {\n this.setAttribute('start', this.withTime(parsed));\n this.removeAttribute('end');\n } else {\n const [a, b] = [this.getAttr('start')!.split('T')[0]!, parsed].sort();\n this.setAttribute('start', this.withTime(a));\n this.setAttribute('end', this.withTime(b));\n }\n } else {\n const list = this.values.filter((v) => v.split('T')[0] !== parsed);\n list.push(this.withTime(parsed));\n this.values = list;\n }\n } finally {\n this._suppressRender = false;\n }\n\n if (this.mode === 'single') {\n this.emitChange({ value: this.value });\n } else if (this.mode === 'range') {\n this.emitChange({ start: this.getAttr('start'), end: this.getAttr('end') });\n } else {\n this.emitChange({ values: this.values });\n }\n this.render();\n }\n}\n\nexport function defineFxDatepicker(): typeof FxDatepicker {\n return defineElement('fx-datepicker', FxDatepicker);\n}\n\n"],"names":["display"],"mappings":";;;;AAiCO,MAAM,gBAAN,MAAM,sBAAqB,cAAc;AAAA,EAAzC,cAAA;AAAA,UAAA,GAAA,SAAA;AAgKL,SAAQ,kBAAkB;AAqC1B,SAAQ,OAAO,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAA;AAAA,EAAE;AAAA,EA1ClC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,SAAS,SAAS,OAAO,UAAU,QAAQ,OAAO,OAAO,eAAe,YAAY,UAAU,aAAa,aAAa,aAAa,QAAQ,SAAS,WAAW,WAAW,OAAO;AAAA,EAC7L;AAAA,EAKmB,yBAAyB,MAAc,UAAyB,UAA+B;AAGhH,QAAI,CAAC,KAAK,iBAAiB;AACzB,YAAM,yBAAyB,MAAM,UAAU,QAAQ;AAAA,IACzD;AAEA,QAAI,SAAS,WAAW,SAAS,WAAW,SAAS,SAAS,SAAS,UAAU;AAC/E,WAAK,gBAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGmB,eAAyC;AAC1D,QAAI,KAAK,SAAS,SAAS;AACzB,YAAM,QAAQ,KAAK,QAAQ,OAAO;AAClC,YAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,UAAI,CAAC,KAAK,QAAS,CAAC,SAAS,CAAC,IAAM,QAAO;AAC3C,YAAM,KAAK,IAAI,SAAA;AACf,UAAI,MAAO,IAAG,OAAO,GAAG,KAAK,IAAI,UAAU,KAAK;AAChD,UAAI,IAAK,IAAG,OAAO,GAAG,KAAK,IAAI,QAAQ,GAAG;AAC1C,aAAO;AAAA,IACT;AACA,QAAI,KAAK,SAAS,YAAY;AAC5B,YAAM,SAAS,KAAK;AACpB,UAAI,CAAC,KAAK,QAAQ,OAAO,WAAW,EAAG,QAAO;AAC9C,YAAM,KAAK,IAAI,SAAA;AACf,aAAO,QAAQ,CAAC,MAAM,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC;AAC7C,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,OAAO,KAAK;AAAA,EAClC;AAAA,EAQA,IAAI,OAAe;AACjB,UAAM,IAAI,KAAK,QAAQ,QAAQ,QAAQ;AACvC,WAAO,CAAC,UAAU,SAAS,UAAU,EAAE,SAAS,CAAC,IAAI,IAAI;AAAA,EAC3D;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,SAAK,aAAa,QAAQ,KAAK;AAAA,EACjC;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,QAAQ,OAAO;AAAA,EAC7B;AAAA,EACA,IAAI,MAAM,GAAuC;AAC/C,SAAK,aAAa,SAAS,KAAK,OAAO,KAAK,OAAO,CAAC,CAAC;AAAA,EACvD;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,QAAQ,OAAO;AAAA,EAC7B;AAAA,EACA,IAAI,MAAM,GAA8B;AACtC,SAAK,aAAa,SAAS,KAAK,OAAO,KAAK,OAAO,CAAC,CAAC;AAAA,EACvD;AAAA,EAEA,IAAI,MAAc;AAChB,WAAO,KAAK,QAAQ,KAAK;AAAA,EAC3B;AAAA,EACA,IAAI,IAAI,GAA8B;AACpC,SAAK,aAAa,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC,CAAC;AAAA,EACrD;AAAA;AAAA,EAGA,IAAI,SAAmB;AACrB,UAAM,MAAM,KAAK,QAAQ,QAAQ;AACjC,WAAO,MAAM,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,OAAO,IAAI,CAAA;AAAA,EACrE;AAAA,EACA,IAAI,OAAO,MAAmC;AAC5C,UAAM,SAAS,MAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,MAAM,IAAI,CAAA;AACxD,SAAK,aAAa,UAAU,OAAO,KAAK,GAAG,CAAC;AAAA,EAC9C;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AAAA,EACA,IAAI,SAAS,OAAgB;AAC3B,SAAK,WAAW,YAAY,QAAQ,KAAK,CAAC;AAAA,EAC5C;AAAA;AAAA,EAGA,IAAI,cAAuB;AACzB,WAAO,KAAK,QAAQ,WAAW;AAAA,EACjC;AAAA,EACA,IAAI,YAAY,OAAgB;AAC9B,SAAK,WAAW,aAAa,QAAQ,KAAK,CAAC;AAAA,EAC7C;AAAA,EAEA,IAAI,cAAuB;AACzB,WAAO,KAAK,QAAQ,WAAW;AAAA,EACjC;AAAA,EACA,IAAI,YAAY,OAAgB;AAC9B,SAAK,WAAW,aAAa,QAAQ,KAAK,CAAC;AAAA,EAC7C;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK,QAAQ,WAAW;AAAA,EACjC;AAAA,EACA,IAAI,UAAU,OAAgB;AAC5B,SAAK,WAAW,aAAa,QAAQ,KAAK,CAAC;AAAA,EAC7C;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;AAC3C,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,gBAAgB;AAAA,QACnB,OAAO,KAAK,QAAQ,OAAO;AAAA,QAC3B,OAAO,KAAK,QAAQ,OAAO;AAAA,QAC3B,KAAK,KAAK,QAAQ,KAAK;AAAA,QACvB,QAAQ,KAAK,QAAQ,QAAQ;AAAA,MAAA;AAAA,IAEjC;AACA,UAAM,kBAAA;AAEN,SAAK,cAAc,CAAC,MAAa;AAC/B,UAAI,CAAC,KAAK,QAAQ,MAAM,EAAG;AAC3B,UAAI,EAAE,aAAA,EAAe,SAAS,IAAI,EAAG;AACrC,WAAK,gBAAgB,MAAM;AAAA,IAC7B;AACA,aAAS,iBAAiB,SAAS,KAAK,WAAW;AAAA,EACrD;AAAA,EAEmB,uBAA6B;AAC9C,UAAM,qBAAA;AACN,QAAI,KAAK,YAAa,UAAS,oBAAoB,SAAS,KAAK,WAAW;AAAA,EAC9E;AAAA;AAAA,EAGS,oBAA0B;AACjC,UAAM,IAAI,KAAK,iBAAiB,EAAE,OAAO,IAAI,OAAO,IAAI,KAAK,IAAI,QAAQ,GAAA;AACzE,SAAK,kBAAkB;AACvB,QAAI;AACF,WAAK,aAAa,SAAS,EAAE,KAAK;AAClC,WAAK,aAAa,SAAS,EAAE,KAAK;AAClC,WAAK,aAAa,OAAO,EAAE,GAAG;AAC9B,WAAK,aAAa,UAAU,EAAE,MAAM;AAAA,IACtC,UAAA;AACE,WAAK,kBAAkB;AAAA,IACzB;AACA,SAAK,gBAAA;AACL,QAAI,KAAK,YAAa,MAAK,OAAA;AAAA,EAC7B;AAAA;AAAA,EAGQ,WAAW,KAAqB;AACtC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,MAAM,KAAK,QAAQ,QAAQ;AAGjC,QAAI,CAAC,IAAK,QAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACjC,UAAM,CAAC,UAAU,QAAQ,IAAI,IAAI,MAAM,GAAG;AAC1C,UAAM,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,IAAI,SAAS,MAAM,GAAG;AACnD,UAAM,CAAC,IAAI,MAAM,IAAI,MAAM,IAAI,IAAI,KAAK,YAAY,IAAI,MAAM,GAAG;AAEjE,WAAO,IACJ,QAAQ,UAAU,GAAQ,EAC1B,QAAQ,OAAO,GAAQ,EACvB,QAAQ,OAAO,GAAQ,EACvB,QAAQ,QAAQ,GAAQ,EACxB,QAAQ,OAAO,GAAQ,EACvB,QAAQ,QAAQ,GAAQ,EACxB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC;AAAA,EACzB;AAAA;AAAA,EAGQ,cAAsB;AAC5B,QAAI,KAAK,SAAS,SAAS;AACzB,YAAM,IAAI,KAAK,QAAQ,OAAO;AAC9B,YAAM,IAAI,KAAK,QAAQ,KAAK;AACd,WAAK,QAAQ,WAAW,IAAI,MAAM;AAChD,UAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,UAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,cAAM,IAAI,CAAC,QAA0B,MAAM,GAAG,KAAK,WAAW,GAAG,CAAC,IAAI,KAAK,UAAU,KAAK,MAAM,GAAG,KAAK,WAAW,GAAG,CAAC,OAAO;AAC9H,eAAO,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AAAA,MAC3C;AAEA,aAAO,KAAK,IAAI,GAAG,KAAK,WAAW,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,KAAK,IAAI,GAAG,KAAK,WAAW,CAAC,CAAC,SAAS;AAAA,IACtG;AACA,QAAI,KAAK,SAAS,YAAY;AAC5B,aAAO,KAAK,OAAO,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI;AAAA,IAC7D;AACA,UAAM,OAAO,KAAK,WAAW,KAAK,KAAK;AACvC,WAAO,QAAQ,KAAK,QAAQ,WAAW,IAAI,GAAG,IAAI,IAAI,KAAK,SAAA,CAAU,KAAK;AAAA,EAC5E;AAAA;AAAA,EAGQ,WAAmB;AACzB,UAAM,IAAI,CAAC,MAAsB,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AAC1D,WAAO,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC;AAAA,EAC9D;AAAA;AAAA,EAGQ,oBAA0B;AAChC,UAAM,MAAM,KAAK,SAAS,KAAK,QAAQ,OAAO,KAAK;AACnD,UAAM,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC;AAC1B,QAAI,CAAC,EAAG;AACR,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AACzD,SAAK,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,KAAK,GAAG,GAAG,KAAK,EAAA;AAAA,EAC9C;AAAA;AAAA,EAGQ,SAAS,KAAqB;AACpC,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,QAAQ,WAAW,IAC3B,GAAG,GAAG,IAAI,KAAK,SAAA,CAAU,KACzB;AAAA,EACN;AAAA,EAEmB,SAAe;AAChC,UAAM,WAAW,KAAK;AACtB,UAAM,YAAY,KAAK,QAAQ,WAAW;AAC1C,UAAM,OAAO,KAAK,YAAA;AAClB,UAAM,WAAW,KAAK,SAAS;AAC/B,UAAM,WAAW,KAAK,QAAQ,WAAW;AACzC,SAAK,kBAAA;AAGL,QAAI,WAAW,QAAQ,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC,UAAU,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC;AACjF,QAAI,KAAK,SAAS,SAAS;AACzB,kBAAY,iBAAiB,IAAI,KAAK,QAAQ,OAAO,CAAC,CAAC,UAAU,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC;AAAA,IAC3F,WAAW,KAAK,SAAS,YAAY;AAEnC,YAAM,QAAQ,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,GAAG;AAC9D,kBAAY,4BAA4B,IAAI,KAAK,CAAC;AAAA,IACpD,OAAO;AACL,YAAM,IAAI,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK;AACtC,kBAAY,WAAW,IAAI,CAAC,CAAC;AAAA,IAC/B;AAEA,SAAK,YAAY;AAAA;AAAA,4DAEuC,KAAK,QAAQ,WAAW,KAAK,CAAC,WAAW,KAAK,UAAU;AAAA,yBAC3F,IAAI,KAAK,QAAQ,eAAe,YAAY,CAAC,CAAC;AAAA,mBACpD,IAAI,IAAI,CAAC,KAAK,WAAW,aAAa,EAAE;AAAA;AAAA,YAE/C,aAAa,WAAW,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAY1B,QAAQ;AAAA,UAErB,WACI;AAAA,2EAC6D,KAAK,KAAK,CAAC;AAAA,2EACX,KAAK,KAAK,CAAC;AAAA,2EACX,KAAK,KAAK,CAAC;AAAA,wBAExE,EACN;AAAA;AAAA,KAEH;AAED,UAAM,QAAQ,KAAK,KAAK,cAAc,QAAQ;AAC9C,QAAI,CAAC,MAAO;AACZ,QAAI,SAAU;AAEd,UAAM,iBAAiB,SAAS,CAAC,MAAM;AACrC,UAAK,EAAE,OAAuB,QAAQ,QAAQ,EAAG;AAEjD,UAAI,KAAK,QAAQ,WAAW,KAAM,EAAE,OAAuB,UAAU,SAAS,SAAS,EAAG;AAC1F,WAAK,gBAAgB,MAAM;AAE3B,UAAI,KAAK,QAAQ,MAAM,GAAG;AACxB,cAAM,IAAI,KAAK,sBAAA;AACf,aAAK,gBAAgB,MAAM,EAAE,SAAS,MAAM,OAAO,eAAe,EAAE,MAAM,GAAG;AAAA,MAC/E,OAAO;AACL,aAAK,gBAAgB,IAAI;AAAA,MAC3B;AAAA,IACF,CAAC;AAGD,SAAK,KAAK,cAAc,QAAQ,GAAG,iBAAiB,SAAS,CAAC,MAAM;AAClE,QAAE,gBAAA;AACF,WAAK,kBAAkB;AACvB,UAAI;AACF,aAAK,gBAAgB,OAAO;AAC5B,aAAK,gBAAgB,OAAO;AAC5B,aAAK,gBAAgB,KAAK;AAC1B,aAAK,gBAAgB,QAAQ;AAAA,MAC/B,UAAA;AACE,aAAK,kBAAkB;AAAA,MACzB;AACA,WAAK,gBAAgB,MAAM;AAC3B,WAAK,OAAA;AACL,WAAK,WAAW,EAAE;AAAA,IACpB,CAAC;AAGD,UAAM,MAAM,KAAK,KAAK,cAA2B,kBAAkB;AACnE,SAAK,iBAAiB,UAAU,CAAC,MAAM;AACrC,YAAM,IAAK,EAAkB,UAAU,CAAA;AAGvC,WAAK,kBAAkB;AACvB,UAAI;AACF,YAAI,EAAE,UAAU,UAAa,EAAE,QAAQ,QAAW;AAChD,cAAI,EAAE,MAAO,MAAK,aAAa,SAAS,KAAK,SAAS,EAAE,KAAK,CAAC;AAAA,cACzD,MAAK,gBAAgB,OAAO;AACjC,cAAI,EAAE,IAAK,MAAK,aAAa,OAAO,KAAK,SAAS,EAAE,GAAG,CAAC;AAAA,cACnD,MAAK,gBAAgB,KAAK;AAAA,QACjC;AACA,YAAI,EAAE,WAAW,QAAW;AAC1B,eAAK,SAAS,EAAE,OAAO,IAAI,CAAC,MAAc,KAAK,SAAS,CAAC,CAAC;AAAA,QAC5D;AACA,YAAI,EAAE,UAAU,QAAW;AACzB,cAAI,EAAE,MAAO,MAAK,aAAa,SAAS,KAAK,SAAS,EAAE,KAAK,CAAC;AAAA,cACzD,MAAK,gBAAgB,OAAO;AAEjC,cAAI,KAAK,SAAS,SAAU,MAAK,gBAAgB,MAAM;AAAA,QACzD;AAAA,MACF,UAAA;AACE,aAAK,kBAAkB;AAAA,MACzB;AACA,WAAK,OAAA;AACL,WAAK,WAAW,CAAC;AAAA,IACnB,CAAC;AAGD,UAAM,UAAU,KAAK,KAAK,cAAgC,UAAU;AACpE,QAAI,WAAW,KAAK,QAAQ,WAAW,KAAK,CAAC,UAAU;AACrD,cAAQ,iBAAiB,WAAW,CAAC,MAAM;AACzC,YAAI,EAAE,QAAQ,SAAS;AACrB,YAAE,eAAA;AACF,eAAK,YAAY,OAAO;AAAA,QAC1B;AAAA,MACF,CAAC;AACD,cAAQ,iBAAiB,QAAQ,MAAM,KAAK,YAAY,OAAO,CAAC;AAAA,IAClE;AAGA,QAAI,UAAU;AACZ,YAAM,YAAY,MAAY;AAC5B,cAAM,IAAI,KAAK,KAAK,cAAgC,MAAM;AAC1D,cAAM,IAAI,KAAK,KAAK,cAAgC,MAAM;AAC1D,cAAM,IAAI,KAAK,KAAK,cAAgC,MAAM;AAC1D,cAAM,QAAQ,CAAC,GAAuB,QACpC,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,SAAS,KAAK,KAAK,EAAE,KAAK,CAAC,CAAC;AACxD,aAAK,OAAO;AAAA,UACV,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,UACrB,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,UACrB,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,QAAA;AAGvB,cAAM,WAAW,CAAC,QAAwB,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC,IAAI,KAAK,UAAU,KAAK;AAG1G,aAAK,kBAAkB;AACvB,YAAI;AACF,cAAI,KAAK,MAAO,MAAK,aAAa,SAAS,SAAS,KAAK,KAAK,CAAC;AAC/D,cAAI,KAAK,QAAQ,OAAO,EAAG,MAAK,aAAa,SAAS,SAAS,KAAK,QAAQ,OAAO,CAAC,CAAC;AACrF,cAAI,KAAK,QAAQ,KAAK,EAAG,MAAK,aAAa,OAAO,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC;AAC/E,cAAI,KAAK,OAAO,QAAQ;AACtB,iBAAK,SAAS,KAAK,OAAO,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC;AAAA,UAClD;AAAA,QACF,UAAA;AACE,eAAK,kBAAkB;AAAA,QACzB;AAEA,cAAMA,WAAU,KAAK,KAAK,cAAgC,UAAU;AACpE,YAAIA,SAASA,UAAQ,QAAQ,KAAK,YAAA;AAClC,aAAK,WAAW,EAAE,OAAO,KAAK,OAAO;AAAA,MACvC;AACA,OAAC,QAAQ,QAAQ,MAAM,EAAE,QAAQ,CAAC,QAAQ;AACxC,aAAK,KAAK,cAAc,GAAG,GAAG,iBAAiB,SAAS,SAAS;AAAA,MACnE,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,WAAW,QAAuC;AAGxD,SAAK,gBAAgB,MAAM;AAC3B,SAAK;AAAA,MACH,IAAI,YAAY,UAAU,EAAE,SAAS,MAAM,UAAU,MAAM,OAAA,CAAQ;AAAA,IAAA;AAAA,EAEvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,KAA4B;AAC7C,UAAM,OAAO,IAAI,KAAA;AACjB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,MAAM,KAAK,QAAQ,UAAU,YAAY,EAAE,YAAA;AAGjD,UAAM,QAAQ,CAAC,MAAM,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AAChE,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,QAAQ,KAAK,MAAM,IAAI,OAAO,SAAS,CAAC,EAAE,OAAO,OAAO;AAC9D,QAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,UAAM,MAA8B,CAAA;AACpC,UAAM,QAAQ,CAAC,GAAG,MAAO,IAAI,CAAC,IAAI,MAAM,CAAC,CAAG;AAC5C,UAAM,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,GAAG,GAAG,CAAC,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,GAAG,GAAG,CAAC;AAGtG,UAAM,IAAI,oBAAI,KAAK,GAAG,GAAG,WAAW;AACpC,QAAI,OAAO,MAAM,EAAE,QAAA,CAAS,EAAG,QAAO;AACtC,UAAM,CAAC,GAAG,GAAG,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AAC7D,QAAI,EAAE,YAAA,MAAkB,KAAK,EAAE,SAAA,IAAa,MAAM,KAAK,EAAE,QAAA,MAAc,IAAK,QAAO;AAGnF,UAAM,MAAM,CAAC,GAAW,MAAsB,EAAE,MAAM,GAAG,EAAE,CAAC,EAAG,cAAc,EAAE,MAAM,GAAG,EAAE,CAAC,CAAE;AAC7F,UAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,UAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,QAAI,OAAO,IAAI,KAAK,GAAG,IAAI,EAAG,QAAO;AACrC,QAAI,OAAO,IAAI,KAAK,GAAG,IAAI,EAAG,QAAO;AAErC,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,YAAY,OAA+B;AACjD,UAAM,SAAS,KAAK,WAAW,MAAM,KAAK;AAC1C,QAAI,CAAC,QAAQ;AAEX,YAAM,UAAU,IAAI,SAAS;AAC7B,WAAK;AAAA,QACH,IAAI,YAAY,WAAW,EAAE,SAAS,MAAM,UAAU,MAAM,QAAQ,EAAE,MAAM,MAAM,MAAA,GAAS;AAAA,MAAA;AAE7F,YAAM,QAAQ,KAAK,YAAA;AACnB,iBAAW,MAAM,MAAM,UAAU,OAAO,SAAS,GAAG,IAAI;AACxD;AAAA,IACF;AACA,SAAK,kBAAkB;AACvB,QAAI;AACF,UAAI,KAAK,SAAS,UAAU;AAC1B,aAAK,aAAa,SAAS,KAAK,SAAS,MAAM,CAAC;AAAA,MAClD,WAAW,KAAK,SAAS,SAAS;AAEhC,YAAI,CAAC,KAAK,QAAQ,OAAO,KAAM,KAAK,QAAQ,OAAO,KAAK,KAAK,QAAQ,KAAK,GAAI;AAC5E,eAAK,aAAa,SAAS,KAAK,SAAS,MAAM,CAAC;AAChD,eAAK,gBAAgB,KAAK;AAAA,QAC5B,OAAO;AACL,gBAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,OAAO,EAAG,MAAM,GAAG,EAAE,CAAC,GAAI,MAAM,EAAE,KAAA;AAC/D,eAAK,aAAa,SAAS,KAAK,SAAS,CAAC,CAAC;AAC3C,eAAK,aAAa,OAAO,KAAK,SAAS,CAAC,CAAC;AAAA,QAC3C;AAAA,MACF,OAAO;AACL,cAAM,OAAO,KAAK,OAAO,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,MAAM,MAAM;AACjE,aAAK,KAAK,KAAK,SAAS,MAAM,CAAC;AAC/B,aAAK,SAAS;AAAA,MAChB;AAAA,IACF,UAAA;AACE,WAAK,kBAAkB;AAAA,IACzB;AAEA,QAAI,KAAK,SAAS,UAAU;AAC1B,WAAK,WAAW,EAAE,OAAO,KAAK,OAAO;AAAA,IACvC,WAAW,KAAK,SAAS,SAAS;AAChC,WAAK,WAAW,EAAE,OAAO,KAAK,QAAQ,OAAO,GAAG,KAAK,KAAK,QAAQ,KAAK,EAAA,CAAG;AAAA,IAC5E,OAAO;AACL,WAAK,WAAW,EAAE,QAAQ,KAAK,QAAQ;AAAA,IACzC;AACA,SAAK,OAAA;AAAA,EACP;AACF;AApoBE,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADpB,IAAM,eAAN;AAuoBA,SAAS,qBAA0C;AACxD,SAAO,cAAc,iBAAiB,YAAY;AACpD;"}
|
|
@@ -14,7 +14,10 @@ export declare class FxDialog extends FxElement {
|
|
|
14
14
|
protected render(): void;
|
|
15
15
|
private _cleanup?;
|
|
16
16
|
private _previouslyFocused;
|
|
17
|
-
|
|
17
|
+
private _locked;
|
|
18
|
+
/** Desfaz listeners, foco e scroll lock ao fechar/desconectar. */
|
|
19
|
+
private _release;
|
|
20
|
+
/** Mantém o foco dentro do modal (WCAG 2.4.3 / 2.1.2), incluindo o slot. */
|
|
18
21
|
private _trapFocus;
|
|
19
22
|
private _restoreFocus;
|
|
20
23
|
disconnectedCallback(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/components/dialog/dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAc5C;;;;;;GAMG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,SA6EpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,IAAI,IAAI,OAAO,CAAiC;IACpD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAAqC;cAEzC,MAAM,IAAI,IAAI;IA4EjC,OAAO,CAAC,QAAQ,CAAC,CAAa;IAC9B,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,OAAO,CAAS;IAExB,kEAAkE;IAClE,OAAO,CAAC,QAAQ;IAWhB,4EAA4E;IAC5E,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,aAAa;IAKZ,oBAAoB,IAAI,IAAI;CAItC;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
|
|
@@ -2,10 +2,12 @@ import { FxElement } from "../../core/base.js";
|
|
|
2
2
|
import { css } from "../../core/css.js";
|
|
3
3
|
import { defineElement } from "../../core/define.js";
|
|
4
4
|
import { esc } from "../../core/sanitize.js";
|
|
5
|
+
import { lockBodyScroll, firstLightFocusable, modalFocusables, isTopmostModal, registerModalOpen, registerModalClose, unlockBodyScroll } from "../../core/modal.js";
|
|
5
6
|
const _FxDialog = class _FxDialog extends FxElement {
|
|
6
7
|
constructor() {
|
|
7
8
|
super(...arguments);
|
|
8
9
|
this._previouslyFocused = null;
|
|
10
|
+
this._locked = false;
|
|
9
11
|
}
|
|
10
12
|
static get observedAttributes() {
|
|
11
13
|
return ["open", "size", "heading"];
|
|
@@ -19,6 +21,11 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
19
21
|
render() {
|
|
20
22
|
const isOpen = this.open;
|
|
21
23
|
const heading = this.getAttr("heading");
|
|
24
|
+
if (!isOpen) {
|
|
25
|
+
this._release();
|
|
26
|
+
this.setTemplate("");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
22
29
|
this.setTemplate(`
|
|
23
30
|
<div class="overlay" part="overlay" ${isOpen ? "" : "hidden"}>
|
|
24
31
|
<div class="dialog" role="dialog" aria-modal="true" aria-label="${esc(heading)}" tabindex="-1">
|
|
@@ -37,10 +44,8 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
37
44
|
const overlay = this.root.querySelector(".overlay");
|
|
38
45
|
const closeBtn = this.root.querySelector(".close");
|
|
39
46
|
const close = () => {
|
|
47
|
+
if (!this.open) return;
|
|
40
48
|
this.open = false;
|
|
41
|
-
this._cleanup?.();
|
|
42
|
-
this._cleanup = void 0;
|
|
43
|
-
this._restoreFocus();
|
|
44
49
|
this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
|
|
45
50
|
};
|
|
46
51
|
overlay?.addEventListener("click", (e) => {
|
|
@@ -49,6 +54,7 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
49
54
|
closeBtn?.addEventListener("click", close);
|
|
50
55
|
const onKey = (e) => {
|
|
51
56
|
if (e.key === "Escape") {
|
|
57
|
+
if (!isTopmostModal(this)) return;
|
|
52
58
|
e.stopPropagation();
|
|
53
59
|
close();
|
|
54
60
|
return;
|
|
@@ -57,29 +63,48 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
57
63
|
};
|
|
58
64
|
document.addEventListener("keydown", onKey);
|
|
59
65
|
this._cleanup = () => document.removeEventListener("keydown", onKey);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
const alreadyOpen = this._locked;
|
|
67
|
+
if (!this._locked) {
|
|
68
|
+
lockBodyScroll();
|
|
69
|
+
registerModalOpen(this);
|
|
70
|
+
this._locked = true;
|
|
71
|
+
}
|
|
72
|
+
if (!alreadyOpen) {
|
|
73
|
+
requestAnimationFrame(() => {
|
|
74
|
+
if (!this.open) return;
|
|
75
|
+
this._previouslyFocused = document.activeElement;
|
|
76
|
+
const first = firstLightFocusable(this) ?? this.root.querySelector(".dialog");
|
|
77
|
+
first?.focus();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
64
80
|
this.dispatchEvent(new CustomEvent("open", { bubbles: true, composed: true }));
|
|
65
81
|
}
|
|
66
|
-
/**
|
|
82
|
+
/** Desfaz listeners, foco e scroll lock ao fechar/desconectar. */
|
|
83
|
+
_release() {
|
|
84
|
+
this._cleanup?.();
|
|
85
|
+
this._cleanup = void 0;
|
|
86
|
+
registerModalClose(this);
|
|
87
|
+
if (this._locked) {
|
|
88
|
+
unlockBodyScroll();
|
|
89
|
+
this._locked = false;
|
|
90
|
+
}
|
|
91
|
+
this._restoreFocus();
|
|
92
|
+
}
|
|
93
|
+
/** Mantém o foco dentro do modal (WCAG 2.4.3 / 2.1.2), incluindo o slot. */
|
|
67
94
|
_trapFocus(e) {
|
|
68
95
|
const dialog = this.root.querySelector(".dialog");
|
|
69
96
|
if (!dialog) return;
|
|
70
|
-
const focusables = dialog
|
|
71
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
72
|
-
);
|
|
97
|
+
const focusables = modalFocusables(dialog, this);
|
|
73
98
|
if (!focusables.length) return;
|
|
74
99
|
const first = focusables[0];
|
|
75
100
|
const last = focusables[focusables.length - 1];
|
|
76
|
-
const active = this.root.activeElement;
|
|
77
|
-
if (e.shiftKey && (active === first || active === dialog)) {
|
|
101
|
+
const active = this.root.activeElement ?? document.activeElement;
|
|
102
|
+
if (e.shiftKey && (active === first || active === dialog || active === this)) {
|
|
78
103
|
e.preventDefault();
|
|
79
|
-
last
|
|
104
|
+
last?.focus();
|
|
80
105
|
} else if (!e.shiftKey && active === last) {
|
|
81
106
|
e.preventDefault();
|
|
82
|
-
first
|
|
107
|
+
first?.focus();
|
|
83
108
|
}
|
|
84
109
|
}
|
|
85
110
|
_restoreFocus() {
|
|
@@ -88,9 +113,7 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
88
113
|
}
|
|
89
114
|
disconnectedCallback() {
|
|
90
115
|
super.disconnectedCallback();
|
|
91
|
-
this.
|
|
92
|
-
this._cleanup = void 0;
|
|
93
|
-
this._restoreFocus();
|
|
116
|
+
this._release();
|
|
94
117
|
}
|
|
95
118
|
};
|
|
96
119
|
_FxDialog.styles = css`
|