@ecopages/scripts-injector 0.1.0
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/LICENSE +21 -0
- package/README.md +37 -0
- package/package.json +30 -0
- package/scripts-injector.js +1 -0
- package/types.d.ts +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Andrea Zanenghi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Scripts Injector
|
|
2
|
+
|
|
3
|
+
The Scripts Injector is a custom element designed to dynamically load scripts into your web page. It provides a way to load scripts based on certain conditions and events.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
To use the Scripts Injector, you need to include it in your HTML:
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
<scripts-injector></scripts-injector>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
|
|
15
|
+
The ScriptInjectorProps type defines the properties that can be used to control the behavior of the script-injector custom element. Here are the available properties:
|
|
16
|
+
|
|
17
|
+
`"on:idle"`: This optional boolean property determines whether the script should be loaded once the DOM is ready. For example, `<script-injector on:idle></script-injector>` will load the script as soon as the DOM is ready.
|
|
18
|
+
|
|
19
|
+
`"on:interaction"`: This optional property allows you to specify a series of events that will trigger the loading of the script. It can be either "touchstart,click" or "mouseenter,focusin". For example, `<script-injector on:interaction="mouseenter, focusin"></script-injector>` will load the script when the user either mouses over the element or focuses on it.
|
|
20
|
+
|
|
21
|
+
`"on:visible"`: This optional property can be either a string or a boolean. If it's a string, it should specify the root margin for an Intersection Observer that will load the script when the element comes into view. If it's a boolean and it's true, the script will be loaded when the element comes into view with a default root margin. For example, `<script-injector on:visible="50px 1px"></script-injector>` will load the script when the element comes into view, with a root margin of 50px 1px.
|
|
22
|
+
|
|
23
|
+
`scripts`: This property should be a comma-separated string of scripts to be loaded. For example, `<script-injector scripts="script1.js,script2.js"></script-injector>` will load script1.js and script2.js.
|
|
24
|
+
|
|
25
|
+
## Typical usage
|
|
26
|
+
|
|
27
|
+
This passage provides a standard use case for the custom element. The script is designed to load when the user interacts with it, either through mouse entry or a focus event. If multiple script injectors are present with the same script, only the first one will execute the load. This is because once a script is loaded, all script injectors are notified and subsequently remove the script from their loading responsibilities.
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<script-injector on:interaction="mouseenter,focusin" scripts={["path/to/my/element"]}>
|
|
31
|
+
<lit-counter class="lit-counter" count={8}></lit-counter>
|
|
32
|
+
</script-injector>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Inspiration
|
|
36
|
+
|
|
37
|
+
This is undeniably heavily influenced by [11ty/is-land](https://github.com/11ty/is-land), [Astro island](https://docs.astro.build/en/concepts/islands/) and the "component island" concept, originally introduced by Etsy's frontend architect, Katie Sylor-Miller.
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ecopages/scripts-injector",
|
|
3
|
+
"main": "scripts-injector.js",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "types.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./package.json": {
|
|
9
|
+
"import": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/scripts-injector.js",
|
|
13
|
+
"require": "./dist/scripts-injector.js"
|
|
14
|
+
},
|
|
15
|
+
"./types": "./types.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build:lib": "rm -rf dist && bun build --entry scripts-injector.ts --outdir . --minify"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/bun": "latest"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"typescript": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"scripts-injector.js",
|
|
28
|
+
"/types.d.ts"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i;(function(t){t["DATA_LOADED"]="data-loaded"})(i||(i={}));var o=["on:visible","on:idle","on:interaction"];class n extends HTMLElement{intersectionObserver=null;scriptsToLoad=[];registeredEvents=[];conditionsMap={visible:this.onVisible.bind(this),idle:this.onIdle.bind(this),interaction:this.onInteraction.bind(this)};constructor(){super();this.loadScripts=this.loadScripts.bind(this),this.listenToDataLoaded=this.listenToDataLoaded.bind(this)}connectedCallback(){this.scriptsToLoad=this.getAttribute("scripts")?.split(",")||[],document.addEventListener(i.DATA_LOADED,this.listenToDataLoaded),this.applyConditions()}disconnectedCallback(){if(this.unregisterEvents(),this.intersectionObserver)this.intersectionObserver.disconnect()}notifyInjectors(){document.dispatchEvent(new CustomEvent(i.DATA_LOADED,{detail:{loadedScripts:this.scriptsToLoad}}))}applyConditions(){const e=Object.keys(this.conditionsMap);for(let t of e)if(this.hasAttribute(`on:${t}`))this.conditionsMap[t]()}onVisible(){return this.setupIntersectionObserver()}onIdle(){return this.loadScripts()}onInteraction(){const e=this.getAttribute("on:interaction");if(!e)return;for(let t of e.split(","))this.addEventListener(t,this.loadScripts),this.registeredEvents.push({type:t,listener:this.loadScripts})}listenToDataLoaded(e){if(this.hasAttribute("data-loaded"))return;const{loadedScripts:t}=e.detail;if(this.scriptsToLoad=this.scriptsToLoad.filter((s)=>!t.includes(s)),this.scriptsToLoad.length===0)this.setAttribute("data-loaded",""),this.unregisterEvents()}unregisterEvents(){document.dispatchEvent(new CustomEvent(i.DATA_LOADED,{detail:{loadedScripts:this.scriptsToLoad}})),this.intersectionObserver?.disconnect();for(let{type:e,listener:t}of this.registeredEvents)this.removeEventListener(e,t)}loadScripts(){try{for(let e of this.scriptsToLoad)this.loadScript(e)}catch(e){console.error("Error loading scripts",e)}finally{this.setAttribute("data-loaded",""),this.unregisterEvents(),this.notifyInjectors()}}loadScript(e){const t=document.createElement("script");t.src=e,t.type="module",document.head.appendChild(t)}setupIntersectionObserver(){const e={rootMargin:"50px 0px",threshold:0.1};this.intersectionObserver=new IntersectionObserver((t)=>{for(let s of t)if(s.isIntersecting)this.loadScripts()},e),this.intersectionObserver.observe(this)}}customElements.define("scripts-injector",n);export{o as conditions,n as ScriptsInjector,i as ScriptInjectorEvents};
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ScriptsInjector } from './scripts-injector';
|
|
2
|
+
import { ScriptInjectorEvents, type conditions } from './scripts-injector';
|
|
3
|
+
|
|
4
|
+
export type OnDataLoadedEvent = CustomEvent<{ loadedScripts: string[] }>;
|
|
5
|
+
|
|
6
|
+
export type Conditions = (typeof conditions)[number];
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
interface HTMLElementTagNameMap {
|
|
10
|
+
'scripts-injector': ScriptsInjector;
|
|
11
|
+
}
|
|
12
|
+
namespace JSX {
|
|
13
|
+
interface IntrinsicElements {
|
|
14
|
+
'scripts-injector': HtmlTag & ScriptInjectorProps;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
interface HTMLElementEventMap {
|
|
18
|
+
[ScriptInjectorEvents.DATA_LOADED]: OnDataLoadedEvent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare type ScriptInjectorProps = {
|
|
23
|
+
/**
|
|
24
|
+
* @description Load the script once the dom is ready
|
|
25
|
+
* @example <script-injector on:idle></script-injector>
|
|
26
|
+
*/
|
|
27
|
+
'on:idle'?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* @description Load the script based on a series of events
|
|
30
|
+
* @example <script-injector on:interaction="mouseenter, focusin"></script-injector>
|
|
31
|
+
*/
|
|
32
|
+
'on:interaction'?: 'touchstart,click' | 'mouseenter,focusin';
|
|
33
|
+
/**
|
|
34
|
+
* @description Import a script to be loaded when the observer detects the element is in the viewport
|
|
35
|
+
* @example <script-injector on:visible="50px 1px"></script-injector>
|
|
36
|
+
*/
|
|
37
|
+
'on:visible'?: string | boolean;
|
|
38
|
+
/**
|
|
39
|
+
* A list of scripts to be loaded, comma separated.
|
|
40
|
+
*/
|
|
41
|
+
scripts: string;
|
|
42
|
+
};
|