kempo-ui 0.0.9 → 0.0.11

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 (60) hide show
  1. package/dist/src/components/Card.js +3 -3
  2. package/dist/src/components/Dialog.js +11 -11
  3. package/dist/src/components/Icon.js +1 -1
  4. package/dist/src/components/Toggle.js +15 -14
  5. package/dist/src/components/Tree.js +11 -3
  6. package/docs/components/accordion.html +34 -123
  7. package/docs/components/card.html +43 -58
  8. package/docs/components/collapsible.html +38 -159
  9. package/docs/components/content-slider.html +36 -183
  10. package/docs/components/dialog.html +38 -303
  11. package/docs/components/focus-capture.html +42 -79
  12. package/docs/components/hybrid-component.html +36 -73
  13. package/docs/components/icon.html +43 -123
  14. package/docs/components/import.html +43 -40
  15. package/docs/components/light-component.html +37 -61
  16. package/docs/components/persistant-collapsible.html +40 -85
  17. package/docs/components/photo-viewer.html +39 -187
  18. package/docs/components/resize.html +40 -141
  19. package/docs/components/shadow-component.html +41 -54
  20. package/docs/components/show-more.html +42 -94
  21. package/docs/components/side-menu.html +41 -124
  22. package/docs/components/sortable.html +38 -95
  23. package/docs/components/split.html +41 -113
  24. package/docs/components/table.html +34 -271
  25. package/docs/components/tableControls.html +54 -56
  26. package/docs/components/tableCustomFields.html +54 -92
  27. package/docs/components/tableFetchRecords.html +52 -91
  28. package/docs/components/tableFieldSortHide.html +54 -54
  29. package/docs/components/tablePagination.html +54 -79
  30. package/docs/components/tableRecordEditing.html +54 -123
  31. package/docs/components/tableRecordFiltering.html +52 -65
  32. package/docs/components/tableRecordHiding.html +53 -58
  33. package/docs/components/tableRecordSearching.html +54 -54
  34. package/docs/components/tableRecordSelection.html +55 -55
  35. package/docs/components/tableRowControls.html +55 -58
  36. package/docs/components/tableSorting.html +54 -52
  37. package/docs/components/tabs.html +45 -148
  38. package/docs/components/tags.html +40 -73
  39. package/docs/components/theme-switcher.html +38 -87
  40. package/docs/components/timestamp.html +40 -45
  41. package/docs/components/toast.html +32 -318
  42. package/docs/components/toggle.html +41 -120
  43. package/docs/components/tree.html +41 -146
  44. package/docs/index.html +56 -209
  45. package/docs/src/components/Card.js +3 -3
  46. package/docs/src/components/Dialog.js +11 -11
  47. package/docs/src/components/Icon.js +1 -1
  48. package/docs/src/components/Toggle.js +15 -14
  49. package/docs/src/components/Tree.js +11 -3
  50. package/docs/utils/debounce.html +31 -33
  51. package/docs/utils/drag.html +31 -44
  52. package/docs/utils/formatTimestamp.html +31 -69
  53. package/docs/utils/init.js +1 -0
  54. package/docs/utils/propConverters.html +26 -82
  55. package/docs/utils/toTitleCase.html +31 -26
  56. package/docs/utils/watchWindowSize.html +8 -98
  57. package/package.json +1 -1
  58. package/src/components/Card.js +3 -0
  59. package/src/components/Toggle.js +69 -69
  60. package/src/components/Tree.js +10 -2
@@ -1,4 +1,4 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class Card extends ShadowComponent{static properties={label:{type:String,reflect:!0}};constructor(){super(),this.label=""}static styles=css`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class Card extends ShadowComponent{static properties={label:{type:String,reflect:!0}};constructor(){super(),this.label=null}static styles=css`
2
2
  :host {
3
3
  display: block;
4
4
  }
@@ -20,11 +20,11 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
20
20
  padding: var(--spacer_h);
21
21
  }
22
22
  :host([label]) {
23
- padding-top: calc(1.5 * var(--spacer));
24
- margin-top: var(--spacer);
23
+ padding-top: 0.1px; /* Prevent Collapsing Margins */
25
24
  }
26
25
  :host([label]) #card {
27
26
  padding-top: calc(1.5 * var(--spacer));
27
+ margin-top: var(--spacer);
28
28
  }
29
29
  :host(:not([label])) #label {
30
30
  display: none;
@@ -1,4 +1,4 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=t=>t.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=t=>{const{target:e}=t,o=e.id||e.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o?(this.cancelAction(t),t.defaultPrevented||this.close()):"confirm"===o&&(this.confirmAction(t),t.defaultPrevented||this.close())};handleKeydown=t=>{27===t.keyCode&&this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(t){super.updated(t),t.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown);const t=this.updateComplete||this.requestUpdate();t&&t.then?t.then(()=>{const t=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);t&&t.focus()}):setTimeout(()=>{const t=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);t&&t.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown)}toggle(){this.opened?this.close():this.open()}focus(){const t=firstFocusable(this.shadowRoot);t&&(this.previousFocus=document.activeElement,t.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=e=>e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=e=>{const{target:t}=e,o=t.id||t.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o?(this.cancelAction(e),e.defaultPrevented||this.close()):"confirm"===o&&(this.confirmAction(e),e.defaultPrevented||this.close())};handleKeydown=e=>{27===e.keyCode&&this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(e){super.updated(e),e.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown);const e=this.updateComplete||this.requestUpdate();e&&e.then?e.then(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()}):setTimeout(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown)}toggle(){this.opened?this.close():this.open()}focus(){const e=firstFocusable(this.shadowRoot);e&&(this.previousFocus=document.activeElement,e.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
2
2
  :host {
3
3
  position: fixed;
4
4
  top: 0;
@@ -77,13 +77,13 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
77
77
  `;render(){return html`
78
78
  <button id="overlay" aria-label="Close the Dialog" @click=${this.handleClick}></button>
79
79
  <div id="wrapper">
80
- <div
81
- id="dialog"
82
- role="dialog"
83
- aria-modal="true"
84
- aria-labelledby="title"
85
- >
86
- <k-focus-capture>
80
+ <k-focus-capture>
81
+ <div
82
+ id="dialog"
83
+ role="dialog"
84
+ aria-modal="true"
85
+ aria-labelledby="title"
86
+ >
87
87
  <div
88
88
  id="header"
89
89
  class="${this.hasTitle()?"has-title":""}"
@@ -114,7 +114,7 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
114
114
  `:""}
115
115
  </div>
116
116
  `:""}
117
- </k-focus-capture>
118
- </div>
117
+ </div>
118
+ </k-focus-capture>
119
119
  </div>
120
- `}static create(t="",e={}){!1!==e.closeExisting&&document.querySelectorAll("k-dialog").forEach(t=>t.close());const{removeOnClose:o=!0,closeCallback:s=()=>{}}=e,i=new Dialog;return Object.assign(i,{opened:!0,...e,closeCallback:(...t)=>{o&&i.remove(),s(...t)}}),t instanceof HTMLElement||t instanceof DocumentFragment?i.appendChild(t):t&&(i.innerHTML=t),e.width&&i.style.setProperty("--width",e.width),e.minWidth&&i.style.setProperty("--min_width",e.minWidth),e.maxWidth&&i.style.setProperty("--max_width",e.maxWidth),e.height&&i.style.setProperty("--height",e.height),e.minHeight&&i.style.setProperty("--min_height",e.minHeight),e.maxHeight&&i.style.setProperty("--max_height",e.maxHeight),document.body.appendChild(i),i.open(),i}static confirm(t,e,o={}){const s=o.title||"Confirm";return Dialog.create(`\n\t\t\t<h5 slot="title" class="pyh px m0">${s}</h5>\n\t\t\t<p class="p">${t}</p>\n\t\t`,{closeBtn:!1,overlayClose:!1,confirmText:"Yes",confirmClasses:"success ml",confirmAction:()=>e(!0),cancelText:"No",cancelClasses:"danger",cancelAction:()=>e(!1),...o})}static alert(t,e,o={}){const s=o.title||"Alert";return Dialog.create(`\n\t\t\t<h5 slot="title" class="pyh px m0">${s}</h5>\n\t\t\t<p class="p">${t}</p>\n\t\t`,{closeCallback:e,cancelText:"Ok",...o})}static error(t,e,o={}){const s=o.title||"Error";return Dialog.create(`\n\t\t\t<h5 slot="title" class="pyh px m0 tc-danger">${s}</h5>\n\t\t\t<p class="p">${t}</p>\n\t\t`,{closeCallback:e,cancelText:"Ok",...o})}static success(t,e,o={}){const s=o.title||"Success";return Dialog.create(`\n\t\t\t<h5 slot="title" class="pyh px m0 tc-success">${s}</h5>\n\t\t\t<p class="p">${t}</p>\n\t\t`,{closeCallback:e,cancelText:"Ok",...o})}}customElements.define("k-dialog",Dialog);
120
+ `}static create(e="",t={}){!1!==t.closeExisting&&document.querySelectorAll("k-dialog").forEach(e=>e.close());const{removeOnClose:o=!0,closeCallback:s=()=>{},title:i="",titleClasses:l="pyh px m0"}=t,n=new Dialog;if(Object.assign(n,{opened:!0,...t,closeCallback:(...e)=>{o&&n.remove(),s(...e)}}),i){const e=document.createElement("h5");e.slot="title",e.className=l,i instanceof HTMLElement?e.appendChild(i):e.innerHTML=i,n.appendChild(e)}if(e instanceof HTMLElement||e instanceof DocumentFragment)n.appendChild(e);else if(e){if(/<[^>]+>/.test(e))n.innerHTML+=e;else{const t=document.createElement("p");t.className="p",t.textContent=e,n.appendChild(t)}}return t.width&&n.style.setProperty("--width",t.width),t.minWidth&&n.style.setProperty("--min_width",t.minWidth),t.maxWidth&&n.style.setProperty("--max_width",t.maxWidth),t.height&&n.style.setProperty("--height",t.height),t.minHeight&&n.style.setProperty("--min_height",t.minHeight),t.maxHeight&&n.style.setProperty("--max_height",t.maxHeight),document.body.appendChild(n),n.open(),n}static confirm(e,t,o={}){return Dialog.create(e,{title:o.title||"Confirm",closeBtn:!1,overlayClose:!1,confirmText:"Yes",confirmClasses:"success ml",confirmAction:()=>t(!0),cancelText:"No",cancelClasses:"danger",cancelAction:()=>t(!1),...o})}static alert(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Alert",closeCallback:t,cancelText:"Ok",...o})}static error(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Error",titleClasses:"pyh px m0 tc-danger",closeCallback:t,cancelText:"Ok",...o})}static success(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Success",titleClasses:"pyh px m0 tc-success",closeCallback:t,cancelText:"Ok",...o})}}customElements.define("k-dialog",Dialog);
@@ -1,4 +1,4 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css,unsafeHTML}from"../lit-all.min.js";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise(async(e,n)=>{const o=(new AbortController).signal;try{const n=await fetch(t,{signal:o});200===n.status?e(await n.text()):404===n.status&&e(null)}catch(t){"AbortError"!==t.name&&n(t)}}).catch(()=>null)),await cache[t]),getIconByName=t=>{const e=async e=>getIconByPath(`${e}/${t}.svg`);return new Promise(async(t,n)=>{let o;for(let t=0;t<Icon.pathToIcons.length&&!o;t++)try{o=await e(Icon.pathToIcons[t])}catch(t){}t(o||null)})};export default class Icon extends ShadowComponent{static properties={src:{type:String,reflect:!0},name:{type:String,reflect:!0},iconContent:{type:String}};constructor(t=""){super(),this.src="",this.name=t,this.iconContent=""}updated(t){super.updated(),(t.has("src")||t.has("name"))&&this.loadIcon(),t.has("iconContent")&&this.fixSVG()}async loadIcon(){let t;if(this.src?t=await getIconByPath(this.src):this.name&&(t=await getIconByName(this.name)),t)this.iconContent=t;else{const t=this.innerHTML.trim();this.iconContent=t||Icon.fallback}}fixSVG(){setTimeout(()=>{const t=this.querySelector("svg");t&&(t.removeAttribute("width"),t.removeAttribute("height"),t.querySelectorAll("path, rect, circle").forEach(t=>{t.setAttribute("fill","currentColor")}))},0)}static styles=css`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css,unsafeHTML}from"../lit-all.min.js";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise(async(e,n)=>{const o=(new AbortController).signal;try{const n=await fetch(t,{signal:o});200===n.status?e(await n.text()):404===n.status&&e(null)}catch(t){"AbortError"!==t.name&&n(t)}}).catch(()=>null)),await cache[t]),getIconByName=t=>{const e=async e=>getIconByPath(`${e}/${t}.svg`);return new Promise(async(t,n)=>{let o;for(let t=0;t<Icon.pathToIcons.length&&!o;t++)try{o=await e(Icon.pathToIcons[t])}catch(t){}t(o||null)})};export default class Icon extends ShadowComponent{static properties={src:{type:String,reflect:!0},name:{type:String,reflect:!0},iconContent:{type:String}};constructor(t=""){super(),this.src="",this.name=t,this.iconContent=""}updated(t){super.updated(),(t.has("src")||t.has("name"))&&this.loadIcon()}async loadIcon(){let t;if(this.src?t=await getIconByPath(this.src):this.name&&(t=await getIconByName(this.name)),t)this.iconContent=this.fixSVG(t);else{const t=this.innerHTML.trim();this.iconContent=t?this.fixSVG(t):Icon.fallback}}fixSVG(t){if(!t)return t;const e=(new DOMParser).parseFromString(t,"image/svg+xml").querySelector("svg");return e?(e.removeAttribute("width"),e.removeAttribute("height"),e.querySelectorAll("path, rect, circle").forEach(t=>{t.setAttribute("fill","currentColor")}),(new XMLSerializer).serializeToString(e)):t}static styles=css`
2
2
  :host {
3
3
  display: inline-block;
4
4
  vertical-align: bottom;
@@ -1,4 +1,11 @@
1
- import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComponent.js";export default class Toggle extends ShadowComponent{static properties={value:{type:Boolean,reflect:!0}};static styles=css`
1
+ import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComponent.js";export default class Toggle extends ShadowComponent{static properties={value:{type:Boolean,reflect:!0}};constructor(){super(),this.value=!1,this.tabIndex=0}updated(e){super.updated(e),e.has("value")&&this.dispatchEvent(new CustomEvent("change",{detail:{value:this.value},bubbles:!0}))}connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.handleClick),this.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("click",this.handleClick),this.removeEventListener("keydown",this.handleKeyDown)}handleClick=()=>{this.toggle()};handleKeyDown=e=>{["Space","Enter"].includes(e.code)&&(e.preventDefault(),this.toggle())};on(){return this.value=!0,this.dispatchEvent(new CustomEvent("on",{detail:{value:!0},bubbles:!0})),this}off(){return this.value=!1,this.dispatchEvent(new CustomEvent("off",{detail:{value:!1},bubbles:!0})),this}toggle(){return this.value?this.off():this.on(),this.dispatchEvent(new CustomEvent("toggle",{detail:{value:this.value},bubbles:!0})),this}render(){return html`
2
+ <div id="switch">
3
+ <div id="handle"></div>
4
+ </div>
5
+ <label id="label">
6
+ <slot></slot>
7
+ </label>
8
+ `}static styles=css`
2
9
  :host {
3
10
  --switch_height: 2rem;
4
11
  --switch_width: 3rem;
@@ -19,11 +26,6 @@ import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComp
19
26
  outline: none;
20
27
  }
21
28
 
22
- :host(:focus) {
23
- outline: 2px solid var(--c_primary, blue);
24
- outline-offset: 2px;
25
- }
26
-
27
29
  #switch {
28
30
  display: flex;
29
31
  align-items: center;
@@ -33,6 +35,12 @@ import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComp
33
35
  background: var(--switch_background__off);
34
36
  border-radius: 999rem;
35
37
  margin-right: var(--spacer);
38
+ box-shadow: 0 0 0 transparent;
39
+ will-change: box-shadow;
40
+ transition: box-shadow var(--animation_ms, 256ms);
41
+ }
42
+ :host(:focus) #switch {
43
+ box-shadow: var(--focus_shadow);
36
44
  }
37
45
 
38
46
  #handle {
@@ -64,11 +72,4 @@ import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComp
64
72
  flex: 1 1 auto;
65
73
  padding: 0;
66
74
  }
67
- `;constructor(){super(),this.value=!1,this.tabIndex=0}updated(e){super.updated(e),e.has("value")&&this.dispatchEvent(new CustomEvent("change",{detail:{value:this.value},bubbles:!0}))}connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.handleClick),this.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("click",this.handleClick),this.removeEventListener("keydown",this.handleKeyDown)}handleClick=()=>{this.toggle()};handleKeyDown=e=>{["Space","Enter"].includes(e.code)&&(e.preventDefault(),this.toggle())};on(){return this.value=!0,this.dispatchEvent(new CustomEvent("on",{detail:{value:!0},bubbles:!0})),this}off(){return this.value=!1,this.dispatchEvent(new CustomEvent("off",{detail:{value:!1},bubbles:!0})),this}toggle(){return this.value?this.off():this.on(),this.dispatchEvent(new CustomEvent("toggle",{detail:{value:this.value},bubbles:!0})),this}render(){return html`
68
- <div id="switch">
69
- <div id="handle"></div>
70
- </div>
71
- <label id="label">
72
- <slot></slot>
73
- </label>
74
- `}}window.customElements.define("k-toggle",Toggle);
75
+ `}window.customElements.define("k-toggle",Toggle);
@@ -6,12 +6,12 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,render}from"..
6
6
  <div class="tree-root">
7
7
  ${Tree.renderValue(this.data,null,0,this.depth)}
8
8
  </div>
9
- `:html`<slot></slot>`}static leafs=[];static addLeaf=(...e)=>{e.forEach(e=>Tree.leafs.unshift(e))};static renderValue(e,t=null,s=0,r=0){const a=Tree.leafs.find(t=>t.detect(e));if(a){const s=document.createElement("span");if(s.className="d-b",null!==t){const e=document.createElement("span");e.className="number"==typeof t?"tc-muted":"",e.textContent=`${t}: `,s.appendChild(e)}const r=new a(e).render();if(r instanceof Node)s.appendChild(r);else{const e=document.createElement("span");render(r,e),s.appendChild(e)}return s}if("object"==typeof e&&null!==e){const a=new TreeBranch;return a.value=e,a.key=t,a.currentDepth=s,a.maxDepth=r,a}return html`<span class="d-b primitive">${null!==t?html`<span class="${"number"==typeof t?"tc-muted":""}">${t}: </span>`:""}${e}</span>`}}window.customElements.define("k-tree",Tree);export class TreeBranch extends ShadowComponent{static properties={value:{type:Object},key:{type:String},currentDepth:{type:Number},maxDepth:{type:Number},opened:{type:Boolean,converter:boolExists,reflect:!0}};constructor(){super(),this.value=null,this.key=null,this.currentDepth=0,this.maxDepth=0,this.opened=!1}connectedCallback(){super.connectedCallback(),this.currentDepth<=this.maxDepth&&(this.opened=!0)}get tree(){return this.closest("k-tree")}toggle=()=>{this.opened=!this.opened};render(){const e=null!==this.key?`${this.key}: `:"",t=Array.isArray(this.value)?"Array":"Object";return html`
9
+ `:html`<slot></slot>`}static leafs=[];static addLeaf=(...e)=>{e.forEach(e=>Tree.leafs.unshift(e))};static renderValue(e,t=null,s=0,a=0){const r=Tree.leafs.find(t=>t.detect(e));if(r){const s=document.createElement("span");if(s.className="d-b",null!==t){const e=document.createElement("span");e.className="number"==typeof t?"tc-muted":"",e.textContent=`${t}: `,s.appendChild(e)}const a=new r(e).render();if(a instanceof Node)s.appendChild(a);else{const e=document.createElement("span");render(a,e),s.appendChild(e)}return s}if("object"==typeof e&&null!==e){const r=new TreeBranch;return r.value=e,r.key=t,r.currentDepth=s,r.maxDepth=a,r}return html`<span class="d-b primitive">${null!==t?html`<span class="${"number"==typeof t?"tc-muted":""}">${t}: </span>`:""}${e}</span>`}}window.customElements.define("k-tree",Tree);export class TreeBranch extends ShadowComponent{static properties={value:{type:Object},key:{type:String},currentDepth:{type:Number},maxDepth:{type:Number},opened:{type:Boolean,converter:boolExists,reflect:!0}};constructor(){super(),this.value=null,this.key=null,this.currentDepth=0,this.maxDepth=0,this.opened=!1}connectedCallback(){super.connectedCallback(),this.currentDepth<=this.maxDepth&&(this.opened=!0)}get tree(){return this.closest("k-tree")}toggle=()=>{this.opened=!this.opened};render(){const e=null!==this.key?`${this.key}: `:"",t=Array.isArray(this.value)?"Array":"Object";return html`
10
10
  <div>
11
- <div class="branch-label" @click=${this.toggle}>
11
+ <button class="branch-label no-btn" @click=${this.toggle} aria-expanded="${this.opened}">
12
12
  <k-icon name="chevron-right" class="toggle-icon ${this.opened?"opened":""}"></k-icon>
13
13
  ${e}${t}
14
- </div>
14
+ </button>
15
15
  ${this.opened?html`
16
16
  <div class="pl">
17
17
  ${this.value?(Array.isArray(this.value)?this.value.map((e,t)=>[t,e]):Object.entries(this.value)).map(([e,t])=>Tree.renderValue(t,e,this.currentDepth+1,this.maxDepth)):""}
@@ -23,11 +23,19 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,render}from"..
23
23
  display: block;
24
24
  }
25
25
  .branch-label{
26
+ display: block;
27
+ width: 100%;
26
28
  cursor: pointer;
27
29
  }
28
30
  .branch-label:hover{
29
31
  background-color: var(--c_bg__alt, #f5f5f5);
30
32
  }
33
+ .branch-label:focus{
34
+ box-shadow: none;
35
+ }
36
+ .branch-label:focus-visible{
37
+ box-shadow: var(--focus_shadow);
38
+ }
31
39
  .toggle-icon{
32
40
  transition: transform var(--animation_ms, 200ms);
33
41
  }
@@ -3,158 +3,69 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Accordion - Components - Kempo Docs - A Web Components Solution</title>
7
- <link rel="stylesheet" href="../src/kempo-vars.css" /><link rel="stylesheet" href="../kempo.min.css" />
8
- <link rel="stylesheet" href="../src/kempo-hljs.css" />
6
+ <title>watchWindowSize - Kempo UI</title>
7
+ <link rel="stylesheet" href="../kempo-vars.css" />
8
+ <link rel="stylesheet" href="../kempo.min.css" />
9
+ <link rel="stylesheet" href="../kempo-hljs.css" />
9
10
  <link rel="stylesheet" href="../styles.css" />
11
+ <script>window.litDisableBundleWarning = true;</script>
10
12
  <script type="module" src="./init.js"></script>
11
13
  </head>
12
14
  <body>
13
15
  <k-import src="../nav.inc.html"></k-import>
14
- <h1 class="ta-center">Accordion</h1>
15
16
  <main>
17
+ <h1>watchWindowSize</h1>
18
+
16
19
  <details
17
20
  class="b r mb"
18
21
  >
19
22
  <summary class="p">Table of Contents</summary>
20
23
  <div class="m mt0 pl">
21
- <h6>Examples</h6>
24
+ <h6>Usage</h6>
22
25
  <a href="#basicUsage">Basic Usage</a><br />
23
- <a href="#defaultOpen">Default Open Panel</a><br />
24
- <h6 class="mt">JavaScript Reference</h6>
25
- <a href="#constructor">Constructor</a><br />
26
- <a href="#requirements">Requirements</a><br />
27
- <a href="#properties">Properties</a><br />
28
- <a href="#methods">Methods</a><br />
29
- <a href="#events">Events</a><br />
26
+ <a href="#functions">Functions</a><br />
27
+ <a href="#examples">Examples</a><br />
30
28
  </div>
31
29
  </details>
32
30
 
33
31
  <h3>Description</h3>
34
- <p>A widget that allows a single panel of content to be opened and closed.</p>
32
+ <p>The <code>watchWindowSize</code> utility provides a debounced way to monitor window resize events. It manages multiple handlers efficiently and automatically debounces resize events to improve performance.</p>
35
33
 
36
34
  <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
37
- <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-header</span> <span class="hljs-attr">for-panel</span>=<span class="hljs-string">"panel1"</span>&gt;</span>Panel 1<span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-header</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-panel</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"panel1"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m"</span>&gt;</span>Panel 1 content<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-panel</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-header</span> <span class="hljs-attr">for-panel</span>=<span class="hljs-string">"panel2"</span>&gt;</span>Panel 2<span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-header</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-panel</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"panel2"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m"</span>&gt;</span>Panel 2 content<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-panel</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-header</span> <span class="hljs-attr">for-panel</span>=<span class="hljs-string">"panel3"</span>&gt;</span>Panel 3<span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-header</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-panel</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"panel3"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m"</span>&gt;</span>Panel 3 content<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-panel</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion</span>&gt;</span></code></pre>
38
- <k-accordion
39
- class="mb"
40
- >
41
- <k-accordion-header for-panel="panel1">Panel 1</k-accordion-header>
42
- <k-accordion-panel name="panel1">
43
- <p class="m">Panel 1 content</p>
44
- </k-accordion-panel>
45
- <k-accordion-header for-panel="panel2">Panel 2</k-accordion-header>
46
- <k-accordion-panel name="panel2">
47
- <p class="m">Panel 2 content</p>
48
- </k-accordion-panel>
49
- <k-accordion-header for-panel="panel3">Panel 3</k-accordion-header>
50
- <k-accordion-panel name="panel3">
51
- <p class="m">Panel 3 content</p>
52
- </k-accordion-panel>
53
- </k-accordion>
54
-
55
- <h3 id="defaultOpen"><a href="#defaultOpen" class="no-link">Default Open Panel</a></h3>
56
- <p>You can have a panel open by default by setting the <code>active</code> attribute to <code>true</code> on both the header and the panel.</p>
57
- <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-accordion</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-header</span> <span class="hljs-attr">for-panel</span>=<span class="hljs-string">"panel1"</span> <span class="hljs-attr">active</span>=<span class="hljs-string">"true"</span>&gt;</span>Panel 1<span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-header</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-panel</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"panel1"</span> <span class="hljs-attr">active</span>=<span class="hljs-string">"true"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m"</span>&gt;</span>Panel 1 content<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-panel</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-header</span> <span class="hljs-attr">for-panel</span>=<span class="hljs-string">"panel2"</span>&gt;</span>Panel 2<span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-header</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-panel</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"panel2"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m"</span>&gt;</span>Panel 2 content<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-panel</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-header</span> <span class="hljs-attr">for-panel</span>=<span class="hljs-string">"panel3"</span>&gt;</span>Panel 3<span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-header</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-accordion-panel</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"panel3"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m"</span>&gt;</span>Panel 3 content<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion-panel</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-accordion</span>&gt;</span></code></pre>
58
- <k-accordion
59
- class="mb"
60
- >
61
- <k-accordion-header for-panel="panel1" active="true">Panel 1</k-accordion-header>
62
- <k-accordion-panel name="panel1" active="true">
63
- <p class="m">Panel 1 content</p>
64
- </k-accordion-panel>
65
- <k-accordion-header for-panel="panel2">Panel 2</k-accordion-header>
66
- <k-accordion-panel name="panel2">
67
- <p class="m">Panel 2 content</p>
68
- </k-accordion-panel>
69
- <k-accordion-header for-panel="panel3">Panel 3</k-accordion-header>
70
- <k-accordion-panel name="panel3">
71
- <p class="m">Panel 3 content</p>
72
- </k-accordion-panel>
73
- </k-accordion>
74
-
75
- <h2 id="jsRef">JavaScript Reference</h2>
76
-
77
- <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
78
- <h6>Extends <a href="./component.html">ShadowComponent</a></h6>
79
- <h5>
80
- <code>new Accordion()</code><br />
81
- </h5>
82
-
83
- <h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
84
- <ul>
85
- <li><a href="./component.html">ShadowComponent</a></li>
86
- </ul>
87
-
88
- <h3 id="properties"><a href="#properties" class="no-link">Properties</a></h3>
89
-
90
- <h4>Accordion (Main Component)</h4>
91
- <h5><code>activeHeader<i>: HTMLElement | null</i></code></h5>
92
- <p>Returns the currently active header element, or <code>null</code> if no panel is open.</p>
93
- <h5><code>activePanel<i>: HTMLElement | null</i></code></h5>
94
- <p>Returns the currently active panel element, or <code>null</code> if no panel is open.</p>
35
+ <p>Import and use the window size watching functions:</p>
36
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> { watchWindowSize, unwatchWindowSize } <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/watchWindowSize.js'</span>;<br /><br /><span class="hljs-keyword">const</span> handler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Window width:'</span>, width);<br />};<br /><br /><span class="hljs-comment">// Start watching</span><br /><span class="hljs-keyword">const</span> currentWidth = watchWindowSize(handler);<br /><br /><span class="hljs-comment">// Stop watching when done</span><br />unwatchWindowSize(handler);</code></pre>
95
37
 
96
- <h4>AccordionHeader</h4>
97
- <h5><code>forPanel<i>: string</i></code></h5>
98
- <p>The panel name that this header controls. Syncs with the <code>for-panel</code> attribute.</p>
99
- <h5><code>active<i>: boolean</i></code></h5>
100
- <p>Whether this header is currently active (its associated panel is open). Syncs with the <code>active</code> attribute.</p>
101
- <h5><code>accordion<i>: Accordion | null</i></code></h5>
102
- <p>Returns the parent accordion component, or <code>null</code> if not inside an accordion.</p>
103
-
104
- <h4>AccordionPanel</h4>
105
- <h5><code>name<i>: string</i></code></h5>
106
- <p>Unique identifier for this panel. Must match the <code>for-panel</code> attribute of its associated header. Syncs with the <code>name</code> attribute.</p>
107
- <h5><code>active<i>: boolean</i></code></h5>
108
- <p>Whether this panel is currently open. Syncs with the <code>active</code> attribute.</p>
109
- <h5><code>transitioning<i>: boolean</i></code></h5>
110
- <p>Whether this panel is currently animating (opening or closing). Syncs with the <code>transitioning</code> attribute.</p>
111
- <h5><code>accordion<i>: Accordion | null</i></code></h5>
112
- <p>Returns the parent accordion component, or <code>null</code> if not inside an accordion.</p>
113
-
114
- <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
38
+ <h3 id="functions"><a href="#functions" class="no-link">Functions</a></h3>
115
39
 
116
- <h4>Accordion (Main Component)</h4>
117
- <h5><code>getHeader(panelName)<i>: HTMLElement | null</i></code></h5>
118
- <p>Returns the header element associated with the specified panel name, or <code>null</code> if not found.</p>
40
+ <h5><code>watchWindowSize(handler)</code></h5>
41
+ <p>Registers a handler function to be called when the window is resized.</p>
119
42
  <ul>
120
- <li><strong>panelName</strong> <code>string</code> - The name of the panel whose header to retrieve</li>
43
+ <li><strong>handler:</strong> Function that receives the new window width</li>
44
+ <li><strong>Returns:</strong> Current window width</li>
121
45
  </ul>
122
46
 
123
- <h5><code>getPanel(panelName)<i>: HTMLElement | null</i></code></h5>
124
- <p>Returns the panel element with the specified name, or <code>null</code> if not found.</p>
47
+ <h5><code>unwatchWindowSize(handler)</code></h5>
48
+ <p>Unregisters a previously registered handler function.</p>
125
49
  <ul>
126
- <li><strong>panelName</strong> <code>string</code> - The name of the panel to retrieve</li>
50
+ <li><strong>handler:</strong> The same function reference that was registered</li>
127
51
  </ul>
128
52
 
129
- <h5><code>openPanel(panelName)<i>: void</i></code></h5>
130
- <p>Opens the panel with the specified name. Closes any currently open panel first. Dispatches an <code>openpanel</code> event.</p>
131
- <ul>
132
- <li><strong>panelName</strong> <code>string</code> - The name of the panel to open</li>
133
- </ul>
53
+ <h3 id="examples"><a href="#examples" class="no-link">Examples</a></h3>
54
+
55
+ <h4>Responsive Component</h4>
56
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> { watchWindowSize, unwatchWindowSize } <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/watchWindowSize.js'</span>;<br /><br /><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ResponsiveComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">LitElement</span> </span>{<br /> <span class="hljs-keyword">constructor</span>() {<br /> <span class="hljs-keyword">super</span>();<br /> <span class="hljs-keyword">this</span>.isMobile = <span class="hljs-literal">false</span>;<br /> <span class="hljs-keyword">this</span>.resizeHandler = <span class="hljs-keyword">this</span>.handleResize.bind(<span class="hljs-keyword">this</span>);<br /> }<br /><br /> connectedCallback() {<br /> <span class="hljs-keyword">super</span>.connectedCallback();<br /> <span class="hljs-keyword">const</span> currentWidth = watchWindowSize(<span class="hljs-keyword">this</span>.resizeHandler);<br /> <span class="hljs-keyword">this</span>.handleResize(currentWidth);<br /> }<br /><br /> disconnectedCallback() {<br /> <span class="hljs-keyword">super</span>.disconnectedCallback();<br /> unwatchWindowSize(<span class="hljs-keyword">this</span>.resizeHandler);<br /> }<br /><br /> handleResize(width) {<br /> <span class="hljs-keyword">this</span>.isMobile = width &lt; <span class="hljs-number">768</span>;<br /> <span class="hljs-keyword">this</span>.requestUpdate();<br /> }<br /><br /> render() {<br /> <span class="hljs-keyword">return</span> html`<span class="xml"><br /> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"</span></span></span><span class="hljs-subst">${<span class="hljs-keyword">this</span>.isMobile ? <span class="hljs-string">'mobile'</span> : <span class="hljs-string">'desktop'</span>}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span><br /> Content adapts to screen size<br /> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br /> `</span>;<br /> }<br />}</code></pre>
134
57
 
135
- <h5><code>closePanel(panelName)<i>: void</i></code></h5>
136
- <p>Closes the panel with the specified name. Dispatches a <code>closepanel</code> event.</p>
137
- <ul>
138
- <li><strong>panelName</strong> <code>string</code> - The name of the panel to close</li>
139
- </ul>
58
+ <h4>Multiple Handlers</h4>
59
+ <pre><code class="hljs javascript"><span class="hljs-comment">// Handler for navigation</span><br /><span class="hljs-keyword">const</span> navHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-keyword">const</span> nav = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.navigation'</span>);<br /> nav.classList.toggle(<span class="hljs-string">'mobile'</span>, width &lt; <span class="hljs-number">1024</span>);<br />};<br /><br /><span class="hljs-comment">// Handler for sidebar</span><br /><span class="hljs-keyword">const</span> sidebarHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-keyword">const</span> sidebar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.sidebar'</span>);<br /> sidebar.style.display = width &lt; <span class="hljs-number">768</span> ? <span class="hljs-string">'none'</span> : <span class="hljs-string">'block'</span>;<br />};<br /><br /><span class="hljs-comment">// Both handlers will be called on resize</span><br />watchWindowSize(navHandler);<br />watchWindowSize(sidebarHandler);<br /><br /><span class="hljs-comment">// Clean up when needed</span><br />unwatchWindowSize(navHandler);<br />unwatchWindowSize(sidebarHandler);</code></pre>
140
60
 
141
- <h5><code>togglePanel(panelName)<i>: void</i></code></h5>
142
- <p>Toggles the panel with the specified name (opens if closed, closes if open). Dispatches a <code>togglepanel</code> event.</p>
143
- <ul>
144
- <li><strong>panelName</strong> <code>string</code> - The name of the panel to toggle</li>
145
- </ul>
61
+ <h4>Breakpoint Detection</h4>
62
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> breakpoints = {<br /> <span class="hljs-attr">mobile</span>: <span class="hljs-number">768</span>,<br /> <span class="hljs-attr">tablet</span>: <span class="hljs-number">1024</span>,<br /> <span class="hljs-attr">desktop</span>: <span class="hljs-number">1200</span><br />};<br /><br /><span class="hljs-keyword">const</span> breakpointHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-keyword">let</span> currentBreakpoint = <span class="hljs-string">'mobile'</span>;<br /> <br /> <span class="hljs-keyword">if</span> (width &gt;= breakpoints.desktop) {<br /> currentBreakpoint = <span class="hljs-string">'desktop'</span>;<br /> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (width &gt;= breakpoints.tablet) {<br /> currentBreakpoint = <span class="hljs-string">'tablet'</span>;<br /> }<br /> <br /> <span class="hljs-built_in">document</span>.body.setAttribute(<span class="hljs-string">'data-breakpoint'</span>, currentBreakpoint);<br /> <br /> <span class="hljs-comment">// Dispatch custom event</span><br /> <span class="hljs-built_in">window</span>.dispatchEvent(<span class="hljs-keyword">new</span> CustomEvent(<span class="hljs-string">'breakpointchange'</span>, {<br /> <span class="hljs-attr">detail</span>: { width, <span class="hljs-attr">breakpoint</span>: currentBreakpoint }<br /> }));<br />};<br /><br />watchWindowSize(breakpointHandler);</code></pre>
63
+
64
+ <h4>Performance Benefits</h4>
65
+ <p>The utility automatically debounces resize events, so you don't need to worry about performance:</p>
66
+ <pre><code class="hljs javascript"><span class="hljs-comment">// This handler will only be called after resize events stop</span><br /><span class="hljs-keyword">const</span> expensiveHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-comment">// Expensive DOM calculations or API calls</span><br /> recalculateLayout();<br /> updateCharts();<br /> reflow();<br />};<br /><br />watchWindowSize(expensiveHandler); <span class="hljs-comment">// Automatically debounced</span></code></pre>
146
67
 
147
- <h3 id="events"><a href="#events" class="no-link">Events</a></h3>
148
- <h5><code>openpanel</code></h5>
149
- <p>Fired when a panel is opened. The event detail contains <code>{ panelName }</code>.</p>
150
- <h5><code>closepanel</code></h5>
151
- <p>Fired when a panel is closed. The event detail contains <code>{ panelName }</code>.</p>
152
- <h5><code>togglepanel</code></h5>
153
- <p>Fired when a panel is toggled. The event detail contains <code>{ panelName }</code>.</p>
154
-
155
68
  </main>
156
69
  <div style="height:33vh"></div>
157
- <script type="module" src="../src/components/Import.js"></script>
158
- <script type="module" src="../src/components/Accordion.js"></script>
159
70
  </body>
160
- </html>
71
+ </html>
@@ -3,84 +3,69 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Card - Components - Kempo Docs - A Web Components Solution</title>
7
- <link rel="stylesheet" href="../src/kempo-vars.css" /><link rel="stylesheet" href="../kempo.min.css" />
8
- <link rel="stylesheet" href="../src/kempo-hljs.css" />
6
+ <title>watchWindowSize - Kempo UI</title>
7
+ <link rel="stylesheet" href="../kempo-vars.css" />
8
+ <link rel="stylesheet" href="../kempo.min.css" />
9
+ <link rel="stylesheet" href="../kempo-hljs.css" />
9
10
  <link rel="stylesheet" href="../styles.css" />
11
+ <script>window.litDisableBundleWarning = true;</script>
10
12
  <script type="module" src="./init.js"></script>
11
13
  </head>
12
14
  <body>
13
15
  <k-import src="../nav.inc.html"></k-import>
14
- <h1 class="ta-center">Card</h1>
15
16
  <main>
16
- <details class="b r mb">
17
+ <h1>watchWindowSize</h1>
18
+
19
+ <details
20
+ class="b r mb"
21
+ >
17
22
  <summary class="p">Table of Contents</summary>
18
23
  <div class="m mt0 pl">
19
- <h6>Examples</h6>
24
+ <h6>Usage</h6>
20
25
  <a href="#basicUsage">Basic Usage</a><br />
21
-
22
- <h6 class="mt">JavaScript Reference</h6>
23
- <a href="#constructor">Constructor</a><br />
24
- <a href="#requirements">Requirements</a><br />
25
- <a href="#properties">Properties</a><br />
26
- <a href="#methods">Methods</a><br />
26
+ <a href="#functions">Functions</a><br />
27
+ <a href="#examples">Examples</a><br />
27
28
  </div>
28
29
  </details>
29
30
 
30
- <p>A card is a "box" around some content with a border, padding and margins that make it stand out.</p>
31
- <div class="row -mx mb">
32
- <div class="col d-span-6 m-span-12 px">
33
- <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-card</span>&gt;</span><br /> This is a card<br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-card</span>&gt;</span></code></pre>
34
- </div>
35
- <div class="col d-span-6 m-span-12 px">
36
- <k-card>
37
- This is a card
38
- </k-card>
39
- </div>
40
- </div>
41
-
42
- <h3 id="labels"><a href="#labels" class="no-link">Labels</a></h3>
43
- <p>A label gives some text in a box on the top border of the card, helping identify the purpose of the contents.</p>
44
- <div class="row -mx mb">
45
- <div class="col d-span-6 m-span-12 px">
46
- <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-card</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"I'm a Label"</span>&gt;</span><br /> This is a card<br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-card</span>&gt;</span></code></pre>
47
- </div>
48
- <div class="col d-span-6 m-span-12 px">
49
- <k-card label="I'm a Label">
50
- This is a card
51
- </k-card>
52
- </div>
53
- </div>
31
+ <h3>Description</h3>
32
+ <p>The <code>watchWindowSize</code> utility provides a debounced way to monitor window resize events. It manages multiple handlers efficiently and automatically debounces resize events to improve performance.</p>
54
33
 
55
- <h2 id="jsRef">JavaScript Reference</h2>
34
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
35
+ <p>Import and use the window size watching functions:</p>
36
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> { watchWindowSize, unwatchWindowSize } <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/watchWindowSize.js'</span>;<br /><br /><span class="hljs-keyword">const</span> handler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Window width:'</span>, width);<br />};<br /><br /><span class="hljs-comment">// Start watching</span><br /><span class="hljs-keyword">const</span> currentWidth = watchWindowSize(handler);<br /><br /><span class="hljs-comment">// Stop watching when done</span><br />unwatchWindowSize(handler);</code></pre>
56
37
 
57
- <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
58
- <h6>Extends <a href="./shadow-component.html">ShadowComponent</a></h6>
59
- <h5>
60
- <code>new Card()</code><br />
61
- <code>new Card(<i>string</i> label)</code>
62
- </h5>
63
-
64
- <h4>Parameters</h4>
65
- <h5><code>label<i>: string</i></code></h5>
66
- <p>The label for the card.</p>
38
+ <h3 id="functions"><a href="#functions" class="no-link">Functions</a></h3>
39
+
40
+ <h5><code>watchWindowSize(handler)</code></h5>
41
+ <p>Registers a handler function to be called when the window is resized.</p>
42
+ <ul>
43
+ <li><strong>handler:</strong> Function that receives the new window width</li>
44
+ <li><strong>Returns:</strong> Current window width</li>
45
+ </ul>
67
46
 
68
- <h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
47
+ <h5><code>unwatchWindowSize(handler)</code></h5>
48
+ <p>Unregisters a previously registered handler function.</p>
69
49
  <ul>
70
- <li><a href="./shadow-component.html">ShadowComponent</a></li>
50
+ <li><strong>handler:</strong> The same function reference that was registered</li>
71
51
  </ul>
72
52
 
73
- <h3 id="properties"><a href="#properties" class="no-link">Properties</a></h3>
74
- <h5><code>label<i>: string</i></code></h5>
75
- <p>The label for the card. Syncs with the <code>label</code> attribute.</p>
53
+ <h3 id="examples"><a href="#examples" class="no-link">Examples</a></h3>
54
+
55
+ <h4>Responsive Component</h4>
56
+ <pre><code class="hljs javascript"><span class="hljs-keyword">import</span> { watchWindowSize, unwatchWindowSize } <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/watchWindowSize.js'</span>;<br /><br /><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ResponsiveComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">LitElement</span> </span>{<br /> <span class="hljs-keyword">constructor</span>() {<br /> <span class="hljs-keyword">super</span>();<br /> <span class="hljs-keyword">this</span>.isMobile = <span class="hljs-literal">false</span>;<br /> <span class="hljs-keyword">this</span>.resizeHandler = <span class="hljs-keyword">this</span>.handleResize.bind(<span class="hljs-keyword">this</span>);<br /> }<br /><br /> connectedCallback() {<br /> <span class="hljs-keyword">super</span>.connectedCallback();<br /> <span class="hljs-keyword">const</span> currentWidth = watchWindowSize(<span class="hljs-keyword">this</span>.resizeHandler);<br /> <span class="hljs-keyword">this</span>.handleResize(currentWidth);<br /> }<br /><br /> disconnectedCallback() {<br /> <span class="hljs-keyword">super</span>.disconnectedCallback();<br /> unwatchWindowSize(<span class="hljs-keyword">this</span>.resizeHandler);<br /> }<br /><br /> handleResize(width) {<br /> <span class="hljs-keyword">this</span>.isMobile = width &lt; <span class="hljs-number">768</span>;<br /> <span class="hljs-keyword">this</span>.requestUpdate();<br /> }<br /><br /> render() {<br /> <span class="hljs-keyword">return</span> html`<span class="xml"><br /> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"</span></span></span><span class="hljs-subst">${<span class="hljs-keyword">this</span>.isMobile ? <span class="hljs-string">'mobile'</span> : <span class="hljs-string">'desktop'</span>}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span><br /> Content adapts to screen size<br /> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br /> `</span>;<br /> }<br />}</code></pre>
57
+
58
+ <h4>Multiple Handlers</h4>
59
+ <pre><code class="hljs javascript"><span class="hljs-comment">// Handler for navigation</span><br /><span class="hljs-keyword">const</span> navHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-keyword">const</span> nav = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.navigation'</span>);<br /> nav.classList.toggle(<span class="hljs-string">'mobile'</span>, width &lt; <span class="hljs-number">1024</span>);<br />};<br /><br /><span class="hljs-comment">// Handler for sidebar</span><br /><span class="hljs-keyword">const</span> sidebarHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-keyword">const</span> sidebar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.sidebar'</span>);<br /> sidebar.style.display = width &lt; <span class="hljs-number">768</span> ? <span class="hljs-string">'none'</span> : <span class="hljs-string">'block'</span>;<br />};<br /><br /><span class="hljs-comment">// Both handlers will be called on resize</span><br />watchWindowSize(navHandler);<br />watchWindowSize(sidebarHandler);<br /><br /><span class="hljs-comment">// Clean up when needed</span><br />unwatchWindowSize(navHandler);<br />unwatchWindowSize(sidebarHandler);</code></pre>
60
+
61
+ <h4>Breakpoint Detection</h4>
62
+ <pre><code class="hljs javascript"><span class="hljs-keyword">const</span> breakpoints = {<br /> <span class="hljs-attr">mobile</span>: <span class="hljs-number">768</span>,<br /> <span class="hljs-attr">tablet</span>: <span class="hljs-number">1024</span>,<br /> <span class="hljs-attr">desktop</span>: <span class="hljs-number">1200</span><br />};<br /><br /><span class="hljs-keyword">const</span> breakpointHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-keyword">let</span> currentBreakpoint = <span class="hljs-string">'mobile'</span>;<br /> <br /> <span class="hljs-keyword">if</span> (width &gt;= breakpoints.desktop) {<br /> currentBreakpoint = <span class="hljs-string">'desktop'</span>;<br /> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (width &gt;= breakpoints.tablet) {<br /> currentBreakpoint = <span class="hljs-string">'tablet'</span>;<br /> }<br /> <br /> <span class="hljs-built_in">document</span>.body.setAttribute(<span class="hljs-string">'data-breakpoint'</span>, currentBreakpoint);<br /> <br /> <span class="hljs-comment">// Dispatch custom event</span><br /> <span class="hljs-built_in">window</span>.dispatchEvent(<span class="hljs-keyword">new</span> CustomEvent(<span class="hljs-string">'breakpointchange'</span>, {<br /> <span class="hljs-attr">detail</span>: { width, <span class="hljs-attr">breakpoint</span>: currentBreakpoint }<br /> }));<br />};<br /><br />watchWindowSize(breakpointHandler);</code></pre>
76
63
 
77
- <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
78
- <h5><code>setLabel(<i>string</i> label)<i>: void</i></code></h5>
79
- <p>Sets the label for the card.</p>
64
+ <h4>Performance Benefits</h4>
65
+ <p>The utility automatically debounces resize events, so you don't need to worry about performance:</p>
66
+ <pre><code class="hljs javascript"><span class="hljs-comment">// This handler will only be called after resize events stop</span><br /><span class="hljs-keyword">const</span> expensiveHandler = <span class="hljs-function">(<span class="hljs-params">width</span>) =&gt;</span> {<br /> <span class="hljs-comment">// Expensive DOM calculations or API calls</span><br /> recalculateLayout();<br /> updateCharts();<br /> reflow();<br />};<br /><br />watchWindowSize(expensiveHandler); <span class="hljs-comment">// Automatically debounced</span></code></pre>
80
67
 
81
68
  </main>
82
69
  <div style="height:33vh"></div>
83
- <script type="module" src="../src/components/Import.js"></script>
84
- <script type="module" src="../src/components/Card.js"></script>
85
70
  </body>
86
- </html>
71
+ </html>