@adobe-commerce/elsie 1.0.1-alpha04140951 → 1.0.1-alpha04151720

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.
@@ -32,6 +32,41 @@ The `<Slot />` component is used to define a slot in a container. It receives a
32
32
 
33
33
  The name of the slot in _PascalCase_. `string` (required).
34
34
 
35
+ ### slotTag
36
+
37
+ The HTML tag to use for the slot's wrapper element. This allows you to change the wrapper element from the default `div` to any valid HTML tag (e.g., 'span', 'p', 'a', etc.). When using specific tags like 'a', you can also provide their respective HTML attributes (e.g., 'href', 'target', etc.).
38
+
39
+ Example:
40
+ ```tsx
41
+ // Render with a span wrapper
42
+ <Slot name="MySlot" slotTag="span">
43
+ Inline content
44
+ </Slot>
45
+
46
+ // Render with an anchor wrapper
47
+ <Slot name="MySlot" slotTag="a" href="https://example.com" target="_blank">
48
+ Link content
49
+ </Slot>
50
+ ```
51
+
52
+ ### contentTag
53
+
54
+ The HTML tag to use for wrapping dynamically inserted content within the slot. This is separate from the slot's wrapper tag and allows you to control how dynamic content is structured. Defaults to 'div'.
55
+
56
+ Example:
57
+ ```tsx
58
+ <Slot
59
+ name="MySlot"
60
+ slotTag="article" // The outer wrapper will be an article
61
+ contentTag="section" // Dynamic content will be wrapped in sections
62
+ slot={(ctx) => {
63
+ const elem = document.createElement('div');
64
+ elem.innerHTML = 'Dynamic content';
65
+ ctx.appendChild(elem); // This will be wrapped in a section tag
66
+ }}
67
+ />
68
+ ```
69
+
35
70
  ### slot (required)
36
71
 
37
72
  - `ctx`: An object representing the context of the slot, including methods for manipulating the slot's content.
package/src/lib/slot.tsx CHANGED
@@ -7,7 +7,7 @@
7
7
  * accompanying it.
8
8
  *******************************************************************/
9
9
 
10
- import { cloneElement, ComponentChildren, RefObject, VNode } from 'preact';
10
+ import { cloneElement, ComponentChildren, RefObject, VNode, createElement } from 'preact';
11
11
  import {
12
12
  StateUpdater,
13
13
  useContext,
@@ -42,7 +42,7 @@ interface PrivateContext<T> {
42
42
  _registerMethod: (
43
43
  cb: (next: T & DefaultSlotContext<T>, state: State) => void
44
44
  ) => void;
45
- _htmlElementToVNode: (element: HTMLElement) => VNode;
45
+ _htmlElementToVNode: (element: HTMLElement, tag: keyof HTMLElementTagNameMap) => VNode;
46
46
  }
47
47
 
48
48
  interface DefaultSlotContext<T> extends PrivateContext<T> {
@@ -69,13 +69,14 @@ export type SlotMethod<P = any> = (
69
69
  ) => void;
70
70
 
71
71
  // Slot Hook
72
- export function useSlot<K, V extends HTMLDivElement>(
72
+ export function useSlot<K, V extends HTMLElement>(
73
73
  name: string,
74
74
  // @ts-ignore
75
75
  context: Context<K> = {},
76
76
  callback?: SlotProps<K>,
77
77
  children?: ComponentChildren,
78
- render?: Function
78
+ render?: Function,
79
+ contentTag: keyof HTMLElementTagNameMap = 'div'
79
80
  ): [RefObject<V>, Record<string, any>] {
80
81
  const slotsQueue = useContext(SlotQueueContext);
81
82
 
@@ -146,15 +147,17 @@ export function useSlot<K, V extends HTMLDivElement>(
146
147
  context._registerMethod = _registerMethod;
147
148
 
148
149
  const _htmlElementToVNode = useCallback((elem: HTMLElement) => {
149
- return (
150
- <div
151
- data-slot-html-element={elem.tagName.toLowerCase()}
152
- ref={(refElem) => {
150
+ return createElement(
151
+ contentTag,
152
+ {
153
+ 'data-slot-html-element': elem.tagName.toLowerCase(),
154
+ ref: (refElem: HTMLElement | null): void => {
153
155
  refElem?.appendChild(elem);
154
- }}
155
- />
156
+ }
157
+ },
158
+ null
156
159
  );
157
- }, []);
160
+ }, [contentTag]);
158
161
 
159
162
  // @ts-ignore
160
163
  context._htmlElementToVNode = _htmlElementToVNode;
@@ -351,11 +354,14 @@ export function useSlot<K, V extends HTMLDivElement>(
351
354
 
352
355
  // Slot Component
353
356
  interface SlotPropsComponent<T>
354
- extends Omit<HTMLAttributes<HTMLDivElement>, 'slot'> {
357
+ extends Omit<HTMLAttributes<HTMLElement>, 'slot'> {
355
358
  name: string;
356
359
  slot?: SlotProps<T>;
357
360
  context?: Context<T>;
358
361
  render?: (props: Record<string, any>) => VNode | VNode[];
362
+ slotTag?: keyof HTMLElementTagNameMap; // The tag for the slot wrapper itself
363
+ contentTag?: keyof HTMLElementTagNameMap; // The tag for dynamically inserted content
364
+ children?: ComponentChildren;
359
365
  }
360
366
 
361
367
  export function Slot<T>({
@@ -364,16 +370,19 @@ export function Slot<T>({
364
370
  slot,
365
371
  children,
366
372
  render,
373
+ slotTag = 'div',
374
+ contentTag = 'div',
367
375
  ...props
368
376
  }: Readonly<SlotPropsComponent<T>>) {
369
377
  const slotsQueue = useContext(SlotQueueContext);
370
378
 
371
- const [elementRef, slotProps] = useSlot<T, HTMLDivElement>(
379
+ const [elementRef, slotProps] = useSlot<T, HTMLElement>(
372
380
  name,
373
381
  context,
374
382
  slot,
375
383
  children,
376
- render
384
+ render,
385
+ contentTag
377
386
  );
378
387
 
379
388
  useMemo(() => {
@@ -388,10 +397,14 @@ export function Slot<T>({
388
397
  }
389
398
  }, [name, slotsQueue]);
390
399
 
391
- return (
392
- <div {...props} ref={elementRef} data-slot={name}>
393
- {slotProps.children}
394
- </div>
400
+ return createElement(
401
+ slotTag,
402
+ {
403
+ ...props,
404
+ ref: elementRef,
405
+ 'data-slot': name,
406
+ },
407
+ slotProps.children
395
408
  );
396
409
  }
397
410