kempo 3.2.0 → 4.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.
@@ -0,0 +1 @@
1
+ import Component from"./Component.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";const _position=Symbol();export class ToastContainer extends Component{constructor(t="bottom center"){super(),this[_position]=t.toLowerCase(),this.setAttribute("position",this[_position])}get position(){return this[_position]}set position(t){t!==this[_position]&&(this[_position]=t.toLowerCase(),this.setAttribute("position",this[_position]))}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n position: fixed;\n display: flex;\n flex-direction: column;\n gap: 8px;\n z-index: 1000;\n pointer-events: none;\n padding: 16px;\n box-sizing: border-box;\n max-width: 100%;\n max-height: 100%;\n overflow: hidden;\n }\n\n :host([position*="top"]) {\n top: 0;\n }\n\n :host([position*="bottom"]) {\n bottom: 0;\n }\n\n :host([position*="left"]) {\n left: 0;\n align-items: flex-start;\n }\n\n :host([position*="right"]) {\n right: 0;\n align-items: flex-end;\n }\n\n :host([position*="center"]:not([position*="top"]):not([position*="bottom"])) {\n top: 50%;\n transform: translateY(-50%);\n }\n \n :host([position="center"]),\n :host([position*="center"][position*="top"]),\n :host([position*="center"][position*="bottom"]) {\n left: 50%;\n transform: translateX(-50%);\n }\n\n :host([position*="center"]:not([position="center"])) {\n align-items: center;\n }\n \n /* Combined transforms for center vertically and horizontally */\n :host([position*="center"]:not([position*="top"]):not([position*="bottom"]):not([position="center"])) {\n transform: translate(-50%, -50%);\n }\n\n ::slotted(*) {\n pointer-events: auto;\n }\n `}}window.customElements.define("k-toast-container",ToastContainer);const closeHandler=Symbol(),actionHandler=Symbol(),timeoutId=Symbol(),animationEndHandler=Symbol(),_toastPosition=Symbol(),_closing=Symbol();export default class Toast extends Component{constructor({actionCallback:t=()=>{},closeCallback:n=()=>{},timeout:o=0,position:i="bottom center"}={}){super(),this[timeoutId]=-1,this[_toastPosition]=i.toLowerCase(),this[_closing]=!1,this[closeHandler]=()=>{this.close()},this[actionHandler]=()=>{!1!==this.actionCallback()&&this.close()},this[animationEndHandler]=t=>{t.animationName.includes("toast-hide")?(this.removeAttribute("animating"),this.opened=!1,dispatchEvent(this,"close openchange"),this.closeCallback(),this[_closing]=!1):t.animationName.includes("toast-show")&&this.removeAttribute("animating")},this.registerProps({actionCallback:t,closeCallback:n}),this.registerAttributes({hasAction:!1,hasClose:!1,hasIcon:!1,timeout:o,opened:!1,position:this[_toastPosition]})}async render(t){return!!await super.render(t)&&(onEvent(this.shadowRoot.getElementById("action"),"click",this[actionHandler]),onEvent(this.shadowRoot.getElementById("close"),"click",this[closeHandler]),onEvent(this,"animationend",this[animationEndHandler]),!0)}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this[timeoutId]),offEvent(this.shadowRoot.getElementById("action"),"click",this[actionHandler]),offEvent(this.shadowRoot.getElementById("close"),"click",this[closeHandler]),offEvent(this,"animationend",this[animationEndHandler])}attributeChangedCallback(t,n,o){super.attributeChangedCallback(t,n,o),"opened"===t&&n!==o&&o&&(this.hasAction=!!this.querySelector('[slot="action"]'),this.hasClose=!!this.querySelector('[slot="close"]'),this.hasIcon=!!this.querySelector('[slot="icon"]'))}open(){this[_closing]=!1,this.setAttribute("animating","in"),this.opened=!0,this.timeout&&(clearTimeout(this[timeoutId]),this[timeoutId]=setTimeout((()=>{this.close()}),this.timeout)),dispatchEvent(this,"open openchange")}close(){clearTimeout(this[timeoutId]),this.opened&&!this[_closing]&&(this[_closing]=!0,this.setAttribute("animating","out"))}get shadowTemplate(){return`\n <div id="icon">\n <slot name="icon"></slot>\n </div>\n <div id="message">\n ${super.shadowTemplate}\n </div>\n <button id="action" class="no-style">\n <slot name="action"></slot>\n </button>\n <button id="close" class="no-style">\n <slot name="close"></slot>\n </button>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n @keyframes toast-show-bottom {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes toast-hide-bottom {\n from { opacity: 1; transform: translateY(0); }\n to { opacity: 0; transform: translateY(20px); }\n }\n @keyframes toast-show-top {\n from { opacity: 0; transform: translateY(-20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes toast-hide-top {\n from { opacity: 1; transform: translateY(0); }\n to { opacity: 0; transform: translateY(-20px); }\n }\n @keyframes toast-show-left {\n from { opacity: 0; transform: translateX(-20px); }\n to { opacity: 1; transform: translateX(0); }\n }\n @keyframes toast-hide-left {\n from { opacity: 1; transform: translateX(0); }\n to { opacity: 0; transform: translateX(-20px); }\n }\n @keyframes toast-show-right {\n from { opacity: 0; transform: translateX(20px); }\n to { opacity: 1; transform: translateX(0); }\n }\n @keyframes toast-hide-right {\n from { opacity: 1; transform: translateX(0); }\n to { opacity: 0; transform: translateX(20px); }\n }\n :host {\n display: none;\n min-width: 16rem;\n max-width: calc(100% - ( 2 * var(--spacer) ));\n background-color: var(--c_bg);\n border-radius: var(--radius);\n box-shadow: var(--drop_shadow);\n }\n :host([opened="true"]) {\n display: flex;\n }\n :host([animating="in"][position*="bottom"]) {\n animation: toast-show-bottom var(--animation_ms, 300ms) ease forwards;\n }\n :host([animating="out"][position*="bottom"]) {\n animation: toast-hide-bottom var(--animation_ms, 300ms) ease forwards;\n }\n :host([animating="in"][position*="top"]) {\n animation: toast-show-top var(--animation_ms, 300ms) ease forwards;\n }\n :host([animating="out"][position*="top"]) {\n animation: toast-hide-top var(--animation_ms, 300ms) ease forwards;\n }\n :host([animating="in"][position*="left"]:not([position*="top"]):not([position*="bottom"])) {\n animation: toast-show-left var(--animation_ms, 300ms) ease forwards;\n }\n :host([animating="out"][position*="left"]:not([position*="top"]):not([position*="bottom"])) {\n animation: toast-hide-left var(--animation_ms, 300ms) ease forwards;\n }\n :host([animating="in"][position*="right"]:not([position*="top"]):not([position*="bottom"])) {\n animation: toast-show-right var(--animation_ms, 300ms) ease forwards;\n }\n :host([animating="out"][position*="right"]:not([position*="top"]):not([position*="bottom"])) {\n animation: toast-hide-right var(--animation_ms, 300ms) ease forwards;\n }\n #icon {\n padding: var(--spacer);\n padding-right: 0;\n }\n #message {\n padding: var(--spacer);\n flex: 1 1 auto;\n }\n :host(:not([has-close])) #close,\n :host(:not([has-action])) #action,\n :host(:not([has-icon])) #icon {\n display: none;\n }\n #action {\n background: transparent;\n border: none;\n color: var(--tc_primary, blue);\n cursor: pointer;\n padding: var(--spacer);\n }\n #action:hover {\n color: var(--tc_primary__hover, lightblue);\n }\n #close {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: var(--spacer);\n opacity: 0.7;\n transition: opacity var(--animation_ms);\n }\n #close:hover {\n opacity: 1;\n }\n `}static observedAttributes=[...super.observedAttributes,"opened"];static create(t,n={}){let{position:o="auto",removeOnClose:i=!0,closeCallback:s=()=>{},action:e=!1,close:a=!1,icon:r=!1}=n;"auto"===o&&(o=window.innerWidth<=768?"bottom center":"top right");let c=document.querySelector(`k-toast-container[position="${o}"]`);c||(c=new ToastContainer(o),document.body.appendChild(c));const l=new Toast({position:o,timeout:5e3,...n,closeCallback:(...t)=>{i&&(l.remove(),0===c.children.length&&c.remove()),s(...t)}});if(l.innerHTML=t,r)if(r instanceof HTMLElement)r.slot="icon",l.appencChild(r);else if("string"==typeof r){const t=document.createElement("span");t.slot="icon",t.innerHTML=r,l.appendChild(t)}if(e)if(e instanceof HTMLElement)e.slot="action",l.appencChild(e);else if("string"==typeof e){const t=document.createElement("span");t.slot="action",t.innerHTML=e,l.appendChild(t)}if(a)if(a instanceof HTMLElement)a.slot="close",l.appencChild(a);else if("string"==typeof a){const t=document.createElement("span");t.slot="close",t.innerHTML=a,l.appendChild(t)}return l.open(),c.appendChild(l),l}static success(t,n={}){const o=Toast.create(t,{icon:'<k-icon name="check"></k-icon>',...n});return o.classList.add("bg-success"),o}static warning(t,n={}){const o=Toast.create(t,{icon:'<k-icon name="warning"></k-icon>',...n});return o.classList.add("bg-warning"),o}static error(t,n={}){const o=Toast.create(t,{icon:'<k-icon name="error"></k-icon>',...n});return o.classList.add("bg-danger"),o}}window.customElements.define("k-toast",Toast);
@@ -0,0 +1 @@
1
+ import http from"http";import{readFile}from"fs/promises";import{join,extname,normalize}from"path";export class Server{constructor(t={}){this.routes=[]}route(t,e,r){const n=r?r.map((t=>t.toUpperCase())):["ANY"];return this.routes.push({pattern:t,handler:e,methods:n}),this}get(t,e){return this.route(t,e,["GET"])}post(t,e){return this.route(t,e,["POST"])}put(t,e){return this.route(t,e,["PUT"])}delete(t,e){return this.route(t,e,["DELETE"])}patch(t,e){return this.route(t,e,["PATCH"])}async handleRequest(t,e){try{const[r,n=""]=t.url.split("?"),s=n.split("&").filter((t=>""!=t)).map((t=>{const[e,r]=t.split("=");return{[e]:decodeURIComponent(r)}})),i=this.routes.find((({methods:e,pattern:n})=>{if(!e.includes(t.method||"GET")&&!e.includes("ANY"))return!1;const s=r.split("/").filter((t=>""!=t)),i=n.split("/").filter((t=>""!=t));if(0==s.length&&0==i.length)return!0;const o=i.length>0&&"*"===i[i.length-1];if(!o&&s.length!==i.length)return!1;if(o&&s.length<i.length-1)return!1;const a=o?i.length-1:i.length;for(let t=0;t<a;t++)if(!i[t].startsWith(":")&&i[t]!==s[t])return!1;return!0}));if(!i)return e.writeHead(404,{"Content-Type":"text/plain"}),void e.end("Not Found");const{pattern:o,handler:a}=i;if("string"==typeof a){const t=o.split("/").filter((t=>""!=t)),n=r.split("/").filter((t=>""!=t)),s="*"===t[t.length-1]?n.slice(t.length-1).join("/"):"",i=a.endsWith("*")?a.slice(0,-1):a,l=i.startsWith("/")||/^[A-Z]:/i.test(i)?normalize(join(i,s)):join(process.cwd(),normalize(join(i,s)));l.endsWith("/")||l.endsWith("\\")?await Server.serveFile(join(l,"index.html"),e):await Server.serveFile(l,e)}else if("function"==typeof a){const n={};o.includes(":")&&o.split("/").filter((t=>""!=t)).forEach(((t,e)=>{t.startsWith(":")&&(n[t.slice(1)]=r.split("/").filter((t=>""!=t))[e])})),await a(t,e,n,s)}else console.error("Invalid Route Handler"),e.writeHead(500,{"Content-Type":"text/plain"}),e.end("Internal server error")}catch(t){console.error("Request error:",t),e.writeHead(500,{"Content-Type":"text/plain"}),e.end("Internal server error")}}listen(t=8080){return new Promise(((e,r)=>{try{const n=http.createServer(((t,e)=>this.handleRequest(t,e)));n.on("error",(e=>{"EADDRINUSE"===e.code?r(new Error(`Port ${t} is already in use`)):r(e)})),n.listen(t,(()=>{e(n)}))}catch(t){r(t)}}))}static mimeTypes={".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".txt":"text/plain; charset=utf-8",".md":"text/plain; charset=utf-8",".map":"application/json; charset=utf-8"};static textExtensions=[".txt",".md",".js",".css",".html",".json",".map"];static getMimeType(t){const e=extname(t).toLowerCase();return this.mimeTypes[e]?this.mimeTypes[e]:this.textExtensions.includes(e)?"text/plain; charset=utf-8":"application/octet-stream"}static async serveFile(t,e,r={}){try{const n=await readFile(t);e.writeHead(200,{"Content-Type":Server.getMimeType(t),...r}),e.end(n)}catch(t){const r="ENOENT"===t.code?404:500,n=404===r?"Not found":"Internal server error";e.writeHead(r,{"Content-Type":"text/plain"}),e.end(n)}}}
@@ -1 +1 @@
1
- export default(e={})=>{let t,o,n;const{element:s=(()=>{}),callback:a=(()=>{}),startCallback:v=(()=>{}),moveCallback:i=(()=>{}),endCallback:r=(()=>{}),preventScroll:d=!1}=e,u=s=>{d&&s.preventDefault(),clearTimeout(n);const v=s.pageX||s.touches[0].pageX,r=s.pageY||s.touches[0].pageY,u={x:v-t,y:r-o,...e};a(u),i(u)},c=({pageX:s,pageY:v})=>{clearTimeout(n);const i={x:s-t,y:v-o,...e};a(i),r(i),m()},p=s=>{clearTimeout(n),t=s.pageX||s.touches[0].pageX,o=s.pageY||s.touches[0].pageY;const a={x:0,y:0,...e};v(a),window.addEventListener("mousemove",u,{passive:!d}),window.addEventListener("mouseup",c,{passive:!d}),window.addEventListener("touchmove",u,{passive:!d}),window.addEventListener("touchend",c,{passive:!d})},m=()=>{window.removeEventListener("mousemove",u),window.removeEventListener("mouseup",c),window.removeEventListener("touchmove",u),window.removeEventListener("touchend",c)};return s.addEventListener("mousedown",p,{passive:!d}),s.addEventListener("touchstart",p,{passive:!d}),()=>{s.removeEventListener("mousedown",p),m()}};
1
+ export default(e={})=>{let t,o,n;const{element:s=()=>{},callback:a=()=>{},startCallback:v=()=>{},moveCallback:i=()=>{},endCallback:r=()=>{},preventScroll:d=!1}=e,u=s=>{d&&s.preventDefault(),clearTimeout(n);const v=s.pageX||s.touches[0].pageX,r=s.pageY||s.touches[0].pageY,u={x:v-t,y:r-o,...e};a(u),i(u)},c=({pageX:s,pageY:v})=>{clearTimeout(n);const i={x:s-t,y:v-o,...e};a(i),r(i),m()},p=s=>{clearTimeout(n),t=s.pageX||s.touches[0].pageX,o=s.pageY||s.touches[0].pageY;const a={x:0,y:0,...e};v(a),window.addEventListener("mousemove",u,{passive:!d}),window.addEventListener("mouseup",c,{passive:!d}),window.addEventListener("touchmove",u,{passive:!d}),window.addEventListener("touchend",c,{passive:!d})},m=()=>{window.removeEventListener("mousemove",u),window.removeEventListener("mouseup",c),window.removeEventListener("touchmove",u),window.removeEventListener("touchend",c)};return s.addEventListener("mousedown",p,{passive:!d}),s.addEventListener("touchstart",p,{passive:!d}),()=>{s.removeEventListener("mousedown",p),m()}};
@@ -0,0 +1 @@
1
+ import fs from"fs/promises";import path from"path";export async function ensureDir(i){try{await fs.mkdir(i,{recursive:!0})}catch(i){if("EEXIST"!==i.code)throw i}}export async function copyDir(i,r){await ensureDir(r);const t=await fs.readdir(i,{withFileTypes:!0});for(const a of t){const t=path.join(i,a.name),o=path.join(r,a.name);a.isDirectory()?await copyDir(t,o):await fs.copyFile(t,o)}}export async function emptyDir(i){try{const r=await fs.readdir(i);await Promise.all(r.map((r=>fs.rm(path.join(i,r),{recursive:!0,force:!0}))))}catch(i){if("ENOENT"!==i.code)throw i}}
@@ -0,0 +1 @@
1
+ export function minifyCss(e){return e.replace(/\s*\{\s*/g,"{").replace(/\s*\}\s*/g,"}").replace(/\n/g,"").replace(/\r/g,"").replace(/\s+/g," ").replace(/\s*\:\s*/g,":").replace(/\s*\;\s*/g,";").replace(/;\}/g,"}")}
package/docs/nav.inc.html CHANGED
@@ -55,6 +55,7 @@
55
55
  <a href="%%root%%components/table.html">Table</a>
56
56
  <a href="%%root%%components/tabs.html">Tabs</a>
57
57
  <a href="%%root%%components/tags.html">Tags</a>
58
+ <a href="%%root%%components/toast.html">Toast</a>
58
59
  <a href="%%root%%components/theme-switcher.html">Theme Switcher</a>
59
60
  <a href="%%root%%components/timestamp.html">Timestamp</a>
60
61
  <a href="%%root%%components/toggle.html">Toggle</a>
@@ -62,7 +63,7 @@
62
63
 
63
64
  <h5 class="mb0">Utils</h5>
64
65
  <div class="pl mb">
65
-
66
+ <a href="%%root%%utils/Server.html">Server</a>
66
67
  </div>
67
68
  </menu>
68
69
  </k-side-menu>
@@ -0,0 +1,119 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Template - Utils - Kempo Docs - A Web Components Solution</title>
7
+ <link rel="stylesheet" href="../kempo/kempo-vars.css" /><link rel="stylesheet" href="../kempo/kempo-styles.css" />
8
+ <link rel="stylesheet" href="../kempo/kempo-hljs.css" />
9
+ <script type="module">
10
+ import Import from '../kempo/components/Import.js';
11
+ import Icon from '../kempo/components/Icon.js';
12
+ import Search from '../kempo/components/Search.js';
13
+ Import.addReplacement('root', '../');
14
+ Icon.pathToIcons = ['../icons'];
15
+ Search.searchDataUrl = '../search.json';
16
+ </script>
17
+ </head>
18
+ <body>
19
+ <k-import src="../nav.inc.html"></k-import>
20
+ <h1 class="ta-center">Server</h1>
21
+ <main>
22
+ <details class="b r mb">
23
+ <summary class="p">Table of Contents</summary>
24
+ <div class="m mt0 pl">
25
+ <h6>Examples</h6>
26
+ <a href="#basicUsage">Basic Usage</a><br />
27
+ <a href="#staticServing">Static File Serving</a><br />
28
+ <a href="#routePatterns">Route Patterns</a><br />
29
+
30
+ <h6 class="mt">JavaScript Reference</h6>
31
+ <a href="#constructor">Constructor</a><br />
32
+ <a href="#requirements">Requirements</a><br />
33
+ <a href="#methods">Methods</a><br />
34
+ <a href="#staticMembers">Static Members</a><br />
35
+ </div>
36
+ </details>
37
+
38
+ <h3>Description</h3>
39
+ <p>A lightweight HTTP server utility for serving static files and handling route patterns with wildcard support.</p>
40
+
41
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
42
+ <pre><code class="language-javascript">import { Server } from '../utils/Server.js';
43
+
44
+ const server = new Server();
45
+ server
46
+ .static('/assets', './public')
47
+ .route('/api/*.json', async (req, res) => {
48
+ // Handle JSON requests
49
+ });
50
+
51
+ await server.listen(8080);</code></pre>
52
+
53
+ <h3 id="staticServing"><a href="#staticServing" class="no-link">Static File Serving</a></h3>
54
+ <pre><code class="language-javascript">// Serve entire directories
55
+ server.static('/assets', './public');
56
+ server.static('/docs', './documentation');
57
+
58
+ // Serve single files with custom headers
59
+ server.route('/style.css', async (req, res) => {
60
+ await server.serveFile('./styles/main.css', res, {
61
+ 'Cache-Control': 'no-cache'
62
+ });
63
+ });</code></pre>
64
+
65
+ <h3 id="routePatterns"><a href="#routePatterns" class="no-link">Route Patterns</a></h3>
66
+ <p>Routes support wildcard patterns for flexible path matching:</p>
67
+ <ul>
68
+ <li><code>*</li>
69
+ <li><code>**</code> - Matches any characters including /</li>
70
+ </ul>
71
+ <pre><code class="language-javascript">// Match specific file types
72
+ server.route('/api/*.json', handler);
73
+
74
+ // Match files in subdirectories
75
+ server.route('/docs/**/*.html', handler);
76
+
77
+ // Match everything under a path
78
+ server.route('/assets/**/*', handler);</code></pre>
79
+
80
+ <h2 id="jsRef">JavaScript Reference</h2>
81
+
82
+ <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
83
+ <h5><code>new Server()</code></h5>
84
+ <p>Creates a new server instance.</p>
85
+
86
+ <h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
87
+ <ul>
88
+ <li>Node.js built-in modules only (http, fs/promises, path)</li>
89
+ <li>No external dependencies</li>
90
+ </ul>
91
+
92
+ <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
93
+
94
+ <h5><code>route(path: string, handler: Function, methods?: string[]): Server</code></h5>
95
+ <p>Registers a route handler with optional HTTP method restrictions. Returns this for chaining.</p>
96
+
97
+ <h5><code>static(urlPath: string, dirPath: string): Server</code></h5>
98
+ <p>Serves an entire directory of static files. Returns this for chaining.</p>
99
+
100
+ <h5><code>serveFile(filePath: string, res: Response, headers?: object): Promise&lt;void&gt;</code></h5>
101
+ <p>Serves a single file with appropriate MIME type and optional headers.</p>
102
+
103
+ <h5><code>listen(port?: number): Promise&lt;http.Server&gt;</code></h5>
104
+ <p>Starts the server on the specified port. Returns a Promise that resolves to the server instance.</p>
105
+
106
+ <h3 id="staticMembers"><a href="#staticMembers" class="no-link">Static Members</a></h3>
107
+
108
+ <h5><code>static mimeTypes: Record&lt;string, string&gt;</code></h5>
109
+ <p>MIME type mappings for file extensions.</p>
110
+
111
+ <h5><code>static textExtensions: string[]</code></h5>
112
+ <p>List of file extensions that should be served as text.</p>
113
+
114
+ <h5><code>static getMimeType(filePath: string): string</code></h5>
115
+ <p>Returns the appropriate MIME type for a file based on its extension.</p>
116
+ </main>
117
+ <script type="module" src="../kempo/components/Import.js"></script>
118
+ </body>
119
+ </html>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="error" d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240ZM330-120 120-330v-300l210-210h300l210 210v300L630-120H330Zm34-80h232l164-164v-232L596-760H364L200-596v232l164 164Zm116-280Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="warning" d="m40-120 440-760 440 760H40Zm138-80h604L480-720 178-200Zm302-40q17 0 28.5-11.5T520-280q0-17-11.5-28.5T480-320q-17 0-28.5 11.5T440-280q0 17 11.5 28.5T480-240Zm-40-120h80v-200h-80v200Zm40-100Z"/></svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo",
3
- "version": "3.2.0",
3
+ "version": "4.1.0",
4
4
  "type": "module",
5
5
  "description": "A web component library",
6
6
  "exports": {
@@ -11,7 +11,7 @@
11
11
  "./components/*": "./dist/components/*.js"
12
12
  },
13
13
  "scripts": {
14
- "test": "node ./scrips/test.js",
14
+ "test": "node ./test/index.js",
15
15
  "build": "node ./scripts/build.js",
16
16
  "docs": "node ./scripts/docs.js",
17
17
  "docs-src": "node ./scripts/docs.js -s"
@@ -20,8 +20,6 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {},
22
22
  "devDependencies": {
23
- "express": "^4.18.3",
24
- "fs-extra": "^11.2.0",
25
23
  "terser": "^5.29.2"
26
24
  },
27
25
  "directories": {
package/scripts/build.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import fs from 'fs/promises';
2
- import fse from 'fs-extra';
3
2
  import { minify } from 'terser';
4
3
  import path from 'path';
5
4
  import { fileURLToPath } from 'url';
5
+ import { ensureDir, copyDir, emptyDir } from '../src/utils/fs-utils.js';
6
6
 
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = path.dirname(__filename);
@@ -57,7 +57,7 @@ process.stdout.write(`0/${components.length} = 0%`);
57
57
  await Promise.all(components.map(async component => {
58
58
  const relativePath = path.relative(componentsDir, component);
59
59
  const destPath = path.join('./dist/components', relativePath);
60
- await fse.ensureDir(path.dirname(destPath));
60
+ await ensureDir(path.dirname(destPath));
61
61
  await fs.writeFile(destPath, minifiedComponents[component], 'utf-8');
62
62
  process.stdout.write("\r");
63
63
  complete++;
@@ -95,7 +95,7 @@ process.stdout.write("\n");
95
95
  complete = 0;
96
96
  console.log('Saving Minified Utils to dist/');
97
97
  process.stdout.write(`0/${utils.length} = 0%`);
98
- await fse.ensureDir('./dist/utils');
98
+ await ensureDir('./dist/utils');
99
99
  await Promise.all(utils.map(async util => {
100
100
  await fs.writeFile(`./dist/utils/${util}.js`, minifiedUtils[util], 'utf-8');
101
101
  process.stdout.write("\r");
@@ -139,16 +139,16 @@ console.log('Saving kempo-hljs.css');
139
139
  await fs.writeFile('./dist/kempo-hljs.css', minifiedKempoHljsCSS, 'utf-8');
140
140
 
141
141
  console.log('Copying dist/ to docs/');
142
- await fse.ensureDir('./docs/'); // will also ensure docs/kempo
143
- await fse.copy('./dist', './docs/kempo')
142
+ await ensureDir('./docs/'); // will also ensure docs/kempo
143
+ await copyDir('./dist', './docs/kempo')
144
144
 
145
145
  const iconsSrcDir = path.join(__dirname, '../icons');
146
146
  const iconsDestDir = path.join(__dirname, '../docs/icons');
147
147
 
148
148
  console.log('Deleting svg icons currently in docs/icons');
149
- await fse.ensureDir(iconsDestDir);
150
- await fse.emptyDir(iconsDestDir);
149
+ await ensureDir(iconsDestDir);
150
+ await emptyDir(iconsDestDir);
151
151
  console.log('Copying all files from icons to docs/icons');
152
- await fse.copy(iconsSrcDir, iconsDestDir);
152
+ await copyDir(iconsSrcDir, iconsDestDir);
153
153
 
154
154
  console.log('Build Complete');
package/scripts/docs.js CHANGED
@@ -1,10 +1,7 @@
1
- import express from 'express';
2
1
  import { dirname, join } from 'path';
3
2
  import { fileURLToPath } from 'url';
4
- import {
5
- getArgs,
6
- runChildNodeProcess
7
- } from '../src/utils/cli.js';
3
+ import { Server } from '../src/utils/Server.js';
4
+ import { getArgs, runChildNodeProcess } from '../src/utils/cli.js';
8
5
 
9
6
  const __filename = fileURLToPath(import.meta.url);
10
7
  const __dirname = dirname(__filename);
@@ -25,23 +22,26 @@ if(build){
25
22
  runChildNodeProcess(join(__dirname, 'build.js'));
26
23
  }
27
24
 
28
- const app = express();
25
+ const server = new Server();
29
26
 
30
- if(src){
31
- app.use('/kempo', express.static('src'));
32
- app.use('/icons', express.static('icons'));
33
- app.get('/kempo/kempo-styles.css', (req, res) => {
34
- res.set('Cache-Control', 'no-cache');
35
- res.sendFile(join(__dirname, '../src/kempo-styles.css'));
36
- })
27
+ if (src) {
28
+ server
29
+ .get('/', join(__dirname, '../docs/'))
30
+ .get('/kempo/kempo-styles.css', join(__dirname, '../src/kempo-styles.css'))
31
+ .get('/kempo/*', join(__dirname, '../src/*'))
32
+ .get('/icons/*', join(__dirname, '../icons/*'))
33
+ .get('/*', join(__dirname, '../docs/*'));
37
34
  } else {
38
- app.get('/kempo/kempo-styles.css', (req, res) => {
39
- res.set('Cache-Control', 'private');
40
- res.sendFile(join(__dirname, '../docs/kempo/kempo-styles.css'));
41
- });
35
+ server
36
+ .get('/', join(__dirname, '../docs/'))
37
+ .get('/kempo/kempo-styles.css', join(__dirname, '../docs/kempo/kempo-styles.css'))
38
+ .get('/*', join(__dirname, '../docs/*'));
42
39
  }
43
- app.use(express.static('docs'));
44
40
 
45
- app.listen(port, () => {
41
+ try {
42
+ await server.listen(port);
46
43
  console.log(`Docs Running on: http://localhost:${port}`);
47
- });
44
+ } catch (err) {
45
+ console.error('Failed to start server:', err.message);
46
+ process.exit(1);
47
+ }
@@ -107,3 +107,4 @@ export default class Component extends HTMLElement {
107
107
  static observedAttributes = ['rendered'];
108
108
  static pathToKempo = import.meta.url.substring(0, import.meta.url.indexOf('/components/Component.js') );
109
109
  }
110
+ window.customElements.define('k-component', Component);
@@ -244,9 +244,19 @@ export default class Dialog extends Component {
244
244
 
245
245
  static create(contents = '', options = {}){
246
246
  if(options.closeExisting!==false)document.querySelectorAll('k-dialog').forEach(d=>d.close());
247
+ const {
248
+ removeOnClose = true,
249
+ closeCallback = () => {}
250
+ } = options;
247
251
  const $dialog = new Dialog({
248
252
  opened: true,
249
- ...options
253
+ ...options,
254
+ closeCallback: (...args) => {
255
+ if(removeOnClose){
256
+ $dialog.remove();
257
+ }
258
+ closeCallback(...args);
259
+ }
250
260
  });
251
261
  onEvent($dialog, 'close', $dialog.remove);
252
262
  if(contents instanceof HTMLElement || contents instanceof DocumentFragment){
@@ -2,14 +2,12 @@ import Component from './Component.js';
2
2
  import { isInView } from '../utils/element.js';
3
3
  import raf from '../utils/raf.js';
4
4
 
5
- const inViewIntervalTimeout = Symbol(),
6
- inViewIntervalId = Symbol();
5
+ const inViewIntervalId = Symbol();
7
6
  export default class LazyComponent extends Component {
8
7
  constructor(shadowDetails){
9
8
  super(shadowDetails);
10
9
 
11
10
  /* Private Members */
12
- this[inViewIntervalTimeout] = 2000;
13
11
  this[inViewIntervalId] = null;
14
12
 
15
13
  (new IntersectionObserver(([comp]) => {
@@ -20,7 +18,10 @@ export default class LazyComponent extends Component {
20
18
  }
21
19
  })).observe(this);
22
20
 
23
- this.registerAttribute('unrender', false);
21
+ this.registerAttributes({
22
+ unrender: false,
23
+ inViewInterval: 0
24
+ });
24
25
  }
25
26
  connectedCallback(){
26
27
  // Do not do super.connectedCallback() because it will render the component prematurely
@@ -43,7 +44,7 @@ export default class LazyComponent extends Component {
43
44
  if(await isInView(this)){
44
45
  this.inViewCallback();
45
46
  }
46
- }, this[inViewIntervalTimeout]);
47
+ }, this.inViewInterval || LazyComponent.inViewIntervalTimeout);
47
48
  if(this.unrender){
48
49
  await this.renderSkelton();
49
50
  return true;
@@ -58,4 +59,7 @@ export default class LazyComponent extends Component {
58
59
  get skeletonTemplate(){
59
60
  return /*html*/`<div style="height: var(--skeleton_height, 1rem)"></div>`;
60
61
  }
62
+
63
+ /* Static Members */
64
+ static inViewIntervalTimeout = 1000;
61
65
  }