@adobe-commerce/elsie 1.6.0-alpha999 → 1.6.0-beta1

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/src/lib/slot.tsx CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  RefObject,
16
16
  VNode,
17
17
  } from 'preact';
18
- import { HTMLAttributes } from 'preact/compat';
18
+ import { Children, HTMLAttributes, isValidElement } from 'preact/compat';
19
19
  import {
20
20
  StateUpdater,
21
21
  useCallback,
@@ -41,6 +41,7 @@ interface SlotElement {
41
41
  prependChild: MutateElement;
42
42
  appendSibling: MutateElement;
43
43
  prependSibling: MutateElement;
44
+ remove: () => void;
44
45
  }
45
46
 
46
47
  interface PrivateContext<T> {
@@ -60,6 +61,7 @@ interface DefaultSlotContext<T> extends PrivateContext<T> {
60
61
  prependChild: MutateElement;
61
62
  appendSibling: MutateElement;
62
63
  prependSibling: MutateElement;
64
+ remove: () => void;
63
65
  onRender: (cb: (next: T & DefaultSlotContext<T>) => void) => void;
64
66
  onChange: (cb: (next: T & DefaultSlotContext<T>) => void) => void;
65
67
  }
@@ -85,7 +87,7 @@ export function useSlot<K, V extends HTMLElement>(
85
87
  render?: Function,
86
88
  // eslint-disable-next-line no-undef
87
89
  contentTag: keyof HTMLElementTagNameMap = 'div'
88
- ): [RefObject<V>, Record<string, any>] {
90
+ ): [RefObject<V>, Record<string, any>, 'loading' | 'pending' | 'ready'] {
89
91
  const slotsQueue = useContext(SlotQueueContext);
90
92
 
91
93
  // HTML Element
@@ -207,6 +209,10 @@ export function useSlot<K, V extends HTMLElement>(
207
209
  const parent = element.parentNode;
208
210
  parent?.insertBefore(elem, element);
209
211
  },
212
+
213
+ remove: () => {
214
+ element.remove();
215
+ },
210
216
  };
211
217
  },
212
218
  [name]
@@ -301,6 +307,14 @@ export function useSlot<K, V extends HTMLElement>(
301
307
  [_registerMethod]
302
308
  );
303
309
 
310
+ // @ts-ignore
311
+ context.remove = useCallback(() => {
312
+ // @ts-ignore
313
+ _registerMethod(() => {
314
+ elementRef.current?.remove();
315
+ });
316
+ }, [_registerMethod]);
317
+
304
318
  const handleLifeCycleRender = useCallback(async () => {
305
319
  if (status.current === 'loading') return;
306
320
 
@@ -363,9 +377,51 @@ export function useSlot<K, V extends HTMLElement>(
363
377
  // eslint-disable-next-line react-hooks/exhaustive-deps
364
378
  }, [JSON.stringify(context), JSON.stringify(_state)]);
365
379
 
366
- return [elementRef, props];
380
+ return [elementRef, props, status.current];
367
381
  }
368
382
 
383
+ /**
384
+ * Recursively processes children elements to conditionally prevent image loading.
385
+ *
386
+ * This function traverses the children tree and modifies img elements to prevent
387
+ * premature loading during slot initialization. When isReady is false, img src
388
+ * attributes are set to empty string to prevent network requests, while preserving
389
+ * the original src in a data attribute for debugging purposes.
390
+ *
391
+ * @param children - The children elements to process (can be any React/Preact children)
392
+ * @param isReady - Whether the slot is ready to load images (true) or should prevent loading (false)
393
+ * @returns Processed children with conditional image src attributes
394
+ */
395
+ const processChildren = (children: any, isReady: boolean): any => {
396
+ return Children.map(children, child => {
397
+ // Handle text nodes, numbers, etc.
398
+ if (!isValidElement(child)) {
399
+ return child;
400
+ }
401
+
402
+ // Handle img elements - conditionally set src
403
+ if (child.props.src) {
404
+ return cloneElement(child, {
405
+ ...child.props,
406
+ src: isReady ? child.props.src : "",
407
+ // Optionally preserve original src in data attribute for debugging
408
+ 'data-original-src': child.props.src,
409
+ });
410
+ }
411
+
412
+ // Handle elements with children - recursively process them
413
+ if (child.props && child.props.children) {
414
+ return cloneElement(child, {
415
+ ...child.props,
416
+ children: processChildren(child.props.children, isReady),
417
+ });
418
+ }
419
+
420
+ // Return other elements as-is
421
+ return child;
422
+ });
423
+ };
424
+
369
425
  // Slot Component
370
426
  interface SlotPropsComponent<T>
371
427
  extends Omit<HTMLAttributes<HTMLElement>, 'slot'> {
@@ -398,7 +454,7 @@ export function Slot<T>({
398
454
  }> {
399
455
  const slotsQueue = useContext(SlotQueueContext);
400
456
 
401
- const [elementRef, slotProps] = useSlot<T, HTMLElement>(
457
+ const [elementRef, slotProps, status] = useSlot<T, HTMLElement>(
402
458
  name,
403
459
  context,
404
460
  slot,
@@ -426,7 +482,7 @@ export function Slot<T>({
426
482
  ref: elementRef,
427
483
  'data-slot': name,
428
484
  },
429
- slotProps.children
485
+ processChildren(slotProps.children, status === 'ready')
430
486
  );
431
487
  }
432
488