@gears-frontx/react 0.2.0-alpha.2 → 0.2.0-alpha.4

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/dist/index.js CHANGED
@@ -1089,6 +1089,9 @@ function useRegisteredPackages() {
1089
1089
  // src/mfe/ThemeAwareReactLifecycle.tsx
1090
1090
  import { createRoot } from "react-dom/client";
1091
1091
  import { jsx as jsx4 } from "react/jsx-runtime";
1092
+ var ADOPTED_HOST_STYLE_ATTR = "data-frontx-adopted-host-style";
1093
+ var BASE_RESETS_STYLE_ID = "__frontx-base-resets__";
1094
+ var SHELL_OWNED_SHADOW_STYLE_IDS = ["__frontx-shadow-isolation__", "__frontx-css-variables__"];
1092
1095
  function resolveProviderMountOptions(app, bridge, mountContext) {
1093
1096
  const extensionId = mountContext?.extensionId;
1094
1097
  const domainId = mountContext?.domainId;
@@ -1143,27 +1146,108 @@ var ThemeAwareReactLifecycle = class {
1143
1146
  /**
1144
1147
  * Copy all inline <style> and <link rel="stylesheet"> from the host document
1145
1148
  * into the shadow root so that Tailwind and component styles apply inside the MFE.
1149
+ *
1150
+ * The clones are inserted ahead of everything already in the shadow root, never
1151
+ * appended, because adopted host styles are context rather than authority: where
1152
+ * they and the MFE's own CSS declare the same property at equal specificity, the
1153
+ * MFE has to win. Appending inverted that. `MfeHandlerMF` injects the MFE's
1154
+ * compiled stylesheet into the shadow root before it calls mount, so an appended
1155
+ * clone of the shell's Tailwind preflight - whose
1156
+ * `button, [type='button'], [type='reset'], [type='submit']` rule sets
1157
+ * `background-color: transparent` at specificity (0,1,0) - tied with
1158
+ * `@gears-frontx/ui-kit`'s single-class button rule and won on document order,
1159
+ * leaving every kit Button transparent until it was hovered.
1160
+ *
1161
+ * Inserting at the front rather than pushing MFE styles to the back is what makes
1162
+ * the invariant hold for stylesheets that appear after mount too - a lazily
1163
+ * imported component's CSS module, or whatever initializeStyles() adds - since
1164
+ * those land behind the adopted block by construction.
1165
+ *
1166
+ * A cascade layer around the adopted CSS was the alternative and is rejected:
1167
+ * layered rules lose to unlayered ones at *any* specificity, so a host utility
1168
+ * class would stop overriding an MFE element selector. Document order keeps
1169
+ * specificity in charge and only settles the ties.
1170
+ *
1171
+ * The block replaces its predecessor instead of stacking on it, the same
1172
+ * upsert shape `injectStylesheet` and `injectCssVariables` use in
1173
+ * `@gears-frontx/mfes`' shadow utilities, widened from one id-keyed node to a
1174
+ * marked block. Replacement is required rather than tidy: `createShadowRoot`
1175
+ * hands back an existing `element.shadowRoot` instead of attaching a new one,
1176
+ * so remounting the same container runs this method again on a shadow root
1177
+ * that already holds a full adopted block, and appending would grow it once
1178
+ * per mount with no bound. It does not save re-resolving the cloned `<link>`s -
1179
+ * every mount clones them afresh and the browser resolves each clone; what
1180
+ * replacement buys is that the count stays at one block instead of one per
1181
+ * mount.
1182
+ *
1183
+ * The block is rebuilt from the host head rather than left in place, because
1184
+ * the head is not fixed: a lazily imported chunk adds stylesheets to it after
1185
+ * the first mount, and the second mount has to pick those up.
1186
+ *
1187
+ * The fresh block goes in before the previous one comes out. Removing first
1188
+ * leaves the shadow root with no host CSS at all, and everything between that
1189
+ * removal and the insertion is synchronous work in this same task - so the gap
1190
+ * never reaches a paint, but any code that reads computed styles from the tree
1191
+ * in between sees an unstyled one. Inserting first costs one moment with two
1192
+ * blocks present, which changes nothing: the old block is a clone of the same
1193
+ * host head and loses every tie to the new one on document order anyway.
1146
1194
  */
1147
1195
  adoptHostStylesIntoShadowRoot(shadowRoot) {
1148
- const styleElements = document.head.querySelectorAll("style");
1149
- styleElements.forEach((el) => {
1150
- const clone = document.createElement("style");
1151
- clone.textContent = el.textContent ?? "";
1152
- shadowRoot.appendChild(clone);
1153
- });
1154
- const linkElements = document.head.querySelectorAll('link[rel="stylesheet"]');
1155
- linkElements.forEach((el) => {
1156
- const clone = el.cloneNode(true);
1157
- shadowRoot.appendChild(clone);
1196
+ const supersededStyles = Array.from(shadowRoot.querySelectorAll(`[${ADOPTED_HOST_STYLE_ATTR}]`));
1197
+ const adoptedStyles = document.createDocumentFragment();
1198
+ const hostStyleNodes = document.head.querySelectorAll('style, link[rel="stylesheet"]');
1199
+ hostStyleNodes.forEach((el) => {
1200
+ if (el instanceof HTMLStyleElement) {
1201
+ const clone = document.createElement("style");
1202
+ clone.textContent = el.textContent ?? "";
1203
+ clone.setAttribute(ADOPTED_HOST_STYLE_ATTR, "");
1204
+ adoptedStyles.appendChild(clone);
1205
+ return;
1206
+ }
1207
+ const linkClone = el.cloneNode(true);
1208
+ linkClone.setAttribute(ADOPTED_HOST_STYLE_ATTR, "");
1209
+ adoptedStyles.appendChild(linkClone);
1158
1210
  });
1211
+ shadowRoot.insertBefore(adoptedStyles, shadowRoot.firstChild);
1212
+ supersededStyles.forEach((el) => el.remove());
1159
1213
  }
1160
1214
  /**
1161
1215
  * Box-model resets and :host defaults needed inside every shadow root.
1162
1216
  * These aren't part of Tailwind's compiled output but are required for
1163
1217
  * consistent rendering across browsers.
1218
+ *
1219
+ * Reuses the node a previous mount left behind, exactly as `injectStylesheet`
1220
+ * does in `@gears-frontx/mfes`' shadow utilities: a remount runs against a
1221
+ * shadow root that `createShadowRoot` handed back intact, and appending a
1222
+ * second copy would add one node per mount forever.
1223
+ *
1224
+ * Reuse alone is not enough, which is why the node is also positioned on every
1225
+ * mount rather than left where the previous one sat. These resets are context
1226
+ * like the adopted block is - `border-color: currentColor` on `*` is a 0-0-0
1227
+ * declaration that beats an MFE preflight's own 0-0-0 reset purely on document
1228
+ * order - so they have to stay ahead of the MFE's CSS, and where they land is
1229
+ * not stable on its own. `MfeHandlerMF.wrapLifecycleWithStylesheets` removes the
1230
+ * MFE's stylesheet on unmount and appends a fresh one before the next mount, so
1231
+ * a node merely left in place ends up ahead of that stylesheet on a remount and
1232
+ * behind it on the first mount: two different cascades for one component.
1233
+ * Anchoring it to the end of the shell-owned head gives every mount the same
1234
+ * order - adopted block, isolation, resets, MFE - which is also the order the
1235
+ * adoption method's invariant is written against.
1236
+ *
1237
+ * With no shell-owned node to anchor to, the resets go to the front. That is the
1238
+ * plain-`Element` container: no adopted block, no isolation style, and the MFE
1239
+ * stylesheet already appended by the handler, so the front is again the only
1240
+ * position that leaves the MFE winning ties.
1241
+ *
1242
+ * Looked up by attribute-selector form rather than `getElementById`, which
1243
+ * `ShadowRoot` has but `Element` does not, and this hook takes either.
1164
1244
  */
1165
1245
  injectBaseResets(container) {
1166
- const style = document.createElement("style");
1246
+ const existing = container.querySelector(
1247
+ `style[id="${BASE_RESETS_STYLE_ID}"]`
1248
+ );
1249
+ const style = existing ?? document.createElement("style");
1250
+ style.id = BASE_RESETS_STYLE_ID;
1167
1251
  style.textContent = `
1168
1252
  *, *::before, *::after {
1169
1253
  box-sizing: border-box;
@@ -1181,7 +1265,17 @@ var ThemeAwareReactLifecycle = class {
1181
1265
  background-color: hsl(var(--background));
1182
1266
  }
1183
1267
  `;
1184
- container.appendChild(style);
1268
+ const shellOwnedHead = container.querySelectorAll(
1269
+ [
1270
+ `[${ADOPTED_HOST_STYLE_ATTR}]`,
1271
+ ...SHELL_OWNED_SHADOW_STYLE_IDS.map((id) => `[id="${id}"]`)
1272
+ ].join(", ")
1273
+ );
1274
+ const lastShellOwnedNode = shellOwnedHead[shellOwnedHead.length - 1];
1275
+ container.insertBefore(
1276
+ style,
1277
+ lastShellOwnedNode ? lastShellOwnedNode.nextSibling : container.firstChild
1278
+ );
1185
1279
  }
1186
1280
  /**
1187
1281
  * Hook for subclasses to inject additional CSS not covered by the adopted host
@@ -1259,7 +1353,6 @@ import {
1259
1353
  registerSlice,
1260
1354
  hasSlice,
1261
1355
  createSlice,
1262
- LayoutDomain,
1263
1356
  layoutReducer,
1264
1357
  layoutDomainReducers,
1265
1358
  LAYOUT_SLICE_NAME,
@@ -1326,9 +1419,6 @@ import {
1326
1419
  RestEndpointProtocol,
1327
1420
  SseProtocol,
1328
1421
  SseStreamProtocol,
1329
- RestMockPlugin,
1330
- SseMockPlugin,
1331
- MockEventSource,
1332
1422
  ApiPluginBase,
1333
1423
  ApiPlugin,
1334
1424
  ApiProtocol,
@@ -1406,7 +1496,6 @@ import {
1406
1496
  ExclusiveMountStrategy
1407
1497
  } from "@gears-frontx/framework";
1408
1498
  import { MfeHandlerMF, gtsPlugin } from "@gears-frontx/framework";
1409
- import { themeSchema, languageSchema, extensionScreenSchema } from "@gears-frontx/framework";
1410
1499
  import {
1411
1500
  createShadowRoot,
1412
1501
  injectCssVariables,
@@ -1447,7 +1536,6 @@ export {
1447
1536
  LAYOUT_SLICE_NAME,
1448
1537
  Language,
1449
1538
  LanguageDisplayMode,
1450
- LayoutDomain,
1451
1539
  MOCK_PLUGIN,
1452
1540
  MfeBridgeFactory,
1453
1541
  MfeContext,
@@ -1456,18 +1544,15 @@ export {
1456
1544
  MfeProvider,
1457
1545
  MfeRegistry,
1458
1546
  MfeRegistryFactory,
1459
- MockEventSource,
1460
1547
  MockEvents,
1461
1548
  MountStrategy,
1462
1549
  OptionalMountStrategy,
1463
1550
  ParentMfeBridge,
1464
1551
  RestEndpointProtocol,
1465
- RestMockPlugin,
1466
1552
  RestPlugin,
1467
1553
  RestPluginWithConfig,
1468
1554
  RestProtocol,
1469
1555
  SUPPORTED_LANGUAGES,
1470
- SseMockPlugin,
1471
1556
  SsePlugin,
1472
1557
  SsePluginWithConfig,
1473
1558
  SseProtocol,
@@ -1494,7 +1579,6 @@ export {
1494
1579
  createThemeRegistry,
1495
1580
  effects,
1496
1581
  eventBus2 as eventBus,
1497
- extensionScreenSchema,
1498
1582
  extractGtsPackage,
1499
1583
  footerActions,
1500
1584
  footerSlice,
@@ -1516,7 +1600,6 @@ export {
1516
1600
  isRestShortCircuit,
1517
1601
  isShortCircuit,
1518
1602
  isSseShortCircuit,
1519
- languageSchema,
1520
1603
  layout,
1521
1604
  layoutDomainReducers,
1522
1605
  layoutReducer,
@@ -1574,7 +1657,6 @@ export {
1574
1657
  tenantActions,
1575
1658
  tenantReducer,
1576
1659
  tenantSlice,
1577
- themeSchema,
1578
1660
  themes,
1579
1661
  toggleMenu,
1580
1662
  toggleMockMode,