Skip to content

Commit ee963aa

Browse files
authored
Merge pull request #49 from EranGrin/feature/init-promise
Feature/init-promise
2 parents 873a2ac + 3476c8f commit ee963aa

13 files changed

+336
-75
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format
44

55
## [Unreleased]
66

7+
## [1.7.0] - 10.02.2025
8+
### Added
9+
- Added support for `asyncInitialization`
10+
- Added support for loader
11+
- Added support for `hideSlotContentUntilMounted`
12+
713
## [1.6.11] - 16.12.2024
814
### Fixed
915
- Fixed issue with slots with no shadow DOM

README.md

+86-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ See the [Documentation](https://erangrin.github.io/vue-web-component-wrapper) fo
4646
- **Disable Removal of Styles on Unmount**: Control the removal of styles upon component unmount to solve issues with CSS transitions.
4747
- **Disable Shadow DOM**: Option to disable Shadow DOM for web components.
4848
- **Replace `:root` with `:host`**: Optionally replace `:root` selectors with `:host` in your CSS to ensure styles are correctly scoped within the Shadow DOM.
49-
49+
- **Async Initialization**: Option to delay the initialization until its Promise resolves.
50+
- **Loader Support**: Support for loader spinner elements until the application is fully initialized.
51+
- **Hide slot content until the component is fully mounted**: Option to hide the content of named slots until the web-component is fully mounted.
5052
## CSS Frameworks Examples
5153

5254
- **Tailwind CSS**: [Demo](https://stackblitz.com/edit/vue-web-component-wrapper?file=README.md&startScript=tailwind-demo)
@@ -144,6 +146,8 @@ createWebComponent({
144146
disableStyleRemoval: false, // default is false
145147
disableShadowDOM: false, // default is false
146148
replaceRootWithHostInCssFramework: false, // default is false
149+
loaderAttribute: 'data-web-component-loader', // default is 'data-web-component-loader'
150+
hideSlotContentUntilMounted: true, // default is false
147151
});
148152
```
149153

@@ -160,11 +164,92 @@ createWebComponent({
160164
- **disableStyleRemoval**: Disable removal of styles on unmount (useful for CSS transitions).
161165
- **disableShadowDOM**: Disable Shadow DOM for web components.
162166
- **replaceRootWithHostInCssFramework**: Replace `:root` selectors with `:host` in your CSS styles.
167+
- **asyncInitialization**: Accepts a function that returns a Promise.
168+
- **loaderAttribute**: Defines the attribute used to mark loader spinner (default is `data-web-component-loader`).
169+
- **hideSlotContentUntilMounted**: Hide the content of named slots until the component is fully mounted.
170+
171+
### asyncInitialization
172+
173+
The `asyncInitialization` option accepts a function that returns a Promise. The custom element waits for this Promise to resolve before completing its initialization. This is useful for performing asynchronous tasks (e.g., API calls, dynamic imports) before the app mounts.
174+
175+
#### Example Usage
176+
177+
```javascript
178+
const asyncPromise = () => {
179+
return new Promise((resolve) => {
180+
setTimeout(() => {
181+
resolve()
182+
}, 1000)
183+
})
184+
}
185+
186+
createWebComponent({
187+
rootComponent: App,
188+
elementName: 'my-web-component',
189+
plugins: pluginsWrapper,
190+
cssFrameworkStyles: tailwindStyles,
191+
VueDefineCustomElement,
192+
h,
193+
createApp,
194+
getCurrentInstance,
195+
asyncInitialization: asyncPromise, // default is Promise.resolve()
196+
loaderAttribute: 'data-web-component-loader',
197+
hideSlotContentUntilMounted: true, // default is false
198+
});
199+
```
200+
201+
### loaderAttribute
202+
203+
The `loaderAttribute` option defines the attribute used to mark loader spinner elements in your custom element's DOM. Elements with this attribute will be removed automatically once the component is fully mounted.
204+
205+
```html
206+
<my-web-component
207+
class="my-web-component"
208+
>
209+
<!-- named slot -->
210+
<div class="customName" data-web-component-loader slot="customName">
211+
<div class="spinner"></div>
212+
</div>
213+
</my-web-component>
214+
215+
<style>
216+
.spinner {
217+
border: 4px solid rgba(0, 0, 0, 0.1);
218+
border-left-color: #4a90e2; /* Customize spinner color if needed */
219+
border-radius: 50%;
220+
width: 30px;
221+
height: 30px;
222+
animation: spin 1s linear infinite;
223+
margin: auto;
224+
}
225+
226+
@keyframes spin {
227+
to {
228+
transform: rotate(360deg);
229+
}
230+
}
231+
</style>
232+
```
233+
234+
### hideSlotContentUntilMounted
235+
236+
The `hideSlotContentUntilMounted` option hides the content of named slots until the component is fully mounted.
237+
- By using the `hidden` attribute on the slot element, the content will be hidden until the component is fully mounted, and the web component wrapper will remove the `hidden` attribute once the component is fully mounted.
238+
- This could be break the layout of your application, if you use the `hidden` attribute internally in your application.
239+
- If you want to use the `hidden` attribute internally in your application, you can set the `hideSlotContentUntilMounted` option to `false`.
240+
241+
```html
242+
<my-web-component>
243+
<!-- named slot -->
244+
<div class="customName" hidden slot="customName">I am a custom named slot </div>
245+
</my-web-component>
246+
```
163247

164248
### replaceRootWithHostInCssFramework
165249

166250
The `replaceRootWithHostInCssFramework` option replaces all occurrences of `:root` with `:host` in your `cssFrameworkStyles`. This is useful when working with CSS variables defined on `:root`, ensuring they are properly scoped within the Shadow DOM.
167251

252+
168253
#### Example Usage
169254

170255
```javascript

demo-app-vite/index.html

+19-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ <h2>Custom Event</h2>
5858
></my-child-component>
5959

6060
<!-- named slot -->
61-
<div slot="customName">I am a custom named slot </div>
61+
<div class="customName" hidden slot="customName">I am a custom named slot </div>
62+
<div class="customName" data-web-component-loader slot="customName">
63+
<div class="spinner"></div>
64+
</div>
6265
</my-web-component>
6366

6467
<script type="module" src="./src/main.ts"></script>
@@ -117,5 +120,20 @@ <h2>Custom Event</h2>
117120
color: white;
118121
font-weight: bold;
119122
}
123+
.spinner {
124+
border: 4px solid rgba(0, 0, 0, 0.1);
125+
border-left-color: #4a90e2; /* Customize spinner color if needed */
126+
border-radius: 50%;
127+
width: 30px;
128+
height: 30px;
129+
animation: spin 1s linear infinite;
130+
margin: auto;
131+
}
132+
133+
@keyframes spin {
134+
to {
135+
transform: rotate(360deg);
136+
}
137+
}
120138
</style>
121139
</html>

demo-app-vite/src/App.vue

+44-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<template>
2-
<div>
3-
<header class="bg-blue-300 rounded-lg shadow-lg p-4 mx-0 md:mx-20">
4-
2+
<div :style="{ opacity: componentOpacity, transition: 'opacity 0.5s ease' }"> <header class="bg-blue-300 rounded-lg shadow-lg p-4 mx-0 md:mx-20">
53
<nav>
64
<ul class="flex justify-between sm:mx-1 md:mx-20">
75
<li>
@@ -22,34 +20,38 @@
2220
<header
2321
class="bg-gray-800 text-white text-center p-4 mt-4 rounded-lg shadow-lg mx-0 md:mx-20"
2422
>
25-
<div
26-
style="display: flex; justify-content: space-between; "
27-
>
28-
<div class=" text-black flex">
29-
<input
30-
class="p-2 rounded-lg shadow-lg"
31-
:value="modelValue"
32-
@input="$emit('update:modelValue', ($event.target as HTMLInputElement)?.value)"
33-
/>
34-
<div class="text-white ml-4 align self-center">
35-
{{ modelValue }}
23+
<div style="display: flex; justify-content: space-between">
24+
<div class="text-black flex">
25+
<input
26+
class="p-2 rounded-lg shadow-lg"
27+
:value="modelValue"
28+
@input="
29+
$emit(
30+
'update:modelValue',
31+
($event.target as HTMLInputElement)?.value
32+
)
33+
"
34+
/>
35+
<div class="text-white ml-4 align self-center">
36+
{{ modelValue }}
37+
</div>
3638
</div>
39+
40+
<button
41+
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
42+
@click="testEmit"
43+
>
44+
emit event outside
45+
</button>
3746
</div>
38-
39-
<button
40-
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
41-
@click="testEmit"
42-
>
43-
emit event outside
44-
</button>
45-
</div>
4647
</header>
4748
<main class="mt-4 p-4 mx-0 md:mx-20 bg-gray-200 rounded-lg shadow-lg">
4849
<router-view />
4950
</main>
50-
<footer class="bg-gray-800 text-white text-center p-4 mt-4 rounded-lg shadow-lg mx-0 md:mx-20">
51+
<footer
52+
class="bg-gray-800 text-white text-center p-4 mt-4 rounded-lg shadow-lg mx-0 md:mx-20"
53+
>
5154
<div class="mb-2">
52-
5355
<slot></slot>
5456
</div>
5557
<div class="flex justify-center">
@@ -60,6 +62,8 @@
6062
</template>
6163

6264
<script lang="ts">
65+
66+
6367
export default {
6468
name: 'App',
6569
namedSlots: ['customName'], // only needed with Shadow DOM
@@ -81,19 +85,25 @@ export default {
8185
apiToken: '',
8286
baseUri: '',
8387
},
88+
componentOpacity: 0, // Initial opacity set to 0
8489
}),
8590
91+
mounted() {
92+
// Delay setting opacity to 1 to trigger fade-in
93+
setTimeout(() => {
94+
this.componentOpacity = 1;
95+
}, 10); // Small delay, adjust if needed
96+
},
97+
8698
8799
methods: {
88100
testEmit() {
89-
this.$emit('customEventTest',
90-
{
91-
testEvent: '123456789',
92-
}
93-
)
94-
}
101+
this.$emit('customEventTest', {
102+
testEvent: '123456789',
103+
});
104+
},
95105
},
96-
}
106+
};
97107
</script>
98108
<style>
99109
.test-heading {
@@ -104,11 +114,12 @@ a {
104114
@apply text-gray-900 hover:text-gray-700;
105115
}
106116
107-
header {
117+
header {
108118
@apply font-sans;
109119
}
110120
111121
main {
112122
@apply font-sans;
113123
}
114-
</style>
124+
125+
</style>

demo-app-vite/src/main.ts

+14
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ import createWebComponent from '../../package/index.js'
1515
// import createWebComponent from 'vue-web-component-wrapper'
1616
// add vue-web-component-wrapper to package.json asl well
1717

18+
const asyncPromise = () => {
19+
return new Promise((resolve) => {
20+
setTimeout(() => {
21+
resolve("p1")
22+
}, 1000)
23+
})
24+
}
25+
1826
createWebComponent({
1927
rootComponent: app,
2028
elementName: 'my-web-component',
@@ -24,15 +32,21 @@ createWebComponent({
2432
h,
2533
createApp,
2634
getCurrentInstance,
35+
disableShadowDOM: false,
36+
asyncInitialization: asyncPromise,
37+
hideSlotContentUntilMounted: true
2738
})
2839

2940
createWebComponent({
3041
rootComponent: appChild,
3142
elementName: 'my-child-component',
3243
plugins: pluginsWrapper,
3344
cssFrameworkStyles: style,
45+
disableShadowDOM: false,
3446
VueDefineCustomElement,
3547
h,
3648
createApp,
3749
getCurrentInstance,
50+
asyncInitialization: () => new Promise((res) => setTimeout(() => res("p1"), 1000)),
51+
hideSlotContentUntilMounted: true
3852
})

demo-app-vite/src/routes/test1-page.vue

+14
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,24 @@
1212

1313
<script lang="ts">
1414
import TestComponent1 from '../components/TestComponent1.vue'
15+
import { useRoute } from 'vue-router';
16+
1517
export default {
1618
components: {
1719
TestComponent1,
1820
},
21+
22+
setup() {
23+
24+
const route = useRoute();
25+
const param = route.query.param;
26+
27+
28+
console.log(param);
29+
30+
return {}
31+
},
32+
1933
}
2034
</script>
2135

demo-app-vite/src/routes/test2-page.vue

+13
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,23 @@
33
<h1 class="test2 text-2xl bg-blue-400 rounded-xl p-4">
44
I am a route 2
55
<span class=".nest text-4xl">Test SCSS</span>
6+
7+
<a href="http://localhost:5173/#/test1?param=test" target="blank">http://localhost:8080/?param=1</a>
68
</h1>
79
</div>
810
</template>
911

12+
<script lang="ts">
13+
export default {
14+
created() {
15+
// this.$route.query.param;
16+
console.log(this.$route.query.param);
17+
console.log("created")
18+
},
19+
}
20+
21+
</script>
22+
1023
<style scoped lang="scss">
1124
.test2 {
1225
color: red;

docs/.vitepress/config.js

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export default {
2222
{ text: 'disable-shadow-dom', link: '/disable-shadow-dom' },
2323
{ text: 'host-implementation', link: '/host-implementation' },
2424
{ text: 'SFC as Custom Element', link: '/sfc-as-custom-element' },
25+
{ text: 'Async Initialization', link: '/async-initialization' },
26+
{ text: 'Replace :root with :host', link: '/replace-root-with-host' },
2527
],
2628
},
2729
{

0 commit comments

Comments
 (0)