@adobe-commerce/elsie 1.5.1-alpha003 → 1.6.0-alpha999
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/bin/builders/serve/index.js +3 -1
- package/config/vite.mjs +13 -8
- package/package.json +1 -1
- package/src/components/Picker/Picker.tsx +5 -3
- package/src/components/ProductItemCard/ProductItemCard.css +1 -39
- package/src/components/ProductItemCard/ProductItemCard.stories.tsx +7 -10
- package/src/components/ProductItemCard/ProductItemCard.tsx +4 -21
- package/src/components/TextSwatch/TextSwatch.tsx +5 -2
- package/src/docs/API/render.mdx +16 -0
- package/src/docs/slots.mdx +7 -1
- package/src/lib/render.tsx +21 -7
- package/src/lib/slot.tsx +39 -26
|
@@ -4,7 +4,9 @@ const path = require('path');
|
|
|
4
4
|
module.exports = async function generateResourceBuilder({ argv }) {
|
|
5
5
|
const { build, preview } = await import('vite');
|
|
6
6
|
|
|
7
|
-
const configFile =
|
|
7
|
+
const configFile =
|
|
8
|
+
argv?.config ??
|
|
9
|
+
path.resolve(...[__dirname, '..', '..', '..', 'config', 'vite.mjs']);
|
|
8
10
|
|
|
9
11
|
let built = false;
|
|
10
12
|
|
package/config/vite.mjs
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { glob } from 'glob';
|
|
@@ -25,7 +25,12 @@ import banner from 'vite-plugin-banner';
|
|
|
25
25
|
const env = loadEnv('', process.cwd());
|
|
26
26
|
|
|
27
27
|
// Load Elsie Config
|
|
28
|
-
const
|
|
28
|
+
const elsieConfigPath = path.resolve(process.cwd(), './.elsie.js');
|
|
29
|
+
// Convert Windows paths to file:// URLs for ES module imports
|
|
30
|
+
const elsieConfigUrl = elsieConfigPath.startsWith('file://')
|
|
31
|
+
? elsieConfigPath
|
|
32
|
+
: `file://${elsieConfigPath.replace(/\\/g, '/')}`;
|
|
33
|
+
const elsieConfig = await import(elsieConfigUrl).then((m) => m.default);
|
|
29
34
|
|
|
30
35
|
// Read package.json using createRequire (compatible with Node 20 and 22)
|
|
31
36
|
const require = createRequire(import.meta.url);
|
|
@@ -293,19 +298,19 @@ export default {
|
|
|
293
298
|
generateBundle(options, bundle) {
|
|
294
299
|
for (const fileName in bundle) {
|
|
295
300
|
const chunk = bundle[fileName];
|
|
296
|
-
|
|
301
|
+
|
|
297
302
|
// Process both .map files and JS/TS files with sourcemaps
|
|
298
|
-
if ((chunk.type === 'asset' && fileName.endsWith('.map')) ||
|
|
303
|
+
if ((chunk.type === 'asset' && fileName.endsWith('.map')) ||
|
|
299
304
|
(chunk.type === 'chunk' && chunk.map)) {
|
|
300
305
|
try {
|
|
301
306
|
// Get the sourcemap object - either from the asset source or the chunk's map
|
|
302
307
|
const map = chunk.type === 'asset' ? JSON.parse(chunk.source) : chunk.map;
|
|
303
|
-
|
|
308
|
+
|
|
304
309
|
if (map.sources) {
|
|
305
310
|
map.sources = map.sources.map((input) => {
|
|
306
311
|
return input.replace(/(?:\.\.?\/)+src\//, `/${packageJSON.name}/src/`);
|
|
307
312
|
});
|
|
308
|
-
|
|
313
|
+
|
|
309
314
|
// Update the sourcemap in the appropriate place
|
|
310
315
|
if (chunk.type === 'asset') {
|
|
311
316
|
chunk.source = JSON.stringify(map);
|
package/package.json
CHANGED
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { Icon } from '@adobe-commerce/elsie/components';
|
|
11
|
-
import '@adobe-commerce/elsie/components/Picker/Picker.css';
|
|
12
11
|
import { ChevronDown } from '@adobe-commerce/elsie/icons';
|
|
13
12
|
import { classes } from '@adobe-commerce/elsie/lib';
|
|
14
13
|
import { FunctionComponent, VNode } from 'preact';
|
|
15
14
|
import { HTMLAttributes, useEffect, useState } from 'preact/compat';
|
|
16
15
|
|
|
16
|
+
import '@adobe-commerce/elsie/components/Picker/Picker.css';
|
|
17
|
+
|
|
17
18
|
type PickerValue = string | null;
|
|
18
19
|
|
|
19
20
|
export interface PickerOption {
|
|
@@ -68,9 +69,10 @@ export const Picker: FunctionComponent<PickerProps> = ({
|
|
|
68
69
|
defaultOption,
|
|
69
70
|
icon,
|
|
70
71
|
className,
|
|
72
|
+
id,
|
|
71
73
|
...props
|
|
72
74
|
}) => {
|
|
73
|
-
const
|
|
75
|
+
const uniqueId = id ?? name ?? `dropin-picker-${Math.random().toString(36)}`;
|
|
74
76
|
const isRequired = !!props?.required;
|
|
75
77
|
|
|
76
78
|
// find the first option that is not disabled
|
|
@@ -154,7 +156,7 @@ export const Picker: FunctionComponent<PickerProps> = ({
|
|
|
154
156
|
)}
|
|
155
157
|
|
|
156
158
|
<select
|
|
157
|
-
id={
|
|
159
|
+
id={uniqueId}
|
|
158
160
|
className={classes([
|
|
159
161
|
'dropin-picker__select',
|
|
160
162
|
`dropin-picker__select--${variant}`,
|
|
@@ -23,16 +23,14 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.dropin-product-item-card__image-container {
|
|
26
|
-
overflow:
|
|
26
|
+
overflow: hidden;
|
|
27
27
|
width: 100%;
|
|
28
28
|
height: auto;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
.dropin-product-item-card__image img {
|
|
32
|
-
display: block;
|
|
33
32
|
width: 100%;
|
|
34
33
|
max-height: 375px;
|
|
35
|
-
border-radius: var(--border-radius-xsmall, 4px);
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
.dropin-product-item-card__content {
|
|
@@ -76,42 +74,6 @@
|
|
|
76
74
|
width: 100%;
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
.dropin-product-item-card__image a:focus {
|
|
80
|
-
outline: 1px solid var(--color-neutral-400);
|
|
81
|
-
outline-offset: 1px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.dropin-product-item-card__image {
|
|
85
|
-
display: inline-block;
|
|
86
|
-
border-radius: var(--border-radius-xsmall, 4px);
|
|
87
|
-
outline: none;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.dropin-product-item-card__image:focus-visible {
|
|
91
|
-
outline: 2px solid var(--color-interactive-focus, #005fcc);
|
|
92
|
-
outline-offset: 2px;
|
|
93
|
-
z-index: 2;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.dropin-product-item-card__image-container {
|
|
97
|
-
overflow: visible;
|
|
98
|
-
width: 100%;
|
|
99
|
-
height: auto;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.dropin-product-item-card__image-link {
|
|
103
|
-
display: block;
|
|
104
|
-
border-radius: var(--border-radius-xsmall, 4px);
|
|
105
|
-
outline: none;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.dropin-product-item-card__image-link:focus-visible {
|
|
109
|
-
outline: 3px solid var(--color-interactive-focus, #333333);
|
|
110
|
-
outline-offset: 3px;
|
|
111
|
-
position: relative;
|
|
112
|
-
z-index: 1;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
77
|
/* Medium (portrait tablets and large phones, 768px and up) */
|
|
116
78
|
/* @media only screen and (min-width: 768px) { } */
|
|
117
79
|
|
|
@@ -47,15 +47,13 @@ const meta: Meta<ProductItemCardProps> = {
|
|
|
47
47
|
options: ['DefaultImage', 'Empty'],
|
|
48
48
|
mapping: {
|
|
49
49
|
DefaultImage: (
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
/>
|
|
58
|
-
</a>
|
|
50
|
+
<Image
|
|
51
|
+
src="https://picsum.photos/300/375"
|
|
52
|
+
width="300"
|
|
53
|
+
height="375"
|
|
54
|
+
alt="Product Image"
|
|
55
|
+
loading="lazy"
|
|
56
|
+
/>
|
|
59
57
|
),
|
|
60
58
|
Empty: null,
|
|
61
59
|
},
|
|
@@ -255,7 +253,6 @@ export const Default: Story = {
|
|
|
255
253
|
sku: 'DefaultSku' as any,
|
|
256
254
|
swatches: 'DefaultSwatches' as any,
|
|
257
255
|
actionButton: 'DefaultButton' as any,
|
|
258
|
-
imageHref: '#',
|
|
259
256
|
},
|
|
260
257
|
};
|
|
261
258
|
|
|
@@ -22,8 +22,6 @@ export interface ProductItemCardProps
|
|
|
22
22
|
actionButton?: VNode;
|
|
23
23
|
swatches?: VNode;
|
|
24
24
|
initialized?: boolean;
|
|
25
|
-
imageHref?: string;
|
|
26
|
-
imageLinkLabel?: string;
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
export const ProductItemCard: FunctionComponent<ProductItemCardProps> = ({
|
|
@@ -35,8 +33,6 @@ export const ProductItemCard: FunctionComponent<ProductItemCardProps> = ({
|
|
|
35
33
|
actionButton,
|
|
36
34
|
swatches,
|
|
37
35
|
initialized = false,
|
|
38
|
-
imageHref,
|
|
39
|
-
imageLinkLabel,
|
|
40
36
|
...props
|
|
41
37
|
}) => {
|
|
42
38
|
if (!initialized) {
|
|
@@ -49,23 +45,10 @@ export const ProductItemCard: FunctionComponent<ProductItemCardProps> = ({
|
|
|
49
45
|
>
|
|
50
46
|
<div className="dropin-product-item-card__image-container">
|
|
51
47
|
{image && (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
aria-label={imageLinkLabel}
|
|
57
|
-
>
|
|
58
|
-
<VComponent
|
|
59
|
-
node={image}
|
|
60
|
-
className={classes(['dropin-product-item-card__image'])}
|
|
61
|
-
/>
|
|
62
|
-
</a>
|
|
63
|
-
) : (
|
|
64
|
-
<VComponent
|
|
65
|
-
node={image}
|
|
66
|
-
className={classes(['dropin-product-item-card__image'])}
|
|
67
|
-
/>
|
|
68
|
-
)
|
|
48
|
+
<VComponent
|
|
49
|
+
node={image}
|
|
50
|
+
className={classes(['dropin-product-item-card__image'])}
|
|
51
|
+
/>
|
|
69
52
|
)}
|
|
70
53
|
</div>
|
|
71
54
|
<div className="dropin-product-item-card__content">
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
useEffect,
|
|
15
15
|
useRef,
|
|
16
16
|
useCallback,
|
|
17
|
+
useMemo,
|
|
17
18
|
} from 'preact/compat';
|
|
18
19
|
import { classes } from '@adobe-commerce/elsie/lib';
|
|
19
20
|
import '@adobe-commerce/elsie/components/TextSwatch/TextSwatch.css';
|
|
@@ -93,6 +94,8 @@ export const TextSwatch: FunctionComponent<TextSwatchProps> = ({
|
|
|
93
94
|
}
|
|
94
95
|
}, [label]);
|
|
95
96
|
|
|
97
|
+
const uniqueId = useMemo(() => id ?? `${name}_${id}_${Math.random().toString(36)}`, [name, id]);
|
|
98
|
+
|
|
96
99
|
return (
|
|
97
100
|
<div
|
|
98
101
|
className="dropin-text-swatch__container"
|
|
@@ -101,7 +104,7 @@ export const TextSwatch: FunctionComponent<TextSwatchProps> = ({
|
|
|
101
104
|
<input
|
|
102
105
|
type={multi ? 'checkbox' : 'radio'}
|
|
103
106
|
name={name}
|
|
104
|
-
id={
|
|
107
|
+
id={uniqueId}
|
|
105
108
|
value={value}
|
|
106
109
|
aria-label={handleAriaLabel()}
|
|
107
110
|
checked={selected}
|
|
@@ -116,7 +119,7 @@ export const TextSwatch: FunctionComponent<TextSwatchProps> = ({
|
|
|
116
119
|
])}
|
|
117
120
|
/>
|
|
118
121
|
<label
|
|
119
|
-
htmlFor={
|
|
122
|
+
htmlFor={uniqueId}
|
|
120
123
|
ref={spanRef}
|
|
121
124
|
className={classes([
|
|
122
125
|
'dropin-text-swatch__label',
|
package/src/docs/API/render.mdx
CHANGED
|
@@ -121,5 +121,21 @@ button.addEventListener('click', () => {
|
|
|
121
121
|
});
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
### Unmounting components without instance access
|
|
125
|
+
|
|
126
|
+
The `Render.unmount` static method provides a way to unmount components from the DOM when you don't have direct access to the component instance.
|
|
127
|
+
This is particularly useful in scenarios where components are rendered inside modals, dialogs, or other temporary containers that need to be cleaned up.
|
|
128
|
+
|
|
129
|
+
#### Example
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
// Close the dialog
|
|
133
|
+
dialog.close();
|
|
134
|
+
|
|
135
|
+
// Unmount any dropin containers rendered in the modal
|
|
136
|
+
dialog.querySelectorAll('[data-dropin-container]').forEach(Render.unmount);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
This approach ensures that all dropin components are properly cleaned up when their container elements are removed from the DOM, preventing memory leaks and maintaining application performance.
|
|
124
140
|
</Unstyled>
|
|
125
141
|
|
package/src/docs/slots.mdx
CHANGED
|
@@ -32,6 +32,12 @@ 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
|
+
### lazy
|
|
36
|
+
|
|
37
|
+
Controls whether the slot should be loaded immediately or deferred for later initialization. `boolean` (optional).
|
|
38
|
+
|
|
39
|
+
When `lazy={false}`, the slot is initialized as soon as the container mounts. When `lazy={true}`, the slot can be initialized later on when it is needed. This is useful for performance optimization, especially when the slot's content is not immediately required.
|
|
40
|
+
|
|
35
41
|
### slotTag
|
|
36
42
|
|
|
37
43
|
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.).
|
|
@@ -71,7 +77,7 @@ Example:
|
|
|
71
77
|
|
|
72
78
|
- `ctx`: An object representing the context of the slot, including methods for manipulating the slot's content.
|
|
73
79
|
|
|
74
|
-
The slot property, which is implemented as a promise function, provides developers with the flexibility to dynamically generate and manipulate content within slots.
|
|
80
|
+
The slot property, which is implemented as a promise function, provides developers with the flexibility to dynamically generate and manipulate content within slots.
|
|
75
81
|
However, it's important to note that this promise is render-blocking, meaning that the component will not render until the promise is resolved.
|
|
76
82
|
|
|
77
83
|
### context
|
package/src/lib/render.tsx
CHANGED
|
@@ -71,17 +71,21 @@ export class Render {
|
|
|
71
71
|
rootElement.innerHTML = '';
|
|
72
72
|
|
|
73
73
|
// clone the root element to initialize rendering on the background
|
|
74
|
-
const
|
|
74
|
+
const root = document.createElement('div');
|
|
75
75
|
|
|
76
76
|
// apply base design tokens and global styles to the root element
|
|
77
77
|
rootElement.classList.add('dropin-design');
|
|
78
|
+
rootElement.setAttribute('data-dropin-container', Component.name);
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
// store the virtual root element
|
|
81
|
+
(rootElement as any).__rootElement = root;
|
|
82
|
+
|
|
83
|
+
render(<Root next={state} />, root);
|
|
80
84
|
|
|
81
85
|
// API object to control the rendered component
|
|
82
86
|
const API: RenderAPI = {
|
|
83
87
|
remove: () => {
|
|
84
|
-
render(null,
|
|
88
|
+
render(null, root);
|
|
85
89
|
},
|
|
86
90
|
setProps: (cb: (prev: T) => T) => {
|
|
87
91
|
const next = cb(state.peek());
|
|
@@ -97,7 +101,7 @@ export class Render {
|
|
|
97
101
|
rootElement.classList.add('dropin-design');
|
|
98
102
|
|
|
99
103
|
// append the rendered component to the DOM only when all slots are resolved
|
|
100
|
-
rootElement.appendChild(
|
|
104
|
+
rootElement.appendChild(root.firstChild ?? root);
|
|
101
105
|
|
|
102
106
|
return resolve(API);
|
|
103
107
|
}
|
|
@@ -106,14 +110,24 @@ export class Render {
|
|
|
106
110
|
};
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Unmounts a container from a root element.
|
|
115
|
+
* @param rootElement - The root element to unmount the container from.
|
|
116
|
+
*/
|
|
117
|
+
static unmount(rootElement: HTMLElement) {
|
|
118
|
+
const root = (rootElement as any)?.__rootElement;
|
|
119
|
+
if (!root) throw new Error('Root element is not defined');
|
|
120
|
+
render(null, root);
|
|
121
|
+
}
|
|
122
|
+
|
|
109
123
|
/**
|
|
110
124
|
* UnRenders a component from a root element.
|
|
111
125
|
* @param rootElement - The root element to unmount the component from.
|
|
112
|
-
* @deprecated Use `remove` method from the returned object of the `mount` method instead.
|
|
126
|
+
* @deprecated Use `remove` method from the returned object of the `mount` method instead or `unmount` method from the `Render` class.
|
|
113
127
|
*/
|
|
114
128
|
unmount(rootElement: HTMLElement) {
|
|
115
129
|
if (!rootElement) throw new Error('Root element is not defined');
|
|
116
|
-
rootElement.firstChild?.remove();
|
|
130
|
+
rootElement.firstChild?.remove();
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
/**
|
|
@@ -135,4 +149,4 @@ export class Render {
|
|
|
135
149
|
{ ...options }
|
|
136
150
|
);
|
|
137
151
|
}
|
|
138
|
-
}
|
|
152
|
+
}
|
package/src/lib/slot.tsx
CHANGED
|
@@ -2,30 +2,35 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { IntlContext, Lang } from '@adobe-commerce/elsie/i18n';
|
|
11
|
+
import {
|
|
12
|
+
cloneElement,
|
|
13
|
+
ComponentChildren,
|
|
14
|
+
createElement,
|
|
15
|
+
RefObject,
|
|
16
|
+
VNode,
|
|
17
|
+
} from 'preact';
|
|
18
|
+
import { HTMLAttributes } from 'preact/compat';
|
|
11
19
|
import {
|
|
12
20
|
StateUpdater,
|
|
21
|
+
useCallback,
|
|
13
22
|
useContext,
|
|
14
|
-
useState,
|
|
15
|
-
useRef,
|
|
16
23
|
useEffect,
|
|
17
24
|
useMemo,
|
|
18
|
-
|
|
25
|
+
useRef,
|
|
26
|
+
useState,
|
|
19
27
|
} from 'preact/hooks';
|
|
20
|
-
import { IntlContext, Lang } from '@adobe-commerce/elsie/i18n';
|
|
21
|
-
import { HTMLAttributes } from 'preact/compat';
|
|
22
28
|
import { SlotQueueContext } from './render';
|
|
23
29
|
|
|
24
30
|
import '@adobe-commerce/elsie/components/UIProvider/debugger.css';
|
|
25
31
|
|
|
26
32
|
type MutateElement = (elem: HTMLElement) => void;
|
|
27
33
|
|
|
28
|
-
|
|
29
34
|
interface State {
|
|
30
35
|
get: (key: string) => void;
|
|
31
36
|
set: (key: string, value: any) => void;
|
|
@@ -149,18 +154,21 @@ export function useSlot<K, V extends HTMLElement>(
|
|
|
149
154
|
// @ts-ignore
|
|
150
155
|
context._registerMethod = _registerMethod;
|
|
151
156
|
|
|
152
|
-
const _htmlElementToVNode = useCallback(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
refElem
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
const _htmlElementToVNode = useCallback(
|
|
158
|
+
(elem: HTMLElement) => {
|
|
159
|
+
return createElement(
|
|
160
|
+
contentTag,
|
|
161
|
+
{
|
|
162
|
+
'data-slot-html-element': elem.tagName.toLowerCase(),
|
|
163
|
+
ref: (refElem: HTMLElement | null): void => {
|
|
164
|
+
refElem?.appendChild(elem);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
null
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
[contentTag]
|
|
171
|
+
);
|
|
164
172
|
|
|
165
173
|
// @ts-ignore
|
|
166
174
|
context._htmlElementToVNode = _htmlElementToVNode;
|
|
@@ -322,7 +330,10 @@ export function useSlot<K, V extends HTMLElement>(
|
|
|
322
330
|
status.current = 'loading';
|
|
323
331
|
|
|
324
332
|
log(`🟩 "${name}" Slot Initialized`);
|
|
325
|
-
await callback(
|
|
333
|
+
await callback(
|
|
334
|
+
context as K & DefaultSlotContext<K>,
|
|
335
|
+
elementRef.current as HTMLDivElement | null
|
|
336
|
+
);
|
|
326
337
|
} catch (error) {
|
|
327
338
|
console.error(`Error in "${callback.name}" Slot callback`, error);
|
|
328
339
|
} finally {
|
|
@@ -336,7 +347,7 @@ export function useSlot<K, V extends HTMLElement>(
|
|
|
336
347
|
// Initialization
|
|
337
348
|
useEffect(() => {
|
|
338
349
|
handleLifeCycleInit().finally(() => {
|
|
339
|
-
if (slotsQueue) {
|
|
350
|
+
if (slotsQueue && slotsQueue.value.has(name)) {
|
|
340
351
|
slotsQueue.value.delete(name);
|
|
341
352
|
slotsQueue.value = new Set(slotsQueue.value);
|
|
342
353
|
}
|
|
@@ -359,6 +370,7 @@ export function useSlot<K, V extends HTMLElement>(
|
|
|
359
370
|
interface SlotPropsComponent<T>
|
|
360
371
|
extends Omit<HTMLAttributes<HTMLElement>, 'slot'> {
|
|
361
372
|
name: string;
|
|
373
|
+
lazy?: boolean;
|
|
362
374
|
slot?: SlotProps<T>;
|
|
363
375
|
context?: Context<T>;
|
|
364
376
|
render?: (props: Record<string, any>) => VNode | VNode[];
|
|
@@ -371,6 +383,7 @@ interface SlotPropsComponent<T>
|
|
|
371
383
|
|
|
372
384
|
export function Slot<T>({
|
|
373
385
|
name,
|
|
386
|
+
lazy = false,
|
|
374
387
|
context,
|
|
375
388
|
slot,
|
|
376
389
|
children,
|
|
@@ -400,11 +413,11 @@ export function Slot<T>({
|
|
|
400
413
|
}
|
|
401
414
|
|
|
402
415
|
// add slot to queue
|
|
403
|
-
if (slotsQueue) {
|
|
416
|
+
if (slotsQueue && lazy === false) {
|
|
404
417
|
slotsQueue.value.add(name);
|
|
405
418
|
slotsQueue.value = new Set(slotsQueue.value);
|
|
406
419
|
}
|
|
407
|
-
}, [name, slotsQueue]);
|
|
420
|
+
}, [name, lazy, slotsQueue]);
|
|
408
421
|
|
|
409
422
|
return createElement(
|
|
410
423
|
slotTag,
|