kempo 3.0.0 → 3.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.
@@ -1 +1 @@
1
- import Component from"./Component.js";export default class Import extends Component{constructor(e=""){super(),this.registerAttribute("src",e)}attributeChangedCallback(e,t,r){"src"===e&&t!==r&&this.render(!0)}async render(e){const t=this.src;if(t&&await super.render(e)){let e=await(await fetch(t)).text();for(const[t,r]of Object.entries(Import.replacements))e=e.replace(new RegExp(`%%${t}%%`,"g"),r);const r=document.createElement("template");r.innerHTML=e;const n=Array.from(r.content.querySelectorAll("script[src]"));n.forEach((e=>e.remove())),this.innerHTML=r.innerHTML;for(const e of n){const t=document.createElement("script");Array.from(e.attributes).forEach((e=>{t.setAttribute(e.name,e.value)})),console.log("Adding script with src:",t.src),await new Promise(((e,r)=>{t.onload=e,t.onerror=r,this.appendChild(t)}))}return!0}return!1}get shadowStyles(){return"\n :host {\n display: contents;\n }\n "}static replacements={root:"./"};static addReplacement(e,t){this.replacements={...this.replacements,[e]:t}}static observedAttributes=[...super.observedAttributes,"src"]}window.customElements.define("k-import",Import);
1
+ import Component from"./Component.js";export default class Import extends Component{constructor(e=""){super(),this.registerAttribute("src",e)}attributeChangedCallback(e,t,r){"src"===e&&t!==r&&this.render(!0)}async render(e){const t=this.src;if(t&&await super.render(e)){let e=await(await fetch(t)).text();for(const[t,r]of Object.entries(Import.replacements))e=e.replace(new RegExp(`%%${t}%%`,"g"),r);const r=document.createElement("template");r.innerHTML=e;const s=Array.from(r.content.querySelectorAll("script[src]"));s.forEach((e=>e.remove())),this.innerHTML=r.innerHTML;for(const e of s){const t=document.createElement("script");Array.from(e.attributes).forEach((e=>{t.setAttribute(e.name,e.value)})),await new Promise(((e,r)=>{t.onload=e,t.onerror=r,this.appendChild(t)}))}return!0}return!1}get shadowStyles(){return":host{display:contents;}"}static replacements={root:"./"};static addReplacement(e,t){this.replacements={...this.replacements,[e]:t}}static observedAttributes=[...super.observedAttributes,"src"]}window.customElements.define("k-import",Import);
@@ -0,0 +1 @@
1
+ import Component from"./Component.js";import"./Icon.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";const openHandler=Symbol(),closeHandler=Symbol(),overlayHandler=Symbol(),prevHandler=Symbol(),nextHandler=Symbol(),keyboardHandler=Symbol(),imgKeyboardHandler=Symbol();export default class PhotoViewer extends Component{constructor(e="",n="",t={}){super();const{keyboardControls:o=!0,global:s=!1}=t;this.registerAttributes({src:e,alt:n,fullscreen:!1,keyboardControls:o,global:s})}attributeChangedCallback(e,n,t){super.attributeChangedCallback(e,n,t),this.rendered&&"src"===e&&n!==t?(this.shadowRoot.getElementById("img").src=t,this.shadowRoot.getElementById("fullscreen-img").src=t):this.rendered&&"alt"===e&&n!==t?(this.shadowRoot.getElementById("img").alt=t,this.shadowRoot.getElementById("fullscreen-img").alt=t):this.rendered&&"fullscreen"===e&&n!==t&&(dispatchEvent(this,"fullscreenchange "+(t?" fullscreen":" fullscreenclose")),this.updateNavigationState(),this.keyboardControls&&(t?document.addEventListener("keydown",this[keyboardHandler]):document.removeEventListener("keydown",this[keyboardHandler])))}async render(e){if(await super.render(e)){const e=this.shadowRoot.getElementById("img");e.src=this.src,e.alt=this.alt;const n=this.shadowRoot.getElementById("fullscreen-img");n.src=this.src,n.alt=this.alt;const t=this.shadowRoot.querySelector('slot[name="fullscreen-caption"]');if(t&&!t.assignedNodes().length){this.shadowRoot.querySelector("slot:not([name])").assignedNodes().forEach((e=>{const n=e.cloneNode(!0);this.appendChild(n).slot="fullscreen-caption"}))}return onEvent(e,"click",this[openHandler]),onEvent(e,"keydown",this[imgKeyboardHandler]),onEvent(this.shadowRoot.getElementById("close"),"click",this[closeHandler]),onEvent(this.shadowRoot.getElementById("fullscreen-overlay"),"click",this[overlayHandler]),onEvent(this.shadowRoot.getElementById("prev"),"click",this[prevHandler]),onEvent(this.shadowRoot.getElementById("next"),"click",this[nextHandler]),this.updateNavigationState(),!0}return!1}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("img"),"click",this[openHandler]),offEvent(this.shadowRoot.getElementById("img"),"keydown",this[imgKeyboardHandler]),offEvent(this.shadowRoot.getElementById("close"),"click",this[closeHandler]),offEvent(this.shadowRoot.getElementById("fullscreen-overlay"),"click",this[overlayHandler]),offEvent(this.shadowRoot.getElementById("prev"),"click",this[prevHandler]),offEvent(this.shadowRoot.getElementById("next"),"click",this[nextHandler]),this.keyboardControls&&document.removeEventListener("keydown",this[keyboardHandler])}[imgKeyboardHandler]=e=>{"Enter"!==e.key||this.fullscreen||this.open()};[openHandler]=()=>{this.open()};[closeHandler]=()=>{this.close()};[overlayHandler]=e=>{e.target===e.currentTarget&&this.close()};[prevHandler]=e=>{e.stopPropagation();const n=this.getPrevSibling();n&&(this.close(),n.open())};[nextHandler]=e=>{e.stopPropagation();const n=this.getNextSibling();n&&(this.close(),n.open())};[keyboardHandler]=e=>{if("Escape"===e.key)this.close();else if("ArrowLeft"===e.key){const e=this.getPrevSibling();e&&(this.close(),e.open())}else if("ArrowRight"===e.key){const e=this.getNextSibling();e&&(this.close(),e.open())}};getPrevSibling(){if(this.global){const e=Array.from(document.getElementsByTagName("k-photo-viewer")),n=e.indexOf(this);return-1===n?null:e[0===n?e.length-1:n-1]}{let e=this.previousElementSibling;for(;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.previousElementSibling;if(!e&&this.hasPhotoSiblings())for(e=this.parentElement.lastElementChild;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.previousElementSibling;return e}}getNextSibling(){if(this.global){const e=Array.from(document.getElementsByTagName("k-photo-viewer")),n=e.indexOf(this);return-1===n?null:e[n===e.length-1?0:n+1]}{let e=this.nextElementSibling;for(;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.nextElementSibling;if(!e&&this.hasPhotoSiblings())for(e=this.parentElement.firstElementChild;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.nextElementSibling;return e}}hasPhotoSiblings(){return this.global?document.getElementsByTagName("k-photo-viewer").length>1:Array.from(this.parentElement.children).filter((e=>e!==this&&"K-PHOTO-VIEWER"===e.tagName)).length>0}updateNavigationState(){const e=this.fullscreen&&this.hasPhotoSiblings();this.shadowRoot.querySelectorAll(".nav-btn").forEach((n=>n.classList.toggle("d-n",!e)))}open(){this.fullscreen=!0}close(){this.fullscreen=!1}toggle(){this.fullscreen=!this.fullscreen}get shadowTemplate(){return'\n <div id="wrapper">\n <img id="img" tabindex="0" />\n <div class="caption">\n <slot></slot>\n </div>\n <div id="fullscreen-overlay">\n <button id="close" class="no-btn">\n <slot name="close">\n <k-icon name="close"></k-icon>\n </slot>\n </button>\n <button id="prev" class="nav-btn no-btn">\n <slot name="prev">\n <k-icon name="chevron-left"></k-icon>\n </slot>\n </button>\n <button id="next" class="nav-btn no-btn">\n <slot name="next">\n <k-icon name="chevron-right"></k-icon>\n </slot>\n </button>\n <div class="content">\n <img id="fullscreen-img" />\n <div class="caption">\n <slot name="fullscreen-caption"></slot>\n </div>\n </div>\n </div>\n </div>\n '}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n }\n #wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n }\n #img {\n max-width: 100%;\n height: auto;\n cursor: pointer;\n outline: none;\n border-radius: var(--img_radius, 0);\n }\n #img:focus {\n box-shadow: var(--focus_shadow);\n }\n #fullscreen-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 9999;\n background: rgba(0,0,0,0.9);\n padding: 2rem;\n display: none;\n }\n :host([fullscreen]) #fullscreen-overlay {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .content {\n position: relative;\n max-width: 100%;\n max-height: 90vh;\n }\n #fullscreen-img {\n max-height: 75vh;\n max-width: 100%;\n object-fit: contain;\n cursor: default;\n }\n #close {\n position: absolute;\n top: 1rem;\n right: 1rem;\n z-index: 1;\n background: none;\n border: none;\n color: white;\n cursor: pointer;\n }\n .nav-btn {\n position: fixed;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n background: none;\n border: none;\n color: white;\n cursor: pointer;\n padding: 1rem;\n opacity: 0.7;\n font-size: 3rem;\n -webkit-text-stroke: 1px black;\n text-stroke: 1px black;\n text-shadow: 0 0 3px rgba(0,0,0,0.8);\n }\n .nav-btn::slotted(*),\n .nav-btn k-icon {\n filter: drop-shadow(0 0 2px rgba(0,0,0,1)) drop-shadow(0 0 2px rgba(0,0,0,1));\n }\n .nav-btn:hover {\n opacity: 1;\n }\n #prev {\n left: 1rem;\n }\n #next {\n right: 1rem;\n }\n .caption {\n text-align: center;\n max-width: 600px;\n width: fit-content;\n margin: 1rem auto 0;\n }\n #fullscreen-overlay .caption {\n color: white;\n }\n :host([fullscreen]) .caption {\n color: white;\n }\n `}static observedAttributes=[...super.observedAttributes,"src","alt","fullscreen","keyboard-controls","global"]}window.customElements.define("k-photo-viewer",PhotoViewer);
@@ -1 +1 @@
1
- :root{--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_small:calc(0.6 * var(--fs_base));--fs_large:calc(1.5 * var(--fs_base));--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--link_decoration:underline;--input_padding:var(--spacer_h) var(--spacer);--input_border_width:1px;--c_input_border:var(--c_border);--btn_padding:var(--spacer_h) var(--spacer);--date_picker_icon_filter:invert(0)}:root,[theme="light"]{color-scheme:light;--c_bg:#f9f9f9;--c_bg__inv:#333;--c_bg__alt:#eee;--c_overscroll:white;--c_border:#ccc;--c_border__inv:var(--d_c_bg_border);--c_primary:#36f;--c_primary__hover:#14d;--c_secondary:#93f;--c_secondary__hover:#71d;--c_success:#080;--c_success__hover:#060;--c_warning:#f60;--c_warning__hover:#d40;--c_danger:#f03;--c_danger__hover:#d01;--c_input_accent:#36f;--c_highlight:rgba(41, 100, 210, 0.25);--tc:rgba(0, 0, 0, 0.93);--tc_dark:var(--tc);--tc_light:rgba(255, 255, 255, 0.93);--tc_inv:var(--tc_light);--tc_muted:rgba(0, 0, 0, 0.5);--tc_on_primary:var(--tc_light);--tc_on_secondary:var(--tc_light);--tc_on_success:var(--tc_light);--tc_on_warning:var(--tc_light);--tc_on_danger:var(--tc_light);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:#36f;--tc_secondary:#93f;--tc_success:#080;--tc_warning:#f60;--tc_danger:#f03;--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--btn_bg:#ddd;--btn_bg__hover:#ccc;--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(0, 0, 0, 0.05);--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:white;--input_tc:rgba(0, 0, 0, 0.93);--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.333)}[auto-theme="dark"]:not([theme="light"]),[theme="dark"]{color-scheme:dark;--c_bg:#333;--c_bg__inv:#f9f9f9;--c_bg__alt:#222;--c_overscroll:black;--c_border:#777;--c_border__inv:#ccc;--c_primary:#36f;--c_primary__hover:#14d;--c_secondary:#93f;--c_secondary__hover:#71d;--c_success:#080;--c_success__hover:#060;--c_warning:#f60;--c_warning__hover:#d40;--c_danger:#f03;--c_danger__hover:#d01;--c_input_accent:#36f;--c_highlight:rgba(0, 89, 255, 0.25);--tc:rgba(255, 255, 255, 0.93);--tc_dark:rgba(0, 0, 0, 0.93);--tc_light:var(--tc);--tc_inv:var(--tc_dark);--tc_muted:rgba(255, 255, 255, 0.5);--tc_on_primary:var(--tc);--tc_on_secondary:var(--tc);--tc_on_success:var(--tc);--tc_on_warning:var(--tc);--tc_on_danger:var(--tc);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:#8ab4f8;--tc_secondary:#b6f;--tc_success:#6b6;--tc_warning:#f93;--tc_danger:#f57;--btn_bg:#aaa;--btn_bg__hover:#bbb;--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(255, 255, 255, 0.05);--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:var(--c_bg__alt);--input_tc:var(--tc);--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.25);--link_decoration:underline;--date_picker_icon_filter:invert(1)}
1
+ :root{--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_small:calc(0.6 * var(--fs_base));--fs_large:calc(1.5 * var(--fs_base));--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--link_decoration:underline;--input_padding:var(--spacer_h) var(--spacer);--input_border_width:1px;--c_input_border:var(--c_border);--btn_padding:var(--spacer_h) var(--spacer);--date_picker_icon_filter:invert(0)}:root,[theme="light"]{color-scheme:light;--c_bg:rgb(249, 249, 249);--c_bg__inv:rgb(51, 51, 51);--c_bg__alt:rgb(238, 238, 238);--c_overscroll:rgb(255, 255, 255);--c_border:rgb(204, 204, 204);--c_border__inv:var(--d_c_bg_border);--c_primary:rgb(51, 102, 255);--c_primary__hover:rgb(17, 68, 221);--c_secondary:rgb(153, 51, 255);--c_secondary__hover:rgb(119, 17, 221);--c_success:rgb(0, 136, 0);--c_success__hover:rgb(0, 102, 0);--c_warning:rgb(255, 102, 0);--c_warning__hover:rgb(221, 68, 0);--c_danger:rgb(255, 0, 51);--c_danger__hover:rgb(221, 0, 17);--c_input_accent:rgb(51, 102, 255);--c_highlight:rgba(41, 100, 210, 0.25);--tc:rgba(0, 0, 0, 0.93);--tc_dark:var(--tc);--tc_light:rgba(255, 255, 255, 0.93);--tc_inv:var(--tc_light);--tc_muted:rgba(0, 0, 0, 0.5);--tc_on_primary:var(--tc_light);--tc_on_secondary:var(--tc_light);--tc_on_success:var(--tc_light);--tc_on_warning:var(--tc_light);--tc_on_danger:var(--tc_light);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:#36f;--tc_secondary:#93f;--tc_success:#080;--tc_warning:#f60;--tc_danger:#f03;--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--btn_bg:rgb(221, 221, 221);--btn_bg__hover:rgb(204, 204, 204);--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(0, 0, 0, 0.05);--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:white;--input_tc:rgba(0, 0, 0, 0.93);--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.333)}[auto-theme="dark"]:not([theme="light"]),[theme="dark"]{color-scheme:dark;--c_bg:rgb(51, 51, 51);--c_bg__inv:rgb(249, 249, 249);--c_bg__alt:rgb(34, 34, 34);--c_overscroll:rgb(0, 0, 0);--c_border:rgb(119, 119, 119);--c_border__inv:rgb(204, 204, 204);--c_primary:rgb(51, 102, 255);--c_primary__hover:rgb(17, 68, 221);--c_secondary:rgb(153, 51, 255);--c_secondary__hover:rgb(119, 17, 221);--c_success:rgb(0, 136, 0);--c_success__hover:rgb(0, 102, 0);--c_warning:rgb(255, 102, 0);--c_warning__hover:rgb(221, 68, 0);--c_danger:rgb(255, 0, 51);--c_danger__hover:rgb(221, 0, 17);--c_input_accent:rgb(51, 102, 255);--c_highlight:rgba(0, 89, 255, 0.25);--tc:rgba(255, 255, 255, 0.93);--tc_dark:rgba(0, 0, 0, 0.93);--tc_light:var(--tc);--tc_inv:var(--tc_dark);--tc_muted:rgba(255, 255, 255, 0.5);--tc_on_primary:var(--tc);--tc_on_secondary:var(--tc);--tc_on_success:var(--tc);--tc_on_warning:var(--tc);--tc_on_danger:var(--tc);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:rgb(138, 180, 248);--tc_secondary:rgb(187, 102, 255);--tc_success:rgb(102, 187, 102);--tc_warning:rgb(255, 153, 51);--tc_danger:rgb(255, 85, 119);--btn_bg:rgb(170, 170, 170);--btn_bg__hover:rgb(187, 187, 187);--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(255, 255, 255, 0.05);--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:var(--c_bg__alt);--input_tc:var(--tc);--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.25);--link_decoration:underline;--date_picker_icon_filter:invert(1)}
@@ -1 +1 @@
1
- import Component from"./Component.js";export default class Import extends Component{constructor(e=""){super(),this.registerAttribute("src",e)}attributeChangedCallback(e,t,r){"src"===e&&t!==r&&this.render(!0)}async render(e){const t=this.src;if(t&&await super.render(e)){let e=await(await fetch(t)).text();for(const[t,r]of Object.entries(Import.replacements))e=e.replace(new RegExp(`%%${t}%%`,"g"),r);const r=document.createElement("template");r.innerHTML=e;const n=Array.from(r.content.querySelectorAll("script[src]"));n.forEach((e=>e.remove())),this.innerHTML=r.innerHTML;for(const e of n){const t=document.createElement("script");Array.from(e.attributes).forEach((e=>{t.setAttribute(e.name,e.value)})),console.log("Adding script with src:",t.src),await new Promise(((e,r)=>{t.onload=e,t.onerror=r,this.appendChild(t)}))}return!0}return!1}get shadowStyles(){return"\n :host {\n display: contents;\n }\n "}static replacements={root:"./"};static addReplacement(e,t){this.replacements={...this.replacements,[e]:t}}static observedAttributes=[...super.observedAttributes,"src"]}window.customElements.define("k-import",Import);
1
+ import Component from"./Component.js";export default class Import extends Component{constructor(e=""){super(),this.registerAttribute("src",e)}attributeChangedCallback(e,t,r){"src"===e&&t!==r&&this.render(!0)}async render(e){const t=this.src;if(t&&await super.render(e)){let e=await(await fetch(t)).text();for(const[t,r]of Object.entries(Import.replacements))e=e.replace(new RegExp(`%%${t}%%`,"g"),r);const r=document.createElement("template");r.innerHTML=e;const s=Array.from(r.content.querySelectorAll("script[src]"));s.forEach((e=>e.remove())),this.innerHTML=r.innerHTML;for(const e of s){const t=document.createElement("script");Array.from(e.attributes).forEach((e=>{t.setAttribute(e.name,e.value)})),await new Promise(((e,r)=>{t.onload=e,t.onerror=r,this.appendChild(t)}))}return!0}return!1}get shadowStyles(){return":host{display:contents;}"}static replacements={root:"./"};static addReplacement(e,t){this.replacements={...this.replacements,[e]:t}}static observedAttributes=[...super.observedAttributes,"src"]}window.customElements.define("k-import",Import);
@@ -0,0 +1 @@
1
+ import Component from"./Component.js";import"./Icon.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";const openHandler=Symbol(),closeHandler=Symbol(),overlayHandler=Symbol(),prevHandler=Symbol(),nextHandler=Symbol(),keyboardHandler=Symbol(),imgKeyboardHandler=Symbol();export default class PhotoViewer extends Component{constructor(e="",n="",t={}){super();const{keyboardControls:o=!0,global:s=!1}=t;this.registerAttributes({src:e,alt:n,fullscreen:!1,keyboardControls:o,global:s})}attributeChangedCallback(e,n,t){super.attributeChangedCallback(e,n,t),this.rendered&&"src"===e&&n!==t?(this.shadowRoot.getElementById("img").src=t,this.shadowRoot.getElementById("fullscreen-img").src=t):this.rendered&&"alt"===e&&n!==t?(this.shadowRoot.getElementById("img").alt=t,this.shadowRoot.getElementById("fullscreen-img").alt=t):this.rendered&&"fullscreen"===e&&n!==t&&(dispatchEvent(this,"fullscreenchange "+(t?" fullscreen":" fullscreenclose")),this.updateNavigationState(),this.keyboardControls&&(t?document.addEventListener("keydown",this[keyboardHandler]):document.removeEventListener("keydown",this[keyboardHandler])))}async render(e){if(await super.render(e)){const e=this.shadowRoot.getElementById("img");e.src=this.src,e.alt=this.alt;const n=this.shadowRoot.getElementById("fullscreen-img");n.src=this.src,n.alt=this.alt;const t=this.shadowRoot.querySelector('slot[name="fullscreen-caption"]');if(t&&!t.assignedNodes().length){this.shadowRoot.querySelector("slot:not([name])").assignedNodes().forEach((e=>{const n=e.cloneNode(!0);this.appendChild(n).slot="fullscreen-caption"}))}return onEvent(e,"click",this[openHandler]),onEvent(e,"keydown",this[imgKeyboardHandler]),onEvent(this.shadowRoot.getElementById("close"),"click",this[closeHandler]),onEvent(this.shadowRoot.getElementById("fullscreen-overlay"),"click",this[overlayHandler]),onEvent(this.shadowRoot.getElementById("prev"),"click",this[prevHandler]),onEvent(this.shadowRoot.getElementById("next"),"click",this[nextHandler]),this.updateNavigationState(),!0}return!1}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("img"),"click",this[openHandler]),offEvent(this.shadowRoot.getElementById("img"),"keydown",this[imgKeyboardHandler]),offEvent(this.shadowRoot.getElementById("close"),"click",this[closeHandler]),offEvent(this.shadowRoot.getElementById("fullscreen-overlay"),"click",this[overlayHandler]),offEvent(this.shadowRoot.getElementById("prev"),"click",this[prevHandler]),offEvent(this.shadowRoot.getElementById("next"),"click",this[nextHandler]),this.keyboardControls&&document.removeEventListener("keydown",this[keyboardHandler])}[imgKeyboardHandler]=e=>{"Enter"!==e.key||this.fullscreen||this.open()};[openHandler]=()=>{this.open()};[closeHandler]=()=>{this.close()};[overlayHandler]=e=>{e.target===e.currentTarget&&this.close()};[prevHandler]=e=>{e.stopPropagation();const n=this.getPrevSibling();n&&(this.close(),n.open())};[nextHandler]=e=>{e.stopPropagation();const n=this.getNextSibling();n&&(this.close(),n.open())};[keyboardHandler]=e=>{if("Escape"===e.key)this.close();else if("ArrowLeft"===e.key){const e=this.getPrevSibling();e&&(this.close(),e.open())}else if("ArrowRight"===e.key){const e=this.getNextSibling();e&&(this.close(),e.open())}};getPrevSibling(){if(this.global){const e=Array.from(document.getElementsByTagName("k-photo-viewer")),n=e.indexOf(this);return-1===n?null:e[0===n?e.length-1:n-1]}{let e=this.previousElementSibling;for(;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.previousElementSibling;if(!e&&this.hasPhotoSiblings())for(e=this.parentElement.lastElementChild;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.previousElementSibling;return e}}getNextSibling(){if(this.global){const e=Array.from(document.getElementsByTagName("k-photo-viewer")),n=e.indexOf(this);return-1===n?null:e[n===e.length-1?0:n+1]}{let e=this.nextElementSibling;for(;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.nextElementSibling;if(!e&&this.hasPhotoSiblings())for(e=this.parentElement.firstElementChild;e&&"K-PHOTO-VIEWER"!==e.tagName;)e=e.nextElementSibling;return e}}hasPhotoSiblings(){return this.global?document.getElementsByTagName("k-photo-viewer").length>1:Array.from(this.parentElement.children).filter((e=>e!==this&&"K-PHOTO-VIEWER"===e.tagName)).length>0}updateNavigationState(){const e=this.fullscreen&&this.hasPhotoSiblings();this.shadowRoot.querySelectorAll(".nav-btn").forEach((n=>n.classList.toggle("d-n",!e)))}open(){this.fullscreen=!0}close(){this.fullscreen=!1}toggle(){this.fullscreen=!this.fullscreen}get shadowTemplate(){return'\n <div id="wrapper">\n <img id="img" tabindex="0" />\n <div class="caption">\n <slot></slot>\n </div>\n <div id="fullscreen-overlay">\n <button id="close" class="no-btn">\n <slot name="close">\n <k-icon name="close"></k-icon>\n </slot>\n </button>\n <button id="prev" class="nav-btn no-btn">\n <slot name="prev">\n <k-icon name="chevron-left"></k-icon>\n </slot>\n </button>\n <button id="next" class="nav-btn no-btn">\n <slot name="next">\n <k-icon name="chevron-right"></k-icon>\n </slot>\n </button>\n <div class="content">\n <img id="fullscreen-img" />\n <div class="caption">\n <slot name="fullscreen-caption"></slot>\n </div>\n </div>\n </div>\n </div>\n '}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n }\n #wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n }\n #img {\n max-width: 100%;\n height: auto;\n cursor: pointer;\n outline: none;\n border-radius: var(--img_radius, 0);\n }\n #img:focus {\n box-shadow: var(--focus_shadow);\n }\n #fullscreen-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 9999;\n background: rgba(0,0,0,0.9);\n padding: 2rem;\n display: none;\n }\n :host([fullscreen]) #fullscreen-overlay {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .content {\n position: relative;\n max-width: 100%;\n max-height: 90vh;\n }\n #fullscreen-img {\n max-height: 75vh;\n max-width: 100%;\n object-fit: contain;\n cursor: default;\n }\n #close {\n position: absolute;\n top: 1rem;\n right: 1rem;\n z-index: 1;\n background: none;\n border: none;\n color: white;\n cursor: pointer;\n }\n .nav-btn {\n position: fixed;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n background: none;\n border: none;\n color: white;\n cursor: pointer;\n padding: 1rem;\n opacity: 0.7;\n font-size: 3rem;\n -webkit-text-stroke: 1px black;\n text-stroke: 1px black;\n text-shadow: 0 0 3px rgba(0,0,0,0.8);\n }\n .nav-btn::slotted(*),\n .nav-btn k-icon {\n filter: drop-shadow(0 0 2px rgba(0,0,0,1)) drop-shadow(0 0 2px rgba(0,0,0,1));\n }\n .nav-btn:hover {\n opacity: 1;\n }\n #prev {\n left: 1rem;\n }\n #next {\n right: 1rem;\n }\n .caption {\n text-align: center;\n max-width: 600px;\n width: fit-content;\n margin: 1rem auto 0;\n }\n #fullscreen-overlay .caption {\n color: white;\n }\n :host([fullscreen]) .caption {\n color: white;\n }\n `}static observedAttributes=[...super.observedAttributes,"src","alt","fullscreen","keyboard-controls","global"]}window.customElements.define("k-photo-viewer",PhotoViewer);
@@ -1 +1 @@
1
- :root{--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_small:calc(0.6 * var(--fs_base));--fs_large:calc(1.5 * var(--fs_base));--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--link_decoration:underline;--input_padding:var(--spacer_h) var(--spacer);--input_border_width:1px;--c_input_border:var(--c_border);--btn_padding:var(--spacer_h) var(--spacer);--date_picker_icon_filter:invert(0)}:root,[theme="light"]{color-scheme:light;--c_bg:#f9f9f9;--c_bg__inv:#333;--c_bg__alt:#eee;--c_overscroll:white;--c_border:#ccc;--c_border__inv:var(--d_c_bg_border);--c_primary:#36f;--c_primary__hover:#14d;--c_secondary:#93f;--c_secondary__hover:#71d;--c_success:#080;--c_success__hover:#060;--c_warning:#f60;--c_warning__hover:#d40;--c_danger:#f03;--c_danger__hover:#d01;--c_input_accent:#36f;--c_highlight:rgba(41, 100, 210, 0.25);--tc:rgba(0, 0, 0, 0.93);--tc_dark:var(--tc);--tc_light:rgba(255, 255, 255, 0.93);--tc_inv:var(--tc_light);--tc_muted:rgba(0, 0, 0, 0.5);--tc_on_primary:var(--tc_light);--tc_on_secondary:var(--tc_light);--tc_on_success:var(--tc_light);--tc_on_warning:var(--tc_light);--tc_on_danger:var(--tc_light);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:#36f;--tc_secondary:#93f;--tc_success:#080;--tc_warning:#f60;--tc_danger:#f03;--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--btn_bg:#ddd;--btn_bg__hover:#ccc;--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(0, 0, 0, 0.05);--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:white;--input_tc:rgba(0, 0, 0, 0.93);--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.333)}[auto-theme="dark"]:not([theme="light"]),[theme="dark"]{color-scheme:dark;--c_bg:#333;--c_bg__inv:#f9f9f9;--c_bg__alt:#222;--c_overscroll:black;--c_border:#777;--c_border__inv:#ccc;--c_primary:#36f;--c_primary__hover:#14d;--c_secondary:#93f;--c_secondary__hover:#71d;--c_success:#080;--c_success__hover:#060;--c_warning:#f60;--c_warning__hover:#d40;--c_danger:#f03;--c_danger__hover:#d01;--c_input_accent:#36f;--c_highlight:rgba(0, 89, 255, 0.25);--tc:rgba(255, 255, 255, 0.93);--tc_dark:rgba(0, 0, 0, 0.93);--tc_light:var(--tc);--tc_inv:var(--tc_dark);--tc_muted:rgba(255, 255, 255, 0.5);--tc_on_primary:var(--tc);--tc_on_secondary:var(--tc);--tc_on_success:var(--tc);--tc_on_warning:var(--tc);--tc_on_danger:var(--tc);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:#8ab4f8;--tc_secondary:#b6f;--tc_success:#6b6;--tc_warning:#f93;--tc_danger:#f57;--btn_bg:#aaa;--btn_bg__hover:#bbb;--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(255, 255, 255, 0.05);--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:var(--c_bg__alt);--input_tc:var(--tc);--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.25);--link_decoration:underline;--date_picker_icon_filter:invert(1)}
1
+ :root{--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_small:calc(0.6 * var(--fs_base));--fs_large:calc(1.5 * var(--fs_base));--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--link_decoration:underline;--input_padding:var(--spacer_h) var(--spacer);--input_border_width:1px;--c_input_border:var(--c_border);--btn_padding:var(--spacer_h) var(--spacer);--date_picker_icon_filter:invert(0)}:root,[theme="light"]{color-scheme:light;--c_bg:rgb(249, 249, 249);--c_bg__inv:rgb(51, 51, 51);--c_bg__alt:rgb(238, 238, 238);--c_overscroll:rgb(255, 255, 255);--c_border:rgb(204, 204, 204);--c_border__inv:var(--d_c_bg_border);--c_primary:rgb(51, 102, 255);--c_primary__hover:rgb(17, 68, 221);--c_secondary:rgb(153, 51, 255);--c_secondary__hover:rgb(119, 17, 221);--c_success:rgb(0, 136, 0);--c_success__hover:rgb(0, 102, 0);--c_warning:rgb(255, 102, 0);--c_warning__hover:rgb(221, 68, 0);--c_danger:rgb(255, 0, 51);--c_danger__hover:rgb(221, 0, 17);--c_input_accent:rgb(51, 102, 255);--c_highlight:rgba(41, 100, 210, 0.25);--tc:rgba(0, 0, 0, 0.93);--tc_dark:var(--tc);--tc_light:rgba(255, 255, 255, 0.93);--tc_inv:var(--tc_light);--tc_muted:rgba(0, 0, 0, 0.5);--tc_on_primary:var(--tc_light);--tc_on_secondary:var(--tc_light);--tc_on_success:var(--tc_light);--tc_on_warning:var(--tc_light);--tc_on_danger:var(--tc_light);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:#36f;--tc_secondary:#93f;--tc_success:#080;--tc_warning:#f60;--tc_danger:#f03;--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--btn_bg:rgb(221, 221, 221);--btn_bg__hover:rgb(204, 204, 204);--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(0, 0, 0, 0.05);--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:white;--input_tc:rgba(0, 0, 0, 0.93);--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.333)}[auto-theme="dark"]:not([theme="light"]),[theme="dark"]{color-scheme:dark;--c_bg:rgb(51, 51, 51);--c_bg__inv:rgb(249, 249, 249);--c_bg__alt:rgb(34, 34, 34);--c_overscroll:rgb(0, 0, 0);--c_border:rgb(119, 119, 119);--c_border__inv:rgb(204, 204, 204);--c_primary:rgb(51, 102, 255);--c_primary__hover:rgb(17, 68, 221);--c_secondary:rgb(153, 51, 255);--c_secondary__hover:rgb(119, 17, 221);--c_success:rgb(0, 136, 0);--c_success__hover:rgb(0, 102, 0);--c_warning:rgb(255, 102, 0);--c_warning__hover:rgb(221, 68, 0);--c_danger:rgb(255, 0, 51);--c_danger__hover:rgb(221, 0, 17);--c_input_accent:rgb(51, 102, 255);--c_highlight:rgba(0, 89, 255, 0.25);--tc:rgba(255, 255, 255, 0.93);--tc_dark:rgba(0, 0, 0, 0.93);--tc_light:var(--tc);--tc_inv:var(--tc_dark);--tc_muted:rgba(255, 255, 255, 0.5);--tc_on_primary:var(--tc);--tc_on_secondary:var(--tc);--tc_on_success:var(--tc);--tc_on_warning:var(--tc);--tc_on_danger:var(--tc);--c_overlay:rgba(0, 0, 0, 0.5);--tc_primary:rgb(138, 180, 248);--tc_secondary:rgb(187, 102, 255);--tc_success:rgb(102, 187, 102);--tc_warning:rgb(255, 153, 51);--tc_danger:rgb(255, 85, 119);--btn_bg:rgb(170, 170, 170);--btn_bg__hover:rgb(187, 187, 187);--btn_tc:rgba(0, 0, 0, 0.93);--btn_transparent__hover:rgba(255, 255, 255, 0.05);--btn_box_shadow:0 0 0 transparent;--btn_box_shadow__hover:0 0 0 transparent;--btn_border:transparent;--tc_link:var(--tc_primary);--tc_link__hover:var(--tc_secondary);--tc_link__inv:var(--tc_primary__inv);--tc_link__inv__hover:var(--tc_secondary__inv);--focus_shadow:0 0 2px 2px var(--c_primary);--focus_shadow_on_primary:0 0 2px 2px var(--tc_on_primary);--input_bg:var(--c_bg__alt);--input_tc:var(--tc);--ff_body:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_heading:"Helvetica Neue", Helvetica, Arial, sans-serif;--ff_mono:Consolas, monaco, monospace;--fs_base:16px;--fs_h6:var(--fs_base);--fs_h5:calc(1.25 * var(--fs_base));--fs_h4:calc(1.5 * var(--fs_base));--fs_h3:calc(1.75 * var(--fs_base));--fs_h2:calc(2 * var(--fs_base));--fs_h1:calc(2.5 * var(--fs_base));--fw_base:400;--fw_bold:700;--spacer:1rem;--spacer_h:calc(0.5 * var(--spacer));--spacer_q:calc(0.25 * var(--spacer));--line-height:1.35em;--container_width:90rem;--animation_ms:256ms;--radius:0.25rem;--drop_shadow:0 0.25rem 0.5rem rgba(0, 0, 0, 0.25);--link_decoration:underline;--date_picker_icon_filter:invert(1)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "description": "A web component library",
6
6
  "exports": {
@@ -1,5 +1,4 @@
1
1
  import Component from './Component.js';
2
-
3
2
  export default class Import extends Component {
4
3
  constructor(src = ''){
5
4
  super();
@@ -13,59 +12,36 @@ export default class Import extends Component {
13
12
  async render(force){
14
13
  const src = this.src;
15
14
  if(src && await super.render(force)){
16
- // Get raw text and process replacements first
17
15
  let contents = await (await fetch(src)).text();
18
16
  for (const [name, value] of Object.entries(Import.replacements)) {
19
17
  contents = contents.replace(new RegExp(`%%${name}%%`, 'g'), value);
20
18
  }
21
-
22
- // Now parse into template
23
19
  const temp = document.createElement('template');
24
20
  temp.innerHTML = contents;
25
-
26
- // Extract and handle scripts
27
21
  const scripts = Array.from(temp.content.querySelectorAll('script[src]'));
28
22
  scripts.forEach(script => script.remove());
29
-
30
- // Set content
31
23
  this.innerHTML = temp.innerHTML;
32
-
33
- // Add scripts back
34
24
  for (const script of scripts) {
35
25
  const newScript = document.createElement('script');
36
26
  Array.from(script.attributes).forEach(attr => {
37
27
  newScript.setAttribute(attr.name, attr.value);
38
28
  });
39
- console.log('Adding script with src:', newScript.src); // Debug
40
29
  await new Promise((resolve, reject) => {
41
30
  newScript.onload = resolve;
42
31
  newScript.onerror = reject;
43
32
  this.appendChild(newScript);
44
33
  });
45
34
  }
46
-
47
35
  return true;
48
36
  }
49
37
  return false;
50
38
  }
51
-
52
39
  get shadowStyles(){
53
- return /*css*/`
54
- :host {
55
- display: contents;
56
- }
57
- `;
40
+ return /*css*/`:host{display:contents;}`;
58
41
  }
59
-
60
- /* Static Members */
61
- static replacements = {
62
- root: './'
63
- };
42
+ static replacements = {root: './'};
64
43
  static addReplacement(name, value){
65
- this.replacements = {
66
- ...this.replacements,
67
- [name]: value,
68
- };
44
+ this.replacements = {...this.replacements,[name]: value};
69
45
  }
70
46
  static observedAttributes = [...super.observedAttributes, 'src'];
71
47
  }
@@ -0,0 +1,358 @@
1
+ import Component from './Component.js';
2
+ import './Icon.js';
3
+ import {
4
+ onEvent,
5
+ offEvent,
6
+ dispatchEvent
7
+ } from '../utils/element.js';
8
+
9
+ const openHandler = Symbol(),
10
+ closeHandler = Symbol(),
11
+ overlayHandler = Symbol(),
12
+ prevHandler = Symbol(),
13
+ nextHandler = Symbol(),
14
+ keyboardHandler = Symbol(),
15
+ imgKeyboardHandler = Symbol();
16
+ export default class PhotoViewer extends Component {
17
+ constructor(src = '', alt = '', options = {}){
18
+ super();
19
+ const {
20
+ keyboardControls = true,
21
+ global = false
22
+ } = options;
23
+
24
+ this.registerAttributes({
25
+ src,
26
+ alt,
27
+ fullscreen: false,
28
+ keyboardControls,
29
+ global
30
+ });
31
+ }
32
+ /* Lifecycle Callbacks */
33
+ attributeChangedCallback(n, oV, nV){
34
+ super.attributeChangedCallback(n, oV, nV);
35
+ if(this.rendered && n === 'src' && oV !== nV){
36
+ this.shadowRoot.getElementById('img').src = nV;
37
+ this.shadowRoot.getElementById('fullscreen-img').src = nV;
38
+ } else if(this.rendered && n === 'alt' && oV !== nV){
39
+ this.shadowRoot.getElementById('img').alt = nV;
40
+ this.shadowRoot.getElementById('fullscreen-img').alt = nV;
41
+ } else if(this.rendered && n === 'fullscreen' && oV !== nV){
42
+ dispatchEvent(this, `fullscreenchange ${nV?' fullscreen':' fullscreenclose'}`);
43
+ this.updateNavigationState();
44
+ if(this.keyboardControls) {
45
+ if(nV) {
46
+ document.addEventListener('keydown', this[keyboardHandler]);
47
+ } else {
48
+ document.removeEventListener('keydown', this[keyboardHandler]);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ async render(force){
54
+ if(await super.render(force)){
55
+ const $img = this.shadowRoot.getElementById('img')
56
+ $img.src = this.src;
57
+ $img.alt = this.alt;
58
+ const $fullImg = this.shadowRoot.getElementById('fullscreen-img')
59
+ $fullImg.src = this.src;
60
+ $fullImg.alt = this.alt;
61
+
62
+ // Handle fullscreen caption
63
+ const fullscreenSlot = this.shadowRoot.querySelector('slot[name="fullscreen-caption"]');
64
+ if (fullscreenSlot && !fullscreenSlot.assignedNodes().length) {
65
+ const defaultSlot = this.shadowRoot.querySelector('slot:not([name])');
66
+ const contents = defaultSlot.assignedNodes();
67
+ contents.forEach(node => {
68
+ const clone = node.cloneNode(true);
69
+ this.appendChild(clone).slot = 'fullscreen-caption';
70
+ });
71
+ }
72
+
73
+ onEvent($img, 'click', this[openHandler]);
74
+ onEvent($img, 'keydown', this[imgKeyboardHandler]);
75
+ onEvent(this.shadowRoot.getElementById('close'), 'click', this[closeHandler]);
76
+ onEvent(this.shadowRoot.getElementById('fullscreen-overlay'), 'click', this[overlayHandler]);
77
+ onEvent(this.shadowRoot.getElementById('prev'), 'click', this[prevHandler]);
78
+ onEvent(this.shadowRoot.getElementById('next'), 'click', this[nextHandler]);
79
+ this.updateNavigationState();
80
+ return true;
81
+ }
82
+ return false;
83
+ }
84
+ disconnectedCallback(){
85
+ super.disconnectedCallback();
86
+ offEvent(this.shadowRoot.getElementById('img'), 'click', this[openHandler]);
87
+ offEvent(this.shadowRoot.getElementById('img'), 'keydown', this[imgKeyboardHandler]);
88
+ offEvent(this.shadowRoot.getElementById('close'), 'click', this[closeHandler]);
89
+ offEvent(this.shadowRoot.getElementById('fullscreen-overlay'), 'click', this[overlayHandler]);
90
+ offEvent(this.shadowRoot.getElementById('prev'), 'click', this[prevHandler]);
91
+ offEvent(this.shadowRoot.getElementById('next'), 'click', this[nextHandler]);
92
+ if(this.keyboardControls) {
93
+ document.removeEventListener('keydown', this[keyboardHandler]);
94
+ }
95
+ }
96
+
97
+ /* Private Methods */
98
+ [imgKeyboardHandler] = (e) => {
99
+ if(e.key === 'Enter' && !this.fullscreen) {
100
+ this.open();
101
+ }
102
+ }
103
+ [openHandler] = () => {
104
+ this.open();
105
+ }
106
+ [closeHandler] = () => {
107
+ this.close();
108
+ }
109
+ [overlayHandler] = (e) => {
110
+ if (e.target === e.currentTarget) {
111
+ this.close();
112
+ }
113
+ }
114
+ [prevHandler] = (e) => {
115
+ e.stopPropagation();
116
+ const prev = this.getPrevSibling();
117
+ if(prev) {
118
+ this.close();
119
+ prev.open();
120
+ }
121
+ }
122
+ [nextHandler] = (e) => {
123
+ e.stopPropagation();
124
+ const next = this.getNextSibling();
125
+ if(next) {
126
+ this.close();
127
+ next.open();
128
+ }
129
+ }
130
+ [keyboardHandler] = (e) => {
131
+ if(e.key === 'Escape') {
132
+ this.close();
133
+ } else if(e.key === 'ArrowLeft') {
134
+ const prev = this.getPrevSibling();
135
+ if(prev) {
136
+ this.close();
137
+ prev.open();
138
+ }
139
+ } else if(e.key === 'ArrowRight') {
140
+ const next = this.getNextSibling();
141
+ if(next) {
142
+ this.close();
143
+ next.open();
144
+ }
145
+ }
146
+ }
147
+ getPrevSibling() {
148
+ if (this.global) {
149
+ // Find any PhotoViewer in document
150
+ const viewers = Array.from(document.getElementsByTagName('k-photo-viewer'));
151
+ const index = viewers.indexOf(this);
152
+ if (index === -1) return null;
153
+ return viewers[index === 0 ? viewers.length - 1 : index - 1];
154
+ } else {
155
+ // Only look at direct siblings
156
+ let prev = this.previousElementSibling;
157
+ while (prev && prev.tagName !== 'K-PHOTO-VIEWER') {
158
+ prev = prev.previousElementSibling;
159
+ }
160
+ if (!prev && this.hasPhotoSiblings()) {
161
+ prev = this.parentElement.lastElementChild;
162
+ while (prev && prev.tagName !== 'K-PHOTO-VIEWER') {
163
+ prev = prev.previousElementSibling;
164
+ }
165
+ }
166
+ return prev;
167
+ }
168
+ }
169
+
170
+ getNextSibling() {
171
+ if (this.global) {
172
+ // Find any PhotoViewer in document
173
+ const viewers = Array.from(document.getElementsByTagName('k-photo-viewer'));
174
+ const index = viewers.indexOf(this);
175
+ if (index === -1) return null;
176
+ return viewers[index === viewers.length - 1 ? 0 : index + 1];
177
+ } else {
178
+ // Only look at direct siblings
179
+ let next = this.nextElementSibling;
180
+ while (next && next.tagName !== 'K-PHOTO-VIEWER') {
181
+ next = next.nextElementSibling;
182
+ }
183
+ if (!next && this.hasPhotoSiblings()) {
184
+ next = this.parentElement.firstElementChild;
185
+ while (next && next.tagName !== 'K-PHOTO-VIEWER') {
186
+ next = next.nextElementSibling;
187
+ }
188
+ }
189
+ return next;
190
+ }
191
+ }
192
+
193
+ hasPhotoSiblings() {
194
+ if (this.global) {
195
+ return document.getElementsByTagName('k-photo-viewer').length > 1;
196
+ }
197
+ return Array.from(this.parentElement.children)
198
+ .filter(el => el !== this && el.tagName === 'K-PHOTO-VIEWER')
199
+ .length > 0;
200
+ }
201
+
202
+ updateNavigationState() {
203
+ const hasNavigation = this.fullscreen && this.hasPhotoSiblings();
204
+ const nav = this.shadowRoot.querySelectorAll('.nav-btn');
205
+ nav.forEach(btn => btn.classList.toggle('d-n', !hasNavigation));
206
+ }
207
+
208
+ /* Public Methods */
209
+ open(){
210
+ this.fullscreen = true;
211
+ }
212
+ close(){
213
+ this.fullscreen = false;
214
+ }
215
+ toggle(){
216
+ this.fullscreen = !this.fullscreen;
217
+ }
218
+
219
+ /* Shadow DOM */
220
+ get shadowTemplate(){
221
+ return /*html*/`
222
+ <div id="wrapper">
223
+ <img id="img" tabindex="0" />
224
+ <div class="caption">
225
+ <slot></slot>
226
+ </div>
227
+ <div id="fullscreen-overlay">
228
+ <button id="close" class="no-btn">
229
+ <slot name="close">
230
+ <k-icon name="close"></k-icon>
231
+ </slot>
232
+ </button>
233
+ <button id="prev" class="nav-btn no-btn">
234
+ <slot name="prev">
235
+ <k-icon name="chevron-left"></k-icon>
236
+ </slot>
237
+ </button>
238
+ <button id="next" class="nav-btn no-btn">
239
+ <slot name="next">
240
+ <k-icon name="chevron-right"></k-icon>
241
+ </slot>
242
+ </button>
243
+ <div class="content">
244
+ <img id="fullscreen-img" />
245
+ <div class="caption">
246
+ <slot name="fullscreen-caption"></slot>
247
+ </div>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ `;
252
+ }
253
+ get shadowStyles(){
254
+ return /*css*/`
255
+ ${super.shadowStyles}
256
+ :host {
257
+ display: block;
258
+ }
259
+ #wrapper {
260
+ position: relative;
261
+ width: 100%;
262
+ height: 100%;
263
+ }
264
+ #img {
265
+ max-width: 100%;
266
+ height: auto;
267
+ cursor: pointer;
268
+ outline: none;
269
+ border-radius: var(--img_radius, 0);
270
+ }
271
+ #img:focus {
272
+ box-shadow: var(--focus_shadow);
273
+ }
274
+ #fullscreen-overlay {
275
+ position: fixed;
276
+ top: 0;
277
+ left: 0;
278
+ right: 0;
279
+ bottom: 0;
280
+ z-index: 9999;
281
+ background: rgba(0,0,0,0.9);
282
+ padding: 2rem;
283
+ display: none;
284
+ }
285
+ :host([fullscreen]) #fullscreen-overlay {
286
+ display: flex;
287
+ align-items: center;
288
+ justify-content: center;
289
+ }
290
+ .content {
291
+ position: relative;
292
+ max-width: 100%;
293
+ max-height: 90vh;
294
+ }
295
+ #fullscreen-img {
296
+ max-height: 75vh;
297
+ max-width: 100%;
298
+ object-fit: contain;
299
+ cursor: default;
300
+ }
301
+ #close {
302
+ position: absolute;
303
+ top: 1rem;
304
+ right: 1rem;
305
+ z-index: 1;
306
+ background: none;
307
+ border: none;
308
+ color: white;
309
+ cursor: pointer;
310
+ }
311
+ .nav-btn {
312
+ position: fixed;
313
+ top: 50%;
314
+ transform: translateY(-50%);
315
+ z-index: 1;
316
+ background: none;
317
+ border: none;
318
+ color: white;
319
+ cursor: pointer;
320
+ padding: 1rem;
321
+ opacity: 0.7;
322
+ font-size: 3rem;
323
+ -webkit-text-stroke: 1px black;
324
+ text-stroke: 1px black;
325
+ text-shadow: 0 0 3px rgba(0,0,0,0.8);
326
+ }
327
+ .nav-btn::slotted(*),
328
+ .nav-btn k-icon {
329
+ filter: drop-shadow(0 0 2px rgba(0,0,0,1)) drop-shadow(0 0 2px rgba(0,0,0,1));
330
+ }
331
+ .nav-btn:hover {
332
+ opacity: 1;
333
+ }
334
+ #prev {
335
+ left: 1rem;
336
+ }
337
+ #next {
338
+ right: 1rem;
339
+ }
340
+ .caption {
341
+ text-align: center;
342
+ max-width: 600px;
343
+ width: fit-content;
344
+ margin: 1rem auto 0;
345
+ }
346
+ #fullscreen-overlay .caption {
347
+ color: white;
348
+ }
349
+ :host([fullscreen]) .caption {
350
+ color: white;
351
+ }
352
+ `;
353
+ }
354
+
355
+ /* Static Members */
356
+ static observedAttributes = [...super.observedAttributes, 'src', 'alt', 'fullscreen', 'keyboard-controls', 'global'];
357
+ }
358
+ window.customElements.define('k-photo-viewer', PhotoViewer);
@@ -31,23 +31,23 @@
31
31
  :root,
32
32
  [theme="light"] {
33
33
  color-scheme: light;
34
- --c_bg: #f9f9f9;
35
- --c_bg__inv: #333;
36
- --c_bg__alt: #eee;
37
- --c_overscroll: white;
38
- --c_border: #ccc;
34
+ --c_bg: rgb(249, 249, 249);
35
+ --c_bg__inv: rgb(51, 51, 51);
36
+ --c_bg__alt: rgb(238, 238, 238);
37
+ --c_overscroll: rgb(255, 255, 255);
38
+ --c_border: rgb(204, 204, 204);
39
39
  --c_border__inv: var(--d_c_bg_border);
40
- --c_primary: #36f;
41
- --c_primary__hover: #14d;
42
- --c_secondary: #93f;
43
- --c_secondary__hover: #71d;
44
- --c_success: #080;
45
- --c_success__hover: #060;
46
- --c_warning: #f60;
47
- --c_warning__hover: #d40;
48
- --c_danger: #f03;
49
- --c_danger__hover: #d01;
50
- --c_input_accent: #36f;
40
+ --c_primary: rgb(51, 102, 255);
41
+ --c_primary__hover: rgb(17, 68, 221);
42
+ --c_secondary: rgb(153, 51, 255);
43
+ --c_secondary__hover: rgb(119, 17, 221);
44
+ --c_success: rgb(0, 136, 0);
45
+ --c_success__hover: rgb(0, 102, 0);
46
+ --c_warning: rgb(255, 102, 0);
47
+ --c_warning__hover: rgb(221, 68, 0);
48
+ --c_danger: rgb(255, 0, 51);
49
+ --c_danger__hover: rgb(221, 0, 17);
50
+ --c_input_accent: rgb(51, 102, 255);
51
51
  --c_highlight: rgba(41, 100, 210, 0.25);
52
52
  --tc: rgba(0, 0, 0, 0.93);
53
53
  --tc_dark: var(--tc);
@@ -68,8 +68,8 @@
68
68
  --btn_box_shadow: 0 0 0 transparent;
69
69
  --btn_box_shadow__hover: 0 0 0 transparent;
70
70
  --btn_border: transparent;
71
- --btn_bg: #ddd;
72
- --btn_bg__hover: #ccc;
71
+ --btn_bg: rgb(221, 221, 221);
72
+ --btn_bg__hover: rgb(204, 204, 204);
73
73
  --btn_tc: rgba(0, 0, 0, 0.93);
74
74
  --btn_transparent__hover: rgba(0, 0, 0, 0.05);
75
75
  --tc_link: var(--tc_primary);
@@ -85,23 +85,23 @@
85
85
  [auto-theme="dark"]:not([theme="light"]),
86
86
  [theme="dark"] {
87
87
  color-scheme: dark;
88
- --c_bg: #333;
89
- --c_bg__inv: #f9f9f9;
90
- --c_bg__alt: #222;
91
- --c_overscroll: black;
92
- --c_border: #777;
93
- --c_border__inv: #ccc;
94
- --c_primary: #36f;
95
- --c_primary__hover: #14d;
96
- --c_secondary: #93f;
97
- --c_secondary__hover: #71d;
98
- --c_success: #080;
99
- --c_success__hover: #060;
100
- --c_warning: #f60;
101
- --c_warning__hover: #d40;
102
- --c_danger: #f03;
103
- --c_danger__hover: #d01;
104
- --c_input_accent: #36f;
88
+ --c_bg: rgb(51, 51, 51);
89
+ --c_bg__inv: rgb(249, 249, 249);
90
+ --c_bg__alt: rgb(34, 34, 34);
91
+ --c_overscroll: rgb(0, 0, 0);
92
+ --c_border: rgb(119, 119, 119);
93
+ --c_border__inv: rgb(204, 204, 204);
94
+ --c_primary: rgb(51, 102, 255);
95
+ --c_primary__hover: rgb(17, 68, 221);
96
+ --c_secondary: rgb(153, 51, 255);
97
+ --c_secondary__hover: rgb(119, 17, 221);
98
+ --c_success: rgb(0, 136, 0);
99
+ --c_success__hover: rgb(0, 102, 0);
100
+ --c_warning: rgb(255, 102, 0);
101
+ --c_warning__hover: rgb(221, 68, 0);
102
+ --c_danger: rgb(255, 0, 51);
103
+ --c_danger__hover: rgb(221, 0, 17);
104
+ --c_input_accent: rgb(51, 102, 255);
105
105
  --c_highlight: rgba(0, 89, 255, 0.25);
106
106
  --tc: rgba(255, 255, 255, 0.93);
107
107
  --tc_dark: rgba(0, 0, 0, 0.93);
@@ -114,13 +114,13 @@
114
114
  --tc_on_warning: var(--tc);
115
115
  --tc_on_danger: var(--tc);
116
116
  --c_overlay: rgba(0, 0, 0, 0.5);
117
- --tc_primary: #8ab4f8;
118
- --tc_secondary: #b6f;
119
- --tc_success: #6b6;
120
- --tc_warning: #f93;
121
- --tc_danger: #f57;
122
- --btn_bg: #aaa;
123
- --btn_bg__hover: #bbb;
117
+ --tc_primary: rgb(138, 180, 248);
118
+ --tc_secondary: rgb(187, 102, 255);
119
+ --tc_success: rgb(102, 187, 102);
120
+ --tc_warning: rgb(255, 153, 51);
121
+ --tc_danger: rgb(255, 85, 119);
122
+ --btn_bg: rgb(170, 170, 170);
123
+ --btn_bg__hover: rgb(187, 187, 187);
124
124
  --btn_tc: rgba(0, 0, 0, 0.93);
125
125
  --btn_transparent__hover: rgba(255, 255, 255, 0.05);
126
126
  --btn_box_shadow: 0 0 0 transparent;