kempo 2.6.0 → 2.6.2

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";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise((async(e,n)=>{try{const n=await fetch(t);200===n.status&&e(await n.text())}catch(t){}return n()}))),await cache[t]),getIconByName=t=>{const e=async e=>getIconByPath(`${e}/${t}.svg`);return new Promise((async(t,n)=>{let s;for(let t=0;t<Icon.pathToIcons.length&&!s;t++)s=await e(Icon.pathToIcons[t]);s?t(s):n("Icon not found")}))};export default class Icon extends Component{constructor(){super(),this.registerAttributes({src:"",name:""})}connectedCallback(){}async render(t){if(await super.render(t)){if(this.src){const t=await getIconByPath(this.src);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else if(this.name){const t=await getIconByName(this.name);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else this.rendered=!1,this.innerHTML.length||(this.innerHTML=Icon.fallback);return!0}return!1}attributeChangedCallback(t,e,n){super.attributeChangedCallback(t,e,n),["src","name"].includes(t)&&n&&this.render(!0)}fixSVG(){const t=this.querySelector("svg");t.removeAttribute("width"),t.removeAttribute("height"),t.querySelectorAll("path, rect, circle").forEach((t=>{t.setAttribute("fill","currentColor")}))}get shadowStyles(){return"\n :host {\n display: inline-block;\n vertical-align: bottom;\n }\n ::slotted(svg){\n height: 1.35em;\n vertical-align: middle;\n }\n "}static observedAttributes=["name","src"];static pathToIcons=["/icons"];static fallback='\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currnetColor" d="M480-79q-16 0-30.5-6T423-102L102-423q-11-12-17-26.5T79-480q0-16 6-31t17-26l321-321q12-12 26.5-17.5T480-881q16 0 31 5.5t26 17.5l321 321q12 11 17.5 26t5.5 31q0 16-5.5 30.5T858-423L537-102q-11 11-26 17t-31 6Zm0-80 321-321-321-321-321 321 321 321Zm-40-281h80v-240h-80v240Zm40 120q17 0 28.5-11.5T520-360q0-17-11.5-28.5T480-400q-17 0-28.5 11.5T440-360q0 17 11.5 28.5T480-320Zm0-160Z"/></svg>\n '}window.customElements.define("k-icon",Icon);
1
+ import Component from"./Component.js";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise((async(e,n)=>{try{const n=await fetch(t);200===n.status&&e(await n.text())}catch(t){}return n()}))),await cache[t]),getIconByName=t=>{const e=async e=>getIconByPath(`${e}/${t}.svg`);return new Promise((async(t,n)=>{let s;for(let t=0;t<Icon.pathToIcons.length&&!s;t++)s=await e(Icon.pathToIcons[t]);s?t(s):n("Icon not found")}))};export default class Icon extends Component{constructor(t=""){super(),this.registerAttributes({src:"",name:t})}connectedCallback(){}async render(t){if(await super.render(t)){if(this.src){const t=await getIconByPath(this.src);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else if(this.name){const t=await getIconByName(this.name);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else this.rendered=!1,this.innerHTML.length||(this.innerHTML=Icon.fallback);return!0}return!1}attributeChangedCallback(t,e,n){super.attributeChangedCallback(t,e,n),["src","name"].includes(t)&&n&&this.render(!0)}fixSVG(){const t=this.querySelector("svg");t.removeAttribute("width"),t.removeAttribute("height"),t.querySelectorAll("path, rect, circle").forEach((t=>{t.setAttribute("fill","currentColor")}))}get shadowStyles(){return"\n :host {\n display: inline-block;\n vertical-align: bottom;\n }\n ::slotted(svg){\n height: 1.35em;\n vertical-align: middle;\n }\n "}static observedAttributes=["name","src"];static pathToIcons=["/icons"];static fallback='\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currnetColor" d="M480-79q-16 0-30.5-6T423-102L102-423q-11-12-17-26.5T79-480q0-16 6-31t17-26l321-321q12-12 26.5-17.5T480-881q16 0 31 5.5t26 17.5l321 321q12 11 17.5 26t5.5 31q0 16-5.5 30.5T858-423L537-102q-11 11-26 17t-31 6Zm0-80 321-321-321-321-321 321 321 321Zm-40-281h80v-240h-80v240Zm40 120q17 0 28.5-11.5T520-360q0-17-11.5-28.5T480-400q-17 0-28.5 11.5T440-360q0 17 11.5 28.5T480-320Zm0-160Z"/></svg>\n '}window.customElements.define("k-icon",Icon);
@@ -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})=>{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
+ import Component from"./Component.js";import Icon from"./Icon.js";import{toTitleCase}from"../utils/string.js";export default class Table extends Component{constructor(e=!1,t=!1,r={before:[],after:[]}){super(),this.registerProps({fields:[],records:[],controls:{before:[],after:[]}}),e&&this.setData(e,t,r)}async render(e){return!!await super.render(e)&&(this.renderFields(),this.renderRecords(),!0)}renderFields(){const e=this.controls.before.length?"<th></th>":"",t=this.controls.after.length?"<th></th>":"";this.shadowRoot.getElementById("fields").innerHTML=`${e}${this.fields.map((({label:e})=>`<th>${e}</th>`)).join("")}${t}`}renderRecords(){this.shadowRoot.getElementById("records").innerHTML=this.records.map((e=>`<tr>${this.renderControls(this.controls.before,e)}${this.fields.map((({name:t})=>{let r=e[t]||"";return Array.isArray(r)&&(r=r.join(", ")),`<td>${r}</td>`})).join("")}${this.renderControls(this.controls.after,e)}</tr>`)).join("")}renderControls(e,t){return 0===e.length?"":`<td>${e.map((({icon:e,action:r})=>{const s=document.createElement("button"),o=new Icon(e);return s.appendChild(o),s.addEventListener("click",(()=>r(t,this))),s.outerHTML})).join("")}</td>`}setData(e,t=!1,r={before:[],after:[]}){this.records=e,this.fields=t||Table.extractFieldsFromRecords(this.records),this.controls=r,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 r=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>r.add(e)))})),[...r].map((e=>({name:e,label:toTitleCase(e)})))}}window.customElements.define("k-table",Table);
@@ -23,14 +23,15 @@
23
23
  <a href="#properties">Properties</a><br />
24
24
  <a href="#methods">Methods</a><br />
25
25
  <a href="#arrays">Displaying Arrays</a><br />
26
+ <a href="#controls">Row Controls</a><br />
26
27
  </div>
27
28
  </details>
28
29
 
29
30
  <h3>Description</h3>
30
- <p>The <code>&lt;k-table></code> component is used to render data into a <code>&lt;table></code> using JavaScript.</p>
31
+ <p>The <code>&lt;k-table&gt;</code> component is used to render data into a <code>&lt;table&gt;</code> using JavaScript.</p>
31
32
 
32
33
  <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
33
- <p>In HTML create a <code>&lt;k-table></code>, and then in JavaScript call the <code>setData</code> method passing in an array of objects containing your data.</p>
34
+ <p>In HTML create a <code>&lt;k-table&gt;</code>, and then in JavaScript call the <code>setData</code> method passing in an array of objects containing your data.</p>
34
35
 
35
36
  <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">"contacts"</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">'contacts'</span>).setData([<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Dustin"</span>,<br /> <span class="hljs-attr">phoneNumber</span>: <span class="hljs-string">"(111) 111-1111"</span>,<br /> <span class="hljs-attr">emailAddress</span>: <span class="hljs-string">"dustin@mailserver.com"</span>,<br /> <span class="hljs-attr">gender</span>: <span class="hljs-string">"Male"</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Kayla"</span>,<br /> <span class="hljs-attr">phoneNumber</span>: <span class="hljs-string">"(222) 222-2222"</span>,<br /> <span class="hljs-attr">emailAddress</span>: <span class="hljs-string">"kayla@mailserver.com"</span>,<br /> <span class="hljs-attr">gender</span>: <span class="hljs-string">"Female"</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Alexander"</span>,<br /> <span class="hljs-attr">phoneNumber</span>: <span class="hljs-string">"(333) 333-33333"</span>,<br /> <span class="hljs-attr">emailAddress</span>: <span class="hljs-string">"alex@mailserver.com"</span>,<br /> <span class="hljs-attr">gender</span>: <span class="hljs-string">"Male"</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Amelia"</span>,<br /> <span class="hljs-attr">phoneNumber</span>: <span class="hljs-string">"(444) 444-44444"</span>,<br /> <span class="hljs-attr">emailAddress</span>: <span class="hljs-string">"amelia@mailserver.com"</span>,<br /> <span class="hljs-attr">gender</span>: <span class="hljs-string">"Female"</span><br /> },<br /> ]);<br /></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre>
36
37
  <script type="module" src="../kempo/components/Table.js"></script>
@@ -60,7 +61,7 @@
60
61
  phoneNumber: "(444) 444-44444",
61
62
  emailAddress: "amelia@mailserver.com",
62
63
  gender: "Female"
63
- },
64
+ }
64
65
  ]);
65
66
  </script>
66
67
 
@@ -125,20 +126,73 @@
125
126
  ]);
126
127
  </script>
127
128
 
128
- <h3 id="constructor"><a href="#consttructor" class="no-link">Constructor</a></h3>
129
+ <h3 id="controls"><a href="#controls" class="no-link">Row Controls</a></h3>
130
+ <p>The table component can display controls before or after each row. Each control is defined by an icon name and a callback function. All controls are combined into a single <code>&lt;td&gt;</code> with each control being a button.</p>
131
+ <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">"contacts"</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">'contacts'</span>).setData([<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Dustin"</span>,<br /> <span class="hljs-attr">phoneNumber</span>: <span class="hljs-string">"(111) 111-1111"</span>,<br /> <span class="hljs-attr">emailAddress</span>: <span class="hljs-string">"dustin@mailserver.com"</span>,<br /> <span class="hljs-attr">gender</span>: <span class="hljs-string">"Male"</span><br /> },<br /> {<br /> <span class="hljs-attr">name</span>: <span class="hljs-string">"Kayla"</span>,<br /> <span class="hljs-attr">phoneNumber</span>: <span class="hljs-string">"(222) 222-2222"</span>,<br /> <span class="hljs-attr">emailAddress</span>: <span class="hljs-string">"kayla@mailserver.com"</span>,<br /> <span class="hljs-attr">gender</span>: <span class="hljs-string">"Female"</span><br /> }<br /> ], false, {<br /> before: [<br /> {<br /> icon: 'edit',<br /> action: (record, table) => console.log('Edit', record, table)<br /> }<br /> ],<br /> after: [<br /> {<br /> icon: 'delete',<br /> action: (record, table) => console.log('Delete', record, table)<br /> }<br /> ]<br /> });<br /></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre>
132
+ <k-table id="contacts2"></k-table>
133
+ <script type="module">
134
+ document.getElementById('contacts2').setData([
135
+ {
136
+ name: "Dustin",
137
+ phoneNumber: "(111) 111-1111",
138
+ emailAddress: "dustin@mailserver.com",
139
+ gender: "Male"
140
+ },
141
+ {
142
+ name: "Kayla",
143
+ phoneNumber: "(222) 222-2222",
144
+ emailAddress: "kayla@mailserver.com",
145
+ gender: "Female"
146
+ }
147
+ ], false, {
148
+ before: [
149
+ {
150
+ icon: 'edit',
151
+ action: (record, table) => console.log('Edit', record, table)
152
+ }
153
+ ],
154
+ after: [
155
+ {
156
+ icon: 'delete',
157
+ action: (record, table) => console.log('Delete', record, table)
158
+ }
159
+ ]
160
+ });
161
+ </script>
162
+
163
+ <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
129
164
  <h6>Extends <a href="./component.html">Component</a></h6>
130
165
  <h5>
131
166
  <code>new Table()</code><br />
132
167
  <code>new Table(<i>&lt;Array>object</i> records)</code><br />
133
- <code>new Table(<i>&lt;Array>object</i> records, <i>&lt;Array>object</i> fields)</code>
168
+ <code>new Table(<i>&lt;Array>object</i> records, <i>&lt;Array>object</i> fields)</code><br />
169
+ <code>new Table(<i>&lt;Array>object</i> records, <i>&lt;Array>object</i> fields, <i>object</i> controls)</code>
134
170
  </h5>
135
171
 
136
- <h4>Paramters</h4>
172
+ <h4>Parameters</h4>
137
173
  <h5><code><i>&lt;Array>object</i> records</code></h5>
138
174
  <p>An array of objects that represent the records to be shown in the table.</p>
139
175
 
140
176
  <h5><code><i>&lt;Array>object</i> fields</code></h5>
141
- <p>An array of objects that represents the feilds of the table, each object must contain a <code>name</code> and a <code>label</code>. If this parameter is ommitted the fields will be automatically generated from the first 100 records.</p>
177
+ <p>An array of objects that represents the fields of the table, each object must contain a <code>name</code> and a <code>label</code>. If this parameter is omitted, the fields will be automatically generated from the first 100 records.</p>
178
+
179
+ <h5><code><i>object</i> controls</code></h5>
180
+ <p>An object containing optional arrays of controls to be displayed before and/or after each row. Each control is defined by an icon name and a callback function. The <code>controls</code> object can contain the optional <code>before</code> and <code>after</code> properties, which are arrays of objects containing the <code>icon</code> (string) and <code>action</code> (function).</p>
181
+ <p>Example:</p>
182
+ <pre><code class="hljs javascript">{
183
+ before: [
184
+ {
185
+ icon: 'edit',
186
+ action: (record, table) => console.log('Edit', record, table)
187
+ }
188
+ ],
189
+ after: [
190
+ {
191
+ icon: 'delete',
192
+ action: (record, table) => console.log('Delete', record, table)
193
+ }
194
+ ]
195
+ }</code></pre>
142
196
 
143
197
  <h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
144
198
  <ul>
@@ -152,16 +206,37 @@
152
206
  <h5><code>records<i>: &lt;Array>object</i></code></h5>
153
207
  <p>An array of objects containing the data that will be the records of the table, where the object keys match the field names.</p>
154
208
 
209
+ <h5><code>controls<i>: object</i></code></h5>
210
+ <p>An object containing arrays of controls to be displayed before and/or after each row. Each control is defined by an icon name and a callback function. The <code>controls</code> object can only contain the <code>before</code> and <code>after</code> properties, which are arrays of objects containing the <code>icon</code> (string) and <code>action</code> (function).</p>
211
+ <p>Example:</p>
212
+ <pre><code class="hljs javascript">{
213
+ before: [
214
+ {
215
+ icon: 'edit',
216
+ action: (record, table) => console.log('Edit', record, table)
217
+ }
218
+ ],
219
+ after: [
220
+ {
221
+ icon: 'delete',
222
+ action: (record, table) => console.log('Delete', record, table)
223
+ }
224
+ ]
225
+ }</code></pre>
226
+
155
227
  <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
156
228
 
157
229
  <h5><code>setData(<i>&lt;Array>object</i> records, <i>&lt;Array>object</i> fields)<i>: undefined</i></code></h5>
158
230
  <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>
159
231
 
160
- <h5><code>renderFields()<i>: undefined</i></h5>
232
+ <h5><code>renderFields()<i>: undefined</i></code></h5>
161
233
  <p>Renders the fields as table headers, this is called automatically by setData, you should never have to call it.</p>
162
234
 
163
- <h5><code>renderRecords()<i>: undefined</i></h5>
164
- <p>Renders the records as table row, this is called automatically by setData, you should never have to call it.</p>
235
+ <h5><code>renderRecords()<i>: undefined</i></code></h5>
236
+ <p>Renders the records as table rows, this is called automatically by setData, you should never have to call it.</p>
237
+
238
+ <h5><code>renderControls(<i>&lt;Array>object</i> controls, <i>object</i> record)<i>: string</i></code></h5>
239
+ <p>Renders the controls as table cells. Each control is defined by an icon name and a callback function. This is called automatically by renderRecords, you should never have to call it.</p>
165
240
 
166
241
  </main>
167
242
  <div style="height: 33.333vh"></div>
@@ -1 +1 @@
1
- import Component from"./Component.js";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise((async(e,n)=>{try{const n=await fetch(t);200===n.status&&e(await n.text())}catch(t){}return n()}))),await cache[t]),getIconByName=t=>{const e=async e=>getIconByPath(`${e}/${t}.svg`);return new Promise((async(t,n)=>{let s;for(let t=0;t<Icon.pathToIcons.length&&!s;t++)s=await e(Icon.pathToIcons[t]);s?t(s):n("Icon not found")}))};export default class Icon extends Component{constructor(){super(),this.registerAttributes({src:"",name:""})}connectedCallback(){}async render(t){if(await super.render(t)){if(this.src){const t=await getIconByPath(this.src);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else if(this.name){const t=await getIconByName(this.name);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else this.rendered=!1,this.innerHTML.length||(this.innerHTML=Icon.fallback);return!0}return!1}attributeChangedCallback(t,e,n){super.attributeChangedCallback(t,e,n),["src","name"].includes(t)&&n&&this.render(!0)}fixSVG(){const t=this.querySelector("svg");t.removeAttribute("width"),t.removeAttribute("height"),t.querySelectorAll("path, rect, circle").forEach((t=>{t.setAttribute("fill","currentColor")}))}get shadowStyles(){return"\n :host {\n display: inline-block;\n vertical-align: bottom;\n }\n ::slotted(svg){\n height: 1.35em;\n vertical-align: middle;\n }\n "}static observedAttributes=["name","src"];static pathToIcons=["/icons"];static fallback='\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currnetColor" d="M480-79q-16 0-30.5-6T423-102L102-423q-11-12-17-26.5T79-480q0-16 6-31t17-26l321-321q12-12 26.5-17.5T480-881q16 0 31 5.5t26 17.5l321 321q12 11 17.5 26t5.5 31q0 16-5.5 30.5T858-423L537-102q-11 11-26 17t-31 6Zm0-80 321-321-321-321-321 321 321 321Zm-40-281h80v-240h-80v240Zm40 120q17 0 28.5-11.5T520-360q0-17-11.5-28.5T480-400q-17 0-28.5 11.5T440-360q0 17 11.5 28.5T480-320Zm0-160Z"/></svg>\n '}window.customElements.define("k-icon",Icon);
1
+ import Component from"./Component.js";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise((async(e,n)=>{try{const n=await fetch(t);200===n.status&&e(await n.text())}catch(t){}return n()}))),await cache[t]),getIconByName=t=>{const e=async e=>getIconByPath(`${e}/${t}.svg`);return new Promise((async(t,n)=>{let s;for(let t=0;t<Icon.pathToIcons.length&&!s;t++)s=await e(Icon.pathToIcons[t]);s?t(s):n("Icon not found")}))};export default class Icon extends Component{constructor(t=""){super(),this.registerAttributes({src:"",name:t})}connectedCallback(){}async render(t){if(await super.render(t)){if(this.src){const t=await getIconByPath(this.src);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else if(this.name){const t=await getIconByName(this.name);t?(this.innerHTML=t,this.fixSVG()):this.innerHTML.length||(this.innerHTML=Icon.fallback)}else this.rendered=!1,this.innerHTML.length||(this.innerHTML=Icon.fallback);return!0}return!1}attributeChangedCallback(t,e,n){super.attributeChangedCallback(t,e,n),["src","name"].includes(t)&&n&&this.render(!0)}fixSVG(){const t=this.querySelector("svg");t.removeAttribute("width"),t.removeAttribute("height"),t.querySelectorAll("path, rect, circle").forEach((t=>{t.setAttribute("fill","currentColor")}))}get shadowStyles(){return"\n :host {\n display: inline-block;\n vertical-align: bottom;\n }\n ::slotted(svg){\n height: 1.35em;\n vertical-align: middle;\n }\n "}static observedAttributes=["name","src"];static pathToIcons=["/icons"];static fallback='\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currnetColor" d="M480-79q-16 0-30.5-6T423-102L102-423q-11-12-17-26.5T79-480q0-16 6-31t17-26l321-321q12-12 26.5-17.5T480-881q16 0 31 5.5t26 17.5l321 321q12 11 17.5 26t5.5 31q0 16-5.5 30.5T858-423L537-102q-11 11-26 17t-31 6Zm0-80 321-321-321-321-321 321 321 321Zm-40-281h80v-240h-80v240Zm40 120q17 0 28.5-11.5T520-360q0-17-11.5-28.5T480-400q-17 0-28.5 11.5T440-360q0 17 11.5 28.5T480-320Zm0-160Z"/></svg>\n '}window.customElements.define("k-icon",Icon);
@@ -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})=>{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
+ import Component from"./Component.js";import Icon from"./Icon.js";import{toTitleCase}from"../utils/string.js";export default class Table extends Component{constructor(e=!1,t=!1,r={before:[],after:[]}){super(),this.registerProps({fields:[],records:[],controls:{before:[],after:[]}}),e&&this.setData(e,t,r)}async render(e){return!!await super.render(e)&&(this.renderFields(),this.renderRecords(),!0)}renderFields(){const e=this.controls.before.length?"<th></th>":"",t=this.controls.after.length?"<th></th>":"";this.shadowRoot.getElementById("fields").innerHTML=`${e}${this.fields.map((({label:e})=>`<th>${e}</th>`)).join("")}${t}`}renderRecords(){this.shadowRoot.getElementById("records").innerHTML=this.records.map((e=>`<tr>${this.renderControls(this.controls.before,e)}${this.fields.map((({name:t})=>{let r=e[t]||"";return Array.isArray(r)&&(r=r.join(", ")),`<td>${r}</td>`})).join("")}${this.renderControls(this.controls.after,e)}</tr>`)).join("")}renderControls(e,t){return 0===e.length?"":`<td>${e.map((({icon:e,action:r})=>{const s=document.createElement("button"),o=new Icon(e);return s.appendChild(o),s.addEventListener("click",(()=>r(t,this))),s.outerHTML})).join("")}</td>`}setData(e,t=!1,r={before:[],after:[]}){this.records=e,this.fields=t||Table.extractFieldsFromRecords(this.records),this.controls=r,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 r=new Set;return e.slice(0,t).forEach((e=>{Object.keys(e).forEach((e=>r.add(e)))})),[...r].map((e=>({name:e,label:toTitleCase(e)})))}}window.customElements.define("k-table",Table);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "type": "module",
5
5
  "description": "A web component library",
6
6
  "main": "dist/index.js",
@@ -8,7 +8,7 @@ const getIconByPath = async (path) => {
8
8
  try {
9
9
  const response = await fetch(path);
10
10
  if(response.status === 200) resolve(await (response).text());
11
- } catch(e){ }
11
+ } catch(e){ }
12
12
  return reject();
13
13
  });
14
14
  }
@@ -31,11 +31,11 @@ const getIconByName = (name) => {
31
31
  }
32
32
 
33
33
  export default class Icon extends Component {
34
- constructor(){
34
+ constructor(name = ''){
35
35
  super();
36
36
  this.registerAttributes({
37
37
  src: '',
38
- name: ''
38
+ name: name
39
39
  });
40
40
  }
41
41
  connectedCallback(){
@@ -1,18 +1,20 @@
1
1
  import Component from './Component.js';
2
+ import Icon from './Icon.js'; // Import Icon component
2
3
  import { toTitleCase } from '../utils/string.js';
3
4
 
4
5
  export default class Table extends Component {
5
- constructor(data = false, fields = false){
6
+ constructor(data = false, fields = false, controls = { before: [], after: [] }){
6
7
  super();
7
8
 
8
9
  this.registerProps({
9
10
  fields: [],
10
- records: []
11
+ records: [],
12
+ controls: { before: [], after: [] }
11
13
  });
12
14
 
13
15
  /* Init */
14
16
  if(data){
15
- this.setData(data, fields);
17
+ this.setData(data, fields, controls);
16
18
  }
17
19
  }
18
20
  async render(force){
@@ -24,23 +26,37 @@ export default class Table extends Component {
24
26
  return false;
25
27
  }
26
28
  renderFields(){
27
- this.shadowRoot.getElementById('fields').innerHTML = this.fields.map(({label})=>`<th>${label}</th>`).join('');
29
+ const beforeControls = this.controls.before?.length ? '<th></th>' : '';
30
+ const afterControls = this.controls.after?.length ? '<th></th>' : '';
31
+ this.shadowRoot.getElementById('fields').innerHTML = `${beforeControls}${this.fields.map(({label})=>`<th>${label}</th>`).join('')}${afterControls}`;
28
32
  }
29
33
  renderRecords(){
30
34
  this.shadowRoot.getElementById('records').innerHTML = this.records.map((record) => {
31
- return `<tr>${this.fields.map(({name}) => {
35
+ return `<tr>${this.renderControls(this.controls.before, record)}${this.fields.map(({name}) => {
32
36
  let value = record[name] || '';
33
37
  if (Array.isArray(value)) {
34
38
  value = value.join(', ');
35
39
  }
36
40
  return `<td>${value}</td>`;
37
- }).join('')}</tr>`;
41
+ }).join('')}${this.renderControls(this.controls.after, record)}</tr>`;
38
42
  }).join('');
39
43
  }
40
44
 
41
- setData(records, fields = false){
45
+ renderControls(controls = [], record) {
46
+ if (controls.length === 0) return '';
47
+ return `<td>${controls.map(({ icon, action }) => {
48
+ const button = document.createElement('button');
49
+ const iconElement = new Icon(icon); // Pass icon name as parameter
50
+ button.appendChild(iconElement);
51
+ button.addEventListener('click', () => action(record, this));
52
+ return button.outerHTML;
53
+ }).join('')}</td>`;
54
+ }
55
+
56
+ setData(records, fields = false, controls = { before: [], after: [] }){
42
57
  this.records = records;
43
58
  this.fields = fields || Table.extractFieldsFromRecords(this.records);
59
+ this.controls = controls;
44
60
  this.renderFields();
45
61
  this.renderRecords();
46
62
  }