@wrrdev/fenix-ui 1.1.15 → 1.1.17
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 +2 -2
- 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 +15 -0
- package/dist/components/floatlabel/floatlabel.d.ts.map +1 -1
- package/dist/components/floatlabel/floatlabel.js +47 -1
- 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/pagination/pagination.d.ts +3 -0
- package/dist/components/pagination/pagination.d.ts.map +1 -1
- package/dist/components/pagination/pagination.js +19 -9
- package/dist/components/pagination/pagination.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 +126 -24
- 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/page-range.d.ts +31 -0
- package/dist/core/page-range.d.ts.map +1 -0
- package/dist/core/page-range.js +41 -0
- package/dist/core/page-range.js.map +1 -0
- 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/fenix-ui.umd.min.js +261 -227
- package/dist/fenix-ui4.esm.js +987 -260
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/fenix-ui4.esm.js
CHANGED
|
@@ -506,6 +506,12 @@ const _FxButton = class _FxButton extends FxElement {
|
|
|
506
506
|
set loading(value) {
|
|
507
507
|
this.toggleAttr("loading", Boolean(value));
|
|
508
508
|
}
|
|
509
|
+
get full() {
|
|
510
|
+
return this.hasAttr("full");
|
|
511
|
+
}
|
|
512
|
+
set full(value) {
|
|
513
|
+
this.toggleAttr("full", Boolean(value));
|
|
514
|
+
}
|
|
509
515
|
render() {
|
|
510
516
|
const loading = this.hasAttr("loading");
|
|
511
517
|
const disabled = this.hasAttr("disabled") || loading;
|
|
@@ -685,6 +691,18 @@ const _FxBadge = class _FxBadge extends FxElement {
|
|
|
685
691
|
static get observedAttributes() {
|
|
686
692
|
return ["variant", "round"];
|
|
687
693
|
}
|
|
694
|
+
get variant() {
|
|
695
|
+
return this.getAttr("variant", "default");
|
|
696
|
+
}
|
|
697
|
+
set variant(v) {
|
|
698
|
+
this.setAttribute("variant", v);
|
|
699
|
+
}
|
|
700
|
+
get round() {
|
|
701
|
+
return this.hasAttr("round");
|
|
702
|
+
}
|
|
703
|
+
set round(v) {
|
|
704
|
+
this.toggleAttr("round", Boolean(v));
|
|
705
|
+
}
|
|
688
706
|
render() {
|
|
689
707
|
this.setTemplate(`<span class="badge" part="badge"><slot></slot></span>`);
|
|
690
708
|
}
|
|
@@ -764,10 +782,21 @@ function defineFxSpinner() {
|
|
|
764
782
|
}
|
|
765
783
|
defineFxSpinner();
|
|
766
784
|
const _FxSelect = class _FxSelect extends FxFormElement {
|
|
767
|
-
|
|
768
|
-
|
|
785
|
+
constructor() {
|
|
786
|
+
super(...arguments);
|
|
787
|
+
this._suppressRender = false;
|
|
788
|
+
this._fixedMode = false;
|
|
789
|
+
this._fixedResolved = false;
|
|
790
|
+
}
|
|
791
|
+
// `value` é observado para que `setAttribute('value', ...)` (HTML puro)
|
|
792
|
+
// também atualize o rótulo. Os caminhos internos de change usam
|
|
793
|
+
// `_suppressRender` para renderizar UMA vez, depois de fechar o painel.
|
|
769
794
|
static get observedAttributes() {
|
|
770
|
-
return ["size", "disabled", "placeholder", "searchable", "clearable", "error", "invalid", "success", "valid", "placement"];
|
|
795
|
+
return ["value", "size", "disabled", "placeholder", "searchable", "clearable", "full", "error", "invalid", "success", "valid", "placement"];
|
|
796
|
+
}
|
|
797
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
798
|
+
if (this._suppressRender) return;
|
|
799
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
771
800
|
}
|
|
772
801
|
/** Tamanho do campo. Padrão: `'md'`. */
|
|
773
802
|
get size() {
|
|
@@ -781,7 +810,9 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
781
810
|
return this.getAttr("value");
|
|
782
811
|
}
|
|
783
812
|
set value(value) {
|
|
784
|
-
|
|
813
|
+
const next = value == null ? "" : String(value);
|
|
814
|
+
const prev = this.getAttr("value");
|
|
815
|
+
if (prev !== next) this.setAttribute("value", next);
|
|
785
816
|
this.updateFormValue();
|
|
786
817
|
}
|
|
787
818
|
get disabled() {
|
|
@@ -790,6 +821,24 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
790
821
|
set disabled(value) {
|
|
791
822
|
this.toggleAttr("disabled", Boolean(value));
|
|
792
823
|
}
|
|
824
|
+
get searchable() {
|
|
825
|
+
return this.hasAttr("searchable");
|
|
826
|
+
}
|
|
827
|
+
set searchable(value) {
|
|
828
|
+
this.toggleAttr("searchable", Boolean(value));
|
|
829
|
+
}
|
|
830
|
+
get clearable() {
|
|
831
|
+
return this.hasAttr("clearable");
|
|
832
|
+
}
|
|
833
|
+
set clearable(value) {
|
|
834
|
+
this.toggleAttr("clearable", Boolean(value));
|
|
835
|
+
}
|
|
836
|
+
get full() {
|
|
837
|
+
return this.hasAttr("full");
|
|
838
|
+
}
|
|
839
|
+
set full(value) {
|
|
840
|
+
this.toggleAttr("full", Boolean(value));
|
|
841
|
+
}
|
|
793
842
|
get options() {
|
|
794
843
|
return [...this.querySelectorAll("option")].map((o) => ({
|
|
795
844
|
value: o.getAttribute("value") ?? o.textContent?.trim() ?? "",
|
|
@@ -798,8 +847,17 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
798
847
|
}
|
|
799
848
|
connectedCallback() {
|
|
800
849
|
super.connectedCallback();
|
|
801
|
-
this.observer = new MutationObserver(() =>
|
|
802
|
-
|
|
850
|
+
this.observer = new MutationObserver((mutations) => {
|
|
851
|
+
if (mutations.every((m) => m.type === "attributes" && m.target === this)) return;
|
|
852
|
+
this.render();
|
|
853
|
+
});
|
|
854
|
+
this.observer.observe(this, {
|
|
855
|
+
childList: true,
|
|
856
|
+
subtree: true,
|
|
857
|
+
characterData: true,
|
|
858
|
+
attributes: true,
|
|
859
|
+
attributeFilter: ["value", "selected"]
|
|
860
|
+
});
|
|
803
861
|
this.docListener = (e) => {
|
|
804
862
|
if (!this.hasAttr("open")) return;
|
|
805
863
|
if (e.composedPath().includes(this)) return;
|
|
@@ -816,6 +874,8 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
816
874
|
}
|
|
817
875
|
/** Abre o dropdown, posiciona o painel e passa a reposicioná-lo no reflow. */
|
|
818
876
|
_abrir() {
|
|
877
|
+
this._fixedMode = _FxSelect._precisaFixed(this);
|
|
878
|
+
this._fixedResolved = true;
|
|
819
879
|
this.setAttribute("open", "");
|
|
820
880
|
this._bindReflow();
|
|
821
881
|
}
|
|
@@ -823,6 +883,8 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
823
883
|
_fechar() {
|
|
824
884
|
this.removeAttribute("open");
|
|
825
885
|
this.removeAttribute("data-placement");
|
|
886
|
+
this._fixedMode = false;
|
|
887
|
+
this._fixedResolved = false;
|
|
826
888
|
this._unbindReflow();
|
|
827
889
|
}
|
|
828
890
|
/** Liga o reposicionamento (resize + scroll interno, em capture). */
|
|
@@ -865,7 +927,30 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
865
927
|
_FxSelect.MIN_ALTURA,
|
|
866
928
|
(paraCima ? acima : abaixo) - _FxSelect.GAP
|
|
867
929
|
);
|
|
868
|
-
|
|
930
|
+
const altura = Math.min(desejado || disponivel, disponivel, maxAltura);
|
|
931
|
+
painel.style.maxHeight = `${altura}px`;
|
|
932
|
+
if (!this._fixedResolved) {
|
|
933
|
+
this._fixedMode = _FxSelect._precisaFixed(this);
|
|
934
|
+
this._fixedResolved = true;
|
|
935
|
+
}
|
|
936
|
+
if (this._fixedMode) {
|
|
937
|
+
this.setAttribute("data-fixed", "");
|
|
938
|
+
const largura2 = Math.max(r.width, 220);
|
|
939
|
+
const esquerda = Math.max(
|
|
940
|
+
_FxSelect.MARGEM,
|
|
941
|
+
Math.min(r.left, window.innerWidth - largura2 - _FxSelect.MARGEM)
|
|
942
|
+
);
|
|
943
|
+
const topo = paraCima ? Math.max(_FxSelect.MARGEM, r.top - _FxSelect.GAP - altura) : r.bottom + _FxSelect.GAP;
|
|
944
|
+
painel.style.bottom = "auto";
|
|
945
|
+
painel.style.width = `${largura2}px`;
|
|
946
|
+
painel.style.left = `${esquerda}px`;
|
|
947
|
+
painel.style.top = `${topo}px`;
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
this.removeAttribute("data-fixed");
|
|
951
|
+
painel.style.top = "";
|
|
952
|
+
painel.style.bottom = "";
|
|
953
|
+
painel.style.width = "";
|
|
869
954
|
painel.style.left = "";
|
|
870
955
|
const largura = painel.offsetWidth;
|
|
871
956
|
const excesso = r.left + largura - (window.innerWidth - _FxSelect.MARGEM);
|
|
@@ -874,6 +959,21 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
874
959
|
painel.style.right = "auto";
|
|
875
960
|
}
|
|
876
961
|
}
|
|
962
|
+
/**
|
|
963
|
+
* Existe ancestral com overflow que cortaria o painel absoluto?
|
|
964
|
+
* `getComputedStyle` é defensivo: em jsdom retorna vazio e o modo
|
|
965
|
+
* histórico (absoluto) é mantido.
|
|
966
|
+
*/
|
|
967
|
+
static _precisaFixed(host) {
|
|
968
|
+
let el = host.parentElement;
|
|
969
|
+
while (el && el !== document.body && el !== document.documentElement) {
|
|
970
|
+
const cs = getComputedStyle(el);
|
|
971
|
+
const overflow = `${cs.overflow}${cs.overflowX}${cs.overflowY}`;
|
|
972
|
+
if (/(hidden|clip|auto|scroll|overlay)/.test(overflow)) return true;
|
|
973
|
+
el = el.parentElement;
|
|
974
|
+
}
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
877
977
|
/** Altura máxima do painel a partir de `--fx-select-panel-max-height`. */
|
|
878
978
|
static _maxAltura(host) {
|
|
879
979
|
const raw = getComputedStyle(host).getPropertyValue("--fx-select-panel-max-height");
|
|
@@ -881,7 +981,12 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
881
981
|
return Number.isFinite(n) && n > 0 ? n : 260;
|
|
882
982
|
}
|
|
883
983
|
select(value) {
|
|
884
|
-
this.
|
|
984
|
+
this._suppressRender = true;
|
|
985
|
+
try {
|
|
986
|
+
this.value = value;
|
|
987
|
+
} finally {
|
|
988
|
+
this._suppressRender = false;
|
|
989
|
+
}
|
|
885
990
|
this._fechar();
|
|
886
991
|
this.render();
|
|
887
992
|
this.emitValueEvents();
|
|
@@ -908,6 +1013,10 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
908
1013
|
}
|
|
909
1014
|
render() {
|
|
910
1015
|
const prevOpen = this.hasAttr("open");
|
|
1016
|
+
if (!prevOpen) {
|
|
1017
|
+
this._fixedMode = false;
|
|
1018
|
+
this._fixedResolved = false;
|
|
1019
|
+
}
|
|
911
1020
|
const prevSearch = this.root.querySelector(".search");
|
|
912
1021
|
const search = prevSearch?.value ?? "";
|
|
913
1022
|
const searchFocused = prevSearch != null && this.root.activeElement === prevSearch;
|
|
@@ -984,7 +1093,12 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
984
1093
|
});
|
|
985
1094
|
this.root.querySelector(".clear")?.addEventListener("click", (e) => {
|
|
986
1095
|
e.stopPropagation();
|
|
987
|
-
this.
|
|
1096
|
+
this._suppressRender = true;
|
|
1097
|
+
try {
|
|
1098
|
+
this.value = "";
|
|
1099
|
+
} finally {
|
|
1100
|
+
this._suppressRender = false;
|
|
1101
|
+
}
|
|
988
1102
|
this._fechar();
|
|
989
1103
|
this.render();
|
|
990
1104
|
this.emitValueEvents();
|
|
@@ -999,7 +1113,12 @@ const _FxSelect = class _FxSelect extends FxFormElement {
|
|
|
999
1113
|
}
|
|
1000
1114
|
/** form.reset(): restaura o valor inicial e re-renderiza o rótulo. */
|
|
1001
1115
|
formResetCallback() {
|
|
1002
|
-
|
|
1116
|
+
this._suppressRender = true;
|
|
1117
|
+
try {
|
|
1118
|
+
super.formResetCallback();
|
|
1119
|
+
} finally {
|
|
1120
|
+
this._suppressRender = false;
|
|
1121
|
+
}
|
|
1003
1122
|
this.render();
|
|
1004
1123
|
}
|
|
1005
1124
|
};
|
|
@@ -1122,6 +1241,11 @@ _FxSelect.styles = css`
|
|
|
1122
1241
|
border-radius: var(--fx-radius-md);
|
|
1123
1242
|
box-shadow: var(--fx-shadow-lg);
|
|
1124
1243
|
}
|
|
1244
|
+
/* Ancestral com overflow (ex.: fx-card) cortaria o painel absoluto:
|
|
1245
|
+
nesse caso o JS posiciona com position:fixed a partir do trigger. */
|
|
1246
|
+
:host([data-fixed]) .panel {
|
|
1247
|
+
position: fixed;
|
|
1248
|
+
}
|
|
1125
1249
|
/* Padrão: abre para baixo. */
|
|
1126
1250
|
:host([open]:not([data-placement='top'])) .panel {
|
|
1127
1251
|
top: calc(100% + var(--fx-select-panel-offset, 4px));
|
|
@@ -1187,7 +1311,21 @@ function defineFxSelect() {
|
|
|
1187
1311
|
return defineElement("fx-select", FxSelect);
|
|
1188
1312
|
}
|
|
1189
1313
|
defineFxSelect();
|
|
1190
|
-
const INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
1314
|
+
const INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
1315
|
+
"text",
|
|
1316
|
+
"number",
|
|
1317
|
+
"password",
|
|
1318
|
+
"email",
|
|
1319
|
+
"tel",
|
|
1320
|
+
"url",
|
|
1321
|
+
"search",
|
|
1322
|
+
"date",
|
|
1323
|
+
"time",
|
|
1324
|
+
"datetime-local",
|
|
1325
|
+
"month",
|
|
1326
|
+
"week",
|
|
1327
|
+
"color"
|
|
1328
|
+
]);
|
|
1191
1329
|
function normalizeInputType(value) {
|
|
1192
1330
|
const type = (value || "text").toLowerCase();
|
|
1193
1331
|
return INPUT_TYPES.has(type) ? type : "text";
|
|
@@ -1207,6 +1345,8 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
1207
1345
|
"min",
|
|
1208
1346
|
"max",
|
|
1209
1347
|
"step",
|
|
1348
|
+
"clearable",
|
|
1349
|
+
"full",
|
|
1210
1350
|
"icon",
|
|
1211
1351
|
"icon-pos",
|
|
1212
1352
|
"error",
|
|
@@ -1234,7 +1374,7 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
1234
1374
|
return this.getAttr("value");
|
|
1235
1375
|
}
|
|
1236
1376
|
set value(value) {
|
|
1237
|
-
this.setAttribute("value", value);
|
|
1377
|
+
this.setAttribute("value", value == null ? "" : String(value));
|
|
1238
1378
|
this.syncInternalControl();
|
|
1239
1379
|
this.updateFormValue();
|
|
1240
1380
|
}
|
|
@@ -1244,6 +1384,24 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
1244
1384
|
set disabled(value) {
|
|
1245
1385
|
this.toggleAttr("disabled", Boolean(value));
|
|
1246
1386
|
}
|
|
1387
|
+
get readonly() {
|
|
1388
|
+
return this.hasAttr("readonly");
|
|
1389
|
+
}
|
|
1390
|
+
set readonly(value) {
|
|
1391
|
+
this.toggleAttr("readonly", Boolean(value));
|
|
1392
|
+
}
|
|
1393
|
+
get clearable() {
|
|
1394
|
+
return this.hasAttr("clearable");
|
|
1395
|
+
}
|
|
1396
|
+
set clearable(value) {
|
|
1397
|
+
this.toggleAttr("clearable", Boolean(value));
|
|
1398
|
+
}
|
|
1399
|
+
get full() {
|
|
1400
|
+
return this.hasAttr("full");
|
|
1401
|
+
}
|
|
1402
|
+
set full(value) {
|
|
1403
|
+
this.toggleAttr("full", Boolean(value));
|
|
1404
|
+
}
|
|
1247
1405
|
render() {
|
|
1248
1406
|
const type = this.type;
|
|
1249
1407
|
const placeholder = this.getAttr("placeholder");
|
|
@@ -1274,20 +1432,27 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
1274
1432
|
field.value = this.getAttr("value");
|
|
1275
1433
|
if (disabled) field.setAttribute("disabled", "");
|
|
1276
1434
|
if (readonly) field.setAttribute("readonly", "");
|
|
1277
|
-
const
|
|
1435
|
+
const notifyInput = () => {
|
|
1278
1436
|
this.value = field.value;
|
|
1279
1437
|
this.emitValueEvents();
|
|
1438
|
+
};
|
|
1439
|
+
const notifyChange = () => {
|
|
1440
|
+
this.value = field.value;
|
|
1280
1441
|
this.dispatchEvent(
|
|
1281
|
-
new CustomEvent(
|
|
1442
|
+
new CustomEvent("change", {
|
|
1443
|
+
bubbles: true,
|
|
1444
|
+
composed: true,
|
|
1445
|
+
detail: { value: field.value }
|
|
1446
|
+
})
|
|
1282
1447
|
);
|
|
1283
1448
|
};
|
|
1284
1449
|
field.addEventListener("input", (e) => {
|
|
1285
1450
|
e.stopPropagation();
|
|
1286
|
-
|
|
1451
|
+
notifyInput();
|
|
1287
1452
|
});
|
|
1288
1453
|
field.addEventListener("change", (e) => {
|
|
1289
1454
|
e.stopPropagation();
|
|
1290
|
-
|
|
1455
|
+
notifyChange();
|
|
1291
1456
|
});
|
|
1292
1457
|
field.addEventListener("keydown", (e) => {
|
|
1293
1458
|
if (e.key !== "Enter" || e.isComposing) return;
|
|
@@ -1303,8 +1468,15 @@ const _FxInput = class _FxInput extends FxFormElement {
|
|
|
1303
1468
|
clearBtn?.addEventListener("click", () => {
|
|
1304
1469
|
field.value = "";
|
|
1305
1470
|
syncClear();
|
|
1306
|
-
|
|
1307
|
-
|
|
1471
|
+
this.value = "";
|
|
1472
|
+
this.emitValueEvents();
|
|
1473
|
+
this.dispatchEvent(
|
|
1474
|
+
new CustomEvent("change", {
|
|
1475
|
+
bubbles: true,
|
|
1476
|
+
composed: true,
|
|
1477
|
+
detail: { value: "" }
|
|
1478
|
+
})
|
|
1479
|
+
);
|
|
1308
1480
|
field.focus();
|
|
1309
1481
|
});
|
|
1310
1482
|
field.addEventListener("input", syncClear);
|
|
@@ -1494,7 +1666,8 @@ const _FxSwitch = class _FxSwitch extends FxFormElement {
|
|
|
1494
1666
|
return this.getAttr("value");
|
|
1495
1667
|
}
|
|
1496
1668
|
set value(v) {
|
|
1497
|
-
this.setAttribute("value", v);
|
|
1669
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
1670
|
+
this.updateFormValue();
|
|
1498
1671
|
}
|
|
1499
1672
|
/** No FormData: `value` (ou 'on') quando ligado; null (não envia) quando não. */
|
|
1500
1673
|
getFormValue() {
|
|
@@ -1612,7 +1785,23 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
1612
1785
|
this.selected = /* @__PURE__ */ new Set();
|
|
1613
1786
|
}
|
|
1614
1787
|
static get observedAttributes() {
|
|
1615
|
-
return [
|
|
1788
|
+
return [
|
|
1789
|
+
"values",
|
|
1790
|
+
"size",
|
|
1791
|
+
"disabled",
|
|
1792
|
+
"placeholder",
|
|
1793
|
+
"searchable",
|
|
1794
|
+
"clearable",
|
|
1795
|
+
"full",
|
|
1796
|
+
"error",
|
|
1797
|
+
"invalid",
|
|
1798
|
+
"success",
|
|
1799
|
+
"valid"
|
|
1800
|
+
];
|
|
1801
|
+
}
|
|
1802
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
1803
|
+
if (name === "values") this.selected = new Set(this.values);
|
|
1804
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
1616
1805
|
}
|
|
1617
1806
|
/** Valores selecionados (CSV no atributo / array na propriedade). */
|
|
1618
1807
|
get values() {
|
|
@@ -1620,7 +1809,15 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
1620
1809
|
return csv ? csv.split(",").map((v) => v.trim()).filter(Boolean) : [];
|
|
1621
1810
|
}
|
|
1622
1811
|
set values(list) {
|
|
1623
|
-
|
|
1812
|
+
const values = Array.isArray(list) ? list.map(String) : [];
|
|
1813
|
+
this.selected = new Set(values);
|
|
1814
|
+
const csv = values.join(",");
|
|
1815
|
+
if (this.getAttr("values") !== csv) {
|
|
1816
|
+
this.setAttribute("values", csv);
|
|
1817
|
+
} else if (this.isConnected) {
|
|
1818
|
+
this.render();
|
|
1819
|
+
}
|
|
1820
|
+
this.updateFormValue();
|
|
1624
1821
|
}
|
|
1625
1822
|
/** No FormData do form: uma entrada `name` por valor selecionado. */
|
|
1626
1823
|
getFormValue() {
|
|
@@ -1636,6 +1833,24 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
1636
1833
|
set disabled(value) {
|
|
1637
1834
|
this.toggleAttr("disabled", Boolean(value));
|
|
1638
1835
|
}
|
|
1836
|
+
get searchable() {
|
|
1837
|
+
return this.hasAttr("searchable");
|
|
1838
|
+
}
|
|
1839
|
+
set searchable(value) {
|
|
1840
|
+
this.toggleAttr("searchable", Boolean(value));
|
|
1841
|
+
}
|
|
1842
|
+
get clearable() {
|
|
1843
|
+
return this.hasAttr("clearable");
|
|
1844
|
+
}
|
|
1845
|
+
set clearable(value) {
|
|
1846
|
+
this.toggleAttr("clearable", Boolean(value));
|
|
1847
|
+
}
|
|
1848
|
+
get full() {
|
|
1849
|
+
return this.hasAttr("full");
|
|
1850
|
+
}
|
|
1851
|
+
set full(value) {
|
|
1852
|
+
this.toggleAttr("full", Boolean(value));
|
|
1853
|
+
}
|
|
1639
1854
|
get options() {
|
|
1640
1855
|
return [...this.querySelectorAll("option")].map((o) => ({
|
|
1641
1856
|
value: o.getAttribute("value") ?? o.textContent?.trim() ?? "",
|
|
@@ -1647,7 +1862,15 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
1647
1862
|
if (!this._initialValues) this._initialValues = [...this.values];
|
|
1648
1863
|
super.connectedCallback();
|
|
1649
1864
|
this.observer = new MutationObserver(() => this.render());
|
|
1650
|
-
this.observer.observe(this, {
|
|
1865
|
+
this.observer.observe(this, {
|
|
1866
|
+
childList: true,
|
|
1867
|
+
subtree: true,
|
|
1868
|
+
characterData: true,
|
|
1869
|
+
// Vue atualiza `:value`/`:selected` das <option> no light DOM; sem
|
|
1870
|
+
// observar atributos o label/valor antigo permanecia em tela.
|
|
1871
|
+
attributes: true,
|
|
1872
|
+
attributeFilter: ["value", "selected"]
|
|
1873
|
+
});
|
|
1651
1874
|
this.docListener = (e) => {
|
|
1652
1875
|
if (!this.hasAttr("open")) return;
|
|
1653
1876
|
if (e.composedPath().includes(this)) return;
|
|
@@ -1663,8 +1886,10 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
1663
1886
|
}
|
|
1664
1887
|
/** Aplica a seleção, reflete e emite change. */
|
|
1665
1888
|
commit() {
|
|
1666
|
-
|
|
1667
|
-
this.
|
|
1889
|
+
const next = [...this.selected];
|
|
1890
|
+
const same = next.join(",") === this.values.join(",");
|
|
1891
|
+
this.values = next;
|
|
1892
|
+
if (same && this.isConnected) this.render();
|
|
1668
1893
|
this.updateFormValue();
|
|
1669
1894
|
this.dispatchEvent(
|
|
1670
1895
|
new CustomEvent("change", {
|
|
@@ -1685,8 +1910,6 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
1685
1910
|
formResetCallback() {
|
|
1686
1911
|
const initial = this._initialValues ?? [];
|
|
1687
1912
|
this.values = initial;
|
|
1688
|
-
this.selected = new Set(initial);
|
|
1689
|
-
this.render();
|
|
1690
1913
|
this.updateFormValue();
|
|
1691
1914
|
}
|
|
1692
1915
|
toggleValue(value) {
|
|
@@ -1715,8 +1938,10 @@ const _FxMultiselect = class _FxMultiselect extends FxFormElement {
|
|
|
1715
1938
|
const wasOpen = this.hasAttr("open");
|
|
1716
1939
|
const search = this.root.querySelector(".search")?.value ?? "";
|
|
1717
1940
|
const opts = this.options;
|
|
1718
|
-
|
|
1719
|
-
|
|
1941
|
+
if (opts.length > 0) {
|
|
1942
|
+
for (const v of [...this.selected]) {
|
|
1943
|
+
if (!opts.some((o) => o.value === v)) this.selected.delete(v);
|
|
1944
|
+
}
|
|
1720
1945
|
}
|
|
1721
1946
|
const chips = opts.filter((o) => this.selected.has(o.value));
|
|
1722
1947
|
const placeholder = this.getAttr("placeholder", "Selecione…");
|
|
@@ -2496,13 +2721,16 @@ defineFxCalendar();
|
|
|
2496
2721
|
const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
2497
2722
|
constructor() {
|
|
2498
2723
|
super(...arguments);
|
|
2724
|
+
this._suppressRender = false;
|
|
2499
2725
|
this.time = { h: 0, m: 0, s: 0 };
|
|
2500
2726
|
}
|
|
2501
2727
|
static get observedAttributes() {
|
|
2502
|
-
return ["value", "start", "end", "values", "mode", "min", "max", "placeholder", "disabled", "format", "show-time", "free-text", "error", "invalid", "success", "valid"];
|
|
2728
|
+
return ["value", "start", "end", "values", "mode", "min", "max", "placeholder", "disabled", "format", "show-time", "free-text", "clearable", "full", "error", "invalid", "success", "valid"];
|
|
2503
2729
|
}
|
|
2504
2730
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
2505
|
-
|
|
2731
|
+
if (!this._suppressRender) {
|
|
2732
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
2733
|
+
}
|
|
2506
2734
|
if (name === "value" || name === "start" || name === "end" || name === "values") {
|
|
2507
2735
|
this.updateFormValue();
|
|
2508
2736
|
}
|
|
@@ -2538,7 +2766,19 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2538
2766
|
return this.getAttr("value");
|
|
2539
2767
|
}
|
|
2540
2768
|
set value(v) {
|
|
2541
|
-
this.setAttribute("value", v);
|
|
2769
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
2770
|
+
}
|
|
2771
|
+
get start() {
|
|
2772
|
+
return this.getAttr("start");
|
|
2773
|
+
}
|
|
2774
|
+
set start(v) {
|
|
2775
|
+
this.setAttribute("start", v == null ? "" : String(v));
|
|
2776
|
+
}
|
|
2777
|
+
get end() {
|
|
2778
|
+
return this.getAttr("end");
|
|
2779
|
+
}
|
|
2780
|
+
set end(v) {
|
|
2781
|
+
this.setAttribute("end", v == null ? "" : String(v));
|
|
2542
2782
|
}
|
|
2543
2783
|
/** Datas do modo múltiplo (CSV no atributo). */
|
|
2544
2784
|
get values() {
|
|
@@ -2546,7 +2786,8 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2546
2786
|
return csv ? csv.split(",").map((v) => v.trim()).filter(Boolean) : [];
|
|
2547
2787
|
}
|
|
2548
2788
|
set values(list) {
|
|
2549
|
-
|
|
2789
|
+
const values = Array.isArray(list) ? list.map(String) : [];
|
|
2790
|
+
this.setAttribute("values", values.join(","));
|
|
2550
2791
|
}
|
|
2551
2792
|
get disabled() {
|
|
2552
2793
|
return this.hasAttr("disabled");
|
|
@@ -2554,6 +2795,31 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2554
2795
|
set disabled(value) {
|
|
2555
2796
|
this.toggleAttr("disabled", Boolean(value));
|
|
2556
2797
|
}
|
|
2798
|
+
/** `:show-time` procura a propriedade LITERAL no Vue (sem camelização). */
|
|
2799
|
+
get "show-time"() {
|
|
2800
|
+
return this.hasAttr("show-time");
|
|
2801
|
+
}
|
|
2802
|
+
set "show-time"(value) {
|
|
2803
|
+
this.toggleAttr("show-time", Boolean(value));
|
|
2804
|
+
}
|
|
2805
|
+
get "free-text"() {
|
|
2806
|
+
return this.hasAttr("free-text");
|
|
2807
|
+
}
|
|
2808
|
+
set "free-text"(value) {
|
|
2809
|
+
this.toggleAttr("free-text", Boolean(value));
|
|
2810
|
+
}
|
|
2811
|
+
get clearable() {
|
|
2812
|
+
return this.hasAttr("clearable");
|
|
2813
|
+
}
|
|
2814
|
+
set clearable(value) {
|
|
2815
|
+
this.toggleAttr("clearable", Boolean(value));
|
|
2816
|
+
}
|
|
2817
|
+
get full() {
|
|
2818
|
+
return this.hasAttr("full");
|
|
2819
|
+
}
|
|
2820
|
+
set full(value) {
|
|
2821
|
+
this.toggleAttr("full", Boolean(value));
|
|
2822
|
+
}
|
|
2557
2823
|
connectedCallback() {
|
|
2558
2824
|
if (!this._initialState) {
|
|
2559
2825
|
this._initialState = {
|
|
@@ -2578,11 +2844,17 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2578
2844
|
/** form.reset(): restaura value/start/end/values iniciais (re-render via atributos observados). */
|
|
2579
2845
|
formResetCallback() {
|
|
2580
2846
|
const s = this._initialState ?? { value: "", start: "", end: "", values: "" };
|
|
2581
|
-
this.
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2847
|
+
this._suppressRender = true;
|
|
2848
|
+
try {
|
|
2849
|
+
this.setAttribute("value", s.value);
|
|
2850
|
+
this.setAttribute("start", s.start);
|
|
2851
|
+
this.setAttribute("end", s.end);
|
|
2852
|
+
this.setAttribute("values", s.values);
|
|
2853
|
+
} finally {
|
|
2854
|
+
this._suppressRender = false;
|
|
2855
|
+
}
|
|
2585
2856
|
this.updateFormValue();
|
|
2857
|
+
if (this.isConnected) this.render();
|
|
2586
2858
|
}
|
|
2587
2859
|
/** Formata uma data ISO ("YYYY-MM-DD" ou com "THH:mm:ss") segundo o atributo format. */
|
|
2588
2860
|
formatDate(iso) {
|
|
@@ -2690,10 +2962,15 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2690
2962
|
});
|
|
2691
2963
|
this.root.querySelector(".clear")?.addEventListener("click", (e) => {
|
|
2692
2964
|
e.stopPropagation();
|
|
2693
|
-
this.
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2965
|
+
this._suppressRender = true;
|
|
2966
|
+
try {
|
|
2967
|
+
this.removeAttribute("value");
|
|
2968
|
+
this.removeAttribute("start");
|
|
2969
|
+
this.removeAttribute("end");
|
|
2970
|
+
this.removeAttribute("values");
|
|
2971
|
+
} finally {
|
|
2972
|
+
this._suppressRender = false;
|
|
2973
|
+
}
|
|
2697
2974
|
this.removeAttribute("open");
|
|
2698
2975
|
this.render();
|
|
2699
2976
|
this.emitChange({});
|
|
@@ -2701,19 +2978,24 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2701
2978
|
const cal = this.root.querySelector(".pop fx-calendar");
|
|
2702
2979
|
cal?.addEventListener("change", (e) => {
|
|
2703
2980
|
const d = e.detail ?? {};
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2981
|
+
this._suppressRender = true;
|
|
2982
|
+
try {
|
|
2983
|
+
if (d.start !== void 0 || d.end !== void 0) {
|
|
2984
|
+
if (d.start) this.setAttribute("start", this.withTime(d.start));
|
|
2985
|
+
else this.removeAttribute("start");
|
|
2986
|
+
if (d.end) this.setAttribute("end", this.withTime(d.end));
|
|
2987
|
+
else this.removeAttribute("end");
|
|
2988
|
+
}
|
|
2989
|
+
if (d.values !== void 0) {
|
|
2990
|
+
this.values = d.values.map((v) => this.withTime(v));
|
|
2991
|
+
}
|
|
2992
|
+
if (d.value !== void 0) {
|
|
2993
|
+
if (d.value) this.setAttribute("value", this.withTime(d.value));
|
|
2994
|
+
else this.removeAttribute("value");
|
|
2995
|
+
if (this.mode === "single") this.removeAttribute("open");
|
|
2996
|
+
}
|
|
2997
|
+
} finally {
|
|
2998
|
+
this._suppressRender = false;
|
|
2717
2999
|
}
|
|
2718
3000
|
this.render();
|
|
2719
3001
|
this.emitChange(d);
|
|
@@ -2740,11 +3022,16 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2740
3022
|
s: clamp(s?.value, 59)
|
|
2741
3023
|
};
|
|
2742
3024
|
const reattach = (iso) => iso.split("T")[0] ? `${iso.split("T")[0]}T${this.timeText()}` : iso;
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
this.
|
|
3025
|
+
this._suppressRender = true;
|
|
3026
|
+
try {
|
|
3027
|
+
if (this.value) this.setAttribute("value", reattach(this.value));
|
|
3028
|
+
if (this.getAttr("start")) this.setAttribute("start", reattach(this.getAttr("start")));
|
|
3029
|
+
if (this.getAttr("end")) this.setAttribute("end", reattach(this.getAttr("end")));
|
|
3030
|
+
if (this.values.length) {
|
|
3031
|
+
this.values = this.values.map((v) => reattach(v));
|
|
3032
|
+
}
|
|
3033
|
+
} finally {
|
|
3034
|
+
this._suppressRender = false;
|
|
2748
3035
|
}
|
|
2749
3036
|
const display2 = this.root.querySelector(".display");
|
|
2750
3037
|
if (display2) display2.value = this.displayText();
|
|
@@ -2800,23 +3087,32 @@ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
|
|
|
2800
3087
|
setTimeout(() => input.classList.remove("invalid"), 1500);
|
|
2801
3088
|
return;
|
|
2802
3089
|
}
|
|
3090
|
+
this._suppressRender = true;
|
|
3091
|
+
try {
|
|
3092
|
+
if (this.mode === "single") {
|
|
3093
|
+
this.setAttribute("value", this.withTime(parsed));
|
|
3094
|
+
} else if (this.mode === "range") {
|
|
3095
|
+
if (!this.getAttr("start") || this.getAttr("start") && this.getAttr("end")) {
|
|
3096
|
+
this.setAttribute("start", this.withTime(parsed));
|
|
3097
|
+
this.removeAttribute("end");
|
|
3098
|
+
} else {
|
|
3099
|
+
const [a, b] = [this.getAttr("start").split("T")[0], parsed].sort();
|
|
3100
|
+
this.setAttribute("start", this.withTime(a));
|
|
3101
|
+
this.setAttribute("end", this.withTime(b));
|
|
3102
|
+
}
|
|
3103
|
+
} else {
|
|
3104
|
+
const list = this.values.filter((v) => v.split("T")[0] !== parsed);
|
|
3105
|
+
list.push(this.withTime(parsed));
|
|
3106
|
+
this.values = list;
|
|
3107
|
+
}
|
|
3108
|
+
} finally {
|
|
3109
|
+
this._suppressRender = false;
|
|
3110
|
+
}
|
|
2803
3111
|
if (this.mode === "single") {
|
|
2804
|
-
this.setAttribute("value", this.withTime(parsed));
|
|
2805
3112
|
this.emitChange({ value: this.value });
|
|
2806
3113
|
} else if (this.mode === "range") {
|
|
2807
|
-
if (!this.getAttr("start") || this.getAttr("start") && this.getAttr("end")) {
|
|
2808
|
-
this.setAttribute("start", this.withTime(parsed));
|
|
2809
|
-
this.removeAttribute("end");
|
|
2810
|
-
} else {
|
|
2811
|
-
const [a, b] = [this.getAttr("start").split("T")[0], parsed].sort();
|
|
2812
|
-
this.setAttribute("start", this.withTime(a));
|
|
2813
|
-
this.setAttribute("end", this.withTime(b));
|
|
2814
|
-
}
|
|
2815
3114
|
this.emitChange({ start: this.getAttr("start"), end: this.getAttr("end") });
|
|
2816
3115
|
} else {
|
|
2817
|
-
const list = this.values.filter((v) => v.split("T")[0] !== parsed);
|
|
2818
|
-
list.push(this.withTime(parsed));
|
|
2819
|
-
this.values = list;
|
|
2820
3116
|
this.emitChange({ values: this.values });
|
|
2821
3117
|
}
|
|
2822
3118
|
this.render();
|
|
@@ -2987,7 +3283,7 @@ const _FxCheckbox = class _FxCheckbox extends FxFormElement {
|
|
|
2987
3283
|
this._initialCheckedCaptured = false;
|
|
2988
3284
|
}
|
|
2989
3285
|
static get observedAttributes() {
|
|
2990
|
-
return ["checked", "disabled", "indeterminate"];
|
|
3286
|
+
return ["checked", "disabled", "indeterminate", "size"];
|
|
2991
3287
|
}
|
|
2992
3288
|
get checked() {
|
|
2993
3289
|
return this.hasAttr("checked");
|
|
@@ -3016,7 +3312,8 @@ const _FxCheckbox = class _FxCheckbox extends FxFormElement {
|
|
|
3016
3312
|
return this.getAttr("value");
|
|
3017
3313
|
}
|
|
3018
3314
|
set value(v) {
|
|
3019
|
-
this.setAttribute("value", v);
|
|
3315
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
3316
|
+
this.updateFormValue();
|
|
3020
3317
|
}
|
|
3021
3318
|
/** Tamanho do controle. Padrão: `'md'`. */
|
|
3022
3319
|
get size() {
|
|
@@ -3028,9 +3325,7 @@ const _FxCheckbox = class _FxCheckbox extends FxFormElement {
|
|
|
3028
3325
|
}
|
|
3029
3326
|
render() {
|
|
3030
3327
|
const px = this.size === "sm" ? "14px" : this.size === "lg" ? "22px" : "18px";
|
|
3031
|
-
|
|
3032
|
-
this.style.setProperty("--fx-size-checkbox", px);
|
|
3033
|
-
}
|
|
3328
|
+
this.style.setProperty("--_fx-size-checkbox", px);
|
|
3034
3329
|
this.setTemplate(`
|
|
3035
3330
|
<span class="box" part="box" role="checkbox" tabindex="0"
|
|
3036
3331
|
aria-checked="${this.indeterminate ? "mixed" : this.checked}"
|
|
@@ -3102,8 +3397,8 @@ _FxCheckbox.styles = css`
|
|
|
3102
3397
|
-webkit-tap-highlight-color: transparent;
|
|
3103
3398
|
}
|
|
3104
3399
|
.control {
|
|
3105
|
-
width: var(--fx-size-checkbox, 18px);
|
|
3106
|
-
height: var(--fx-size-checkbox, 18px);
|
|
3400
|
+
width: var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px));
|
|
3401
|
+
height: var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px));
|
|
3107
3402
|
flex-shrink: 0;
|
|
3108
3403
|
display: inline-flex;
|
|
3109
3404
|
align-items: center;
|
|
@@ -3112,7 +3407,7 @@ _FxCheckbox.styles = css`
|
|
|
3112
3407
|
border-radius: calc(var(--fx-radius-sm) + 2px);
|
|
3113
3408
|
background: var(--fx-surface-background);
|
|
3114
3409
|
color: #fff;
|
|
3115
|
-
font-size: calc(var(--fx-size-checkbox, 18px) - 6px);
|
|
3410
|
+
font-size: calc(var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px)) - 6px);
|
|
3116
3411
|
line-height: 1;
|
|
3117
3412
|
transition:
|
|
3118
3413
|
background-color var(--fx-motion-duration-fast) var(--fx-motion-easing),
|
|
@@ -3159,7 +3454,7 @@ const _FxRadio = class _FxRadio extends FxFormElement {
|
|
|
3159
3454
|
this._initialCheckedCaptured = false;
|
|
3160
3455
|
}
|
|
3161
3456
|
static get observedAttributes() {
|
|
3162
|
-
return ["checked", "disabled"];
|
|
3457
|
+
return ["checked", "disabled", "size"];
|
|
3163
3458
|
}
|
|
3164
3459
|
get checked() {
|
|
3165
3460
|
return this.hasAttr("checked");
|
|
@@ -3192,6 +3487,14 @@ const _FxRadio = class _FxRadio extends FxFormElement {
|
|
|
3192
3487
|
getFormValue() {
|
|
3193
3488
|
return this.checked ? this.getAttr("value") || "on" : null;
|
|
3194
3489
|
}
|
|
3490
|
+
/** getter/setter `value` — sem o setter o Vue não consegue escrever a propriedade. */
|
|
3491
|
+
get value() {
|
|
3492
|
+
return this.getAttr("value");
|
|
3493
|
+
}
|
|
3494
|
+
set value(v) {
|
|
3495
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
3496
|
+
this.updateFormValue();
|
|
3497
|
+
}
|
|
3195
3498
|
/** Tamanho do controle. Padrão: `'md'`. */
|
|
3196
3499
|
get size() {
|
|
3197
3500
|
const s = this.getAttr("size", "md");
|
|
@@ -3202,9 +3505,7 @@ const _FxRadio = class _FxRadio extends FxFormElement {
|
|
|
3202
3505
|
}
|
|
3203
3506
|
render() {
|
|
3204
3507
|
const px = this.size === "sm" ? "14px" : this.size === "lg" ? "22px" : "18px";
|
|
3205
|
-
|
|
3206
|
-
this.style.setProperty("--fx-size-radio", px);
|
|
3207
|
-
}
|
|
3508
|
+
this.style.setProperty("--_fx-size-radio", px);
|
|
3208
3509
|
this.setTemplate(`
|
|
3209
3510
|
<span class="box" part="box" role="radio" tabindex="0"
|
|
3210
3511
|
aria-checked="${this.checked}" aria-disabled="${this.disabled}">
|
|
@@ -3292,8 +3593,8 @@ _FxRadio.styles = css`
|
|
|
3292
3593
|
-webkit-tap-highlight-color: transparent;
|
|
3293
3594
|
}
|
|
3294
3595
|
.control {
|
|
3295
|
-
width: var(--fx-size-radio, 18px);
|
|
3296
|
-
height: var(--fx-size-radio, 18px);
|
|
3596
|
+
width: var(--fx-size-radio, var(--_fx-size-radio, 18px));
|
|
3597
|
+
height: var(--fx-size-radio, var(--_fx-size-radio, 18px));
|
|
3297
3598
|
flex-shrink: 0;
|
|
3298
3599
|
display: inline-flex;
|
|
3299
3600
|
align-items: center;
|
|
@@ -3337,6 +3638,43 @@ function defineFxRadio() {
|
|
|
3337
3638
|
return defineElement("fx-radio", FxRadio);
|
|
3338
3639
|
}
|
|
3339
3640
|
defineFxRadio();
|
|
3641
|
+
function toInt(value, fallback) {
|
|
3642
|
+
return Number.isFinite(value) ? Math.trunc(value) : fallback;
|
|
3643
|
+
}
|
|
3644
|
+
function range(start, end) {
|
|
3645
|
+
const out = [];
|
|
3646
|
+
for (let p = start; p <= end; p++) out.push(p);
|
|
3647
|
+
return out;
|
|
3648
|
+
}
|
|
3649
|
+
function pageRange(current, count, options = {}) {
|
|
3650
|
+
const pages = Math.max(0, toInt(count, 0));
|
|
3651
|
+
if (pages === 0) return [];
|
|
3652
|
+
const boundaries = Math.max(0, toInt(options.boundaries ?? 1, 1));
|
|
3653
|
+
const siblings = Math.max(0, toInt(options.siblings ?? 1, 1));
|
|
3654
|
+
const page = Math.min(Math.max(1, toInt(current, 1)), pages);
|
|
3655
|
+
if (pages <= boundaries * 2 + siblings * 2 + 3) return range(1, pages);
|
|
3656
|
+
const startPages = range(1, Math.min(boundaries, pages));
|
|
3657
|
+
const endPages = range(
|
|
3658
|
+
Math.max(pages - boundaries + 1, boundaries + 1),
|
|
3659
|
+
pages
|
|
3660
|
+
);
|
|
3661
|
+
const siblingsStart = Math.max(
|
|
3662
|
+
Math.min(page - siblings, pages - boundaries - siblings * 2 - 1),
|
|
3663
|
+
boundaries + 2
|
|
3664
|
+
);
|
|
3665
|
+
const siblingsEnd = Math.min(
|
|
3666
|
+
Math.max(page + siblings, boundaries + siblings * 2 + 2),
|
|
3667
|
+
endPages.length > 0 ? endPages[0] - 2 : pages - 1
|
|
3668
|
+
);
|
|
3669
|
+
const items = [...startPages];
|
|
3670
|
+
if (siblingsStart > boundaries + 2) items.push("ellipsis");
|
|
3671
|
+
else if (boundaries + 1 < pages - boundaries) items.push(boundaries + 1);
|
|
3672
|
+
items.push(...range(siblingsStart, siblingsEnd));
|
|
3673
|
+
if (siblingsEnd < pages - boundaries - 1) items.push("ellipsis");
|
|
3674
|
+
else if (pages - boundaries > boundaries) items.push(pages - boundaries);
|
|
3675
|
+
items.push(...endPages);
|
|
3676
|
+
return items;
|
|
3677
|
+
}
|
|
3340
3678
|
const LOCALE = "pt-BR";
|
|
3341
3679
|
const PIPES = {
|
|
3342
3680
|
/** Formata como moeda brasileira (R$). */
|
|
@@ -3768,6 +4106,7 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3768
4106
|
this.sortDir = 1;
|
|
3769
4107
|
this.filters = {};
|
|
3770
4108
|
this.globalSearch = "";
|
|
4109
|
+
this._boundToolbarInputs = /* @__PURE__ */ new WeakSet();
|
|
3771
4110
|
this._shellKey = "";
|
|
3772
4111
|
}
|
|
3773
4112
|
static get observedAttributes() {
|
|
@@ -3815,6 +4154,44 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3815
4154
|
get lazy() {
|
|
3816
4155
|
return this.hasAttr("lazy");
|
|
3817
4156
|
}
|
|
4157
|
+
set lazy(value) {
|
|
4158
|
+
this.toggleAttr("lazy", Boolean(value));
|
|
4159
|
+
}
|
|
4160
|
+
get pagination() {
|
|
4161
|
+
return this.hasAttr("pagination");
|
|
4162
|
+
}
|
|
4163
|
+
set pagination(value) {
|
|
4164
|
+
this.toggleAttr("pagination", Boolean(value));
|
|
4165
|
+
}
|
|
4166
|
+
get striped() {
|
|
4167
|
+
return this.hasAttr("striped");
|
|
4168
|
+
}
|
|
4169
|
+
set striped(value) {
|
|
4170
|
+
this.toggleAttr("striped", Boolean(value));
|
|
4171
|
+
}
|
|
4172
|
+
get hover() {
|
|
4173
|
+
return this.hasAttr("hover");
|
|
4174
|
+
}
|
|
4175
|
+
set hover(value) {
|
|
4176
|
+
this.toggleAttr("hover", Boolean(value));
|
|
4177
|
+
}
|
|
4178
|
+
get loading() {
|
|
4179
|
+
return this.hasAttr("loading");
|
|
4180
|
+
}
|
|
4181
|
+
set loading(value) {
|
|
4182
|
+
this.toggleAttr("loading", Boolean(value));
|
|
4183
|
+
}
|
|
4184
|
+
/**
|
|
4185
|
+
* Vue não cameliza atributos de elementos customizados:
|
|
4186
|
+
* `:no-borders-x="false"` procura a propriedade LITERAL
|
|
4187
|
+
* `'no-borders-x'` — o accessor literal evita o contorno `.attr`.
|
|
4188
|
+
*/
|
|
4189
|
+
get "no-borders-x"() {
|
|
4190
|
+
return this.hasAttr("no-borders-x");
|
|
4191
|
+
}
|
|
4192
|
+
set "no-borders-x"(value) {
|
|
4193
|
+
this.toggleAttr("no-borders-x", Boolean(value));
|
|
4194
|
+
}
|
|
3818
4195
|
get columns() {
|
|
3819
4196
|
return [...this.querySelectorAll("fx-column")].map((c) => {
|
|
3820
4197
|
const tpl = this._resolveCellTemplate(c);
|
|
@@ -3898,7 +4275,14 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3898
4275
|
loadFenixIconsFont();
|
|
3899
4276
|
super.connectedCallback();
|
|
3900
4277
|
this._columnObserver = new MutationObserver(() => this.render());
|
|
3901
|
-
this._columnObserver.observe(this, {
|
|
4278
|
+
this._columnObserver.observe(this, {
|
|
4279
|
+
childList: true,
|
|
4280
|
+
subtree: true,
|
|
4281
|
+
// Colunas com header/field/align/sortable/filterable alterados
|
|
4282
|
+
// em runtime também precisam re-renderizar.
|
|
4283
|
+
attributes: true,
|
|
4284
|
+
attributeFilter: ["header", "field", "align", "sortable", "filterable", "template"]
|
|
4285
|
+
});
|
|
3902
4286
|
}
|
|
3903
4287
|
disconnectedCallback() {
|
|
3904
4288
|
this._columnObserver?.disconnect();
|
|
@@ -3980,7 +4364,14 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3980
4364
|
const pages = Math.max(1, Math.ceil(total / this.rowsPerPage));
|
|
3981
4365
|
if (this.page >= pages) this.page = pages - 1;
|
|
3982
4366
|
const toolbar = this.toolbarTemplate;
|
|
3983
|
-
const
|
|
4367
|
+
const toolbarSlotted = toolbar?.localName === "fx-toolbar";
|
|
4368
|
+
const toolbarHasContent = Boolean(
|
|
4369
|
+
toolbar && (toolbarSlotted ? toolbar.children.length > 0 || (toolbar.textContent ?? "").trim() !== "" : toolbar.innerHTML.trim() !== "")
|
|
4370
|
+
);
|
|
4371
|
+
if (toolbarSlotted && toolbarHasContent) {
|
|
4372
|
+
toolbar.setAttribute("slot", "fx-toolbar");
|
|
4373
|
+
}
|
|
4374
|
+
const toolbarHtml = toolbarHasContent ? `<div class="toolbar" part="toolbar">${toolbarSlotted ? '<slot name="fx-toolbar"></slot>' : toolbar.innerHTML}</div>` : "";
|
|
3984
4375
|
const headHtml = cols.map((c) => {
|
|
3985
4376
|
const isSorted = this.sortField === c.field;
|
|
3986
4377
|
const ind = isSorted ? this.sortDir === 1 ? "▲" : "▼" : c.sortable ? "⇅" : "";
|
|
@@ -3991,16 +4382,19 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3991
4382
|
const bodyHtml = rows.length ? rows.map(
|
|
3992
4383
|
(row, i) => `<tr part="row" data-index="${i}">${cols.map((c) => `<td part="cell" style="text-align:${c.align}">${this.cellHtml(c, row)}</td>`).join("")}</tr>`
|
|
3993
4384
|
).join("") : `<tr><td class="empty" colspan="${cols.length || 1}">${esc(this.getAttr("empty-message", "Nenhum registro encontrado"))}</td></tr>`;
|
|
4385
|
+
const pageButtons = pageRange(this.page + 1, pages).map(
|
|
4386
|
+
(item) => item === "ellipsis" ? `<span class="ellipsis" part="ellipsis" aria-hidden="true">…</span>` : `<button type="button" class="pg-btn" part="page" data-pg="${item - 1}"${item === this.page + 1 ? ' aria-current="page"' : ""}>${item}</button>`
|
|
4387
|
+
).join("");
|
|
3994
4388
|
const pagerHtml = !paginated ? "" : `
|
|
3995
4389
|
<div class="pager" part="pager">
|
|
3996
4390
|
<span class="info">Página ${this.page + 1} de ${pages} · ${total} registros</span>
|
|
3997
4391
|
<button type="button" class="pg-btn" part="first" aria-label="Primeira página" data-pg="first" ${this.page === 0 ? "disabled" : ""}>${fenixIconHtml("first_page")}</button>
|
|
3998
4392
|
<button type="button" class="pg-btn" part="prev" aria-label="Página anterior" data-pg="prev" ${this.page === 0 ? "disabled" : ""}>${fenixIconHtml("navigate_before")}</button>
|
|
3999
|
-
${
|
|
4393
|
+
${pageButtons}
|
|
4000
4394
|
<button type="button" class="pg-btn" part="next" aria-label="Próxima página" data-pg="next" ${this.page >= pages - 1 ? "disabled" : ""}>${fenixIconHtml("navigate_next")}</button>
|
|
4001
4395
|
<button type="button" class="pg-btn" part="last" aria-label="Última página" data-pg="last" ${this.page >= pages - 1 ? "disabled" : ""}>${fenixIconHtml("last_page")}</button>
|
|
4002
4396
|
<label class="info">${esc("Por página:")}
|
|
4003
|
-
<fx-select class="rows-sel" size="sm" value="${this.rowsPerPage}" aria-label="Itens por página">${this.getAttr(
|
|
4397
|
+
<fx-select class="rows-sel" size="sm" placement="top" value="${this.rowsPerPage}" aria-label="Itens por página">${this.getAttr(
|
|
4004
4398
|
"rows-options",
|
|
4005
4399
|
"5,10,20,50"
|
|
4006
4400
|
).split(",").map((n) => n.trim()).map((n) => `<option value="${esc(n)}">${esc(n)}</option>`).join("")}</fx-select>
|
|
@@ -4021,13 +4415,15 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4021
4415
|
if (tbody) {
|
|
4022
4416
|
tbody.innerHTML = bodyHtml;
|
|
4023
4417
|
this._bindRowClicks(rows);
|
|
4418
|
+
this._bindSlottedToolbarSearch(toolbar);
|
|
4419
|
+
this._restoreToolbarSearch();
|
|
4024
4420
|
return;
|
|
4025
4421
|
}
|
|
4026
4422
|
}
|
|
4027
4423
|
this._shellKey = shellKey;
|
|
4028
4424
|
this.setTemplate(`
|
|
4029
4425
|
${toolbarHtml}
|
|
4030
|
-
<div class="frame${
|
|
4426
|
+
<div class="frame${toolbarHasContent ? " has-toolbar" : ""}" part="frame">
|
|
4031
4427
|
<div class="table-wrap">
|
|
4032
4428
|
<div class="scroll">
|
|
4033
4429
|
<table part="table">
|
|
@@ -4088,21 +4484,10 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4088
4484
|
});
|
|
4089
4485
|
inp.addEventListener("click", (e) => e.stopPropagation());
|
|
4090
4486
|
});
|
|
4091
|
-
this.root.querySelectorAll("[data-search-fields]").forEach((inp) =>
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
const pos = inp.selectionStart;
|
|
4096
|
-
this.render();
|
|
4097
|
-
const again = this.root.querySelector(
|
|
4098
|
-
"[data-search-fields]"
|
|
4099
|
-
);
|
|
4100
|
-
if (again) {
|
|
4101
|
-
again.focus();
|
|
4102
|
-
again.setSelectionRange(pos, pos);
|
|
4103
|
-
}
|
|
4104
|
-
});
|
|
4105
|
-
});
|
|
4487
|
+
this.root.querySelectorAll("[data-search-fields]").forEach((inp) => this._bindSearchInput(inp, false));
|
|
4488
|
+
if (toolbarSlotted && toolbar) {
|
|
4489
|
+
this._bindSlottedToolbarSearch(toolbar);
|
|
4490
|
+
}
|
|
4106
4491
|
this.root.querySelectorAll(".pg-btn").forEach((btn) => {
|
|
4107
4492
|
btn.addEventListener("click", () => {
|
|
4108
4493
|
const pg = btn.dataset.pg;
|
|
@@ -4155,11 +4540,47 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4155
4540
|
});
|
|
4156
4541
|
});
|
|
4157
4542
|
}
|
|
4543
|
+
/** Liga (uma vez) os inputs de busca de um <fx-toolbar> slotted. */
|
|
4544
|
+
_bindSlottedToolbarSearch(toolbar) {
|
|
4545
|
+
if (!toolbar || toolbar.localName !== "fx-toolbar") return;
|
|
4546
|
+
toolbar.querySelectorAll("[data-search-fields]").forEach((inp) => {
|
|
4547
|
+
if (this._boundToolbarInputs.has(inp)) return;
|
|
4548
|
+
this._boundToolbarInputs.add(inp);
|
|
4549
|
+
this._bindSearchInput(inp, true);
|
|
4550
|
+
});
|
|
4551
|
+
}
|
|
4552
|
+
/**
|
|
4553
|
+
* Liga um input da toolbar à busca global.
|
|
4554
|
+
* `persistent` = input slotted (não é recriado pelo render): o foco volta
|
|
4555
|
+
* no próprio nó. Inputs clonados são substituídos, então o foco é
|
|
4556
|
+
* restaurado no clone novo do shadow.
|
|
4557
|
+
*/
|
|
4558
|
+
_bindSearchInput(inp, persistent) {
|
|
4559
|
+
inp.addEventListener("input", () => {
|
|
4560
|
+
this.globalSearch = inp.value;
|
|
4561
|
+
this.page = 0;
|
|
4562
|
+
const pos = inp.selectionStart;
|
|
4563
|
+
this.render();
|
|
4564
|
+
const again = persistent && inp.isConnected ? inp : this.root.querySelector("[data-search-fields]");
|
|
4565
|
+
if (again) {
|
|
4566
|
+
again.focus();
|
|
4567
|
+
again.setSelectionRange(pos, pos);
|
|
4568
|
+
}
|
|
4569
|
+
});
|
|
4570
|
+
}
|
|
4158
4571
|
_restoreToolbarSearch() {
|
|
4159
|
-
const
|
|
4572
|
+
const shadow = this.root.querySelector(
|
|
4160
4573
|
"[data-search-fields]"
|
|
4161
4574
|
);
|
|
4162
|
-
if (
|
|
4575
|
+
if (shadow && this.globalSearch) shadow.value = this.globalSearch;
|
|
4576
|
+
const toolbar = this.toolbarTemplate;
|
|
4577
|
+
if (toolbar?.localName === "fx-toolbar") {
|
|
4578
|
+
toolbar.querySelectorAll("[data-search-fields]").forEach((input) => {
|
|
4579
|
+
if (this.globalSearch && input.value !== this.globalSearch) {
|
|
4580
|
+
input.value = this.globalSearch;
|
|
4581
|
+
}
|
|
4582
|
+
});
|
|
4583
|
+
}
|
|
4163
4584
|
}
|
|
4164
4585
|
};
|
|
4165
4586
|
_FxTable.styles = css`
|
|
@@ -4352,7 +4773,7 @@ _FxTable.styles = css`
|
|
|
4352
4773
|
.pg-btn .fx-icon {
|
|
4353
4774
|
font-size: calc(var(--fx-font-size) - 1px);
|
|
4354
4775
|
}
|
|
4355
|
-
.pg-btn:hover:not(:disabled):not([aria-current="
|
|
4776
|
+
.pg-btn:hover:not(:disabled):not([aria-current="page"]) {
|
|
4356
4777
|
border-color: var(--fx-color-primary);
|
|
4357
4778
|
color: var(--fx-color-primary);
|
|
4358
4779
|
}
|
|
@@ -4360,12 +4781,25 @@ _FxTable.styles = css`
|
|
|
4360
4781
|
opacity: 0.5;
|
|
4361
4782
|
cursor: not-allowed;
|
|
4362
4783
|
}
|
|
4363
|
-
.pg-btn[aria-current="
|
|
4784
|
+
.pg-btn[aria-current="page"] {
|
|
4364
4785
|
background: var(--fx-color-primary);
|
|
4365
4786
|
border-color: var(--fx-color-primary);
|
|
4366
4787
|
color: #fff;
|
|
4367
4788
|
font-weight: var(--fx-font-weight);
|
|
4368
4789
|
}
|
|
4790
|
+
/* Elipse da janela de páginas (1 … 499 [500] 501 … 1000): ocupa o
|
|
4791
|
+
mesmo espaço de um botão para o rodapé não "pular" ao navegar,
|
|
4792
|
+
mas não é clicável. */
|
|
4793
|
+
.pager .ellipsis {
|
|
4794
|
+
display: inline-flex;
|
|
4795
|
+
align-items: center;
|
|
4796
|
+
justify-content: center;
|
|
4797
|
+
min-width: var(--fx-size-sm);
|
|
4798
|
+
height: var(--fx-size-sm);
|
|
4799
|
+
color: var(--fx-text-muted);
|
|
4800
|
+
font-size: calc(var(--fx-font-size) - 2px);
|
|
4801
|
+
user-select: none;
|
|
4802
|
+
}
|
|
4369
4803
|
/* O seletor "Por página" só mostra números, então não pode herdar a
|
|
4370
4804
|
largura padrão do <fx-select> (200px / 180px em size="sm"), que
|
|
4371
4805
|
deixava o rodapé da paginação desproporcional. O limite tem que ser
|
|
@@ -4402,6 +4836,11 @@ _FxTable.styles = css`
|
|
|
4402
4836
|
.toolbar:empty {
|
|
4403
4837
|
display: none;
|
|
4404
4838
|
}
|
|
4839
|
+
/* <fx-toolbar> é apenas um invólucro no light DOM: o slot projeta
|
|
4840
|
+
seus filhos (com listeners do Vue preservados) direto no flex. */
|
|
4841
|
+
::slotted(fx-toolbar) {
|
|
4842
|
+
display: contents;
|
|
4843
|
+
}
|
|
4405
4844
|
.loading-overlay {
|
|
4406
4845
|
position: absolute;
|
|
4407
4846
|
inset: 0;
|
|
@@ -4452,7 +4891,51 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
|
|
|
4452
4891
|
this._labelClickBound = false;
|
|
4453
4892
|
}
|
|
4454
4893
|
static get observedAttributes() {
|
|
4455
|
-
return ["variant", "error", "invalid", "success", "valid", "full", "icon", "icon-pos"];
|
|
4894
|
+
return ["variant", "error", "invalid", "success", "valid", "full", "icon", "icon-pos", "error-text"];
|
|
4895
|
+
}
|
|
4896
|
+
get error() {
|
|
4897
|
+
return this.hasAttr("error");
|
|
4898
|
+
}
|
|
4899
|
+
set error(value) {
|
|
4900
|
+
this.toggleAttr("error", Boolean(value));
|
|
4901
|
+
}
|
|
4902
|
+
get invalid() {
|
|
4903
|
+
return this.hasAttr("invalid");
|
|
4904
|
+
}
|
|
4905
|
+
set invalid(value) {
|
|
4906
|
+
this.toggleAttr("invalid", Boolean(value));
|
|
4907
|
+
}
|
|
4908
|
+
get success() {
|
|
4909
|
+
return this.hasAttr("success");
|
|
4910
|
+
}
|
|
4911
|
+
set success(value) {
|
|
4912
|
+
this.toggleAttr("success", Boolean(value));
|
|
4913
|
+
}
|
|
4914
|
+
get valid() {
|
|
4915
|
+
return this.hasAttr("valid");
|
|
4916
|
+
}
|
|
4917
|
+
set valid(value) {
|
|
4918
|
+
this.toggleAttr("valid", Boolean(value));
|
|
4919
|
+
}
|
|
4920
|
+
get full() {
|
|
4921
|
+
return this.hasAttr("full");
|
|
4922
|
+
}
|
|
4923
|
+
set full(value) {
|
|
4924
|
+
this.toggleAttr("full", Boolean(value));
|
|
4925
|
+
}
|
|
4926
|
+
get errorText() {
|
|
4927
|
+
return this.getAttr("error-text");
|
|
4928
|
+
}
|
|
4929
|
+
set errorText(value) {
|
|
4930
|
+
this.setAttribute("error-text", value == null ? "" : String(value));
|
|
4931
|
+
}
|
|
4932
|
+
connectedCallback() {
|
|
4933
|
+
super.connectedCallback();
|
|
4934
|
+
this._labelObserver = new MutationObserver(() => {
|
|
4935
|
+
const label = this.findLabel();
|
|
4936
|
+
if (label && label !== this._label) this.render();
|
|
4937
|
+
});
|
|
4938
|
+
this._labelObserver.observe(this, { childList: true });
|
|
4456
4939
|
}
|
|
4457
4940
|
render() {
|
|
4458
4941
|
const control = this.findControl();
|
|
@@ -4496,6 +4979,8 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
|
|
|
4496
4979
|
disconnectedCallback() {
|
|
4497
4980
|
super.disconnectedCallback();
|
|
4498
4981
|
this.observer?.disconnect();
|
|
4982
|
+
this._labelObserver?.disconnect();
|
|
4983
|
+
this._labelObserver = void 0;
|
|
4499
4984
|
}
|
|
4500
4985
|
/** Campo de controle = qualquer filho direto que não seja label. */
|
|
4501
4986
|
findControl() {
|
|
@@ -4785,7 +5270,7 @@ function defineFxFloatlabel() {
|
|
|
4785
5270
|
defineFxFloatlabel();
|
|
4786
5271
|
const _FxTextarea = class _FxTextarea extends FxFormElement {
|
|
4787
5272
|
static get observedAttributes() {
|
|
4788
|
-
return ["size", "placeholder", "disabled", "readonly", "rows", "maxlength", "error", "invalid", "success", "valid"];
|
|
5273
|
+
return ["size", "placeholder", "disabled", "readonly", "full", "rows", "maxlength", "error", "invalid", "success", "valid"];
|
|
4789
5274
|
}
|
|
4790
5275
|
get size() {
|
|
4791
5276
|
const s = this.getAttr("size", "md");
|
|
@@ -4798,10 +5283,22 @@ const _FxTextarea = class _FxTextarea extends FxFormElement {
|
|
|
4798
5283
|
return this.getAttr("value");
|
|
4799
5284
|
}
|
|
4800
5285
|
set value(value) {
|
|
4801
|
-
this.setAttribute("value", value);
|
|
5286
|
+
this.setAttribute("value", value == null ? "" : String(value));
|
|
4802
5287
|
this.syncInternalControl();
|
|
4803
5288
|
this.updateFormValue();
|
|
4804
5289
|
}
|
|
5290
|
+
get readonly() {
|
|
5291
|
+
return this.hasAttr("readonly");
|
|
5292
|
+
}
|
|
5293
|
+
set readonly(value) {
|
|
5294
|
+
this.toggleAttr("readonly", Boolean(value));
|
|
5295
|
+
}
|
|
5296
|
+
get full() {
|
|
5297
|
+
return this.hasAttr("full");
|
|
5298
|
+
}
|
|
5299
|
+
set full(value) {
|
|
5300
|
+
this.toggleAttr("full", Boolean(value));
|
|
5301
|
+
}
|
|
4805
5302
|
render() {
|
|
4806
5303
|
const placeholder = this.getAttr("placeholder");
|
|
4807
5304
|
const readonly = this.hasAttr("readonly");
|
|
@@ -4818,20 +5315,27 @@ const _FxTextarea = class _FxTextarea extends FxFormElement {
|
|
|
4818
5315
|
field.value = this.getAttr("value");
|
|
4819
5316
|
if (disabled) field.setAttribute("disabled", "");
|
|
4820
5317
|
if (readonly) field.setAttribute("readonly", "");
|
|
4821
|
-
const
|
|
5318
|
+
const notifyInput = () => {
|
|
4822
5319
|
this.value = field.value;
|
|
4823
5320
|
this.emitValueEvents();
|
|
5321
|
+
};
|
|
5322
|
+
const notifyChange = () => {
|
|
5323
|
+
this.value = field.value;
|
|
4824
5324
|
this.dispatchEvent(
|
|
4825
|
-
new CustomEvent(
|
|
5325
|
+
new CustomEvent("change", {
|
|
5326
|
+
bubbles: true,
|
|
5327
|
+
composed: true,
|
|
5328
|
+
detail: { value: field.value }
|
|
5329
|
+
})
|
|
4826
5330
|
);
|
|
4827
5331
|
};
|
|
4828
5332
|
field.addEventListener("input", (e) => {
|
|
4829
5333
|
e.stopPropagation();
|
|
4830
|
-
|
|
5334
|
+
notifyInput();
|
|
4831
5335
|
});
|
|
4832
5336
|
field.addEventListener("change", (e) => {
|
|
4833
5337
|
e.stopPropagation();
|
|
4834
|
-
|
|
5338
|
+
notifyChange();
|
|
4835
5339
|
});
|
|
4836
5340
|
this.updateFormValue();
|
|
4837
5341
|
}
|
|
@@ -6118,10 +6622,10 @@ function rootOf(editor) {
|
|
|
6118
6622
|
const root = editor.getRootNode();
|
|
6119
6623
|
return typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root : document;
|
|
6120
6624
|
}
|
|
6121
|
-
function isRangeInside(editor,
|
|
6122
|
-
if (!
|
|
6123
|
-
const start =
|
|
6124
|
-
const end =
|
|
6625
|
+
function isRangeInside(editor, range2) {
|
|
6626
|
+
if (!range2) return false;
|
|
6627
|
+
const start = range2.startContainer;
|
|
6628
|
+
const end = range2.endContainer;
|
|
6125
6629
|
return editor.contains(start) && editor.contains(end);
|
|
6126
6630
|
}
|
|
6127
6631
|
function readSelectionRange(root) {
|
|
@@ -6132,10 +6636,10 @@ function readSelectionRange(root) {
|
|
|
6132
6636
|
const ranges = selection.getComposedRanges({ shadowRoots: [root] });
|
|
6133
6637
|
const first = ranges[0];
|
|
6134
6638
|
if (first) {
|
|
6135
|
-
const
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
return
|
|
6639
|
+
const range2 = document.createRange();
|
|
6640
|
+
range2.setStart(first.startContainer, first.startOffset);
|
|
6641
|
+
range2.setEnd(first.endContainer, first.endOffset);
|
|
6642
|
+
return range2;
|
|
6139
6643
|
}
|
|
6140
6644
|
} catch {
|
|
6141
6645
|
}
|
|
@@ -6143,41 +6647,41 @@ function readSelectionRange(root) {
|
|
|
6143
6647
|
return selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
|
|
6144
6648
|
}
|
|
6145
6649
|
function getRange(editor, fallback) {
|
|
6146
|
-
const
|
|
6147
|
-
if (isRangeInside(editor,
|
|
6650
|
+
const range2 = readSelectionRange(rootOf(editor));
|
|
6651
|
+
if (isRangeInside(editor, range2)) return range2;
|
|
6148
6652
|
if (isRangeInside(editor, fallback ?? null)) return fallback ?? null;
|
|
6149
6653
|
return null;
|
|
6150
6654
|
}
|
|
6151
6655
|
function saveRange(editor) {
|
|
6152
|
-
const
|
|
6153
|
-
return
|
|
6656
|
+
const range2 = getRange(editor);
|
|
6657
|
+
return range2 ? range2.cloneRange() : null;
|
|
6154
6658
|
}
|
|
6155
|
-
function restoreRange(editor,
|
|
6156
|
-
if (!
|
|
6659
|
+
function restoreRange(editor, range2) {
|
|
6660
|
+
if (!range2 || !isRangeInside(editor, range2)) return;
|
|
6157
6661
|
editor.focus({ preventScroll: true });
|
|
6158
6662
|
const selection = document.getSelection();
|
|
6159
6663
|
if (!selection) return;
|
|
6160
6664
|
selection.removeAllRanges();
|
|
6161
|
-
selection.addRange(
|
|
6665
|
+
selection.addRange(range2);
|
|
6162
6666
|
}
|
|
6163
6667
|
function placeCaretAtEnd(editor) {
|
|
6164
|
-
const
|
|
6165
|
-
|
|
6166
|
-
|
|
6668
|
+
const range2 = document.createRange();
|
|
6669
|
+
range2.selectNodeContents(editor);
|
|
6670
|
+
range2.collapse(false);
|
|
6167
6671
|
const selection = document.getSelection();
|
|
6168
6672
|
if (!selection) return;
|
|
6169
6673
|
selection.removeAllRanges();
|
|
6170
|
-
selection.addRange(
|
|
6674
|
+
selection.addRange(range2);
|
|
6171
6675
|
}
|
|
6172
|
-
function getBlock(editor,
|
|
6173
|
-
const active =
|
|
6676
|
+
function getBlock(editor, range2) {
|
|
6677
|
+
const active = range2 ?? getRange(editor);
|
|
6174
6678
|
if (!active) return null;
|
|
6175
6679
|
const node = active.startContainer;
|
|
6176
6680
|
const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
6177
6681
|
const block = el?.closest(BLOCK_SELECTOR) ?? null;
|
|
6178
6682
|
return block && editor.contains(block) ? block : null;
|
|
6179
6683
|
}
|
|
6180
|
-
function getSelectedBlocks(editor,
|
|
6684
|
+
function getSelectedBlocks(editor, range2) {
|
|
6181
6685
|
const active = getRange(editor);
|
|
6182
6686
|
if (!active) return [];
|
|
6183
6687
|
if (active.collapsed) {
|
|
@@ -6197,11 +6701,11 @@ function closestInEditor(editor, node, selector) {
|
|
|
6197
6701
|
const match = el?.closest(selector) ?? null;
|
|
6198
6702
|
return match && editor.contains(match) ? match : null;
|
|
6199
6703
|
}
|
|
6200
|
-
function setSelection(
|
|
6704
|
+
function setSelection(range2) {
|
|
6201
6705
|
const selection = document.getSelection();
|
|
6202
6706
|
if (!selection) return;
|
|
6203
6707
|
selection.removeAllRanges();
|
|
6204
|
-
selection.addRange(
|
|
6708
|
+
selection.addRange(range2);
|
|
6205
6709
|
}
|
|
6206
6710
|
function nativeExec(command, value) {
|
|
6207
6711
|
const exec = document.execCommand;
|
|
@@ -6214,16 +6718,16 @@ function nativeExec(command, value) {
|
|
|
6214
6718
|
}
|
|
6215
6719
|
function insertHtml(editor, html) {
|
|
6216
6720
|
if (nativeExec("insertHTML", html)) return true;
|
|
6217
|
-
const
|
|
6218
|
-
if (!
|
|
6721
|
+
const range2 = getRange(editor);
|
|
6722
|
+
if (!range2) {
|
|
6219
6723
|
editor.insertAdjacentHTML("beforeend", html);
|
|
6220
6724
|
placeCaretAtEnd(editor);
|
|
6221
6725
|
return true;
|
|
6222
6726
|
}
|
|
6223
|
-
|
|
6224
|
-
const fragment =
|
|
6727
|
+
range2.deleteContents();
|
|
6728
|
+
const fragment = range2.createContextualFragment(html);
|
|
6225
6729
|
const last = fragment.lastChild;
|
|
6226
|
-
|
|
6730
|
+
range2.insertNode(fragment);
|
|
6227
6731
|
if (last) {
|
|
6228
6732
|
const next = document.createRange();
|
|
6229
6733
|
next.setStartAfter(last);
|
|
@@ -6239,12 +6743,12 @@ function unwrap(el) {
|
|
|
6239
6743
|
el.remove();
|
|
6240
6744
|
}
|
|
6241
6745
|
function wrapSelection(editor, tag) {
|
|
6242
|
-
const
|
|
6243
|
-
if (!
|
|
6746
|
+
const range2 = getRange(editor);
|
|
6747
|
+
if (!range2) return false;
|
|
6244
6748
|
const el = document.createElement(tag);
|
|
6245
|
-
if (
|
|
6749
|
+
if (range2.collapsed) {
|
|
6246
6750
|
el.appendChild(document.createTextNode(""));
|
|
6247
|
-
|
|
6751
|
+
range2.insertNode(el);
|
|
6248
6752
|
const caret = document.createRange();
|
|
6249
6753
|
caret.setStart(el.firstChild ?? el, 1);
|
|
6250
6754
|
caret.collapse(true);
|
|
@@ -6252,10 +6756,10 @@ function wrapSelection(editor, tag) {
|
|
|
6252
6756
|
return true;
|
|
6253
6757
|
}
|
|
6254
6758
|
try {
|
|
6255
|
-
|
|
6759
|
+
range2.surroundContents(el);
|
|
6256
6760
|
} catch {
|
|
6257
|
-
el.appendChild(
|
|
6258
|
-
|
|
6761
|
+
el.appendChild(range2.extractContents());
|
|
6762
|
+
range2.insertNode(el);
|
|
6259
6763
|
}
|
|
6260
6764
|
return true;
|
|
6261
6765
|
}
|
|
@@ -6271,9 +6775,9 @@ function toggleInline(editor, tag) {
|
|
|
6271
6775
|
};
|
|
6272
6776
|
const nativeCommand = nativeMap[tag];
|
|
6273
6777
|
if (nativeCommand && nativeExec(nativeCommand)) return true;
|
|
6274
|
-
const
|
|
6275
|
-
if (!
|
|
6276
|
-
const current = (
|
|
6778
|
+
const range2 = getRange(editor);
|
|
6779
|
+
if (!range2) return false;
|
|
6780
|
+
const current = (range2.startContainer.nodeType === Node.ELEMENT_NODE ? range2.startContainer : range2.startContainer.parentElement)?.closest(tag);
|
|
6277
6781
|
if (current && editor.contains(current)) {
|
|
6278
6782
|
unwrap(current);
|
|
6279
6783
|
return true;
|
|
@@ -6322,31 +6826,31 @@ function toggleList(editor, ordered) {
|
|
|
6322
6826
|
list.appendChild(li);
|
|
6323
6827
|
block.remove();
|
|
6324
6828
|
}
|
|
6325
|
-
const
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
setSelection(
|
|
6829
|
+
const range2 = document.createRange();
|
|
6830
|
+
range2.selectNodeContents(list.lastElementChild ?? list);
|
|
6831
|
+
range2.collapse(false);
|
|
6832
|
+
setSelection(range2);
|
|
6329
6833
|
return true;
|
|
6330
6834
|
}
|
|
6331
6835
|
function removeFormat(editor) {
|
|
6332
6836
|
if (nativeExec("removeFormat")) return true;
|
|
6333
|
-
const
|
|
6334
|
-
if (!
|
|
6335
|
-
const root =
|
|
6837
|
+
const range2 = getRange(editor);
|
|
6838
|
+
if (!range2) return false;
|
|
6839
|
+
const root = range2.commonAncestorContainer;
|
|
6336
6840
|
const container = root.nodeType === Node.ELEMENT_NODE ? root : root.parentElement;
|
|
6337
6841
|
if (!container) return false;
|
|
6338
6842
|
const tags = ["strong", "b", "em", "i", "u", "s", "strike", "span", "mark", "code", "sub", "sup"];
|
|
6339
6843
|
for (const el of Array.from(container.querySelectorAll(tags.join(",")))) {
|
|
6340
|
-
if (
|
|
6844
|
+
if (range2.intersectsNode(el)) unwrap(el);
|
|
6341
6845
|
}
|
|
6342
6846
|
return true;
|
|
6343
6847
|
}
|
|
6344
6848
|
const linkHtml = (href, label, target) => `<a href="${escapeAttr(href)}" target="${target}" rel="noopener noreferrer">${escapeText(label)}</a>`;
|
|
6345
6849
|
function applyLink(editor, href, text, target = "_blank") {
|
|
6346
6850
|
const label = text?.trim() || href;
|
|
6347
|
-
const
|
|
6348
|
-
if (!
|
|
6349
|
-
const anchor = (
|
|
6851
|
+
const range2 = getRange(editor);
|
|
6852
|
+
if (!range2) return insertHtml(editor, linkHtml(href, label, target));
|
|
6853
|
+
const anchor = (range2.startContainer.nodeType === Node.ELEMENT_NODE ? range2.startContainer : range2.startContainer.parentElement)?.closest("a");
|
|
6350
6854
|
if (anchor && editor.contains(anchor)) {
|
|
6351
6855
|
anchor.setAttribute("href", href);
|
|
6352
6856
|
anchor.setAttribute("target", target);
|
|
@@ -6354,9 +6858,9 @@ function applyLink(editor, href, text, target = "_blank") {
|
|
|
6354
6858
|
if (text && text !== anchor.textContent) anchor.textContent = text;
|
|
6355
6859
|
return true;
|
|
6356
6860
|
}
|
|
6357
|
-
if (!
|
|
6861
|
+
if (!range2.collapsed) {
|
|
6358
6862
|
if (nativeExec("createLink", href)) {
|
|
6359
|
-
const created = (
|
|
6863
|
+
const created = (range2.startContainer.nodeType === Node.ELEMENT_NODE ? range2.startContainer : range2.startContainer.parentElement)?.closest("a");
|
|
6360
6864
|
created?.setAttribute("target", target);
|
|
6361
6865
|
created?.setAttribute("rel", "noopener noreferrer");
|
|
6362
6866
|
return true;
|
|
@@ -6366,10 +6870,10 @@ function applyLink(editor, href, text, target = "_blank") {
|
|
|
6366
6870
|
el.setAttribute("target", target);
|
|
6367
6871
|
el.setAttribute("rel", "noopener noreferrer");
|
|
6368
6872
|
try {
|
|
6369
|
-
|
|
6873
|
+
range2.surroundContents(el);
|
|
6370
6874
|
} catch {
|
|
6371
|
-
el.appendChild(
|
|
6372
|
-
|
|
6875
|
+
el.appendChild(range2.extractContents());
|
|
6876
|
+
range2.insertNode(el);
|
|
6373
6877
|
}
|
|
6374
6878
|
return true;
|
|
6375
6879
|
}
|
|
@@ -6377,9 +6881,9 @@ function applyLink(editor, href, text, target = "_blank") {
|
|
|
6377
6881
|
}
|
|
6378
6882
|
function unlink(editor) {
|
|
6379
6883
|
if (nativeExec("unlink")) return true;
|
|
6380
|
-
const
|
|
6381
|
-
if (!
|
|
6382
|
-
const anchor = (
|
|
6884
|
+
const range2 = getRange(editor);
|
|
6885
|
+
if (!range2) return false;
|
|
6886
|
+
const anchor = (range2.startContainer.nodeType === Node.ELEMENT_NODE ? range2.startContainer : range2.startContainer.parentElement)?.closest("a");
|
|
6383
6887
|
if (anchor && editor.contains(anchor)) {
|
|
6384
6888
|
unwrap(anchor);
|
|
6385
6889
|
return true;
|
|
@@ -6447,10 +6951,10 @@ function cellPosition(table, cell) {
|
|
|
6447
6951
|
}
|
|
6448
6952
|
return null;
|
|
6449
6953
|
}
|
|
6450
|
-
function selectedCells(table,
|
|
6954
|
+
function selectedCells(table, range2) {
|
|
6451
6955
|
return Array.from(table.querySelectorAll("td,th")).filter((cell) => {
|
|
6452
6956
|
try {
|
|
6453
|
-
return
|
|
6957
|
+
return range2.intersectsNode(cell);
|
|
6454
6958
|
} catch {
|
|
6455
6959
|
return false;
|
|
6456
6960
|
}
|
|
@@ -6462,8 +6966,8 @@ function copyCellAttributes(from, to) {
|
|
|
6462
6966
|
to.setAttribute(attr.name, attr.value);
|
|
6463
6967
|
}
|
|
6464
6968
|
}
|
|
6465
|
-
function mergeSelectedCells(table,
|
|
6466
|
-
const cells = selectedCells(table,
|
|
6969
|
+
function mergeSelectedCells(table, range2) {
|
|
6970
|
+
const cells = selectedCells(table, range2);
|
|
6467
6971
|
if (cells.length < 2) return null;
|
|
6468
6972
|
const { matrix } = tableMatrix(table);
|
|
6469
6973
|
const positions = cells.map((cell) => ({ cell, position: cellPosition(table, cell) })).filter((item) => Boolean(item.position));
|
|
@@ -6846,9 +7350,9 @@ const _FxEditor = class _FxEditor extends FxFormElement {
|
|
|
6846
7350
|
mergeCells() {
|
|
6847
7351
|
const table = __privateMethod(this, _FxEditor_instances, currentTable_fn).call(this);
|
|
6848
7352
|
const editor = this.editor;
|
|
6849
|
-
const
|
|
6850
|
-
if (!table || !
|
|
6851
|
-
const merged = mergeSelectedCells(table,
|
|
7353
|
+
const range2 = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
|
|
7354
|
+
if (!table || !range2) return false;
|
|
7355
|
+
const merged = mergeSelectedCells(table, range2);
|
|
6852
7356
|
if (!merged) return false;
|
|
6853
7357
|
placeCaretAtEnd(merged);
|
|
6854
7358
|
__privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
|
|
@@ -6858,8 +7362,8 @@ const _FxEditor = class _FxEditor extends FxFormElement {
|
|
|
6858
7362
|
splitCell() {
|
|
6859
7363
|
const table = __privateMethod(this, _FxEditor_instances, currentTable_fn).call(this);
|
|
6860
7364
|
const editor = this.editor;
|
|
6861
|
-
const
|
|
6862
|
-
const cell = editor ? closestInEditor(editor,
|
|
7365
|
+
const range2 = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
|
|
7366
|
+
const cell = editor ? closestInEditor(editor, range2?.startContainer ?? null, "td,th") : null;
|
|
6863
7367
|
if (!table || !cell) return false;
|
|
6864
7368
|
const created = splitCell(table, cell);
|
|
6865
7369
|
if (!created.length) return false;
|
|
@@ -7119,8 +7623,8 @@ fits_fn = function(extra) {
|
|
|
7119
7623
|
const max = this.maxlength;
|
|
7120
7624
|
if (!max) return true;
|
|
7121
7625
|
const editor = this.editor;
|
|
7122
|
-
const
|
|
7123
|
-
const selected =
|
|
7626
|
+
const range2 = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
|
|
7627
|
+
const selected = range2 && !range2.collapsed ? range2.toString().length : 0;
|
|
7124
7628
|
return this.text.length - selected + extra <= max;
|
|
7125
7629
|
};
|
|
7126
7630
|
limitError_fn = function() {
|
|
@@ -7243,9 +7747,9 @@ bindGlobalSelection_fn = function() {
|
|
|
7243
7747
|
const handler = () => {
|
|
7244
7748
|
const editor = this.editor;
|
|
7245
7749
|
if (!editor) return;
|
|
7246
|
-
const
|
|
7247
|
-
if (!
|
|
7248
|
-
__privateSet(this, _lastRange,
|
|
7750
|
+
const range2 = getRange(editor, __privateGet(this, _lastRange));
|
|
7751
|
+
if (!range2) return;
|
|
7752
|
+
__privateSet(this, _lastRange, range2.cloneRange());
|
|
7249
7753
|
cancelAnimationFrame(__privateGet(this, _selectionRaf));
|
|
7250
7754
|
__privateSet(this, _selectionRaf, requestAnimationFrame(() => __privateMethod(this, _FxEditor_instances, updateToolbar_fn).call(this)));
|
|
7251
7755
|
};
|
|
@@ -7370,8 +7874,8 @@ renderToolbar_fn = function() {
|
|
|
7370
7874
|
updateToolbar_fn = function() {
|
|
7371
7875
|
const editor = this.editor;
|
|
7372
7876
|
if (!editor) return;
|
|
7373
|
-
const
|
|
7374
|
-
if (!
|
|
7877
|
+
const range2 = getRange(editor, __privateGet(this, _lastRange));
|
|
7878
|
+
if (!range2) return;
|
|
7375
7879
|
const setActive = (cmd, active) => {
|
|
7376
7880
|
const button = this.root.querySelector(`[data-cmd="${cmd}"]`);
|
|
7377
7881
|
if (!button) return;
|
|
@@ -7379,7 +7883,7 @@ updateToolbar_fn = function() {
|
|
|
7379
7883
|
button.setAttribute("aria-pressed", String(active));
|
|
7380
7884
|
};
|
|
7381
7885
|
const ancestor = (selector) => {
|
|
7382
|
-
const node =
|
|
7886
|
+
const node = range2.startContainer;
|
|
7383
7887
|
const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
7384
7888
|
const match = el?.closest(selector);
|
|
7385
7889
|
return Boolean(match && editor.contains(match));
|
|
@@ -7391,7 +7895,7 @@ updateToolbar_fn = function() {
|
|
|
7391
7895
|
setActive("code", ancestor("code"));
|
|
7392
7896
|
setActive("insertUnorderedList", ancestor("ul"));
|
|
7393
7897
|
setActive("insertOrderedList", ancestor("ol"));
|
|
7394
|
-
const block = getBlock(editor,
|
|
7898
|
+
const block = getBlock(editor, range2);
|
|
7395
7899
|
const blockTag = block?.tagName.toLowerCase() ?? "p";
|
|
7396
7900
|
const blockSelect = this.root.querySelector("[data-block]");
|
|
7397
7901
|
if (blockSelect) {
|
|
@@ -7635,9 +8139,9 @@ handleKeydown_fn = function(event) {
|
|
|
7635
8139
|
tabInTable_fn = function(backwards) {
|
|
7636
8140
|
const editor = this.editor;
|
|
7637
8141
|
if (!editor) return false;
|
|
7638
|
-
const
|
|
7639
|
-
if (!
|
|
7640
|
-
const cell = closestInEditor(editor,
|
|
8142
|
+
const range2 = getRange(editor, __privateGet(this, _lastRange));
|
|
8143
|
+
if (!range2) return false;
|
|
8144
|
+
const cell = closestInEditor(editor, range2.startContainer, "td,th");
|
|
7641
8145
|
const table = cell?.closest("table");
|
|
7642
8146
|
if (!cell || !table) return false;
|
|
7643
8147
|
const cells = Array.from(table.querySelectorAll("td,th"));
|
|
@@ -7807,14 +8311,14 @@ openLinkDialog_fn = async function(existing) {
|
|
|
7807
8311
|
const editor = this.editor;
|
|
7808
8312
|
if (!editor) return;
|
|
7809
8313
|
if (existing) __privateMethod(this, _FxEditor_instances, selectAnchor_fn).call(this, existing);
|
|
7810
|
-
let
|
|
7811
|
-
if (!
|
|
8314
|
+
let range2 = getRange(editor, __privateGet(this, _lastRange));
|
|
8315
|
+
if (!range2) {
|
|
7812
8316
|
placeCaretAtEnd(editor);
|
|
7813
|
-
|
|
7814
|
-
__privateSet(this, _lastRange,
|
|
8317
|
+
range2 = getRange(editor);
|
|
8318
|
+
__privateSet(this, _lastRange, range2?.cloneRange() ?? null);
|
|
7815
8319
|
}
|
|
7816
|
-
const anchor = existing ?? closestInEditor(editor,
|
|
7817
|
-
const selected =
|
|
8320
|
+
const anchor = existing ?? closestInEditor(editor, range2?.startContainer ?? null, "a");
|
|
8321
|
+
const selected = range2 && !range2.collapsed ? range2.toString() : anchor?.textContent ?? "";
|
|
7818
8322
|
const result = await __privateMethod(this, _FxEditor_instances, openDialog_fn).call(this, "Link", [
|
|
7819
8323
|
{ name: "url", label: "URL", type: "url", value: anchor?.getAttribute("href") ?? "", placeholder: "https://..." },
|
|
7820
8324
|
{ name: "text", label: "Texto (quando não há seleção)", value: selected, placeholder: "Texto do link" },
|
|
@@ -8037,13 +8541,13 @@ selectAnchor_fn = function(anchor) {
|
|
|
8037
8541
|
const editor = this.editor;
|
|
8038
8542
|
if (!editor) return;
|
|
8039
8543
|
editor.focus({ preventScroll: true });
|
|
8040
|
-
const
|
|
8041
|
-
|
|
8544
|
+
const range2 = document.createRange();
|
|
8545
|
+
range2.selectNodeContents(anchor);
|
|
8042
8546
|
const selection = document.getSelection();
|
|
8043
8547
|
if (!selection) return;
|
|
8044
8548
|
selection.removeAllRanges();
|
|
8045
|
-
selection.addRange(
|
|
8046
|
-
__privateSet(this, _lastRange,
|
|
8549
|
+
selection.addRange(range2);
|
|
8550
|
+
__privateSet(this, _lastRange, range2.cloneRange());
|
|
8047
8551
|
};
|
|
8048
8552
|
handleLinkOverlayClick_fn = function(event) {
|
|
8049
8553
|
const action = event.target.closest("[data-link-action]");
|
|
@@ -8314,19 +8818,19 @@ hidePanel_fn = function() {
|
|
|
8314
8818
|
currentTable_fn = function() {
|
|
8315
8819
|
const editor = this.editor;
|
|
8316
8820
|
if (!editor) return null;
|
|
8317
|
-
const
|
|
8318
|
-
return closestInEditor(editor,
|
|
8821
|
+
const range2 = getRange(editor, __privateGet(this, _lastRange));
|
|
8822
|
+
return closestInEditor(editor, range2?.startContainer ?? null, "table");
|
|
8319
8823
|
};
|
|
8320
8824
|
tableAction_fn = function(action) {
|
|
8321
8825
|
const table = __privateMethod(this, _FxEditor_instances, currentTable_fn).call(this);
|
|
8322
8826
|
if (!table) return;
|
|
8323
8827
|
const editor = this.editor;
|
|
8324
|
-
const
|
|
8325
|
-
const cell = editor ? closestInEditor(editor,
|
|
8828
|
+
const range2 = editor ? getRange(editor, __privateGet(this, _lastRange)) : null;
|
|
8829
|
+
const cell = editor ? closestInEditor(editor, range2?.startContainer ?? null, "td,th") : null;
|
|
8326
8830
|
const row = cell?.parentElement;
|
|
8327
8831
|
if (action === "merge-cells") {
|
|
8328
|
-
if (!
|
|
8329
|
-
const merged = mergeSelectedCells(table,
|
|
8832
|
+
if (!range2) return;
|
|
8833
|
+
const merged = mergeSelectedCells(table, range2);
|
|
8330
8834
|
if (!merged) return;
|
|
8331
8835
|
placeCaretAtEnd(merged);
|
|
8332
8836
|
__privateMethod(this, _FxEditor_instances, afterCommand_fn).call(this);
|
|
@@ -8477,10 +8981,61 @@ function defineFxEditor() {
|
|
|
8477
8981
|
return defineElement("fx-editor", FxEditor);
|
|
8478
8982
|
}
|
|
8479
8983
|
defineFxEditor();
|
|
8984
|
+
const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]';
|
|
8985
|
+
function usable(nodes) {
|
|
8986
|
+
return [...nodes].filter(
|
|
8987
|
+
(el) => !el.hasAttribute("disabled") && el.getAttribute("aria-hidden") !== "true" && el.getAttribute("tabindex") !== "-1"
|
|
8988
|
+
);
|
|
8989
|
+
}
|
|
8990
|
+
function modalFocusables(panel, host) {
|
|
8991
|
+
const shadow = panel.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
8992
|
+
const light = host.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
8993
|
+
return usable([...shadow, ...light]);
|
|
8994
|
+
}
|
|
8995
|
+
function firstLightFocusable(host) {
|
|
8996
|
+
return usable(host.querySelectorAll(FOCUSABLE_SELECTOR))[0] ?? null;
|
|
8997
|
+
}
|
|
8998
|
+
const modalStack = [];
|
|
8999
|
+
function registerModalOpen(host) {
|
|
9000
|
+
if (!modalStack.includes(host)) modalStack.push(host);
|
|
9001
|
+
}
|
|
9002
|
+
function registerModalClose(host) {
|
|
9003
|
+
const index = modalStack.indexOf(host);
|
|
9004
|
+
if (index !== -1) modalStack.splice(index, 1);
|
|
9005
|
+
}
|
|
9006
|
+
function isTopmostModal(host) {
|
|
9007
|
+
if (modalStack.length === 0) return true;
|
|
9008
|
+
return modalStack[modalStack.length - 1] === host;
|
|
9009
|
+
}
|
|
9010
|
+
let scrollLocks = 0;
|
|
9011
|
+
let previousOverflow = "";
|
|
9012
|
+
let previousPaddingRight = "";
|
|
9013
|
+
function lockBodyScroll() {
|
|
9014
|
+
if (typeof document === "undefined" || !document.body) return;
|
|
9015
|
+
if (scrollLocks === 0) {
|
|
9016
|
+
const body = document.body;
|
|
9017
|
+
previousOverflow = body.style.overflow;
|
|
9018
|
+
previousPaddingRight = body.style.paddingRight;
|
|
9019
|
+
const scrollbar = window.innerWidth - document.documentElement.clientWidth;
|
|
9020
|
+
body.style.overflow = "hidden";
|
|
9021
|
+
if (scrollbar > 0) body.style.paddingRight = `${scrollbar}px`;
|
|
9022
|
+
}
|
|
9023
|
+
scrollLocks += 1;
|
|
9024
|
+
}
|
|
9025
|
+
function unlockBodyScroll() {
|
|
9026
|
+
if (typeof document === "undefined" || !document.body) return;
|
|
9027
|
+
if (scrollLocks === 0) return;
|
|
9028
|
+
scrollLocks -= 1;
|
|
9029
|
+
if (scrollLocks === 0) {
|
|
9030
|
+
document.body.style.overflow = previousOverflow;
|
|
9031
|
+
document.body.style.paddingRight = previousPaddingRight;
|
|
9032
|
+
}
|
|
9033
|
+
}
|
|
8480
9034
|
const _FxDialog = class _FxDialog extends FxElement {
|
|
8481
9035
|
constructor() {
|
|
8482
9036
|
super(...arguments);
|
|
8483
9037
|
this._previouslyFocused = null;
|
|
9038
|
+
this._locked = false;
|
|
8484
9039
|
}
|
|
8485
9040
|
static get observedAttributes() {
|
|
8486
9041
|
return ["open", "size", "heading"];
|
|
@@ -8494,6 +9049,11 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8494
9049
|
render() {
|
|
8495
9050
|
const isOpen = this.open;
|
|
8496
9051
|
const heading = this.getAttr("heading");
|
|
9052
|
+
if (!isOpen) {
|
|
9053
|
+
this._release();
|
|
9054
|
+
this.setTemplate("");
|
|
9055
|
+
return;
|
|
9056
|
+
}
|
|
8497
9057
|
this.setTemplate(`
|
|
8498
9058
|
<div class="overlay" part="overlay" ${isOpen ? "" : "hidden"}>
|
|
8499
9059
|
<div class="dialog" role="dialog" aria-modal="true" aria-label="${esc(heading)}" tabindex="-1">
|
|
@@ -8512,10 +9072,8 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8512
9072
|
const overlay = this.root.querySelector(".overlay");
|
|
8513
9073
|
const closeBtn = this.root.querySelector(".close");
|
|
8514
9074
|
const close = () => {
|
|
9075
|
+
if (!this.open) return;
|
|
8515
9076
|
this.open = false;
|
|
8516
|
-
this._cleanup?.();
|
|
8517
|
-
this._cleanup = void 0;
|
|
8518
|
-
this._restoreFocus();
|
|
8519
9077
|
this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
|
|
8520
9078
|
};
|
|
8521
9079
|
overlay?.addEventListener("click", (e) => {
|
|
@@ -8524,6 +9082,7 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8524
9082
|
closeBtn?.addEventListener("click", close);
|
|
8525
9083
|
const onKey = (e) => {
|
|
8526
9084
|
if (e.key === "Escape") {
|
|
9085
|
+
if (!isTopmostModal(this)) return;
|
|
8527
9086
|
e.stopPropagation();
|
|
8528
9087
|
close();
|
|
8529
9088
|
return;
|
|
@@ -8532,29 +9091,48 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8532
9091
|
};
|
|
8533
9092
|
document.addEventListener("keydown", onKey);
|
|
8534
9093
|
this._cleanup = () => document.removeEventListener("keydown", onKey);
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
9094
|
+
const alreadyOpen = this._locked;
|
|
9095
|
+
if (!this._locked) {
|
|
9096
|
+
lockBodyScroll();
|
|
9097
|
+
registerModalOpen(this);
|
|
9098
|
+
this._locked = true;
|
|
9099
|
+
}
|
|
9100
|
+
if (!alreadyOpen) {
|
|
9101
|
+
requestAnimationFrame(() => {
|
|
9102
|
+
if (!this.open) return;
|
|
9103
|
+
this._previouslyFocused = document.activeElement;
|
|
9104
|
+
const first = firstLightFocusable(this) ?? this.root.querySelector(".dialog");
|
|
9105
|
+
first?.focus();
|
|
9106
|
+
});
|
|
9107
|
+
}
|
|
8539
9108
|
this.dispatchEvent(new CustomEvent("open", { bubbles: true, composed: true }));
|
|
8540
9109
|
}
|
|
8541
|
-
/**
|
|
9110
|
+
/** Desfaz listeners, foco e scroll lock ao fechar/desconectar. */
|
|
9111
|
+
_release() {
|
|
9112
|
+
this._cleanup?.();
|
|
9113
|
+
this._cleanup = void 0;
|
|
9114
|
+
registerModalClose(this);
|
|
9115
|
+
if (this._locked) {
|
|
9116
|
+
unlockBodyScroll();
|
|
9117
|
+
this._locked = false;
|
|
9118
|
+
}
|
|
9119
|
+
this._restoreFocus();
|
|
9120
|
+
}
|
|
9121
|
+
/** Mantém o foco dentro do modal (WCAG 2.4.3 / 2.1.2), incluindo o slot. */
|
|
8542
9122
|
_trapFocus(e) {
|
|
8543
9123
|
const dialog = this.root.querySelector(".dialog");
|
|
8544
9124
|
if (!dialog) return;
|
|
8545
|
-
const focusables = dialog
|
|
8546
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
8547
|
-
);
|
|
9125
|
+
const focusables = modalFocusables(dialog, this);
|
|
8548
9126
|
if (!focusables.length) return;
|
|
8549
9127
|
const first = focusables[0];
|
|
8550
9128
|
const last = focusables[focusables.length - 1];
|
|
8551
|
-
const active = this.root.activeElement;
|
|
8552
|
-
if (e.shiftKey && (active === first || active === dialog)) {
|
|
9129
|
+
const active = this.root.activeElement ?? document.activeElement;
|
|
9130
|
+
if (e.shiftKey && (active === first || active === dialog || active === this)) {
|
|
8553
9131
|
e.preventDefault();
|
|
8554
|
-
last
|
|
9132
|
+
last?.focus();
|
|
8555
9133
|
} else if (!e.shiftKey && active === last) {
|
|
8556
9134
|
e.preventDefault();
|
|
8557
|
-
first
|
|
9135
|
+
first?.focus();
|
|
8558
9136
|
}
|
|
8559
9137
|
}
|
|
8560
9138
|
_restoreFocus() {
|
|
@@ -8563,9 +9141,7 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8563
9141
|
}
|
|
8564
9142
|
disconnectedCallback() {
|
|
8565
9143
|
super.disconnectedCallback();
|
|
8566
|
-
this.
|
|
8567
|
-
this._cleanup = void 0;
|
|
8568
|
-
this._restoreFocus();
|
|
9144
|
+
this._release();
|
|
8569
9145
|
}
|
|
8570
9146
|
};
|
|
8571
9147
|
_FxDialog.styles = css`
|
|
@@ -8971,7 +9547,35 @@ const _FxFileUpload = class _FxFileUpload extends FxFormElement {
|
|
|
8971
9547
|
return this.getAttr("value");
|
|
8972
9548
|
}
|
|
8973
9549
|
set value(v) {
|
|
8974
|
-
this.setAttribute("value", v);
|
|
9550
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
9551
|
+
}
|
|
9552
|
+
get disabled() {
|
|
9553
|
+
return this.hasAttr("disabled");
|
|
9554
|
+
}
|
|
9555
|
+
set disabled(v) {
|
|
9556
|
+
this.toggleAttr("disabled", Boolean(v));
|
|
9557
|
+
}
|
|
9558
|
+
get multiple() {
|
|
9559
|
+
return this.hasAttr("multiple");
|
|
9560
|
+
}
|
|
9561
|
+
set multiple(v) {
|
|
9562
|
+
this.toggleAttr("multiple", Boolean(v));
|
|
9563
|
+
}
|
|
9564
|
+
get full() {
|
|
9565
|
+
return this.hasAttr("full");
|
|
9566
|
+
}
|
|
9567
|
+
set full(v) {
|
|
9568
|
+
this.toggleAttr("full", Boolean(v));
|
|
9569
|
+
}
|
|
9570
|
+
/**
|
|
9571
|
+
* Nome literal: o Vue NÃO cameliza atributos em elementos customizados,
|
|
9572
|
+
* então `:show-progress` procura a propriedade `'show-progress'`.
|
|
9573
|
+
*/
|
|
9574
|
+
get "show-progress"() {
|
|
9575
|
+
return this.hasAttr("show-progress");
|
|
9576
|
+
}
|
|
9577
|
+
set "show-progress"(v) {
|
|
9578
|
+
this.toggleAttr("show-progress", Boolean(v));
|
|
8975
9579
|
}
|
|
8976
9580
|
get progress() {
|
|
8977
9581
|
return Math.min(100, Math.max(0, Number(this.getAttr("progress", "0")) || 0));
|
|
@@ -9652,6 +10256,9 @@ const _FxAvatar = class _FxAvatar extends FxElement {
|
|
|
9652
10256
|
get variant() {
|
|
9653
10257
|
return this.getAttr("variant", "text");
|
|
9654
10258
|
}
|
|
10259
|
+
set variant(v) {
|
|
10260
|
+
this.setAttribute("variant", v);
|
|
10261
|
+
}
|
|
9655
10262
|
get src() {
|
|
9656
10263
|
return this.getAttr("src");
|
|
9657
10264
|
}
|
|
@@ -9755,6 +10362,18 @@ const _FxIcon = class _FxIcon extends FxElement {
|
|
|
9755
10362
|
if (value) this.setAttribute("label", value);
|
|
9756
10363
|
else this.removeAttribute("label");
|
|
9757
10364
|
}
|
|
10365
|
+
get fill() {
|
|
10366
|
+
return this.hasAttr("fill");
|
|
10367
|
+
}
|
|
10368
|
+
set fill(value) {
|
|
10369
|
+
this.toggleAttr("fill", Boolean(value));
|
|
10370
|
+
}
|
|
10371
|
+
get bold() {
|
|
10372
|
+
return this.hasAttr("bold");
|
|
10373
|
+
}
|
|
10374
|
+
set bold(value) {
|
|
10375
|
+
this.toggleAttr("bold", Boolean(value));
|
|
10376
|
+
}
|
|
9758
10377
|
connectedCallback() {
|
|
9759
10378
|
loadFenixIconsFont();
|
|
9760
10379
|
super.connectedCallback();
|
|
@@ -9801,6 +10420,12 @@ const _FxCard = class _FxCard extends FxElement {
|
|
|
9801
10420
|
static get observedAttributes() {
|
|
9802
10421
|
return ["variant", "radius", "size", "padded", "heading"];
|
|
9803
10422
|
}
|
|
10423
|
+
get padded() {
|
|
10424
|
+
return this.hasAttr("padded");
|
|
10425
|
+
}
|
|
10426
|
+
set padded(value) {
|
|
10427
|
+
this.toggleAttr("padded", Boolean(value));
|
|
10428
|
+
}
|
|
9804
10429
|
render() {
|
|
9805
10430
|
const heading = this.getAttr("heading");
|
|
9806
10431
|
this.setTemplate(`
|
|
@@ -12321,6 +12946,40 @@ const _FxTabs = class _FxTabs extends FxElement {
|
|
|
12321
12946
|
set value(value) {
|
|
12322
12947
|
this.setAttribute("value", value);
|
|
12323
12948
|
}
|
|
12949
|
+
connectedCallback() {
|
|
12950
|
+
super.connectedCallback();
|
|
12951
|
+
this._observeLightDom();
|
|
12952
|
+
}
|
|
12953
|
+
disconnectedCallback() {
|
|
12954
|
+
super.disconnectedCallback();
|
|
12955
|
+
this._lightObserver?.disconnect();
|
|
12956
|
+
this._siblingObserver?.disconnect();
|
|
12957
|
+
this._lightObserver = void 0;
|
|
12958
|
+
this._siblingObserver = void 0;
|
|
12959
|
+
}
|
|
12960
|
+
_observeLightDom() {
|
|
12961
|
+
this._lightObserver?.disconnect();
|
|
12962
|
+
this._lightObserver = new MutationObserver(() => this.render());
|
|
12963
|
+
this._lightObserver.observe(this, {
|
|
12964
|
+
childList: true,
|
|
12965
|
+
subtree: true,
|
|
12966
|
+
attributes: true,
|
|
12967
|
+
attributeFilter: ["tab", "disabled"]
|
|
12968
|
+
});
|
|
12969
|
+
this._siblingObserver?.disconnect();
|
|
12970
|
+
const parent = this.parentNode;
|
|
12971
|
+
if (parent) {
|
|
12972
|
+
this._siblingObserver = new MutationObserver((mutations) => {
|
|
12973
|
+
const affectsPanels = mutations.some(
|
|
12974
|
+
(m) => [...m.addedNodes, ...m.removedNodes].some(
|
|
12975
|
+
(node) => node.nodeType === Node.ELEMENT_NODE && (node.matches("fx-tab-panel") || Boolean(node.querySelector("fx-tab-panel")))
|
|
12976
|
+
)
|
|
12977
|
+
);
|
|
12978
|
+
if (affectsPanels) this.render();
|
|
12979
|
+
});
|
|
12980
|
+
this._siblingObserver.observe(parent, { childList: true });
|
|
12981
|
+
}
|
|
12982
|
+
}
|
|
12324
12983
|
render() {
|
|
12325
12984
|
const tabs = Array.from(this.querySelectorAll("fx-tab"));
|
|
12326
12985
|
const active = this.value || tabs[0]?.getAttribute("tab") || "";
|
|
@@ -12849,10 +13508,11 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12849
13508
|
this._onKeydown = () => {
|
|
12850
13509
|
};
|
|
12851
13510
|
this._previouslyFocused = null;
|
|
13511
|
+
this._locked = false;
|
|
13512
|
+
this._wasOpen = false;
|
|
12852
13513
|
this._close = () => {
|
|
12853
13514
|
if (!this.open) return;
|
|
12854
13515
|
this.open = false;
|
|
12855
|
-
this._restoreFocus();
|
|
12856
13516
|
this.dispatchEvent(
|
|
12857
13517
|
new CustomEvent("close", { bubbles: true, composed: true })
|
|
12858
13518
|
);
|
|
@@ -12882,6 +13542,8 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12882
13542
|
}
|
|
12883
13543
|
render() {
|
|
12884
13544
|
const title = this.getAttr("title");
|
|
13545
|
+
const wasOpen = this._wasOpen;
|
|
13546
|
+
this._wasOpen = this.open;
|
|
12885
13547
|
this.setTemplate(`
|
|
12886
13548
|
<div class="overlay" part="overlay"></div>
|
|
12887
13549
|
<div class="panel" part="panel" role="dialog" aria-modal="true" aria-label="${esc(title || "Drawer")}" tabindex="-1">
|
|
@@ -12892,20 +13554,40 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12892
13554
|
this.root.querySelector(".overlay")?.addEventListener("click", this._close);
|
|
12893
13555
|
this.root.querySelector(".close")?.addEventListener("click", this._close);
|
|
12894
13556
|
if (this.open) {
|
|
12895
|
-
|
|
12896
|
-
|
|
13557
|
+
if (!this._locked) {
|
|
13558
|
+
lockBodyScroll();
|
|
13559
|
+
registerModalOpen(this);
|
|
13560
|
+
this._locked = true;
|
|
13561
|
+
}
|
|
13562
|
+
if (!wasOpen) {
|
|
13563
|
+
requestAnimationFrame(() => {
|
|
13564
|
+
if (!this.open) return;
|
|
12897
13565
|
this._previouslyFocused = document.activeElement;
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
13566
|
+
const first = firstLightFocusable(this) ?? this.root.querySelector(".panel");
|
|
13567
|
+
first?.focus();
|
|
13568
|
+
});
|
|
13569
|
+
}
|
|
13570
|
+
} else {
|
|
13571
|
+
this._release();
|
|
13572
|
+
}
|
|
13573
|
+
}
|
|
13574
|
+
/** Desfaz scroll lock e restaura o foco ao fechar/desconectar. */
|
|
13575
|
+
_release() {
|
|
13576
|
+
registerModalClose(this);
|
|
13577
|
+
if (this._locked) {
|
|
13578
|
+
unlockBodyScroll();
|
|
13579
|
+
this._locked = false;
|
|
12901
13580
|
}
|
|
13581
|
+
this._restoreFocus();
|
|
12902
13582
|
}
|
|
12903
13583
|
connectedCallback() {
|
|
12904
13584
|
super.connectedCallback?.();
|
|
12905
13585
|
this._onKeydown = (e) => {
|
|
12906
|
-
if (
|
|
13586
|
+
if (!this.open) return;
|
|
13587
|
+
if (e.key === "Escape") {
|
|
13588
|
+
if (!isTopmostModal(this)) return;
|
|
12907
13589
|
this._close();
|
|
12908
|
-
} else if (e.key === "Tab"
|
|
13590
|
+
} else if (e.key === "Tab") {
|
|
12909
13591
|
this._trapFocus(e);
|
|
12910
13592
|
}
|
|
12911
13593
|
};
|
|
@@ -12913,26 +13595,24 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12913
13595
|
}
|
|
12914
13596
|
disconnectedCallback() {
|
|
12915
13597
|
document.removeEventListener("keydown", this._onKeydown);
|
|
12916
|
-
this.
|
|
13598
|
+
this._release();
|
|
12917
13599
|
super.disconnectedCallback?.();
|
|
12918
13600
|
}
|
|
12919
|
-
/** Mantém o foco dentro do drawer (WCAG 2.4.3 / 2.1.2). */
|
|
13601
|
+
/** Mantém o foco dentro do drawer (WCAG 2.4.3 / 2.1.2), incluindo o slot. */
|
|
12920
13602
|
_trapFocus(e) {
|
|
12921
13603
|
const panel = this.root.querySelector(".panel");
|
|
12922
13604
|
if (!panel) return;
|
|
12923
|
-
const focusables = panel
|
|
12924
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
12925
|
-
);
|
|
13605
|
+
const focusables = modalFocusables(panel, this);
|
|
12926
13606
|
if (!focusables.length) return;
|
|
12927
13607
|
const first = focusables[0];
|
|
12928
13608
|
const last = focusables[focusables.length - 1];
|
|
12929
|
-
const active = this.root.activeElement;
|
|
12930
|
-
if (e.shiftKey && (active === first || active === panel)) {
|
|
13609
|
+
const active = this.root.activeElement ?? document.activeElement;
|
|
13610
|
+
if (e.shiftKey && (active === first || active === panel || active === this)) {
|
|
12931
13611
|
e.preventDefault();
|
|
12932
|
-
last
|
|
13612
|
+
last?.focus();
|
|
12933
13613
|
} else if (!e.shiftKey && active === last) {
|
|
12934
13614
|
e.preventDefault();
|
|
12935
|
-
first
|
|
13615
|
+
first?.focus();
|
|
12936
13616
|
}
|
|
12937
13617
|
}
|
|
12938
13618
|
_restoreFocus() {
|
|
@@ -13101,17 +13781,16 @@ const _FxPagination = class _FxPagination extends FxElement {
|
|
|
13101
13781
|
const pages = this.pages;
|
|
13102
13782
|
const current = Math.min(Math.max(1, this.page), pages);
|
|
13103
13783
|
const opts = (this.getAttr("rows-options", "5,10,20,50") || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
13104
|
-
const
|
|
13105
|
-
|
|
13106
|
-
|
|
13784
|
+
const pageButtons = pageRange(current, pages).map(
|
|
13785
|
+
(item) => item === "ellipsis" ? `<span class="ellipsis" part="ellipsis" aria-hidden="true">…</span>` : `<button type="button" class="nav ${item === current ? "active" : ""}" part="page" data-go="${item}"${item === current ? ' aria-current="page"' : ""}>${item}</button>`
|
|
13786
|
+
).join("");
|
|
13787
|
+
const from = (current - 1) * this.rows + 1;
|
|
13107
13788
|
const to = Math.min(current * this.rows, this.total);
|
|
13108
13789
|
this.setTemplate(`
|
|
13109
13790
|
<span class="info">${this.total ? `${from}-${to} de ${this.total}` : "0 itens"}</span>
|
|
13110
13791
|
<button type="button" class="nav" part="first" aria-label="Primeira página" data-go="1" ${current <= 1 ? "disabled" : ""}>${fenixIconHtml("first_page")}</button>
|
|
13111
13792
|
<button type="button" class="nav" part="prev" aria-label="Página anterior" data-go="${current - 1}" ${current <= 1 ? "disabled" : ""}>${fenixIconHtml("navigate_before")}</button>
|
|
13112
|
-
${
|
|
13113
|
-
${window_.map((p) => `<button type="button" class="nav ${p === current ? "active" : ""}" data-go="${p}">${p}</button>`).join("")}
|
|
13114
|
-
${window_[window_.length - 1] !== pages && pages > 5 ? `<button type="button" class="nav" data-go="${pages}">${pages}</button>` : ""}
|
|
13793
|
+
${pageButtons}
|
|
13115
13794
|
<button type="button" class="nav" part="next" aria-label="Próxima página" data-go="${current + 1}" ${current >= pages ? "disabled" : ""}>${fenixIconHtml("navigate_next")}</button>
|
|
13116
13795
|
<button type="button" class="nav" part="last" aria-label="Última página" data-go="${pages}" ${current >= pages ? "disabled" : ""}>${fenixIconHtml("last_page")}</button>
|
|
13117
13796
|
${opts.length ? `
|
|
@@ -13119,9 +13798,6 @@ const _FxPagination = class _FxPagination extends FxElement {
|
|
|
13119
13798
|
${opts.map((o) => `<option value="${esc(o)}">${esc(o)}</option>`).join("")}
|
|
13120
13799
|
</fx-select>` : ""}
|
|
13121
13800
|
`);
|
|
13122
|
-
function total0(page, rows) {
|
|
13123
|
-
return (page - 1) * rows + 1;
|
|
13124
|
-
}
|
|
13125
13801
|
this.root.querySelectorAll(".nav[data-go]").forEach((btn) => {
|
|
13126
13802
|
btn.addEventListener("click", () => {
|
|
13127
13803
|
const target = Number(btn.dataset.go);
|
|
@@ -13153,6 +13829,7 @@ _FxPagination.styles = css`
|
|
|
13153
13829
|
:host {
|
|
13154
13830
|
display: flex;
|
|
13155
13831
|
align-items: center;
|
|
13832
|
+
flex-wrap: wrap;
|
|
13156
13833
|
gap: var(--fx-space-sm);
|
|
13157
13834
|
font-family: var(--fx-font-family);
|
|
13158
13835
|
font-size: var(--fx-font-size);
|
|
@@ -13190,6 +13867,18 @@ _FxPagination.styles = css`
|
|
|
13190
13867
|
color: #fff;
|
|
13191
13868
|
font-weight: 600;
|
|
13192
13869
|
}
|
|
13870
|
+
/* Elipse entre a janela de páginas e as bordas: ocupa o mesmo espaço de
|
|
13871
|
+
um botão (para o rodapé não "pular" ao navegar) mas não é clicável. */
|
|
13872
|
+
.ellipsis {
|
|
13873
|
+
display: inline-flex;
|
|
13874
|
+
align-items: center;
|
|
13875
|
+
justify-content: center;
|
|
13876
|
+
min-width: var(--fx-size-sm);
|
|
13877
|
+
height: var(--fx-size-sm);
|
|
13878
|
+
color: var(--fx-text-muted);
|
|
13879
|
+
font-size: calc(var(--fx-font-size) - 2px);
|
|
13880
|
+
user-select: none;
|
|
13881
|
+
}
|
|
13193
13882
|
.info { color: var(--fx-text-muted); font-size: calc(var(--fx-font-size) - 2px); }
|
|
13194
13883
|
fx-select { vertical-align: middle; }
|
|
13195
13884
|
fx-select::part(trigger) {
|
|
@@ -13216,11 +13905,17 @@ const _FxAutocomplete = class _FxAutocomplete extends FxFormElement {
|
|
|
13216
13905
|
set size(v) {
|
|
13217
13906
|
this.setAttribute("size", v);
|
|
13218
13907
|
}
|
|
13908
|
+
get disabled() {
|
|
13909
|
+
return this.hasAttr("disabled");
|
|
13910
|
+
}
|
|
13911
|
+
set disabled(v) {
|
|
13912
|
+
this.toggleAttr("disabled", Boolean(v));
|
|
13913
|
+
}
|
|
13219
13914
|
get value() {
|
|
13220
13915
|
return this.getAttr("value");
|
|
13221
13916
|
}
|
|
13222
13917
|
set value(v) {
|
|
13223
|
-
this.setAttribute("value", v);
|
|
13918
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
13224
13919
|
this.syncInternalControl();
|
|
13225
13920
|
this.updateFormValue();
|
|
13226
13921
|
}
|
|
@@ -13525,8 +14220,8 @@ const _FxKnob = class _FxKnob extends FxFormElement {
|
|
|
13525
14220
|
const min = this.min;
|
|
13526
14221
|
const max = this.max;
|
|
13527
14222
|
const value = this.clampValue(this.value);
|
|
13528
|
-
const
|
|
13529
|
-
const pct =
|
|
14223
|
+
const range2 = max - min;
|
|
14224
|
+
const pct = range2 > 0 ? (value - min) / range2 : 0;
|
|
13530
14225
|
const sizeNum = this.getSizeNum();
|
|
13531
14226
|
const strokeW = this.strokeWidth;
|
|
13532
14227
|
const radius = (sizeNum - strokeW) / 2;
|
|
@@ -13754,6 +14449,7 @@ const _FxOrderList = class _FxOrderList extends FxElement {
|
|
|
13754
14449
|
static get observedAttributes() {
|
|
13755
14450
|
return [
|
|
13756
14451
|
"data",
|
|
14452
|
+
"items",
|
|
13757
14453
|
"data-key",
|
|
13758
14454
|
"filter",
|
|
13759
14455
|
"filter-by",
|
|
@@ -13767,13 +14463,35 @@ const _FxOrderList = class _FxOrderList extends FxElement {
|
|
|
13767
14463
|
];
|
|
13768
14464
|
}
|
|
13769
14465
|
set data(value) {
|
|
13770
|
-
this._data = value;
|
|
14466
|
+
this._data = Array.isArray(value) ? value : [];
|
|
13771
14467
|
this._filteredData = null;
|
|
13772
14468
|
this.render();
|
|
13773
14469
|
}
|
|
13774
14470
|
get data() {
|
|
13775
14471
|
return this._data;
|
|
13776
14472
|
}
|
|
14473
|
+
/**
|
|
14474
|
+
* Alias de `data` para o binding do Vue/React: `data` colide com a
|
|
14475
|
+
* tipagem nativa de atributos HTML (`dataset`/`data-*`) e não reflete em
|
|
14476
|
+
* atributo. `items` funciona como propriedade e como atributo JSON.
|
|
14477
|
+
*/
|
|
14478
|
+
get items() {
|
|
14479
|
+
return this._data;
|
|
14480
|
+
}
|
|
14481
|
+
set items(value) {
|
|
14482
|
+
const list = Array.isArray(value) ? value : [];
|
|
14483
|
+
this._data = list;
|
|
14484
|
+
this._filteredData = null;
|
|
14485
|
+
const json = (() => {
|
|
14486
|
+
try {
|
|
14487
|
+
return JSON.stringify(list);
|
|
14488
|
+
} catch {
|
|
14489
|
+
return null;
|
|
14490
|
+
}
|
|
14491
|
+
})();
|
|
14492
|
+
if (json !== null && this.getAttr("items") !== json) this.setAttribute("items", json);
|
|
14493
|
+
else if (this.isConnected) this.render();
|
|
14494
|
+
}
|
|
13777
14495
|
set selection(value) {
|
|
13778
14496
|
this._selection = value;
|
|
13779
14497
|
this.render();
|
|
@@ -13853,7 +14571,16 @@ const _FxOrderList = class _FxOrderList extends FxElement {
|
|
|
13853
14571
|
super.connectedCallback();
|
|
13854
14572
|
}
|
|
13855
14573
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
13856
|
-
if (
|
|
14574
|
+
if (name === "items") {
|
|
14575
|
+
try {
|
|
14576
|
+
const parsed = JSON.parse(newValue ?? "[]");
|
|
14577
|
+
this._data = Array.isArray(parsed) ? parsed : [];
|
|
14578
|
+
this._filteredData = null;
|
|
14579
|
+
} catch {
|
|
14580
|
+
this._data = [];
|
|
14581
|
+
this._filteredData = null;
|
|
14582
|
+
}
|
|
14583
|
+
} else if (this.getAttr("data", "") !== "") {
|
|
13857
14584
|
this._parseDataAttribute();
|
|
13858
14585
|
}
|
|
13859
14586
|
super.attributeChangedCallback(name, oldValue, newValue);
|