kempo-ui 0.0.4 → 0.0.6

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.
Files changed (70) hide show
  1. package/dist/src/components/Dialog.js +35 -28
  2. package/dist/src/components/ShadowComponent.js +1 -1
  3. package/dist/src/components/Sortable.js +45 -0
  4. package/dist/src/components/Table.js +151 -0
  5. package/dist/src/components/Toast.js +141 -0
  6. package/dist/src/components/Toggle.js +74 -0
  7. package/dist/src/components/tableControls/DeleteRecord.js +7 -0
  8. package/dist/src/components/tableControls/Edit.js +18 -0
  9. package/dist/src/components/tableControls/ExportCSV.js +8 -0
  10. package/dist/src/components/tableControls/ExportJson.js +8 -0
  11. package/dist/src/components/tableControls/FieldSortHide.js +26 -0
  12. package/dist/src/components/tableControls/Filters.js +47 -0
  13. package/dist/src/components/tableControls/FirstPage.js +9 -0
  14. package/dist/src/components/tableControls/HiddenCount.js +3 -0
  15. package/dist/src/components/tableControls/Hide.js +5 -0
  16. package/dist/src/components/tableControls/LastPage.js +9 -0
  17. package/dist/src/components/tableControls/NextPage.js +9 -0
  18. package/dist/src/components/tableControls/PageSelect.js +22 -0
  19. package/dist/src/components/tableControls/PageSize.js +5 -0
  20. package/dist/src/components/tableControls/PrevPage.js +9 -0
  21. package/dist/src/components/tableControls/Search.js +9 -0
  22. package/dist/src/components/tableControls/ShowAll.js +5 -0
  23. package/dist/src/components/tableControls/TableControl.js +16 -0
  24. package/dist/src/utils/toTitleCase.js +1 -0
  25. package/docs/components/focus-capture.html +58 -13
  26. package/docs/components/hybrid-component.html +112 -0
  27. package/docs/components/icon.html +0 -2
  28. package/docs/components/light-component.html +99 -0
  29. package/docs/components/shadow-component.html +88 -0
  30. package/docs/components/sortable.html +132 -0
  31. package/docs/icons/error.svg +1 -1
  32. package/docs/icons/warning.svg +1 -1
  33. package/docs/index.html +197 -5
  34. package/docs/kempo.min.css +1 -1
  35. package/docs/nav.inc.html +11 -10
  36. package/docs/src/components/Dialog.js +35 -28
  37. package/docs/src/components/ShadowComponent.js +1 -1
  38. package/docs/src/components/Sortable.js +45 -0
  39. package/docs/src/components/Table.js +151 -0
  40. package/docs/src/components/Toast.js +141 -0
  41. package/docs/src/components/Toggle.js +74 -0
  42. package/docs/src/components/tableControls/DeleteRecord.js +7 -0
  43. package/docs/src/components/tableControls/Edit.js +18 -0
  44. package/docs/src/components/tableControls/ExportCSV.js +8 -0
  45. package/docs/src/components/tableControls/ExportJson.js +8 -0
  46. package/docs/src/components/tableControls/FieldSortHide.js +26 -0
  47. package/docs/src/components/tableControls/Filters.js +47 -0
  48. package/docs/src/components/tableControls/FirstPage.js +9 -0
  49. package/docs/src/components/tableControls/HiddenCount.js +3 -0
  50. package/docs/src/components/tableControls/Hide.js +5 -0
  51. package/docs/src/components/tableControls/LastPage.js +9 -0
  52. package/docs/src/components/tableControls/NextPage.js +9 -0
  53. package/docs/src/components/tableControls/PageSelect.js +22 -0
  54. package/docs/src/components/tableControls/PageSize.js +5 -0
  55. package/docs/src/components/tableControls/PrevPage.js +9 -0
  56. package/docs/src/components/tableControls/Search.js +9 -0
  57. package/docs/src/components/tableControls/ShowAll.js +5 -0
  58. package/docs/src/components/tableControls/TableControl.js +16 -0
  59. package/docs/src/utils/toTitleCase.js +1 -0
  60. package/docs/utils/drag.html +13 -28
  61. package/docs/utils/toTitleCase.html +71 -0
  62. package/package.json +2 -2
  63. package/scripts/build.js +3 -0
  64. package/src/components/Dialog.js +35 -27
  65. package/tests/{HybridComponent.browser-test.js → components/HybridComponent.browser-test.js} +2 -2
  66. package/tests/{LightComponent.browser-test.js → components/LightComponent.browser-test.js} +2 -2
  67. package/tests/{ShadowComponent.browser-test.js → components/ShadowComponent.browser-test.js} +2 -2
  68. package/tests/utils/debounce.test.js +209 -0
  69. package/docs/src/utils/cli.js +0 -1
  70. package/docs/src/utils/fs-utils.js +0 -1
@@ -0,0 +1,18 @@
1
+ import TableControl from"./TableControl.js";import{html,css}from"../../lit-all.min.js";import"../Icon.js";export default class Edit extends TableControl{static properties={isEditing:{type:Boolean}};static styles=[TableControl.styles,css`
2
+ :host {
3
+ display: inline-flex;
4
+ width: max-content;
5
+ align-items: baseline;
6
+ }
7
+ `];constructor(){super({maxWidth:80})}connectedCallback(){super.connectedCallback(),this.onTableEvent("editingChange",this.handleEditingChange)}handleEditingChange=t=>{t.detail.record===this.record&&(this.isEditing=t.detail.editing)};handleEdit=()=>{this.record&&this.table.editRecord(this.record)};handleSave=()=>{this.record&&this.table.saveEditedRecord(this.record)};handleCancel=()=>{this.record&&this.table.cancelEditedRecord(this.record)};render(){return this.isEditing?html`
8
+ <button class="no-btn icon-btn bg-success" @click="${this.handleSave}">
9
+ <k-icon name="check"></k-icon>
10
+ </button>
11
+ <button class="no-btn icon-btn bg-danger" @click="${this.handleCancel}">
12
+ <k-icon name="close"></k-icon>
13
+ </button>
14
+ `:html`
15
+ <button class="no-btn icon-btn" @click="${this.handleEdit}">
16
+ <k-icon name="edit"></k-icon>
17
+ </button>
18
+ `}}customElements.define("k-tc-edit",Edit);
@@ -0,0 +1,8 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class ExportCSV extends TableControl{constructor(){super(),this.maxWidth=136}getCSV(){const t=this.table;if(!t)return"";let e="";const o=[];if(t.fields.forEach(({name:t,calculator:e})=>{e||o.push(t)}),e+=o.join(",")+"\n",this.record){const t=o.map(t=>this.record[t]||"");e+=t.join(",")+"\n"}else t.records.forEach(t=>{const r=o.map(e=>t[e]||"");e+=r.join(",")+"\n"});return e}export=()=>{const t=this.getCSV(),e=new Blob([t],{type:"text/csv"}),o=URL.createObjectURL(e),r=document.createElement("a");r.href=o,r.download="data.csv",r.click(),URL.revokeObjectURL(o)};render(){return html`
2
+ <button class="no-btn ph" @click="${this.export}">
3
+ <slot>
4
+ <k-icon name="export-file"></k-icon>
5
+ Export CSV
6
+ </slot>
7
+ </button>
8
+ `}}customElements.define("k-tc-export-csv",ExportCSV);
@@ -0,0 +1,8 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class ExportJson extends TableControl{constructor(){super(),this.maxWidth=136}export=()=>{const t=this.record;if(t){const e=JSON.stringify(t),o=new Blob([e],{type:"application/json"}),n=URL.createObjectURL(o),r=document.createElement("a");r.href=n,r.download="data.json",r.click(),URL.revokeObjectURL(n)}else{const t=JSON.stringify(this.table.records),e=new Blob([t],{type:"application/json"}),o=URL.createObjectURL(e),n=document.createElement("a");n.href=o,n.download="data.json",n.click(),URL.revokeObjectURL(o)}};render(){return html`
2
+ <button class="no-btn ph" @click="${this.export}">
3
+ <slot>
4
+ <k-icon name="export-file"></k-icon>
5
+ Export JSON
6
+ </slot>
7
+ </button>
8
+ `}}customElements.define("k-tc-export-json",ExportJson);
@@ -0,0 +1,26 @@
1
+ import TableControl from"./TableControl.js";import{html,render}from"../../lit-all.min.js";import"../Icon.js";import Dialog from"../Dialog.js";export default class FieldSortHide extends TableControl{constructor(){super({maxWidth:40})}handleClick=()=>{this.openDialog()};openDialog=()=>{import("../Sortable.js");const e=document.createElement("div");render(html`
2
+ <h3 class="m0 ph" slot="title">Show / Hide Fields</h3>
3
+ <div class="m">
4
+ <k-sortable id="sorting" @sort="${e=>{const t=Array.from(e.target.querySelectorAll("k-sortable-item")).map(e=>e.getAttribute("data-field"));this.table.reorderFields(t)}}">
5
+ ${this.table.fields.map(e=>html`
6
+ <k-sortable-item data-field="${e.name}">
7
+ <label class="field pb0">
8
+ <input
9
+ class="field-visibility"
10
+ data-field="${e.name}"
11
+ type="checkbox"
12
+ .checked="${!e.hidden}"
13
+ @change="${t=>{this.table.setFieldHiddenState(e.name,!t.target.checked)}}"
14
+ style="height: 1.25rem; width: 1.25rem"
15
+ />
16
+ ${e.label}
17
+ </label>
18
+ </k-sortable-item>
19
+ `)}
20
+ </k-sortable>
21
+ </div>
22
+ `,e),Dialog.create(e,{width:"400px",cancelText:"Close"})};render(){return html`
23
+ <button class="no-btn icon-btn" @click="${this.handleClick}">
24
+ <k-icon name="table-visibility"></k-icon>
25
+ </button>
26
+ `}}customElements.define("k-tc-field-sort-hide",FieldSortHide);
@@ -0,0 +1,47 @@
1
+ import TableControl from"./TableControl.js";import{html,render}from"../../lit-all.min.js";import"../Icon.js";import Dialog from"../Dialog.js";export default class Filters extends TableControl{constructor(){super({maxWidth:40})}handleFilter=()=>{this.openDialog()};openDialog=()=>{const t={equals:"equals","not-equals":"does not equal",contains:"contains","not-contains":"does not contain","greater-than":"is greater than","greater-than-or-equal":"is greater than or equal to","less-than":"is less than","less-than-or-equal":"is less than or equal to"},e=document.createElement("div");render(html`
2
+ <h3 slot="title" class="m0 pyh px">Filters</h3>
3
+ <div class="p">
4
+ ${0===this.table.filters.length?html`
5
+ <p>No Current Filters.</p>
6
+ `:html`
7
+ <h5>Current Filters</h5>
8
+ <ul id="currentFilters">
9
+ ${this.table.filters.map(({field:e,condition:i,value:o})=>html`
10
+ <li
11
+ data-field="${e}"
12
+ data-condition="${i}"
13
+ data-value="${o}"
14
+ >
15
+ ${e} ${t[i]} ${o}
16
+ <button class="remove-filter no-btn pq" @click="${t=>{this.table.removeFilter(e,i,o),l.close(),this.openDialog()}}">
17
+ <k-icon name="close"></k-icon>
18
+ </button>
19
+ </li>
20
+ `)}
21
+ </ul>
22
+ `}
23
+ <hr />
24
+ <h5>Add A Filter</h5>
25
+ <form id="addFilter" @submit="${t=>{t.preventDefault();const e=t.target;this.table.addFilter(e.filterField.value,e.filterCondition.value,e.filterValue.value),l.close(),this.openDialog()}}">
26
+ <select id="filterField" class="mb">
27
+ ${this.table.fields.map(({name:t,label:e})=>html`
28
+ <option value="${t}">${e}</option>
29
+ `)}
30
+ </select>
31
+ <select id="filterCondition" class="mb">
32
+ ${Object.entries(t).map(([t,e])=>html`
33
+ <option value="${t}" ?selected="${"contains"===t}">${e}</option>
34
+ `)}
35
+ </select>
36
+ <input id="filterValue" type="text" class="mb" />
37
+ <button type="submit" class="btn primary mb mr">Add Filter</button>
38
+ ${0===this.table.filters.length?"":html`
39
+ <button type="button" class="btn danger mb mr" @click="${()=>{this.table.removeAllFilters(),l.close()}}">Clear All Filters</button>
40
+ `}
41
+ </form>
42
+ </div>
43
+ `,e);const l=Dialog.create(e,{width:"600px"})};render(){return html`
44
+ <button class="no-btn icon-btn" @click="${this.handleFilter}">
45
+ <k-icon name="filter"></k-icon>
46
+ </button>
47
+ `}}customElements.define("k-tc-filters",Filters);
@@ -0,0 +1,9 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class FirstPage extends TableControl{constructor(){super(),this.pageChangeHandler=()=>this.requestUpdate()}connectedCallback(){super.connectedCallback(),this.table&&this.table.addEventListener("pageChange",this.pageChangeHandler)}disconnectedCallback(){super.disconnectedCallback(),this.table&&this.table.removeEventListener("pageChange",this.pageChangeHandler)}handleClick=()=>{this.table&&this.table.firstPage()};get isDisabled(){return!this.table||1===this.table.getCurrentPage()}render(){return html`
2
+ <button
3
+ class="no-btn icon-btn"
4
+ ?disabled="${this.isDisabled}"
5
+ @click="${this.handleClick}"
6
+ >
7
+ <k-icon name="first"></k-icon>
8
+ </button>
9
+ `}}customElements.define("k-tc-first-page",FirstPage);
@@ -0,0 +1,3 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";export default class HiddenCount extends TableControl{static properties={hiddenCount:{type:Number}};connectedCallback(){super.connectedCallback(),this.updateHiddenCount(),this.onTableEvent("recordHidden recordShown",this.handleHiddenChange)}handleHiddenChange=()=>{this.updateHiddenCount()};updateHiddenCount=()=>{this.hiddenCount=this.table?.getHiddenRecords().length||0};render(){return html`
2
+ <div class="pyq pxh"><span>${this.hiddenCount}</span>&nbsp;Hidden Records</div>
3
+ `}}customElements.define("k-tc-hidden-count",HiddenCount);
@@ -0,0 +1,5 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class Hide extends TableControl{constructor(){super({maxWidth:40})}handleHide=()=>{this.record&&this.table.hideRecord(this.record)};render(){return html`
2
+ <button class="no-btn icon-btn" @click="${this.handleHide}">
3
+ <k-icon name="hide"></k-icon>
4
+ </button>
5
+ `}}customElements.define("k-tc-hide",Hide);
@@ -0,0 +1,9 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class LastPage extends TableControl{constructor(){super(),this.pageChangeHandler=()=>this.requestUpdate()}connectedCallback(){super.connectedCallback(),this.table&&this.table.addEventListener("pageChange",this.pageChangeHandler)}disconnectedCallback(){super.disconnectedCallback(),this.table&&this.table.removeEventListener("pageChange",this.pageChangeHandler)}handleClick=()=>{this.table&&this.table.lastPage()};get isDisabled(){return!this.table||this.table.getCurrentPage()===this.table.getTotalPages()}render(){return html`
2
+ <button
3
+ class="no-btn icon-btn"
4
+ ?disabled="${this.isDisabled}"
5
+ @click="${this.handleClick}"
6
+ >
7
+ <k-icon name="last"></k-icon>
8
+ </button>
9
+ `}}customElements.define("k-tc-last-page",LastPage);
@@ -0,0 +1,9 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class NextPage extends TableControl{constructor(){super(),this.pageChangeHandler=()=>this.requestUpdate()}connectedCallback(){super.connectedCallback(),this.table&&this.table.addEventListener("pageChange",this.pageChangeHandler)}disconnectedCallback(){super.disconnectedCallback(),this.table&&this.table.removeEventListener("pageChange",this.pageChangeHandler)}handleClick=()=>{this.table&&this.table.nextPage()};get isDisabled(){return!this.table||this.table.getCurrentPage()===this.table.getTotalPages()}render(){return html`
2
+ <button
3
+ class="no-btn icon-btn"
4
+ ?disabled="${this.isDisabled}"
5
+ @click="${this.handleClick}"
6
+ >
7
+ <k-icon name="chevron-right"></k-icon>
8
+ </button>
9
+ `}}customElements.define("k-tc-next-page",NextPage);
@@ -0,0 +1,22 @@
1
+ import TableControl from"./TableControl.js";import{html,css}from"../../lit-all.min.js";export default class PageSelect extends TableControl{static properties={...TableControl.properties,currentPage:{type:Number,state:!0},totalPages:{type:Number,state:!0}};constructor(){super({maxWidth:null}),this.currentPage=1,this.totalPages=1}connectedCallback(){super.connectedCallback(),this.onTableEvent("pageChange",this.handlePageChange),this.onTableEvent("pageSizeChange",this.handlePageSizeChange),this.onTableEvent("pageCountChanged",this.handlePageCountChange)}disconnectedCallback(){super.disconnectedCallback(),this.table&&(this.table.removeEventListener("pageChange",this.handlePageChange),this.table.removeEventListener("pageSizeChange",this.handlePageSizeChange),this.table.removeEventListener("pageCountChanged",this.handlePageCountChange))}handlePageChange=()=>{this.table&&(this.currentPage=this.table.getCurrentPage())};handlePageSizeChange=()=>{this.table&&(this.currentPage=this.table.getCurrentPage(),this.totalPages=this.table.getTotalPages())};handlePageCountChange=()=>{this.table&&(this.totalPages=this.table.getTotalPages())};handleSelectChange=e=>{this.table&&this.table.setPage(parseInt(e.target.value))};render(){if(!this.table)return html`<div>No table found</div>`;const e=[];for(let t=1;t<=this.totalPages;t++)e.push(html`<option value="${t}" ?selected="${t===this.currentPage}">Page ${t}</option>`);return html`
2
+ <div class="page-select">
3
+ <select class="no-btn ph b r" @change="${this.handleSelectChange}" ?disabled="${this.totalPages<=1}">
4
+ ${e}
5
+ </select>
6
+ <label> out of ${this.totalPages}</label>
7
+ </div>
8
+ `}static styles=css`
9
+ ${TableControl.styles}
10
+
11
+ .page-select {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: 0.25rem;
15
+ white-space: nowrap;
16
+ }
17
+
18
+ label {
19
+ padding: 0;
20
+ margin: 0;
21
+ }
22
+ `}customElements.define("k-tc-page-select",PageSelect);
@@ -0,0 +1,5 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";export default class PageSize extends TableControl{constructor(){super({maxWidth:null}),this.pageChangeHandler=()=>this.requestUpdate(),this.pageSizeChangeHandler=()=>this.requestUpdate()}connectedCallback(){super.connectedCallback(),this.onTableEvent("pageChange",this.pageChangeHandler),this.onTableEvent("pageSizeChange",this.pageSizeChangeHandler)}disconnectedCallback(){super.disconnectedCallback(),this.table&&(this.table.removeEventListener("pageChange",this.pageChangeHandler),this.table.removeEventListener("pageSizeChange",this.pageSizeChangeHandler))}handleChange=e=>{this.table&&this.table.setPageSize(parseInt(e.target.value))};get currentPageSize(){return this.table?this.table.getPageSize():10}get pageSizeOptions(){return this.table?this.table.pageSizeOptions:[10,25,50,100,500]}render(){return html`
2
+ <select class="no-btn ph b r" @change="${this.handleChange}">
3
+ ${this.pageSizeOptions.map(e=>html`<option value="${e}" ?selected="${e===this.currentPageSize}">${e} per page</option>`)}
4
+ </select>
5
+ `}}customElements.define("k-tc-page-size",PageSize);
@@ -0,0 +1,9 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class PrevPage extends TableControl{constructor(){super(),this.pageChangeHandler=()=>this.requestUpdate()}connectedCallback(){super.connectedCallback(),this.table&&this.table.addEventListener("pageChange",this.pageChangeHandler)}disconnectedCallback(){super.disconnectedCallback(),this.table&&this.table.removeEventListener("pageChange",this.pageChangeHandler)}handleClick=()=>{this.table&&this.table.prevPage()};get isDisabled(){return!this.table||1===this.table.getCurrentPage()}render(){return html`
2
+ <button
3
+ class="no-btn icon-btn"
4
+ ?disabled="${this.isDisabled}"
5
+ @click="${this.handleClick}"
6
+ >
7
+ <k-icon name="chevron-left"></k-icon>
8
+ </button>
9
+ `}}customElements.define("k-tc-prev-page",PrevPage);
@@ -0,0 +1,9 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import debounce from"../../utils/debounce.js";export default class Search extends TableControl{static properties={searchTerm:{type:String}};constructor(){super({maxWidth:200}),this.debouncedSearch=debounce(this.performSearch,200)}handleInput=e=>{this.searchTerm=e.target.value,this.debouncedSearch()};performSearch=()=>{this.table&&(!this.searchTerm||this.searchTerm.length<3?this.table.showAllRecords():this.table.search(this.searchTerm))};render(){return html`
2
+ <input
3
+ type="search"
4
+ placeholder="Search"
5
+ class="px pyh"
6
+ .value="${this.searchTerm||""}"
7
+ @input="${this.handleInput}"
8
+ />
9
+ `}}customElements.define("k-tc-search",Search);
@@ -0,0 +1,5 @@
1
+ import TableControl from"./TableControl.js";import{html}from"../../lit-all.min.js";import"../Icon.js";export default class ShowAll extends TableControl{constructor(){super({maxWidth:40})}handleShowAll=()=>{this.table&&this.table.showAllRecords()};render(){return html`
2
+ <button class="no-btn icon-btn" @click="${this.handleShowAll}">
3
+ <k-icon name="show"></k-icon>
4
+ </button>
5
+ `}}customElements.define("k-tc-show-all",ShowAll);
@@ -0,0 +1,16 @@
1
+ import ShadowComponent from"../ShadowComponent.js";import{html,css}from"../../lit-all.min.js";export default class TableControl extends ShadowComponent{static properties={maxWidth:{type:Number,reflect:!0,attribute:"max-width"}};constructor({maxWidth:t=40}={}){super(),this.maxWidth=t}updated(t){super.updated(t),t.has("maxWidth")&&(this.maxWidth?this.style.setProperty("--max-width",`${this.maxWidth}px`):this.style.removeProperty("--max-width"))}firstUpdated(){this.maxWidth?this.style.setProperty("--max-width",`${this.maxWidth}px`):this.style.removeProperty("--max-width")}get table(){return this.getRootNode()instanceof ShadowRoot?this.getRootNode().host.closest("k-table"):this.closest("k-table")}get record(){if(this.getRootNode()instanceof ShadowRoot){const t=this.closest(".record");if(t){const e=t.dataset.index;if(void 0!==e)return this.table.records[e]}}return!1}async onTableEvent(t,e){if(await customElements.whenDefined("k-table"),this.table){t.split(" ").forEach(t=>{this.table.addEventListener(t,e)}),this.requestUpdate()}}static styles=css`
2
+ :host {
3
+ display: inline-flex;
4
+ }
5
+
6
+ .icon-btn {
7
+ display: inline-flex !important;
8
+ align-items: center;
9
+ justify-content: center;
10
+ width: 40px;
11
+ height: 40px;
12
+ }
13
+ .icon-btn:disabled {
14
+ opacity: 0.6;
15
+ }
16
+ `}customElements.define("k-table-control",TableControl);
@@ -0,0 +1 @@
1
+ const toTitleCase=e=>e.replace(/([A-Z])/g," $1").split(/[\s_-]+/).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(" ");export default toTitleCase;
@@ -29,6 +29,7 @@
29
29
  <a href="#basicUsage">Basic Usage</a><br />
30
30
  <a href="#options">Options</a><br />
31
31
  <a href="#callbacks">Callbacks</a><br />
32
+ <a href="#returnValue">Return Value</a><br />
32
33
  <a href="#examples">Examples</a><br />
33
34
  </div>
34
35
  </details>
@@ -37,16 +38,8 @@
37
38
  <p>The <code>drag</code> utility function enables drag interactions on DOM elements. It handles both mouse and touch events, providing callbacks for drag start, move, and end events.</p>
38
39
 
39
40
  <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
40
- <p>Import and use the drag function to make elements draggable:</p>
41
- <pre><code class="hljs javascript">import drag from './utils/drag.js';
42
-
43
- const element = document.querySelector('.draggable');
44
- drag({
45
- element,
46
- callback: (diff) => {
47
- console.log('Dragged:', diff.x, diff.y);
48
- }
49
- });</code></pre>
41
+ <p>Import and use the drag function to make elements draggable. The function returns a cleanup function that should be called to remove all event listeners:</p>
42
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> drag <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/drag.js'</span>;<br /><br /><span class="hljs-keyword">const</span> element = <span class="hljs-built_in">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.draggable'</span>);<br /><span class="hljs-keyword">const</span> cleanup = <span class="hljs-title function_">drag</span>({<br /> element,<br /> <span class="hljs-attr">callback</span>: <span class="hljs-function">(<span class="hljs-params">diff</span>) =&gt;</span> {<br /> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Dragged:'</span>, diff.x, diff.y);<br /> }<br />});<br /><br /><span class="hljs-comment">// Later, when you need to remove the drag functionality</span><br /><span class="hljs-title function_">cleanup</span>();</code></pre>
50
43
 
51
44
  <h3 id="options"><a href="#options" class="no-link">Options</a></h3>
52
45
  <h5><code>element<i>: HTMLElement</i></code></h5>
@@ -58,11 +51,16 @@ drag({
58
51
  <h5><code>callback<i>: Function</i></code> (optional)</h5>
59
52
  <p>Called on every drag move. Receives a diff object with x, y coordinates and original options.</p>
60
53
  <h5><code>startCallback<i>: Function</i></code> (optional)</h5>
61
- <p>Called when drag starts. Receives the starting event.</p>
54
+ <p>Called when drag starts. Receives a diff object with x: 0, y: 0 and original options.</p>
62
55
  <h5><code>moveCallback<i>: Function</i></code> (optional)</h5>
63
56
  <p>Called on every drag move (in addition to callback). Receives a diff object.</p>
64
57
  <h5><code>endCallback<i>: Function</i></code> (optional)</h5>
65
- <p>Called when drag ends. Receives the ending event.</p>
58
+ <p>Called when drag ends. Receives a diff object with the final position.</p>
59
+
60
+ <h3 id="returnValue"><a href="#returnValue" class="no-link">Return Value</a></h3>
61
+ <h5><code>cleanup<i>: Function</i></code></h5>
62
+ <p>The drag function returns a cleanup function. Call this function to remove all event listeners and stop the drag functionality. This is important for preventing memory leaks when removing or destroying components.</p>
63
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> cleanup = <span class="hljs-title function_">drag</span>({ element, callback });<br /><br /><span class="hljs-comment">// Later, when component is destroyed or drag is no longer needed</span><br /><span class="hljs-title function_">cleanup</span>();</code></pre>
66
64
 
67
65
  <h3 id="examples"><a href="#examples" class="no-link">Examples</a></h3>
68
66
 
@@ -80,23 +78,10 @@ drag({
80
78
  });</code></pre>
81
79
 
82
80
  <h4>Resize Handle</h4>
83
- <pre><code class="hljs javascript">const resizeHandle = document.querySelector('.resize-handle');
84
- const panel = document.querySelector('.panel');
81
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> resizeHandle = <span class="hljs-built_in">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.resize-handle'</span>);<br /><span class="hljs-keyword">const</span> panel = <span class="hljs-built_in">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.panel'</span>);<br /><br /><span class="hljs-title function_">drag</span>({<br /> <span class="hljs-attr">element</span>: resizeHandle,<br /> <span class="hljs-attr">preventScroll</span>: <span class="hljs-literal">true</span>,<br /> <span class="hljs-attr">callback</span>: <span class="hljs-function">(<span class="hljs-params">diff</span>) =&gt;</span> {<br /> <span class="hljs-keyword">const</span> newWidth = panel.offsetWidth + diff.x;<br /> panel.style.width = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">max</span>(<span class="hljs-number">100</span>, newWidth) + <span class="hljs-string">'px'</span>;<br /> },<br /> <span class="hljs-attr">startCallback</span>: <span class="hljs-function">() =&gt;</span> {<br /> panel.classList.<span class="hljs-title function_">add</span>(<span class="hljs-string">'resizing'</span>);<br /> },<br /> <span class="hljs-attr">endCallback</span>: <span class="hljs-function">() =&gt;</span> {<br /> panel.classList.<span class="hljs-title function_">remove</span>(<span class="hljs-string">'resizing'</span>);<br /> }<br />});</code></pre>
85
82
 
86
- drag({
87
- element: resizeHandle,
88
- preventScroll: true,
89
- callback: (diff) => {
90
- const newWidth = panel.offsetWidth + diff.x;
91
- panel.style.width = Math.max(100, newWidth) + 'px';
92
- },
93
- startCallback: () => {
94
- panel.classList.add('resizing');
95
- },
96
- endCallback: () => {
97
- panel.classList.remove('resizing');
98
- }
99
- });</code></pre>
83
+ <h4>Component with Cleanup</h4>
84
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> ShadowComponent <span class="hljs-keyword">from</span> <span class="hljs-string">'./ShadowComponent.js'</span>;<br /><span class="hljs-keyword">import</span> drag <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/drag.js'</span>;<br /><br /><span class="hljs-keyword">class</span> <span class="hljs-title class_">DraggableComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ShadowComponent</span> {<br /> <span class="hljs-title function_">constructor</span>() {<br /> <span class="hljs-variable language_">super</span>();<br /> <span class="hljs-variable language_">this</span>.cleanupDrag = <span class="hljs-literal">null</span>;<br /> }<br /><br /> <span class="hljs-title function_">firstUpdated</span>() {<br /> <span class="hljs-keyword">const</span> handle = <span class="hljs-variable language_">this</span>.shadowRoot.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.drag-handle'</span>);<br /> <span class="hljs-variable language_">this</span>.cleanupDrag = <span class="hljs-title function_">drag</span>({<br /> <span class="hljs-attr">element</span>: handle,<br /> <span class="hljs-attr">moveCallback</span>: <span class="hljs-function">(<span class="hljs-params">diff</span>) =&gt;</span> {<br /> <span class="hljs-variable language_">this</span>.style.transform = <span class="hljs-string">`translate(<span class="hljs-subst">${diff.x}</span>px, <span class="hljs-subst">${diff.y}</span>px)`</span>;<br /> }<br /> });<br /> }<br /><br /> <span class="hljs-title function_">disconnectedCallback</span>() {<br /> <span class="hljs-variable language_">super</span>.<span class="hljs-title function_">disconnectedCallback</span>();<br /> <span class="hljs-keyword">if</span>(<span class="hljs-variable language_">this</span>.cleanupDrag) {<br /> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">cleanupDrag</span>();<br /> <span class="hljs-variable language_">this</span>.cleanupDrag = <span class="hljs-literal">null</span>;<br /> }<br /> }<br />}</code></pre>
100
85
 
101
86
  </main>
102
87
  <div style="height:33vh"></div>
@@ -0,0 +1,71 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>toTitleCase - Kempo UI</title>
7
+ <link rel="stylesheet" href="../src/kempo-vars.css" />
8
+ <link rel="stylesheet" href="../kempo.min.css" />
9
+ <link rel="stylesheet" href="../src/kempo-hljs.css" />
10
+ <link rel="stylesheet" href="../styles.css" />
11
+ <script type="module">
12
+ import Import from '../src/components/Import.js';
13
+ import Icon from '../src/components/Icon.js';
14
+ Import.replacements.root = '../';
15
+ Icon.pathToIcons = ['../icons'];
16
+ </script>
17
+ </head>
18
+ <body>
19
+ <k-import src="../nav.inc.html"></k-import>
20
+ <main>
21
+ <h1>toTitleCase</h1>
22
+
23
+ <details
24
+ class="b r mb"
25
+ >
26
+ <summary class="p">Table of Contents</summary>
27
+ <div class="m mt0 pl">
28
+ <h6>Usage</h6>
29
+ <a href="#basicUsage">Basic Usage</a><br />
30
+ <a href="#parameters">Parameters</a><br />
31
+ <a href="#returnValue">Return Value</a><br />
32
+ <a href="#examples">Examples</a><br />
33
+ </div>
34
+ </details>
35
+
36
+ <h3>Description</h3>
37
+ <p>The <code>toTitleCase</code> utility function converts strings to title case format. It handles camelCase, snake_case, kebab-case, and regular text, converting them into properly capitalized title case with spaces between words.</p>
38
+
39
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
40
+ <p>Import and use the toTitleCase function to convert strings to title case:</p>
41
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> toTitleCase <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/toTitleCase.js'</span>;<br /><br /><span class="hljs-keyword">const</span> result = <span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'helloWorld'</span>);<br /><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(result); <span class="hljs-comment">// "Hello World"</span></code></pre>
42
+
43
+ <h3 id="parameters"><a href="#parameters" class="no-link">Parameters</a></h3>
44
+ <h5><code>str<i>: string</i></code></h5>
45
+ <p>The string to convert to title case. Can be in any format (camelCase, snake_case, kebab-case, or regular text).</p>
46
+
47
+ <h3 id="returnValue"><a href="#returnValue" class="no-link">Return Value</a></h3>
48
+ <h5><code><i>string</i></code></h5>
49
+ <p>The input string converted to title case with proper spacing and capitalization.</p>
50
+
51
+ <h3 id="examples"><a href="#examples" class="no-link">Examples</a></h3>
52
+
53
+ <h4>Converting camelCase</h4>
54
+ <pre><code class="hljs javascript"><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'firstName'</span>); <span class="hljs-comment">// "First Name"</span><br /><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'customerAddress'</span>); <span class="hljs-comment">// "Customer Address"</span><br /><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'userId'</span>); <span class="hljs-comment">// "User Id"</span></code></pre>
55
+
56
+ <h4>Converting snake_case</h4>
57
+ <pre><code class="hljs javascript"><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'first_name'</span>); <span class="hljs-comment">// "First Name"</span><br /><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'customer_address'</span>); <span class="hljs-comment">// "Customer Address"</span><br /><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'user_id'</span>); <span class="hljs-comment">// "User Id"</span></code></pre>
58
+
59
+ <h4>Converting kebab-case</h4>
60
+ <pre><code class="hljs javascript"><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'first-name'</span>); <span class="hljs-comment">// "First Name"</span><br /><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'customer-address'</span>); <span class="hljs-comment">// "Customer Address"</span><br /><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'user-id'</span>); <span class="hljs-comment">// "User Id"</span></code></pre>
61
+
62
+ <h4>Converting regular text</h4>
63
+ <pre><code class="hljs javascript"><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'hello world'</span>); <span class="hljs-comment">// "Hello World"</span><br /><span class="hljs-title function_">toTitleCase</span>(<span class="hljs-string">'the quick brown fox'</span>); <span class="hljs-comment">// "The Quick Brown Fox"</span></code></pre>
64
+
65
+ <h4>Practical Use Case: Table Column Headers</h4>
66
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> columns = [<span class="hljs-string">'firstName'</span>, <span class="hljs-string">'lastName'</span>, <span class="hljs-string">'emailAddress'</span>];<br /><br /><span class="hljs-keyword">const</span> headers = columns.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">col</span> =&gt;</span> <span class="hljs-title function_">toTitleCase</span>(col));<br /><span class="hljs-comment">// ["First Name", "Last Name", "Email Address"]</span></code></pre>
67
+
68
+ </main>
69
+ <div style="height:33vh"></div>
70
+ </body>
71
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo-ui",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "description": "A Lit based web-component library",
6
6
  "main": "index.js",
@@ -26,7 +26,7 @@
26
26
  "homepage": "https://github.com/dustinpoissant/kempo-ui#readme",
27
27
  "devDependencies": {
28
28
  "kempo-server": "^1.7.10",
29
- "kempo-testing-framework": "^1.3.6",
29
+ "kempo-testing-framework": "^1.4.0",
30
30
  "terser": "^5.43.1"
31
31
  },
32
32
  "dependencies": {
package/scripts/build.js CHANGED
@@ -89,6 +89,9 @@ const minifiedKempoHljsCSS = kempoHljsCSS.replace(/\s*\{\s*/g, '{').replace(/\s*
89
89
  console.log('Saving kempo-hljs.css');
90
90
  await fs.writeFile('./dist/kempo-hljs.css', minifiedKempoHljsCSS, 'utf-8');
91
91
 
92
+ console.log('Cleaning docs/src/ directory');
93
+ await emptyDir('./docs/src/');
94
+
92
95
  console.log('Copying dist/ to docs/');
93
96
  await ensureDir('./docs/');
94
97
  await copyDir('./dist', './docs/');
@@ -2,6 +2,7 @@ import ShadowComponent from './ShadowComponent.js';
2
2
  import { html, css } from '../lit-all.min.js';
3
3
  import { boolExists } from '../utils/propConverters.js';
4
4
  import './Icon.js';
5
+ import './FocusCapture.js';
5
6
 
6
7
  const firstFocusable = element => {
7
8
  const focusableElements = element.querySelectorAll(
@@ -200,6 +201,11 @@ export default class Dialog extends ShadowComponent {
200
201
  background: transparent;
201
202
  box-shadow: 0 0 0 transparent;
202
203
  color: var(--tc);
204
+ transition: box-shadow var(--animation_ms);
205
+ border-radius: var(--radius);
206
+ }
207
+ #close:focus {
208
+ box-shadow: var(--focus_shadow);
203
209
  }
204
210
  #close k-icon {
205
211
  pointer-events: none;
@@ -227,36 +233,38 @@ export default class Dialog extends ShadowComponent {
227
233
  aria-modal="true"
228
234
  aria-labelledby="title"
229
235
  >
230
- <div
231
- id="header"
232
- class="${this.hasTitle() ? 'has-title' : ''}"
233
- >
234
- <div id="title">
235
- <slot name="title"></slot>
236
- </div>
237
- ${this.closeBtn ? html`
238
- <button id="close" @click=${this.handleClick}>
239
- <k-icon name="close"></k-icon>
240
- </button>
241
- ` : ''}
242
- </div>
243
- <div id="body">
244
- <slot></slot>
245
- </div>
246
- ${this.cancelText || this.confirmText ? html`
247
- <div id="footer">
248
- ${this.cancelText ? html`
249
- <button id="cancel" class="${this.cancelClasses}" @click=${this.handleClick}>
250
- ${this.cancelText}
251
- </button>
252
- ` : ''}
253
- ${this.confirmText ? html`
254
- <button id="confirm" class="${this.confirmClasses}" @click=${this.handleClick}>
255
- ${this.confirmText}
236
+ <k-focus-capture>
237
+ <div
238
+ id="header"
239
+ class="${this.hasTitle() ? 'has-title' : ''}"
240
+ >
241
+ <div id="title">
242
+ <slot name="title"></slot>
243
+ </div>
244
+ ${this.closeBtn ? html`
245
+ <button id="close" @click=${this.handleClick}>
246
+ <k-icon name="close"></k-icon>
256
247
  </button>
257
248
  ` : ''}
258
249
  </div>
259
- ` : ''}
250
+ <div id="body">
251
+ <slot></slot>
252
+ </div>
253
+ ${this.cancelText || this.confirmText ? html`
254
+ <div id="footer">
255
+ ${this.cancelText ? html`
256
+ <button id="cancel" class="${this.cancelClasses}" @click=${this.handleClick}>
257
+ ${this.cancelText}
258
+ </button>
259
+ ` : ''}
260
+ ${this.confirmText ? html`
261
+ <button id="confirm" class="${this.confirmClasses}" @click=${this.handleClick}>
262
+ ${this.confirmText}
263
+ </button>
264
+ ` : ''}
265
+ </div>
266
+ ` : ''}
267
+ </k-focus-capture>
260
268
  </div>
261
269
  </div>
262
270
  `;
@@ -1,5 +1,5 @@
1
- import HybridComponent from '/src/components/HybridComponent.js';
2
- import { html } from '/src/lit-all.min.js';
1
+ import HybridComponent from '../../src/components/HybridComponent.js';
2
+ import { html } from '../../src/lit-all.min.js';
3
3
 
4
4
  class TestHybridComponent extends HybridComponent {
5
5
  render() {
@@ -1,5 +1,5 @@
1
- import LightComponent from '/src/components/LightComponent.js';
2
- import { html } from '/src/lit-all.min.js';
1
+ import LightComponent from '../../src/components/LightComponent.js';
2
+ import { html } from '../../src/lit-all.min.js';
3
3
 
4
4
  class TestLightComponent extends LightComponent {
5
5
  renderLightDom() {
@@ -1,5 +1,5 @@
1
- import ShadowComponent from '/src/components/ShadowComponent.js';
2
- import { html } from '/src/lit-all.min.js';
1
+ import ShadowComponent from '../../src/components/ShadowComponent.js';
2
+ import { html } from '../../src/lit-all.min.js';
3
3
 
4
4
  class TestShadowComponent extends ShadowComponent {
5
5
  render() {