@wrrdev/fenix-ui 1.1.16 → 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/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 +106 -21
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tabs/tabs.d.ts +6 -0
- package/dist/components/tabs/tabs.d.ts.map +1 -1
- package/dist/components/tabs/tabs.js +34 -0
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/components/textarea/textarea.d.ts +5 -1
- package/dist/components/textarea/textarea.d.ts.map +1 -1
- package/dist/components/textarea/textarea.js +25 -6
- package/dist/components/textarea/textarea.js.map +1 -1
- package/dist/core/jsx.d.ts +11 -5
- package/dist/core/jsx.d.ts.map +1 -1
- package/dist/core/jsx.js.map +1 -1
- package/dist/core/modal.d.ts +31 -0
- package/dist/core/modal.d.ts.map +1 -0
- package/dist/core/modal.js +61 -0
- package/dist/core/modal.js.map +1 -0
- package/dist/docs/componentes/input.doc.d.ts.map +1 -1
- package/dist/docs/componentes/orderlist.doc.d.ts.map +1 -1
- package/dist/fenix-ui.umd.min.js +232 -222
- package/dist/fenix-ui4.esm.js +816 -151
- 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;
|
|
@@ -3805,6 +4106,7 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3805
4106
|
this.sortDir = 1;
|
|
3806
4107
|
this.filters = {};
|
|
3807
4108
|
this.globalSearch = "";
|
|
4109
|
+
this._boundToolbarInputs = /* @__PURE__ */ new WeakSet();
|
|
3808
4110
|
this._shellKey = "";
|
|
3809
4111
|
}
|
|
3810
4112
|
static get observedAttributes() {
|
|
@@ -3852,6 +4154,44 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3852
4154
|
get lazy() {
|
|
3853
4155
|
return this.hasAttr("lazy");
|
|
3854
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
|
+
}
|
|
3855
4195
|
get columns() {
|
|
3856
4196
|
return [...this.querySelectorAll("fx-column")].map((c) => {
|
|
3857
4197
|
const tpl = this._resolveCellTemplate(c);
|
|
@@ -3935,7 +4275,14 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
3935
4275
|
loadFenixIconsFont();
|
|
3936
4276
|
super.connectedCallback();
|
|
3937
4277
|
this._columnObserver = new MutationObserver(() => this.render());
|
|
3938
|
-
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
|
+
});
|
|
3939
4286
|
}
|
|
3940
4287
|
disconnectedCallback() {
|
|
3941
4288
|
this._columnObserver?.disconnect();
|
|
@@ -4017,7 +4364,14 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4017
4364
|
const pages = Math.max(1, Math.ceil(total / this.rowsPerPage));
|
|
4018
4365
|
if (this.page >= pages) this.page = pages - 1;
|
|
4019
4366
|
const toolbar = this.toolbarTemplate;
|
|
4020
|
-
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>` : "";
|
|
4021
4375
|
const headHtml = cols.map((c) => {
|
|
4022
4376
|
const isSorted = this.sortField === c.field;
|
|
4023
4377
|
const ind = isSorted ? this.sortDir === 1 ? "▲" : "▼" : c.sortable ? "⇅" : "";
|
|
@@ -4040,7 +4394,7 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4040
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>
|
|
4041
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>
|
|
4042
4396
|
<label class="info">${esc("Por página:")}
|
|
4043
|
-
<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(
|
|
4044
4398
|
"rows-options",
|
|
4045
4399
|
"5,10,20,50"
|
|
4046
4400
|
).split(",").map((n) => n.trim()).map((n) => `<option value="${esc(n)}">${esc(n)}</option>`).join("")}</fx-select>
|
|
@@ -4061,13 +4415,15 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4061
4415
|
if (tbody) {
|
|
4062
4416
|
tbody.innerHTML = bodyHtml;
|
|
4063
4417
|
this._bindRowClicks(rows);
|
|
4418
|
+
this._bindSlottedToolbarSearch(toolbar);
|
|
4419
|
+
this._restoreToolbarSearch();
|
|
4064
4420
|
return;
|
|
4065
4421
|
}
|
|
4066
4422
|
}
|
|
4067
4423
|
this._shellKey = shellKey;
|
|
4068
4424
|
this.setTemplate(`
|
|
4069
4425
|
${toolbarHtml}
|
|
4070
|
-
<div class="frame${
|
|
4426
|
+
<div class="frame${toolbarHasContent ? " has-toolbar" : ""}" part="frame">
|
|
4071
4427
|
<div class="table-wrap">
|
|
4072
4428
|
<div class="scroll">
|
|
4073
4429
|
<table part="table">
|
|
@@ -4128,21 +4484,10 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4128
4484
|
});
|
|
4129
4485
|
inp.addEventListener("click", (e) => e.stopPropagation());
|
|
4130
4486
|
});
|
|
4131
|
-
this.root.querySelectorAll("[data-search-fields]").forEach((inp) =>
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
const pos = inp.selectionStart;
|
|
4136
|
-
this.render();
|
|
4137
|
-
const again = this.root.querySelector(
|
|
4138
|
-
"[data-search-fields]"
|
|
4139
|
-
);
|
|
4140
|
-
if (again) {
|
|
4141
|
-
again.focus();
|
|
4142
|
-
again.setSelectionRange(pos, pos);
|
|
4143
|
-
}
|
|
4144
|
-
});
|
|
4145
|
-
});
|
|
4487
|
+
this.root.querySelectorAll("[data-search-fields]").forEach((inp) => this._bindSearchInput(inp, false));
|
|
4488
|
+
if (toolbarSlotted && toolbar) {
|
|
4489
|
+
this._bindSlottedToolbarSearch(toolbar);
|
|
4490
|
+
}
|
|
4146
4491
|
this.root.querySelectorAll(".pg-btn").forEach((btn) => {
|
|
4147
4492
|
btn.addEventListener("click", () => {
|
|
4148
4493
|
const pg = btn.dataset.pg;
|
|
@@ -4195,11 +4540,47 @@ const _FxTable = class _FxTable extends FxElement {
|
|
|
4195
4540
|
});
|
|
4196
4541
|
});
|
|
4197
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
|
+
}
|
|
4198
4571
|
_restoreToolbarSearch() {
|
|
4199
|
-
const
|
|
4572
|
+
const shadow = this.root.querySelector(
|
|
4200
4573
|
"[data-search-fields]"
|
|
4201
4574
|
);
|
|
4202
|
-
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
|
+
}
|
|
4203
4584
|
}
|
|
4204
4585
|
};
|
|
4205
4586
|
_FxTable.styles = css`
|
|
@@ -4455,6 +4836,11 @@ _FxTable.styles = css`
|
|
|
4455
4836
|
.toolbar:empty {
|
|
4456
4837
|
display: none;
|
|
4457
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
|
+
}
|
|
4458
4844
|
.loading-overlay {
|
|
4459
4845
|
position: absolute;
|
|
4460
4846
|
inset: 0;
|
|
@@ -4505,7 +4891,51 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
|
|
|
4505
4891
|
this._labelClickBound = false;
|
|
4506
4892
|
}
|
|
4507
4893
|
static get observedAttributes() {
|
|
4508
|
-
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 });
|
|
4509
4939
|
}
|
|
4510
4940
|
render() {
|
|
4511
4941
|
const control = this.findControl();
|
|
@@ -4549,6 +4979,8 @@ const _FxFloatlabel = class _FxFloatlabel extends FxElement {
|
|
|
4549
4979
|
disconnectedCallback() {
|
|
4550
4980
|
super.disconnectedCallback();
|
|
4551
4981
|
this.observer?.disconnect();
|
|
4982
|
+
this._labelObserver?.disconnect();
|
|
4983
|
+
this._labelObserver = void 0;
|
|
4552
4984
|
}
|
|
4553
4985
|
/** Campo de controle = qualquer filho direto que não seja label. */
|
|
4554
4986
|
findControl() {
|
|
@@ -4838,7 +5270,7 @@ function defineFxFloatlabel() {
|
|
|
4838
5270
|
defineFxFloatlabel();
|
|
4839
5271
|
const _FxTextarea = class _FxTextarea extends FxFormElement {
|
|
4840
5272
|
static get observedAttributes() {
|
|
4841
|
-
return ["size", "placeholder", "disabled", "readonly", "rows", "maxlength", "error", "invalid", "success", "valid"];
|
|
5273
|
+
return ["size", "placeholder", "disabled", "readonly", "full", "rows", "maxlength", "error", "invalid", "success", "valid"];
|
|
4842
5274
|
}
|
|
4843
5275
|
get size() {
|
|
4844
5276
|
const s = this.getAttr("size", "md");
|
|
@@ -4851,10 +5283,22 @@ const _FxTextarea = class _FxTextarea extends FxFormElement {
|
|
|
4851
5283
|
return this.getAttr("value");
|
|
4852
5284
|
}
|
|
4853
5285
|
set value(value) {
|
|
4854
|
-
this.setAttribute("value", value);
|
|
5286
|
+
this.setAttribute("value", value == null ? "" : String(value));
|
|
4855
5287
|
this.syncInternalControl();
|
|
4856
5288
|
this.updateFormValue();
|
|
4857
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
|
+
}
|
|
4858
5302
|
render() {
|
|
4859
5303
|
const placeholder = this.getAttr("placeholder");
|
|
4860
5304
|
const readonly = this.hasAttr("readonly");
|
|
@@ -4871,20 +5315,27 @@ const _FxTextarea = class _FxTextarea extends FxFormElement {
|
|
|
4871
5315
|
field.value = this.getAttr("value");
|
|
4872
5316
|
if (disabled) field.setAttribute("disabled", "");
|
|
4873
5317
|
if (readonly) field.setAttribute("readonly", "");
|
|
4874
|
-
const
|
|
5318
|
+
const notifyInput = () => {
|
|
4875
5319
|
this.value = field.value;
|
|
4876
5320
|
this.emitValueEvents();
|
|
5321
|
+
};
|
|
5322
|
+
const notifyChange = () => {
|
|
5323
|
+
this.value = field.value;
|
|
4877
5324
|
this.dispatchEvent(
|
|
4878
|
-
new CustomEvent(
|
|
5325
|
+
new CustomEvent("change", {
|
|
5326
|
+
bubbles: true,
|
|
5327
|
+
composed: true,
|
|
5328
|
+
detail: { value: field.value }
|
|
5329
|
+
})
|
|
4879
5330
|
);
|
|
4880
5331
|
};
|
|
4881
5332
|
field.addEventListener("input", (e) => {
|
|
4882
5333
|
e.stopPropagation();
|
|
4883
|
-
|
|
5334
|
+
notifyInput();
|
|
4884
5335
|
});
|
|
4885
5336
|
field.addEventListener("change", (e) => {
|
|
4886
5337
|
e.stopPropagation();
|
|
4887
|
-
|
|
5338
|
+
notifyChange();
|
|
4888
5339
|
});
|
|
4889
5340
|
this.updateFormValue();
|
|
4890
5341
|
}
|
|
@@ -8530,10 +8981,61 @@ function defineFxEditor() {
|
|
|
8530
8981
|
return defineElement("fx-editor", FxEditor);
|
|
8531
8982
|
}
|
|
8532
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
|
+
}
|
|
8533
9034
|
const _FxDialog = class _FxDialog extends FxElement {
|
|
8534
9035
|
constructor() {
|
|
8535
9036
|
super(...arguments);
|
|
8536
9037
|
this._previouslyFocused = null;
|
|
9038
|
+
this._locked = false;
|
|
8537
9039
|
}
|
|
8538
9040
|
static get observedAttributes() {
|
|
8539
9041
|
return ["open", "size", "heading"];
|
|
@@ -8547,6 +9049,11 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8547
9049
|
render() {
|
|
8548
9050
|
const isOpen = this.open;
|
|
8549
9051
|
const heading = this.getAttr("heading");
|
|
9052
|
+
if (!isOpen) {
|
|
9053
|
+
this._release();
|
|
9054
|
+
this.setTemplate("");
|
|
9055
|
+
return;
|
|
9056
|
+
}
|
|
8550
9057
|
this.setTemplate(`
|
|
8551
9058
|
<div class="overlay" part="overlay" ${isOpen ? "" : "hidden"}>
|
|
8552
9059
|
<div class="dialog" role="dialog" aria-modal="true" aria-label="${esc(heading)}" tabindex="-1">
|
|
@@ -8565,10 +9072,8 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8565
9072
|
const overlay = this.root.querySelector(".overlay");
|
|
8566
9073
|
const closeBtn = this.root.querySelector(".close");
|
|
8567
9074
|
const close = () => {
|
|
9075
|
+
if (!this.open) return;
|
|
8568
9076
|
this.open = false;
|
|
8569
|
-
this._cleanup?.();
|
|
8570
|
-
this._cleanup = void 0;
|
|
8571
|
-
this._restoreFocus();
|
|
8572
9077
|
this.dispatchEvent(new CustomEvent("close", { bubbles: true, composed: true }));
|
|
8573
9078
|
};
|
|
8574
9079
|
overlay?.addEventListener("click", (e) => {
|
|
@@ -8577,6 +9082,7 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8577
9082
|
closeBtn?.addEventListener("click", close);
|
|
8578
9083
|
const onKey = (e) => {
|
|
8579
9084
|
if (e.key === "Escape") {
|
|
9085
|
+
if (!isTopmostModal(this)) return;
|
|
8580
9086
|
e.stopPropagation();
|
|
8581
9087
|
close();
|
|
8582
9088
|
return;
|
|
@@ -8585,29 +9091,48 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8585
9091
|
};
|
|
8586
9092
|
document.addEventListener("keydown", onKey);
|
|
8587
9093
|
this._cleanup = () => document.removeEventListener("keydown", onKey);
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
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
|
+
}
|
|
8592
9108
|
this.dispatchEvent(new CustomEvent("open", { bubbles: true, composed: true }));
|
|
8593
9109
|
}
|
|
8594
|
-
/**
|
|
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. */
|
|
8595
9122
|
_trapFocus(e) {
|
|
8596
9123
|
const dialog = this.root.querySelector(".dialog");
|
|
8597
9124
|
if (!dialog) return;
|
|
8598
|
-
const focusables = dialog
|
|
8599
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
8600
|
-
);
|
|
9125
|
+
const focusables = modalFocusables(dialog, this);
|
|
8601
9126
|
if (!focusables.length) return;
|
|
8602
9127
|
const first = focusables[0];
|
|
8603
9128
|
const last = focusables[focusables.length - 1];
|
|
8604
|
-
const active = this.root.activeElement;
|
|
8605
|
-
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)) {
|
|
8606
9131
|
e.preventDefault();
|
|
8607
|
-
last
|
|
9132
|
+
last?.focus();
|
|
8608
9133
|
} else if (!e.shiftKey && active === last) {
|
|
8609
9134
|
e.preventDefault();
|
|
8610
|
-
first
|
|
9135
|
+
first?.focus();
|
|
8611
9136
|
}
|
|
8612
9137
|
}
|
|
8613
9138
|
_restoreFocus() {
|
|
@@ -8616,9 +9141,7 @@ const _FxDialog = class _FxDialog extends FxElement {
|
|
|
8616
9141
|
}
|
|
8617
9142
|
disconnectedCallback() {
|
|
8618
9143
|
super.disconnectedCallback();
|
|
8619
|
-
this.
|
|
8620
|
-
this._cleanup = void 0;
|
|
8621
|
-
this._restoreFocus();
|
|
9144
|
+
this._release();
|
|
8622
9145
|
}
|
|
8623
9146
|
};
|
|
8624
9147
|
_FxDialog.styles = css`
|
|
@@ -9024,7 +9547,35 @@ const _FxFileUpload = class _FxFileUpload extends FxFormElement {
|
|
|
9024
9547
|
return this.getAttr("value");
|
|
9025
9548
|
}
|
|
9026
9549
|
set value(v) {
|
|
9027
|
-
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));
|
|
9028
9579
|
}
|
|
9029
9580
|
get progress() {
|
|
9030
9581
|
return Math.min(100, Math.max(0, Number(this.getAttr("progress", "0")) || 0));
|
|
@@ -9705,6 +10256,9 @@ const _FxAvatar = class _FxAvatar extends FxElement {
|
|
|
9705
10256
|
get variant() {
|
|
9706
10257
|
return this.getAttr("variant", "text");
|
|
9707
10258
|
}
|
|
10259
|
+
set variant(v) {
|
|
10260
|
+
this.setAttribute("variant", v);
|
|
10261
|
+
}
|
|
9708
10262
|
get src() {
|
|
9709
10263
|
return this.getAttr("src");
|
|
9710
10264
|
}
|
|
@@ -9808,6 +10362,18 @@ const _FxIcon = class _FxIcon extends FxElement {
|
|
|
9808
10362
|
if (value) this.setAttribute("label", value);
|
|
9809
10363
|
else this.removeAttribute("label");
|
|
9810
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
|
+
}
|
|
9811
10377
|
connectedCallback() {
|
|
9812
10378
|
loadFenixIconsFont();
|
|
9813
10379
|
super.connectedCallback();
|
|
@@ -9854,6 +10420,12 @@ const _FxCard = class _FxCard extends FxElement {
|
|
|
9854
10420
|
static get observedAttributes() {
|
|
9855
10421
|
return ["variant", "radius", "size", "padded", "heading"];
|
|
9856
10422
|
}
|
|
10423
|
+
get padded() {
|
|
10424
|
+
return this.hasAttr("padded");
|
|
10425
|
+
}
|
|
10426
|
+
set padded(value) {
|
|
10427
|
+
this.toggleAttr("padded", Boolean(value));
|
|
10428
|
+
}
|
|
9857
10429
|
render() {
|
|
9858
10430
|
const heading = this.getAttr("heading");
|
|
9859
10431
|
this.setTemplate(`
|
|
@@ -12374,6 +12946,40 @@ const _FxTabs = class _FxTabs extends FxElement {
|
|
|
12374
12946
|
set value(value) {
|
|
12375
12947
|
this.setAttribute("value", value);
|
|
12376
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
|
+
}
|
|
12377
12983
|
render() {
|
|
12378
12984
|
const tabs = Array.from(this.querySelectorAll("fx-tab"));
|
|
12379
12985
|
const active = this.value || tabs[0]?.getAttribute("tab") || "";
|
|
@@ -12902,10 +13508,11 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12902
13508
|
this._onKeydown = () => {
|
|
12903
13509
|
};
|
|
12904
13510
|
this._previouslyFocused = null;
|
|
13511
|
+
this._locked = false;
|
|
13512
|
+
this._wasOpen = false;
|
|
12905
13513
|
this._close = () => {
|
|
12906
13514
|
if (!this.open) return;
|
|
12907
13515
|
this.open = false;
|
|
12908
|
-
this._restoreFocus();
|
|
12909
13516
|
this.dispatchEvent(
|
|
12910
13517
|
new CustomEvent("close", { bubbles: true, composed: true })
|
|
12911
13518
|
);
|
|
@@ -12935,6 +13542,8 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12935
13542
|
}
|
|
12936
13543
|
render() {
|
|
12937
13544
|
const title = this.getAttr("title");
|
|
13545
|
+
const wasOpen = this._wasOpen;
|
|
13546
|
+
this._wasOpen = this.open;
|
|
12938
13547
|
this.setTemplate(`
|
|
12939
13548
|
<div class="overlay" part="overlay"></div>
|
|
12940
13549
|
<div class="panel" part="panel" role="dialog" aria-modal="true" aria-label="${esc(title || "Drawer")}" tabindex="-1">
|
|
@@ -12945,20 +13554,40 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12945
13554
|
this.root.querySelector(".overlay")?.addEventListener("click", this._close);
|
|
12946
13555
|
this.root.querySelector(".close")?.addEventListener("click", this._close);
|
|
12947
13556
|
if (this.open) {
|
|
12948
|
-
|
|
12949
|
-
|
|
13557
|
+
if (!this._locked) {
|
|
13558
|
+
lockBodyScroll();
|
|
13559
|
+
registerModalOpen(this);
|
|
13560
|
+
this._locked = true;
|
|
13561
|
+
}
|
|
13562
|
+
if (!wasOpen) {
|
|
13563
|
+
requestAnimationFrame(() => {
|
|
13564
|
+
if (!this.open) return;
|
|
12950
13565
|
this._previouslyFocused = document.activeElement;
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
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;
|
|
12954
13580
|
}
|
|
13581
|
+
this._restoreFocus();
|
|
12955
13582
|
}
|
|
12956
13583
|
connectedCallback() {
|
|
12957
13584
|
super.connectedCallback?.();
|
|
12958
13585
|
this._onKeydown = (e) => {
|
|
12959
|
-
if (
|
|
13586
|
+
if (!this.open) return;
|
|
13587
|
+
if (e.key === "Escape") {
|
|
13588
|
+
if (!isTopmostModal(this)) return;
|
|
12960
13589
|
this._close();
|
|
12961
|
-
} else if (e.key === "Tab"
|
|
13590
|
+
} else if (e.key === "Tab") {
|
|
12962
13591
|
this._trapFocus(e);
|
|
12963
13592
|
}
|
|
12964
13593
|
};
|
|
@@ -12966,26 +13595,24 @@ const _FxDrawer = class _FxDrawer extends FxElement {
|
|
|
12966
13595
|
}
|
|
12967
13596
|
disconnectedCallback() {
|
|
12968
13597
|
document.removeEventListener("keydown", this._onKeydown);
|
|
12969
|
-
this.
|
|
13598
|
+
this._release();
|
|
12970
13599
|
super.disconnectedCallback?.();
|
|
12971
13600
|
}
|
|
12972
|
-
/** 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. */
|
|
12973
13602
|
_trapFocus(e) {
|
|
12974
13603
|
const panel = this.root.querySelector(".panel");
|
|
12975
13604
|
if (!panel) return;
|
|
12976
|
-
const focusables = panel
|
|
12977
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
12978
|
-
);
|
|
13605
|
+
const focusables = modalFocusables(panel, this);
|
|
12979
13606
|
if (!focusables.length) return;
|
|
12980
13607
|
const first = focusables[0];
|
|
12981
13608
|
const last = focusables[focusables.length - 1];
|
|
12982
|
-
const active = this.root.activeElement;
|
|
12983
|
-
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)) {
|
|
12984
13611
|
e.preventDefault();
|
|
12985
|
-
last
|
|
13612
|
+
last?.focus();
|
|
12986
13613
|
} else if (!e.shiftKey && active === last) {
|
|
12987
13614
|
e.preventDefault();
|
|
12988
|
-
first
|
|
13615
|
+
first?.focus();
|
|
12989
13616
|
}
|
|
12990
13617
|
}
|
|
12991
13618
|
_restoreFocus() {
|
|
@@ -13278,11 +13905,17 @@ const _FxAutocomplete = class _FxAutocomplete extends FxFormElement {
|
|
|
13278
13905
|
set size(v) {
|
|
13279
13906
|
this.setAttribute("size", v);
|
|
13280
13907
|
}
|
|
13908
|
+
get disabled() {
|
|
13909
|
+
return this.hasAttr("disabled");
|
|
13910
|
+
}
|
|
13911
|
+
set disabled(v) {
|
|
13912
|
+
this.toggleAttr("disabled", Boolean(v));
|
|
13913
|
+
}
|
|
13281
13914
|
get value() {
|
|
13282
13915
|
return this.getAttr("value");
|
|
13283
13916
|
}
|
|
13284
13917
|
set value(v) {
|
|
13285
|
-
this.setAttribute("value", v);
|
|
13918
|
+
this.setAttribute("value", v == null ? "" : String(v));
|
|
13286
13919
|
this.syncInternalControl();
|
|
13287
13920
|
this.updateFormValue();
|
|
13288
13921
|
}
|
|
@@ -13816,6 +14449,7 @@ const _FxOrderList = class _FxOrderList extends FxElement {
|
|
|
13816
14449
|
static get observedAttributes() {
|
|
13817
14450
|
return [
|
|
13818
14451
|
"data",
|
|
14452
|
+
"items",
|
|
13819
14453
|
"data-key",
|
|
13820
14454
|
"filter",
|
|
13821
14455
|
"filter-by",
|
|
@@ -13829,13 +14463,35 @@ const _FxOrderList = class _FxOrderList extends FxElement {
|
|
|
13829
14463
|
];
|
|
13830
14464
|
}
|
|
13831
14465
|
set data(value) {
|
|
13832
|
-
this._data = value;
|
|
14466
|
+
this._data = Array.isArray(value) ? value : [];
|
|
13833
14467
|
this._filteredData = null;
|
|
13834
14468
|
this.render();
|
|
13835
14469
|
}
|
|
13836
14470
|
get data() {
|
|
13837
14471
|
return this._data;
|
|
13838
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
|
+
}
|
|
13839
14495
|
set selection(value) {
|
|
13840
14496
|
this._selection = value;
|
|
13841
14497
|
this.render();
|
|
@@ -13915,7 +14571,16 @@ const _FxOrderList = class _FxOrderList extends FxElement {
|
|
|
13915
14571
|
super.connectedCallback();
|
|
13916
14572
|
}
|
|
13917
14573
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
13918
|
-
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", "") !== "") {
|
|
13919
14584
|
this._parseDataAttribute();
|
|
13920
14585
|
}
|
|
13921
14586
|
super.attributeChangedCallback(name, oldValue, newValue);
|