kempo 2.5.0 → 2.6.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 LazyComponent from"./LazyComponent.js";import Component from"./Component.js";import"./Icon.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";import watchDirectoryHandle from"../utils/watchDirectoryHandle.js";const handle=Symbol(),loadDirectoryClickHandler=Symbol(),openToggleClickHandler=Symbol(),fileSelectedHandler=Symbol(),watchId=Symbol(),changeCallback=Symbol(),filters=Symbol();export default class DirectoryViewer extends LazyComponent{constructor(e=!1){super(),this[handle]=e,this[watchId]=!1,this[filters]=[],this[loadDirectoryClickHandler]=()=>this.loadDirectory(),this[openToggleClickHandler]=()=>{this.root?this.clearDirectoryHandle():(this.toggleOpen(),this.select())},this[changeCallback]=()=>{this.updateContents()},this.slot="content",this.registerAttributes({root:!e,hasHandle:!!e,opened:!e,selected:!1})}async render(e){return!!await super.render(e)&&(onEvent(this.shadowRoot,"fileselected",this[fileSelectedHandler]),onEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),onEvent(this.shadowRoot.getElementById("openToggle"),"click",this[openToggleClickHandler]),this.renderDirectory(),!0)}async renderDirectory(){this[handle]&&this.rendered&&(this.innerHTML=this[handle].name,this.renderContents())}async renderContents(){if(this.querySelectorAll('[slot="content"]').forEach((e=>e.remove())),this.rendered&&this[handle]&&(this.opened||this.root)){for await(const e of this[handle].values())"directory"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new DirectoryViewer(e,!1));for await(const e of this[handle].values())"file"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new File(e));this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot,"fileselected",this[fileSelectedHandler]),offEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),offEvent(this.shadowRoot.getElementById("openToggle"),"click",this[openToggleClickHandler])}attributeChangedCallback(e,t,n){"opened"===e&&t!==n&&(!n&&this.root?this.opened=!0:this.renderContents())}get rootDir(){return this.root?this:this.closest('k-directory-viewer[root="true"]')}get filters(){return this.rootDir[filters]}async loadDirectory(){window.showDirectoryPicker&&(this[handle]=await window.showDirectoryPicker(),this[handle]&&(this.hasHandle=!0,dispatchEvent(this,"directoryloaded",{dirHandle:this[handle],dirComponent:this}),this.renderDirectory()))}open(){this.opened=!0}close(){this.root||(this.opened=!1)}toggleOpen(){this.root||(this.opened=!this.opened)}clearDirectoryHandle(){this[handle]=!1,this.hasHandle=!1,this.innerHTML=""}async updateContents(){if(this.opened||this.root){const e=new Set([...this.querySelectorAll("k-directory-viewer")]);for await(const t of this[handle].values())if("directory"===t.kind){const n=[...e].find((e=>t.isSameEntry(e[handle])&&t.name===e[handle].name));n?(this.appendChild(n),e.delete(n)):this.appendChild(new DirectoryViewer(t,!1))}e.forEach((e=>e.remove()));const t=new Set([...this.querySelectorAll("k-directory-viewer-file")]);for await(const e of this[handle].values())if("file"===e.kind){const n=[...t].find((t=>e.isSameEntry(t[handle])&&e.name===t[handle].name));n?(this.appendChild(n),t.delete(n)):this.appendChild(new File(e))}t.forEach((e=>e.remove())),this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}select(){const e=this.closest("k-directory-viewer[root]").querySelector("k-directory-viewer[selected], k-directory-viewer-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("dirselected",{detail:{dirHandle:this[handle],dirComponent:this},bubbles:!0,composed:!0})))}addFilter(e){this[filters].push(e),this.renderContents()}get shadowTemplate(){return`\n <button\n id="loadDirectory"\n >\n <slot name="selectButton">\n <k-icon name="folder-create"></k-icon> Select Directory\n </slot>\n </button>\n <button\n id="openToggle"\n class="no-btn"\n >\n <k-icon name="folder" id="folder-closed"></k-icon>\n <k-icon name="folder-open" id="folder-opened"></k-icon>\n <k-icon name="folder-clear" id="folder-clear"></k-icon>\n ${super.shadowTemplate}\n </button>\n <div id="tree">\n <slot name="content"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host([has-handle]) #loadDirectory,\n :host([opened]) #folder-closed,\n :host(:not([opened])) #folder-opened,\n :host(:not([has-handle])) #folder-closed,\n :host(:not([has-handle])) #folder-opened,\n :host(:not([has-handle])) #folder-clear,\n :host(:not([root])) #folder-clear,\n :host([root]) #folder-opened {\n display: none;\n }\n #openToggle {\n display: block;\n width: 100%;\n flex: 1 1 auto;\n }\n #tree {\n padding-left: var(--spacer);\n }\n #wrapper {\n display: flex;\n }\n #clearSelectedDirectory {\n flex: none;\n }\n :host([selected]) #openToggle {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n `}static observedAttributes=[...LazyComponent.observedAttributes,"opened"];static watchInterval=1e3}window.customElements.define("k-directory-viewer",DirectoryViewer);const selectFileClickHandler=Symbol();export class File extends Component{constructor(e=!1){super(),this[handle]=e,this[selectFileClickHandler]=()=>this.select(),this.slot="content",this.registerAttribute("selected",!1)}async render(e){if(await super.render(e)){if(this[handle]){this.innerHTML=this[handle].name;const e=this[handle].name.split(".").pop(),t=this.shadowRoot.getElementById("extIcon");["png","gif","jpeg","jpg","tif","webp"].includes(e)?t.name="image":["txt","rtf","js","css","html"].includes(e)?t.name="file-text":t.name="file",onEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}return!0}return!1}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}select(){const e=this.closest("k-directory-viewer[root]").querySelector("k-directory-viewer[selected], k-directory-viewer-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("fileselected",{detail:{fileHandle:this[handle],fileComponent:this},bubbles:!0,composed:!0})))}get shadowTemplate(){return`\n <button\n id="selectFile"\n class="no-btn"\n >\n <k-icon\n id="extIcon"\n name=""\n ></k-icon>\n ${super.shadowTemplate}\n </button>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n }\n :host([selected]) {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n #selectFile {\n width: 100%;\n }\n `}}window.customElements.define("k-directory-viewer-file",File);
1
+ import LazyComponent from"./LazyComponent.js";import Component from"./Component.js";import"./Icon.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";import watchDirectoryHandle from"../utils/watchDirectoryHandle.js";const handle=Symbol(),loadDirectoryClickHandler=Symbol(),selectedHandler=Symbol(),watchId=Symbol(),changeCallback=Symbol(),filters=Symbol();export default class DirectoryViewer extends LazyComponent{constructor(e=!1){super(),this[handle]=e,this[watchId]=!1,this[filters]=[],this[loadDirectoryClickHandler]=()=>this.loadDirectory(),this[openToggleClickHandler]=()=>{this.root?this.clearDirectoryHandle():(this.toggleOpen(),this.select())},this[changeCallback]=()=>{this.updateContents()},this[selectedHandler]=e=>{},this.registerAttributes({hasHandle:!!e,selected:!1})}async render(e){return!!await super.render(e)&&(onEvent(this.shadowRoot,"fileSelected directorySelected",this[selectedHandler]),onEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),this.renderDirectory(),!0)}async renderDirectory(){this[handle]&&this.rendered&&(this.innerHTML=this[handle].name,this.renderContents())}async renderContents(){if(this.querySelectorAll('[slot="content"]').forEach((e=>e.remove())),this.rendered&&this[handle]&&(this.opened||this.root)){for await(const e of this[handle].values())"directory"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new DirectoryViewer(e,!1));for await(const e of this[handle].values())"file"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new File(e));this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot,"fileselected",this[fileSelectedHandler]),offEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),offEvent(this.shadowRoot.getElementById("openToggle"),"click",this[openToggleClickHandler])}attributeChangedCallback(e,t,n){"opened"===e&&t!==n&&(!n&&this.root?this.opened=!0:this.renderContents())}get rootDir(){return this.root?this:this.closest('k-directory-viewer[root="true"]')}get filters(){return this.rootDir[filters]}async loadDirectory(){window.showDirectoryPicker&&(this[handle]=await window.showDirectoryPicker(),this[handle]&&(this.hasHandle=!0,dispatchEvent(this,"directoryloaded",{dirHandle:this[handle],dirComponent:this}),this.renderDirectory()))}clearDirectoryHandle(){this[handle]=!1,this.hasHandle=!1,this.innerHTML=""}async updateContents(){if(this.opened||this.root){const e=new Set([...this.querySelectorAll("k-directory-viewer")]);for await(const t of this[handle].values())if("directory"===t.kind){const n=[...e].find((e=>t.isSameEntry(e[handle])&&t.name===e[handle].name));n?(this.appendChild(n),e.delete(n)):this.appendChild(new DirectoryViewer(t,!1))}e.forEach((e=>e.remove()));const t=new Set([...this.querySelectorAll("k-directory-viewer-file")]);for await(const e of this[handle].values())if("file"===e.kind){const n=[...t].find((t=>e.isSameEntry(t[handle])&&e.name===t[handle].name));n?(this.appendChild(n),t.delete(n)):this.appendChild(new File(e))}t.forEach((e=>e.remove())),this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}select(){const e=this.querySelector("k-directory[selected], k-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("dirSelected",{detail:{dirHandle:this[handle],dirComponent:this},bubbles:!0,composed:!0})))}addFilter(e){this[filters].push(e),this.renderContents()}get shadowTemplate(){return`\n <button\n id="loadDirectory"\n >\n <slot name="selectButton">\n <k-icon name="folder-create"></k-icon> Select Directory\n </slot>\n </button>\n <button\n id="openToggle"\n class="no-btn"\n >\n <k-icon name="folder" id="folder-closed"></k-icon>\n <k-icon name="folder-open" id="folder-opened"></k-icon>\n <k-icon name="folder-clear" id="folder-clear"></k-icon>\n ${super.shadowTemplate}\n </button>\n <div id="tree">\n <slot name="content"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host([has-handle]) #loadDirectory,\n :host([opened]) #folder-closed,\n :host(:not([opened])) #folder-opened,\n :host(:not([has-handle])) #folder-closed,\n :host(:not([has-handle])) #folder-opened,\n :host(:not([has-handle])) #folder-clear,\n :host(:not([root])) #folder-clear,\n :host([root]) #folder-opened {\n display: none;\n }\n #openToggle {\n display: block;\n width: 100%;\n flex: 1 1 auto;\n }\n #tree {\n padding-left: var(--spacer);\n }\n #wrapper {\n display: flex;\n }\n #clearSelectedDirectory {\n flex: none;\n }\n :host([selected]) #openToggle {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n `}static observedAttributes=[...LazyComponent.observedAttributes,"opened"];static watchInterval=1e3}window.customElements.define("k-directory-viewer",DirectoryViewer);export class Directory extends Component{constructor(e=!1){super(),this[handle]=e,this[selectDirectoryClickHandler]=()=>this.select(),this.slot="content",this.registerAttribute("selected",!1)}async render(e){return!!await super.render(e)&&(this[handle]&&(this.innerHTML=this[handle].name,onEvent(this.shadowRoot.getElementById("selectDirectory"),"click",this[selectDirectoryClickHandler])),!0)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("selectDirectory"),"click",this[selectDirectoryClickHandler])}get rootDir(){return this.closest("k-directory-viewer")}select(){const e=this.rootDir.querySelector("k-directory[selected], k-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("directorySelected",{detail:{fileHandle:this[handle],fileComponent:this},bubbles:!0,composed:!0})))}get shadowTemplate(){return`\n <button\n id="selectDirectory"\n >\n <slot name="selectButton">\n <k-icon id="extIcon" name="file"></k-icon> ${this[handle].name}\n </slot>\n </button>\n ${super.shadowTemplate}\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host([selected]) #selectFile {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n `}}const selectFileClickHandler=Symbol();export class File extends Component{constructor(e=!1){super(),this[handle]=e,this[selectFileClickHandler]=()=>this.select(),this.slot="content",this.registerAttribute("selected",!1)}async render(e){if(await super.render(e)){if(this[handle]){this.innerHTML=this[handle].name;const e=this[handle].name.split(".").pop(),t=this.shadowRoot.getElementById("extIcon");["png","gif","jpeg","jpg","tif","webp"].includes(e)?t.name="image":["txt","rtf","js","css","html"].includes(e)?t.name="file-text":t.name="file",onEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}return!0}return!1}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}get rootDir(){return this.closest("k-directory-viewer")}select(){const e=this.rootDir.querySelector("k-directory[selected], k-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("fileSelected",{detail:{fileHandle:this[handle],fileComponent:this},bubbles:!0,composed:!0})))}get shadowTemplate(){return`\n <button\n id="selectFile"\n class="no-btn"\n >\n <k-icon\n id="extIcon"\n name=""\n ></k-icon>\n ${super.shadowTemplate}\n </button>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n }\n :host([selected]) {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n #selectFile {\n width: 100%;\n }\n `}}window.customElements.define("k-directory-viewer-file",File);
@@ -1 +1 @@
1
- import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";export default class Table extends Component{constructor(e=!1,t=!1){super(),this.registerProps({fields:[],records:[]}),e&&this.setData(e,t)}async render(e){return!!await super.render(e)&&(this.renderFields(),this.renderRecords(),!0)}renderFields(){this.shadowRoot.getElementById("fields").innerHTML=this.fields.map((({label:e})=>`<th>${e}</th>`)).join("")}renderRecords(){this.shadowRoot.getElementById("records").innerHTML=this.records.map((e=>`<tr>${this.fields.map((({name:t})=>`<td>${e[t]||""}</td>`)).join("")}</tr>`)).join("")}setData(e,t=!1){this.records=e,this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n ${super.shadowTemplate}\n <div class="responsive-table">\n <table>\n <thead id="fields"></thead>\n <tbody id="records"></tbody>\n </table>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n margin-bottom: var(--spacer);\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}}window.customElements.define("k-table",Table);
1
+ import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";export default class Table extends Component{constructor(e=!1,t=!1){super(),this.registerProps({fields:[],records:[]}),e&&this.setData(e,t)}async render(e){return!!await super.render(e)&&(this.renderFields(),this.renderRecords(),!0)}renderFields(){this.shadowRoot.getElementById("fields").innerHTML=this.fields.map((({label:e})=>`<th>${e}</th>`)).join("")}renderRecords(){this.shadowRoot.getElementById("records").innerHTML=this.records.map((e=>`<tr>${this.fields.map((({name:t})=>{let s=e[t]||"";return Array.isArray(s)&&(s=s.join(", ")),`<td>${s}</td>`})).join("")}</tr>`)).join("")}setData(e,t=!1){this.records=e,this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n ${super.shadowTemplate}\n <div class="responsive-table">\n <table>\n <thead id="fields"></thead>\n <tbody id="records"></tbody>\n </table>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n margin-bottom: var(--spacer);\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}}window.customElements.define("k-table",Table);
@@ -1 +1 @@
1
- import Component from"./Component.js";import{onEvent,dispatchEvent}from"../utils/element.js";import"./Icon.js";const storageWatcher=Symbol("storageWatcher"),clickHandler=Symbol("clickHandler");export default class ThemeSwitcher extends Component{constructor(){super(),this[storageWatcher]=()=>{this.currentTheme=ThemeSwitcher.getCurrentTheme()},this[clickHandler]=()=>{const e=ThemeSwitcher.getCurrentTheme();"auto"===e&&ThemeSwitcher.setTheme("light"),"light"===e&&ThemeSwitcher.setTheme("dark"),"dark"===e&&ThemeSwitcher.setTheme("auto")},this.registerAttribute("currentTheme",ThemeSwitcher.getCurrentTheme())}async render(e){return!!await super.render(e)&&(onEvent(window,"storage",this[storageWatcher]),onEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler]),!0)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler])}get shadowTemplate(){return'\n <button\n id="toggle"\n class="no-btn"\n >\n <k-icon\n id="autoMode"\n name="mode-auto"\n class="mode"\n ></k-icon>\n <k-icon\n id="lightMode"\n name="light-mode"\n class="mode"\n ></k-icon>\n <k-icon\n id="darkMode"\n name="dark-mode"\n class="mode"\n ></k-icon>\n </button>\n '}get shadowStyles(){return'\n :host {\n --padding: var(--spacer);\n\n display: flex;\n }\n #toggle {\n padding: var(--padding);\n }\n .mode {\n display: none;\n }\n :host([current-theme="auto"]) #autoMode,\n :host([current-theme="light"]) #lightMode,\n :host([current-theme="dark"]) #darkMode {\n display: block;\n }\n '}static setTheme(e){localStorage.setItem("theme",e),document.documentElement.setAttribute("theme",e),dispatchEvent(window,"storage",{theme:e})}static getCurrentTheme(){let e=document.documentElement.getAttribute("theme");return e||(e=localStorage.getItem("theme")),e}}const colorSchemeQuery=window.matchMedia("(prefers-color-scheme: dark)"),colorSchemeChangeHandler=e=>document.documentElement.setAttribute("auto-theme",e.matches?"dark":"light");colorSchemeQuery.addEventListener("change",colorSchemeChangeHandler),colorSchemeChangeHandler(colorSchemeQuery),ThemeSwitcher.setTheme(ThemeSwitcher.getCurrentTheme()),window.customElements.define("k-theme-switcher",ThemeSwitcher);
1
+ import Component from"./Component.js";import{onEvent,dispatchEvent}from"../utils/element.js";import"./Icon.js";const storageWatcher=Symbol("storageWatcher"),clickHandler=Symbol("clickHandler");export default class ThemeSwitcher extends Component{constructor(){super(),this[storageWatcher]=()=>{this.currentTheme=ThemeSwitcher.getCurrentTheme()},this[clickHandler]=()=>{const e=ThemeSwitcher.getCurrentTheme();"auto"===e&&ThemeSwitcher.setTheme("light"),"light"===e&&ThemeSwitcher.setTheme("dark"),"dark"===e&&ThemeSwitcher.setTheme("auto")},this.registerAttribute("currentTheme",ThemeSwitcher.getCurrentTheme())}async render(e){return!!await super.render(e)&&(onEvent(window,"storage",this[storageWatcher]),onEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler]),!0)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler])}get shadowTemplate(){return'\n <button\n id="toggle"\n class="no-btn"\n >\n <k-icon\n id="autoMode"\n name="mode-auto"\n class="mode"\n ></k-icon>\n <k-icon\n id="lightMode"\n name="mode-light"\n class="mode"\n ></k-icon>\n <k-icon\n id="darkMode"\n name="mode-dark"\n class="mode"\n ></k-icon>\n </button>\n '}get shadowStyles(){return'\n :host {\n --padding: var(--spacer);\n\n display: flex;\n }\n #toggle {\n padding: var(--padding);\n }\n .mode {\n display: none;\n }\n :host([current-theme="auto"]) #autoMode,\n :host([current-theme="light"]) #lightMode,\n :host([current-theme="dark"]) #darkMode {\n display: block;\n }\n '}static setTheme(e){localStorage.setItem("theme",e),document.documentElement.setAttribute("theme",e),dispatchEvent(window,"storage",{theme:e})}static getCurrentTheme(){let e=document.documentElement.getAttribute("theme");return e||(e=localStorage.getItem("theme")),e}}const colorSchemeQuery=window.matchMedia("(prefers-color-scheme: dark)"),colorSchemeChangeHandler=e=>document.documentElement.setAttribute("auto-theme",e.matches?"dark":"light");colorSchemeQuery.addEventListener("change",colorSchemeChangeHandler),colorSchemeChangeHandler(colorSchemeQuery),ThemeSwitcher.setTheme(ThemeSwitcher.getCurrentTheme()),window.customElements.define("k-theme-switcher",ThemeSwitcher);
@@ -22,6 +22,7 @@
22
22
  <a href="#requirements">Requirements</a><br />
23
23
  <a href="#properties">Properties</a><br />
24
24
  <a href="#methods">Methods</a><br />
25
+ <a href="#arrays">Displaying Arrays</a><br />
25
26
  </div>
26
27
  </details>
27
28
 
@@ -107,6 +108,23 @@
107
108
  ]);
108
109
  </script>
109
110
 
111
+ <h3 id="arrays"><a href="#arrays" class="no-link">Displaying Arrays</a></h3>
112
+ <p>The table component can display arrays of strings or numbers by joining them with a comma and a space.</p>
113
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-table</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hobbies"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-table</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="javascript"><br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'hobbies'</span>).setData([<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Dustin"</span>,<br /> <span class="hljs-attr">hobbies</span>: [<span class="hljs-string">"Reading"</span>, <span class="hljs-string">"Hiking"</span>, <span class="hljs-string">"Gaming"</span>]<br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Kayla"</span>,<br /> <span class="hljs-attr">hobbies</span>: [<span class="hljs-string">"Cooking"</span>, <span class="hljs-string">"Traveling"</span>]<br /> }<br /> ]);<br /></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre>
114
+ <k-table id="hobbies"></k-table>
115
+ <script type="module">
116
+ document.getElementById('hobbies').setData([
117
+ {
118
+ name: "Dustin",
119
+ hobbies: ["Reading", "Hiking", "Gaming"]
120
+ },
121
+ {
122
+ name: "Kayla",
123
+ hobbies: ["Cooking", "Traveling"]
124
+ }
125
+ ]);
126
+ </script>
127
+
110
128
  <h3 id="constructor"><a href="#consttructor" class="no-link">Constructor</a></h3>
111
129
  <h6>Extends <a href="./component.html">Component</a></h6>
112
130
  <h5>
@@ -139,10 +157,10 @@
139
157
  <h5><code>setData(<i>&lt;Array>object</i> records, <i>&lt;Array>object</i> fields)<i>: undefined</i></code></h5>
140
158
  <p>Sets the records and the fields and renders the table, if no fields are provided then they will be automatically generated from the keys of the first 100 records.</p>
141
159
 
142
- <h5><code>renderFields()<i>: undefined</i></code></h5>
160
+ <h5><code>renderFields()<i>: undefined</i></h5>
143
161
  <p>Renders the fields as table headers, this is called automatically by setData, you should never have to call it.</p>
144
162
 
145
- <h5><code>renderRecords()<i>: undefined</i></code></h5>
163
+ <h5><code>renderRecords()<i>: undefined</i></h5>
146
164
  <p>Renders the records as table row, this is called automatically by setData, you should never have to call it.</p>
147
165
 
148
166
  </main>
@@ -1 +1 @@
1
- import LazyComponent from"./LazyComponent.js";import Component from"./Component.js";import"./Icon.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";import watchDirectoryHandle from"../utils/watchDirectoryHandle.js";const handle=Symbol(),loadDirectoryClickHandler=Symbol(),openToggleClickHandler=Symbol(),fileSelectedHandler=Symbol(),watchId=Symbol(),changeCallback=Symbol(),filters=Symbol();export default class DirectoryViewer extends LazyComponent{constructor(e=!1){super(),this[handle]=e,this[watchId]=!1,this[filters]=[],this[loadDirectoryClickHandler]=()=>this.loadDirectory(),this[openToggleClickHandler]=()=>{this.root?this.clearDirectoryHandle():(this.toggleOpen(),this.select())},this[changeCallback]=()=>{this.updateContents()},this.slot="content",this.registerAttributes({root:!e,hasHandle:!!e,opened:!e,selected:!1})}async render(e){return!!await super.render(e)&&(onEvent(this.shadowRoot,"fileselected",this[fileSelectedHandler]),onEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),onEvent(this.shadowRoot.getElementById("openToggle"),"click",this[openToggleClickHandler]),this.renderDirectory(),!0)}async renderDirectory(){this[handle]&&this.rendered&&(this.innerHTML=this[handle].name,this.renderContents())}async renderContents(){if(this.querySelectorAll('[slot="content"]').forEach((e=>e.remove())),this.rendered&&this[handle]&&(this.opened||this.root)){for await(const e of this[handle].values())"directory"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new DirectoryViewer(e,!1));for await(const e of this[handle].values())"file"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new File(e));this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot,"fileselected",this[fileSelectedHandler]),offEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),offEvent(this.shadowRoot.getElementById("openToggle"),"click",this[openToggleClickHandler])}attributeChangedCallback(e,t,n){"opened"===e&&t!==n&&(!n&&this.root?this.opened=!0:this.renderContents())}get rootDir(){return this.root?this:this.closest('k-directory-viewer[root="true"]')}get filters(){return this.rootDir[filters]}async loadDirectory(){window.showDirectoryPicker&&(this[handle]=await window.showDirectoryPicker(),this[handle]&&(this.hasHandle=!0,dispatchEvent(this,"directoryloaded",{dirHandle:this[handle],dirComponent:this}),this.renderDirectory()))}open(){this.opened=!0}close(){this.root||(this.opened=!1)}toggleOpen(){this.root||(this.opened=!this.opened)}clearDirectoryHandle(){this[handle]=!1,this.hasHandle=!1,this.innerHTML=""}async updateContents(){if(this.opened||this.root){const e=new Set([...this.querySelectorAll("k-directory-viewer")]);for await(const t of this[handle].values())if("directory"===t.kind){const n=[...e].find((e=>t.isSameEntry(e[handle])&&t.name===e[handle].name));n?(this.appendChild(n),e.delete(n)):this.appendChild(new DirectoryViewer(t,!1))}e.forEach((e=>e.remove()));const t=new Set([...this.querySelectorAll("k-directory-viewer-file")]);for await(const e of this[handle].values())if("file"===e.kind){const n=[...t].find((t=>e.isSameEntry(t[handle])&&e.name===t[handle].name));n?(this.appendChild(n),t.delete(n)):this.appendChild(new File(e))}t.forEach((e=>e.remove())),this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}select(){const e=this.closest("k-directory-viewer[root]").querySelector("k-directory-viewer[selected], k-directory-viewer-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("dirselected",{detail:{dirHandle:this[handle],dirComponent:this},bubbles:!0,composed:!0})))}addFilter(e){this[filters].push(e),this.renderContents()}get shadowTemplate(){return`\n <button\n id="loadDirectory"\n >\n <slot name="selectButton">\n <k-icon name="folder-create"></k-icon> Select Directory\n </slot>\n </button>\n <button\n id="openToggle"\n class="no-btn"\n >\n <k-icon name="folder" id="folder-closed"></k-icon>\n <k-icon name="folder-open" id="folder-opened"></k-icon>\n <k-icon name="folder-clear" id="folder-clear"></k-icon>\n ${super.shadowTemplate}\n </button>\n <div id="tree">\n <slot name="content"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host([has-handle]) #loadDirectory,\n :host([opened]) #folder-closed,\n :host(:not([opened])) #folder-opened,\n :host(:not([has-handle])) #folder-closed,\n :host(:not([has-handle])) #folder-opened,\n :host(:not([has-handle])) #folder-clear,\n :host(:not([root])) #folder-clear,\n :host([root]) #folder-opened {\n display: none;\n }\n #openToggle {\n display: block;\n width: 100%;\n flex: 1 1 auto;\n }\n #tree {\n padding-left: var(--spacer);\n }\n #wrapper {\n display: flex;\n }\n #clearSelectedDirectory {\n flex: none;\n }\n :host([selected]) #openToggle {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n `}static observedAttributes=[...LazyComponent.observedAttributes,"opened"];static watchInterval=1e3}window.customElements.define("k-directory-viewer",DirectoryViewer);const selectFileClickHandler=Symbol();export class File extends Component{constructor(e=!1){super(),this[handle]=e,this[selectFileClickHandler]=()=>this.select(),this.slot="content",this.registerAttribute("selected",!1)}async render(e){if(await super.render(e)){if(this[handle]){this.innerHTML=this[handle].name;const e=this[handle].name.split(".").pop(),t=this.shadowRoot.getElementById("extIcon");["png","gif","jpeg","jpg","tif","webp"].includes(e)?t.name="image":["txt","rtf","js","css","html"].includes(e)?t.name="file-text":t.name="file",onEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}return!0}return!1}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}select(){const e=this.closest("k-directory-viewer[root]").querySelector("k-directory-viewer[selected], k-directory-viewer-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("fileselected",{detail:{fileHandle:this[handle],fileComponent:this},bubbles:!0,composed:!0})))}get shadowTemplate(){return`\n <button\n id="selectFile"\n class="no-btn"\n >\n <k-icon\n id="extIcon"\n name=""\n ></k-icon>\n ${super.shadowTemplate}\n </button>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n }\n :host([selected]) {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n #selectFile {\n width: 100%;\n }\n `}}window.customElements.define("k-directory-viewer-file",File);
1
+ import LazyComponent from"./LazyComponent.js";import Component from"./Component.js";import"./Icon.js";import{onEvent,offEvent,dispatchEvent}from"../utils/element.js";import watchDirectoryHandle from"../utils/watchDirectoryHandle.js";const handle=Symbol(),loadDirectoryClickHandler=Symbol(),selectedHandler=Symbol(),watchId=Symbol(),changeCallback=Symbol(),filters=Symbol();export default class DirectoryViewer extends LazyComponent{constructor(e=!1){super(),this[handle]=e,this[watchId]=!1,this[filters]=[],this[loadDirectoryClickHandler]=()=>this.loadDirectory(),this[openToggleClickHandler]=()=>{this.root?this.clearDirectoryHandle():(this.toggleOpen(),this.select())},this[changeCallback]=()=>{this.updateContents()},this[selectedHandler]=e=>{},this.registerAttributes({hasHandle:!!e,selected:!1})}async render(e){return!!await super.render(e)&&(onEvent(this.shadowRoot,"fileSelected directorySelected",this[selectedHandler]),onEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),this.renderDirectory(),!0)}async renderDirectory(){this[handle]&&this.rendered&&(this.innerHTML=this[handle].name,this.renderContents())}async renderContents(){if(this.querySelectorAll('[slot="content"]').forEach((e=>e.remove())),this.rendered&&this[handle]&&(this.opened||this.root)){for await(const e of this[handle].values())"directory"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new DirectoryViewer(e,!1));for await(const e of this[handle].values())"file"===e.kind&&this.filters.every((t=>t(e)))&&this.appendChild(new File(e));this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot,"fileselected",this[fileSelectedHandler]),offEvent(this.shadowRoot.getElementById("loadDirectory"),"click",this[loadDirectoryClickHandler]),offEvent(this.shadowRoot.getElementById("openToggle"),"click",this[openToggleClickHandler])}attributeChangedCallback(e,t,n){"opened"===e&&t!==n&&(!n&&this.root?this.opened=!0:this.renderContents())}get rootDir(){return this.root?this:this.closest('k-directory-viewer[root="true"]')}get filters(){return this.rootDir[filters]}async loadDirectory(){window.showDirectoryPicker&&(this[handle]=await window.showDirectoryPicker(),this[handle]&&(this.hasHandle=!0,dispatchEvent(this,"directoryloaded",{dirHandle:this[handle],dirComponent:this}),this.renderDirectory()))}clearDirectoryHandle(){this[handle]=!1,this.hasHandle=!1,this.innerHTML=""}async updateContents(){if(this.opened||this.root){const e=new Set([...this.querySelectorAll("k-directory-viewer")]);for await(const t of this[handle].values())if("directory"===t.kind){const n=[...e].find((e=>t.isSameEntry(e[handle])&&t.name===e[handle].name));n?(this.appendChild(n),e.delete(n)):this.appendChild(new DirectoryViewer(t,!1))}e.forEach((e=>e.remove()));const t=new Set([...this.querySelectorAll("k-directory-viewer-file")]);for await(const e of this[handle].values())if("file"===e.kind){const n=[...t].find((t=>e.isSameEntry(t[handle])&&e.name===t[handle].name));n?(this.appendChild(n),t.delete(n)):this.appendChild(new File(e))}t.forEach((e=>e.remove())),this[watchId]||(this[watchId]=watchDirectoryHandle(this[handle],this[changeCallback]))}else this[watchId]&&(clearInterval(this[watchId]),this[watchId]=!1)}select(){const e=this.querySelector("k-directory[selected], k-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("dirSelected",{detail:{dirHandle:this[handle],dirComponent:this},bubbles:!0,composed:!0})))}addFilter(e){this[filters].push(e),this.renderContents()}get shadowTemplate(){return`\n <button\n id="loadDirectory"\n >\n <slot name="selectButton">\n <k-icon name="folder-create"></k-icon> Select Directory\n </slot>\n </button>\n <button\n id="openToggle"\n class="no-btn"\n >\n <k-icon name="folder" id="folder-closed"></k-icon>\n <k-icon name="folder-open" id="folder-opened"></k-icon>\n <k-icon name="folder-clear" id="folder-clear"></k-icon>\n ${super.shadowTemplate}\n </button>\n <div id="tree">\n <slot name="content"></slot>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host([has-handle]) #loadDirectory,\n :host([opened]) #folder-closed,\n :host(:not([opened])) #folder-opened,\n :host(:not([has-handle])) #folder-closed,\n :host(:not([has-handle])) #folder-opened,\n :host(:not([has-handle])) #folder-clear,\n :host(:not([root])) #folder-clear,\n :host([root]) #folder-opened {\n display: none;\n }\n #openToggle {\n display: block;\n width: 100%;\n flex: 1 1 auto;\n }\n #tree {\n padding-left: var(--spacer);\n }\n #wrapper {\n display: flex;\n }\n #clearSelectedDirectory {\n flex: none;\n }\n :host([selected]) #openToggle {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n `}static observedAttributes=[...LazyComponent.observedAttributes,"opened"];static watchInterval=1e3}window.customElements.define("k-directory-viewer",DirectoryViewer);export class Directory extends Component{constructor(e=!1){super(),this[handle]=e,this[selectDirectoryClickHandler]=()=>this.select(),this.slot="content",this.registerAttribute("selected",!1)}async render(e){return!!await super.render(e)&&(this[handle]&&(this.innerHTML=this[handle].name,onEvent(this.shadowRoot.getElementById("selectDirectory"),"click",this[selectDirectoryClickHandler])),!0)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("selectDirectory"),"click",this[selectDirectoryClickHandler])}get rootDir(){return this.closest("k-directory-viewer")}select(){const e=this.rootDir.querySelector("k-directory[selected], k-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("directorySelected",{detail:{fileHandle:this[handle],fileComponent:this},bubbles:!0,composed:!0})))}get shadowTemplate(){return`\n <button\n id="selectDirectory"\n >\n <slot name="selectButton">\n <k-icon id="extIcon" name="file"></k-icon> ${this[handle].name}\n </slot>\n </button>\n ${super.shadowTemplate}\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host([selected]) #selectFile {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n `}}const selectFileClickHandler=Symbol();export class File extends Component{constructor(e=!1){super(),this[handle]=e,this[selectFileClickHandler]=()=>this.select(),this.slot="content",this.registerAttribute("selected",!1)}async render(e){if(await super.render(e)){if(this[handle]){this.innerHTML=this[handle].name;const e=this[handle].name.split(".").pop(),t=this.shadowRoot.getElementById("extIcon");["png","gif","jpeg","jpg","tif","webp"].includes(e)?t.name="image":["txt","rtf","js","css","html"].includes(e)?t.name="file-text":t.name="file",onEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}return!0}return!1}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("selectFile"),"click",this[selectFileClickHandler])}get rootDir(){return this.closest("k-directory-viewer")}select(){const e=this.rootDir.querySelector("k-directory[selected], k-file[selected]");e!==this&&(e&&(e.selected=!1),this.selected=!0,this.dispatchEvent(new CustomEvent("fileSelected",{detail:{fileHandle:this[handle],fileComponent:this},bubbles:!0,composed:!0})))}get shadowTemplate(){return`\n <button\n id="selectFile"\n class="no-btn"\n >\n <k-icon\n id="extIcon"\n name=""\n ></k-icon>\n ${super.shadowTemplate}\n </button>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n }\n :host([selected]) {\n background-color: var(--c_primary);\n color: var(--tc_on_primary);\n border-radius: var(--raidus);\n }\n #selectFile {\n width: 100%;\n }\n `}}window.customElements.define("k-directory-viewer-file",File);
@@ -1 +1 @@
1
- import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";export default class Table extends Component{constructor(e=!1,t=!1){super(),this.registerProps({fields:[],records:[]}),e&&this.setData(e,t)}async render(e){return!!await super.render(e)&&(this.renderFields(),this.renderRecords(),!0)}renderFields(){this.shadowRoot.getElementById("fields").innerHTML=this.fields.map((({label:e})=>`<th>${e}</th>`)).join("")}renderRecords(){this.shadowRoot.getElementById("records").innerHTML=this.records.map((e=>`<tr>${this.fields.map((({name:t})=>`<td>${e[t]||""}</td>`)).join("")}</tr>`)).join("")}setData(e,t=!1){this.records=e,this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n ${super.shadowTemplate}\n <div class="responsive-table">\n <table>\n <thead id="fields"></thead>\n <tbody id="records"></tbody>\n </table>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n margin-bottom: var(--spacer);\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}}window.customElements.define("k-table",Table);
1
+ import Component from"./Component.js";import{toTitleCase}from"../utils/string.js";export default class Table extends Component{constructor(e=!1,t=!1){super(),this.registerProps({fields:[],records:[]}),e&&this.setData(e,t)}async render(e){return!!await super.render(e)&&(this.renderFields(),this.renderRecords(),!0)}renderFields(){this.shadowRoot.getElementById("fields").innerHTML=this.fields.map((({label:e})=>`<th>${e}</th>`)).join("")}renderRecords(){this.shadowRoot.getElementById("records").innerHTML=this.records.map((e=>`<tr>${this.fields.map((({name:t})=>{let s=e[t]||"";return Array.isArray(s)&&(s=s.join(", ")),`<td>${s}</td>`})).join("")}</tr>`)).join("")}setData(e,t=!1){this.records=e,this.fields=t||Table.extractFieldsFromRecords(this.records),this.renderFields(),this.renderRecords()}get shadowTemplate(){return`\n ${super.shadowTemplate}\n <div class="responsive-table">\n <table>\n <thead id="fields"></thead>\n <tbody id="records"></tbody>\n </table>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n margin-bottom: var(--spacer);\n }\n `}static extractFieldsFromRecords(e,t=100){const s=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>s.add(e)))})),[...s].map((e=>({name:e,label:toTitleCase(e)})))}}window.customElements.define("k-table",Table);
@@ -1 +1 @@
1
- import Component from"./Component.js";import{onEvent,dispatchEvent}from"../utils/element.js";import"./Icon.js";const storageWatcher=Symbol("storageWatcher"),clickHandler=Symbol("clickHandler");export default class ThemeSwitcher extends Component{constructor(){super(),this[storageWatcher]=()=>{this.currentTheme=ThemeSwitcher.getCurrentTheme()},this[clickHandler]=()=>{const e=ThemeSwitcher.getCurrentTheme();"auto"===e&&ThemeSwitcher.setTheme("light"),"light"===e&&ThemeSwitcher.setTheme("dark"),"dark"===e&&ThemeSwitcher.setTheme("auto")},this.registerAttribute("currentTheme",ThemeSwitcher.getCurrentTheme())}async render(e){return!!await super.render(e)&&(onEvent(window,"storage",this[storageWatcher]),onEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler]),!0)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler])}get shadowTemplate(){return'\n <button\n id="toggle"\n class="no-btn"\n >\n <k-icon\n id="autoMode"\n name="mode-auto"\n class="mode"\n ></k-icon>\n <k-icon\n id="lightMode"\n name="light-mode"\n class="mode"\n ></k-icon>\n <k-icon\n id="darkMode"\n name="dark-mode"\n class="mode"\n ></k-icon>\n </button>\n '}get shadowStyles(){return'\n :host {\n --padding: var(--spacer);\n\n display: flex;\n }\n #toggle {\n padding: var(--padding);\n }\n .mode {\n display: none;\n }\n :host([current-theme="auto"]) #autoMode,\n :host([current-theme="light"]) #lightMode,\n :host([current-theme="dark"]) #darkMode {\n display: block;\n }\n '}static setTheme(e){localStorage.setItem("theme",e),document.documentElement.setAttribute("theme",e),dispatchEvent(window,"storage",{theme:e})}static getCurrentTheme(){let e=document.documentElement.getAttribute("theme");return e||(e=localStorage.getItem("theme")),e}}const colorSchemeQuery=window.matchMedia("(prefers-color-scheme: dark)"),colorSchemeChangeHandler=e=>document.documentElement.setAttribute("auto-theme",e.matches?"dark":"light");colorSchemeQuery.addEventListener("change",colorSchemeChangeHandler),colorSchemeChangeHandler(colorSchemeQuery),ThemeSwitcher.setTheme(ThemeSwitcher.getCurrentTheme()),window.customElements.define("k-theme-switcher",ThemeSwitcher);
1
+ import Component from"./Component.js";import{onEvent,dispatchEvent}from"../utils/element.js";import"./Icon.js";const storageWatcher=Symbol("storageWatcher"),clickHandler=Symbol("clickHandler");export default class ThemeSwitcher extends Component{constructor(){super(),this[storageWatcher]=()=>{this.currentTheme=ThemeSwitcher.getCurrentTheme()},this[clickHandler]=()=>{const e=ThemeSwitcher.getCurrentTheme();"auto"===e&&ThemeSwitcher.setTheme("light"),"light"===e&&ThemeSwitcher.setTheme("dark"),"dark"===e&&ThemeSwitcher.setTheme("auto")},this.registerAttribute("currentTheme",ThemeSwitcher.getCurrentTheme())}async render(e){return!!await super.render(e)&&(onEvent(window,"storage",this[storageWatcher]),onEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler]),!0)}disconnectedCallback(){super.disconnectedCallback(),offEvent(this.shadowRoot.getElementById("toggle"),"click",this[clickHandler])}get shadowTemplate(){return'\n <button\n id="toggle"\n class="no-btn"\n >\n <k-icon\n id="autoMode"\n name="mode-auto"\n class="mode"\n ></k-icon>\n <k-icon\n id="lightMode"\n name="mode-light"\n class="mode"\n ></k-icon>\n <k-icon\n id="darkMode"\n name="mode-dark"\n class="mode"\n ></k-icon>\n </button>\n '}get shadowStyles(){return'\n :host {\n --padding: var(--spacer);\n\n display: flex;\n }\n #toggle {\n padding: var(--padding);\n }\n .mode {\n display: none;\n }\n :host([current-theme="auto"]) #autoMode,\n :host([current-theme="light"]) #lightMode,\n :host([current-theme="dark"]) #darkMode {\n display: block;\n }\n '}static setTheme(e){localStorage.setItem("theme",e),document.documentElement.setAttribute("theme",e),dispatchEvent(window,"storage",{theme:e})}static getCurrentTheme(){let e=document.documentElement.getAttribute("theme");return e||(e=localStorage.getItem("theme")),e}}const colorSchemeQuery=window.matchMedia("(prefers-color-scheme: dark)"),colorSchemeChangeHandler=e=>document.documentElement.setAttribute("auto-theme",e.matches?"dark":"light");colorSchemeQuery.addEventListener("change",colorSchemeChangeHandler),colorSchemeChangeHandler(colorSchemeQuery),ThemeSwitcher.setTheme(ThemeSwitcher.getCurrentTheme()),window.customElements.define("k-theme-switcher",ThemeSwitcher);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "type": "module",
5
5
  "description": "A web component library",
6
6
  "main": "dist/index.js",
@@ -10,8 +10,7 @@ import watchDirectoryHandle from '../utils/watchDirectoryHandle.js';
10
10
 
11
11
  const handle = Symbol(),
12
12
  loadDirectoryClickHandler = Symbol(),
13
- openToggleClickHandler = Symbol(),
14
- fileSelectedHandler = Symbol(),
13
+ selectedHandler = Symbol(),
15
14
  watchId = Symbol(),
16
15
  changeCallback = Symbol(),
17
16
  filters = Symbol();
@@ -37,13 +36,13 @@ export default class DirectoryViewer extends LazyComponent {
37
36
  this[changeCallback] = () => {
38
37
  this.updateContents();
39
38
  }
39
+ this[selectedHandler] = (e) => {
40
+
41
+ }
40
42
 
41
43
  /* Init */
42
- this.slot = 'content';
43
44
  this.registerAttributes({
44
- root: !_handle,
45
45
  hasHandle: !!_handle,
46
- opened: !_handle,
47
46
  selected: false
48
47
  });
49
48
  }
@@ -51,9 +50,8 @@ export default class DirectoryViewer extends LazyComponent {
51
50
  /* Lifecycle Callbacks */
52
51
  async render(force){
53
52
  if(await super.render(force)){
54
- onEvent(this.shadowRoot, 'fileselected', this[fileSelectedHandler]);
53
+ onEvent(this.shadowRoot, 'fileSelected directorySelected', this[selectedHandler]);
55
54
  onEvent(this.shadowRoot.getElementById('loadDirectory'), 'click', this[loadDirectoryClickHandler]);
56
- onEvent(this.shadowRoot.getElementById('openToggle'), 'click', this[openToggleClickHandler]);
57
55
  this.renderDirectory();
58
56
  return true;
59
57
  }
@@ -133,15 +131,6 @@ export default class DirectoryViewer extends LazyComponent {
133
131
  }
134
132
  }
135
133
  }
136
- open(){
137
- this.opened = true;
138
- }
139
- close(){
140
- if(!this.root) this.opened = false;
141
- }
142
- toggleOpen(){
143
- if(!this.root) this.opened = !this.opened;
144
- }
145
134
  clearDirectoryHandle(){
146
135
  this[handle] = false;
147
136
  this.hasHandle = false;
@@ -183,11 +172,11 @@ export default class DirectoryViewer extends LazyComponent {
183
172
  }
184
173
  }
185
174
  select(){
186
- const $currentSelection = this.closest('k-directory-viewer[root]').querySelector('k-directory-viewer[selected], k-directory-viewer-file[selected]');
175
+ const $currentSelection = this.querySelector('k-directory[selected], k-file[selected]');
187
176
  if($currentSelection === this) return;
188
177
  if($currentSelection) $currentSelection.selected = false;
189
178
  this.selected = true;
190
- this.dispatchEvent(new CustomEvent('dirselected', {
179
+ this.dispatchEvent(new CustomEvent('dirSelected', {
191
180
  detail: {
192
181
  dirHandle: this[handle],
193
182
  dirComponent: this
@@ -269,6 +258,83 @@ export default class DirectoryViewer extends LazyComponent {
269
258
  }
270
259
  window.customElements.define('k-directory-viewer', DirectoryViewer);
271
260
 
261
+ export class Directory extends Component {
262
+ constructor(_handle = false){
263
+ super();
264
+
265
+ /* Private Members */
266
+ this[handle] = _handle;
267
+
268
+ /* Private Methods */
269
+ this[selectDirectoryClickHandler] = () => this.select();
270
+
271
+ /* Init */
272
+ this.slot = 'content';
273
+ this.registerAttribute('selected', false);
274
+ }
275
+
276
+ /* Lifecycle Callbacks */
277
+ async render(force){
278
+ if(await super.render(force)){
279
+ if(this[handle]){
280
+ this.innerHTML = this[handle].name;
281
+ onEvent(this.shadowRoot.getElementById('selectDirectory'), 'click', this[selectDirectoryClickHandler]);
282
+ }
283
+ return true;
284
+ }
285
+ return false;
286
+ }
287
+ disconnectedCallback(){
288
+ super.disconnectedCallback();
289
+ offEvent(this.shadowRoot.getElementById('selectDirectory'), 'click', this[selectDirectoryClickHandler]);
290
+ }
291
+
292
+ /* Protected Members */
293
+ get rootDir(){
294
+ return this.closest('k-directory-viewer')
295
+ }
296
+
297
+ /* Public Methods */
298
+ select(){
299
+ const $currentSelection = this.rootDir.querySelector('k-directory[selected], k-file[selected]');
300
+ if($currentSelection === this) return;
301
+ if($currentSelection) $currentSelection.selected = false;
302
+ this.selected = true;
303
+ this.dispatchEvent(new CustomEvent('directorySelected', {
304
+ detail: {
305
+ fileHandle: this[handle],
306
+ fileComponent: this
307
+ },
308
+ bubbles: true,
309
+ composed: true
310
+ }));
311
+ }
312
+
313
+ /* Shadow DOM */
314
+ get shadowTemplate(){
315
+ return /*html*/`
316
+ <button
317
+ id="selectDirectory"
318
+ >
319
+ <slot name="selectButton">
320
+ <k-icon id="extIcon" name="file"></k-icon> ${this[handle].name}
321
+ </slot>
322
+ </button>
323
+ ${super.shadowTemplate}
324
+ `;
325
+ }
326
+ get shadowStyles(){
327
+ return /*css*/`
328
+ ${super.shadowStyles}
329
+ :host([selected]) #selectFile {
330
+ background-color: var(--c_primary);
331
+ color: var(--tc_on_primary);
332
+ border-radius: var(--raidus);
333
+ }
334
+ `;
335
+ }
336
+ }
337
+
272
338
  const selectFileClickHandler = Symbol();
273
339
  export class File extends Component {
274
340
  constructor(_handle = false){
@@ -310,13 +376,18 @@ export class File extends Component {
310
376
  offEvent(this.shadowRoot.getElementById('selectFile'), 'click', this[selectFileClickHandler]);
311
377
  }
312
378
 
379
+ /* Protected Members */
380
+ get rootDir(){
381
+ return this.closest('k-directory-viewer')
382
+ }
383
+
313
384
  /* Public Methods */
314
385
  select(){
315
- const $currentSelection = this.closest('k-directory-viewer[root]').querySelector('k-directory-viewer[selected], k-directory-viewer-file[selected]');
386
+ const $currentSelection = this.rootDir.querySelector('k-directory[selected], k-file[selected]');
316
387
  if($currentSelection === this) return;
317
388
  if($currentSelection) $currentSelection.selected = false;
318
389
  this.selected = true;
319
- this.dispatchEvent(new CustomEvent('fileselected', {
390
+ this.dispatchEvent(new CustomEvent('fileSelected', {
320
391
  detail: {
321
392
  fileHandle: this[handle],
322
393
  fileComponent: this
@@ -28,7 +28,13 @@ export default class Table extends Component {
28
28
  }
29
29
  renderRecords(){
30
30
  this.shadowRoot.getElementById('records').innerHTML = this.records.map((record) => {
31
- return `<tr>${this.fields.map(({name})=>`<td>${record[name] || ''}</td>`).join('')}</tr>`;
31
+ return `<tr>${this.fields.map(({name}) => {
32
+ let value = record[name] || '';
33
+ if (Array.isArray(value)) {
34
+ value = value.join(', ');
35
+ }
36
+ return `<td>${value}</td>`;
37
+ }).join('')}</tr>`;
32
38
  }).join('');
33
39
  }
34
40
 
@@ -48,12 +48,12 @@ export default class ThemeSwitcher extends Component {
48
48
  ></k-icon>
49
49
  <k-icon
50
50
  id="lightMode"
51
- name="light-mode"
51
+ name="mode-light"
52
52
  class="mode"
53
53
  ></k-icon>
54
54
  <k-icon
55
55
  id="darkMode"
56
- name="dark-mode"
56
+ name="mode-dark"
57
57
  class="mode"
58
58
  ></k-icon>
59
59
  </button>