@simai-studio/ui 5.20261021.26 → 5.20261022.1233

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +4 -4
  2. package/distr/component/dropdown/js/dropdown.min.js +1 -1
  3. package/distr/core/css/core.css +16 -11
  4. package/distr/core/css/core.min.css +1 -1
  5. package/distr/core/js/composition/sortable.mjs +39 -1
  6. package/distr/core/js/core-loader.js +133 -7
  7. package/distr/core/js/core-loader.min.js +1 -1
  8. package/distr/core/js/smart-base.js +122 -3
  9. package/distr/core/js/smart-base.min.js +1 -1
  10. package/distr/smart/buttons/js/buttons.min.js +8 -8
  11. package/distr/smart/buttons/smart.manifest.json +1 -1
  12. package/distr/smart/composition-overlay/smart.manifest.json +1 -1
  13. package/distr/smart/country-code/js/country-code.min.js +9 -7
  14. package/distr/smart/country-code/smart.manifest.json +6 -1
  15. package/distr/smart/country-code/template/default.js +12 -4
  16. package/distr/smart/datepicker/js/datepicker.min.js +12 -9
  17. package/distr/smart/datepicker/smart.manifest.json +13 -3
  18. package/distr/smart/datepicker/template/default.js +4 -1
  19. package/distr/smart/dropdown/js/dropdown.min.js +10 -10
  20. package/distr/smart/dropdown/smart.manifest.json +1 -1
  21. package/distr/smart/dropdown/template/default.js +10 -3
  22. package/distr/smart/field-renderers/js/field-renderers.min.js +17 -17
  23. package/distr/smart/icon-buttons/js/icon-buttons.min.js +1 -1
  24. package/distr/smart/icon-buttons/smart.manifest.json +1 -1
  25. package/distr/smart/icons/js/icons.min.js +1 -1
  26. package/distr/smart/icons/smart.manifest.json +1 -1
  27. package/distr/smart/inputs/js/inputs.min.js +33 -31
  28. package/distr/smart/inputs/smart.manifest.json +6 -1
  29. package/distr/smart/inputs/template/default.js +8 -0
  30. package/distr/smart/popover/js/popover.min.js +1 -1
  31. package/distr/smart/popover/smart.manifest.json +1 -1
  32. package/distr/smart/resizer/js/resizer.min.js +1 -1
  33. package/distr/smart/resizer/smart.manifest.json +2 -2
  34. package/distr/smart/sortable/js/sortable.min.js +1 -1
  35. package/distr/smart/sortable/smart.manifest.json +2 -2
  36. package/distr/smart/switch/js/switch.min.js +12 -11
  37. package/distr/smart/switch/smart.manifest.json +6 -1
  38. package/distr/smart/switch/template/default.js +2 -1
  39. package/distr/smart/theme-generator/js/theme-generator.min.js +1 -1
  40. package/distr/smart/theme-generator/smart.manifest.json +2 -2
  41. package/package-manifest.json +42 -42
  42. package/package.json +4 -4
@@ -165,6 +165,119 @@ function installIconSubsetRuntime(SFLoaderPlugin, {
165
165
  }
166
166
 
167
167
  return true;
168
+ }; // With the subset disabled the icons stylesheet draws from the full local
169
+ // font, and nothing ever marked an icon loaded: every readiness signal came
170
+ // from the subset path, so `.sf-icon:not(.sf-icon-loaded)` kept each icon at
171
+ // font-size 0 while the font sat loaded (ui-chat, 2026-10-22). An icon is
172
+ // loaded once the family it is drawn in is ready -- the same promise the
173
+ // subset path keeps, so the ligature text still never flashes.
174
+
175
+
176
+ const localIconFontFamilies = Object.freeze({
177
+ outlined: "Material Symbols Outlined",
178
+ rounded: "Material Symbols Rounded",
179
+ sharp: "Material Symbols Sharp"
180
+ });
181
+
182
+ SFLoaderPlugin.prototype.getLocalIconFontFaces = function (type = "") {
183
+ const family = localIconFontFamilies[this.normalizeIconType(type)] || localIconFontFamilies.outlined;
184
+ const faces = [];
185
+ document.fonts?.forEach?.(face => {
186
+ if (String(face.family || "").replace(/["']/g, "").trim() === family) {
187
+ faces.push(face);
188
+ }
189
+ });
190
+ return faces;
191
+ }; // Ready means a face of the family is declared and has loaded (or failed,
192
+ // which settles it too). A family that is not declared yet is not ready:
193
+ // the icons stylesheet arrives after the first scan, and asking the browser
194
+ // before it does answered «nothing to load» -- the ligature text then drew in
195
+ // a system font until the face arrived.
196
+
197
+
198
+ SFLoaderPlugin.prototype.isLocalIconFontReady = function (type = "") {
199
+ const normalizedType = this.normalizeIconType(type);
200
+ if (!this.localIconFontsReady) this.localIconFontsReady = new Set();
201
+ if (this.localIconFontsReady.has(normalizedType)) return true;
202
+ if (!document.fonts?.forEach) return true;
203
+ const settled = this.getLocalIconFontFaces(normalizedType).some(face => face.status === "loaded" || face.status === "error");
204
+
205
+ if (settled) {
206
+ this.localIconFontsReady.add(normalizedType);
207
+ return true;
208
+ }
209
+
210
+ this.waitForLocalIconFont(normalizedType);
211
+ return false;
212
+ };
213
+
214
+ SFLoaderPlugin.prototype.waitForLocalIconFont = function (type = "") {
215
+ const normalizedType = this.normalizeIconType(type);
216
+ if (!this.localIconFontWaits) this.localIconFontWaits = new Map();
217
+
218
+ if (this.localIconFontWaits.has(normalizedType)) {
219
+ return this.localIconFontWaits.get(normalizedType);
220
+ }
221
+
222
+ const fonts = document.fonts;
223
+ const wait = new Promise(resolve => {
224
+ let done = false;
225
+ let poll = 0;
226
+ let timer = 0;
227
+
228
+ const finish = () => {
229
+ if (done) return;
230
+ done = true;
231
+ clearInterval(poll);
232
+ clearTimeout(timer);
233
+ fonts.removeEventListener?.("loadingdone", check);
234
+ fonts.removeEventListener?.("loadingerror", check);
235
+ resolve();
236
+ };
237
+
238
+ const check = () => {
239
+ const faces = this.getLocalIconFontFaces(normalizedType);
240
+
241
+ if (faces.some(face => face.status === "loaded" || face.status === "error")) {
242
+ finish();
243
+ return;
244
+ } // An icon at font-size 0 may not make the browser fetch the face, so
245
+ // the face is asked for as soon as a stylesheet declares it.
246
+
247
+
248
+ faces.forEach(face => {
249
+ if (face.status === "unloaded") face.load().then(check, check);
250
+ });
251
+ };
252
+
253
+ fonts.addEventListener?.("loadingdone", check);
254
+ fonts.addEventListener?.("loadingerror", check); // A stylesheet that declares the face sends no font event; look for it.
255
+
256
+ poll = setInterval(check, 50); // A family no stylesheet ever declares (a rounded or sharp icon with no
257
+ // fallback face) still settles, the way subset-pending lifts on its
258
+ // timer: the icon then shows as it would with no Framework at all,
259
+ // instead of staying zero-sized for good.
260
+
261
+ timer = setTimeout(finish, 6000);
262
+ check();
263
+ }).then(() => {
264
+ this.localIconFontsReady.add(normalizedType);
265
+ this.iconFontReady = true;
266
+ document.body?.classList?.add("sf-icons-loaded");
267
+ this.syncStaticIconLoadedState();
268
+ window.dispatchEvent(new CustomEvent("sf-icons-subset:ready", {
269
+ detail: {
270
+ icons: new Set(),
271
+ all: true,
272
+ local: true,
273
+ types: [normalizedType],
274
+ loader: this
275
+ }
276
+ }));
277
+ return true;
278
+ });
279
+ this.localIconFontWaits.set(normalizedType, wait);
280
+ return wait;
168
281
  };
169
282
 
170
283
  SFLoaderPlugin.prototype.getDefaultIconType = function () {
@@ -608,6 +721,10 @@ function installIconSubsetRuntime(SFLoaderPlugin, {
608
721
 
609
722
  const loadAttrs = this.getIconLoadAttrs(tracked.attrs);
610
723
 
724
+ if (!this.isIconSubsetEnabled()) {
725
+ return this.isLocalIconFontReady(loadAttrs.type);
726
+ }
727
+
611
728
  if (this.isIconCoveredByManifest(tracked.icon, loadAttrs)) {
612
729
  return true;
613
730
  }
@@ -742,10 +859,16 @@ function installIconSubsetRuntime(SFLoaderPlugin, {
742
859
 
743
860
  SFLoaderPlugin.prototype.processStaticIconMutation = function (node, autoLoad = true) {
744
861
  if (!this.isIconSubsetEnabled()) {
745
- return {
862
+ const localIcon = this.getStaticIconElement(node);
863
+ if (!localIcon) return {
746
864
  handled: false,
747
865
  iconCount: 0
748
866
  };
867
+ this.syncStaticIconLoadedState(localIcon);
868
+ return {
869
+ handled: true,
870
+ iconCount: 0
871
+ };
749
872
  }
750
873
 
751
874
  const iconElement = this.getStaticIconElement(node);
@@ -786,7 +909,6 @@ function installIconSubsetRuntime(SFLoaderPlugin, {
786
909
  };
787
910
 
788
911
  SFLoaderPlugin.prototype.syncStaticIconLoadedState = function (root = null) {
789
- if (!this.isIconSubsetEnabled()) return;
790
912
  const host = root || document.body;
791
913
  if (!host) return;
792
914
 
@@ -835,21 +957,25 @@ function installIconSubsetRuntime(SFLoaderPlugin, {
835
957
  };
836
958
 
837
959
  SFLoaderPlugin.prototype.scanStaticIcons = function (root = null, autoLoad = true) {
838
- if (!this.isIconSubsetEnabled()) {
960
+ const host = root?.nodeType === Node.ELEMENT_NODE ? root : root?.parentElement || document.body;
961
+
962
+ if (!host) {
839
963
  return {
840
964
  iconCount: 0
841
965
  };
842
966
  }
843
967
 
844
- const host = root?.nodeType === Node.ELEMENT_NODE ? root : root?.parentElement || document.body;
845
-
846
- if (!host) {
968
+ if (this.shouldSkipNode(host)) {
847
969
  return {
848
970
  iconCount: 0
849
971
  };
850
972
  }
851
973
 
852
- if (this.shouldSkipNode(host)) {
974
+ if (!this.isIconSubsetEnabled()) {
975
+ // Ask for the default family now: an <sf-icon> drawn before this scan
976
+ // waits on the ready event this wait sends.
977
+ this.isLocalIconFontReady(this.getDefaultIconType());
978
+ this.syncStaticIconLoadedState(host);
853
979
  return {
854
980
  iconCount: 0
855
981
  };