kempo-ui 0.0.62 → 0.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,13 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class Main extends ShadowComponent{static properties={panelWidth:{type:String,state:!0},panelSide:{type:String,state:!0}};constructor(){super(),this.panelWidth="0px",this.panelSide="left",this.handlePanelChange=this.handlePanelChange.bind(this)}connectedCallback(){super.connectedCallback(),window.addEventListener("side-panel-change",this.handlePanelChange);const e=document.querySelector("k-side-panel");e&&(this.panelWidth=e.collapsed?"3.5rem":"16rem",this.panelSide=e.side||"left")}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("side-panel-change",this.handlePanelChange)}handlePanelChange(e){this.panelWidth=e.detail.width,this.panelSide=e.detail.side}render(){return html`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class Main extends ShadowComponent{static properties={leftPanelWidth:{type:String,state:!0},rightPanelWidth:{type:String,state:!0}};constructor(){super(),this.leftPanelWidth="0px",this.rightPanelWidth="0px",this.handlePanelChange=this.handlePanelChange.bind(this)}connectedCallback(){super.connectedCallback(),window.addEventListener("side-panel-change",this.handlePanelChange);const t=document.querySelector('k-side-panel:not([side="right"])');t&&(this.leftPanelWidth=t.collapsed?"3.5rem":"16rem");const e=document.querySelector('k-side-panel[side="right"]');e&&(this.rightPanelWidth=e.collapsed?"3.5rem":"16rem")}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("side-panel-change",this.handlePanelChange)}handlePanelChange(t){"right"===t.detail.side?this.rightPanelWidth=t.detail.width:this.leftPanelWidth=t.detail.width}render(){return html`
2
2
  <main>
3
3
  <slot></slot>
4
4
  </main>
5
5
  `}static styles=css`
6
6
  :host {
7
7
  display: block;
8
- margin-left: var(--panel-width, 0px);
9
- transition: margin-left var(--animation_ms, 256ms);
10
- }
11
- :host([panel-side="right"]) {
12
- margin-left: 0;
13
- margin-right: var(--panel-width, 0px);
14
- transition: margin-right var(--animation_ms, 256ms);
8
+ margin-left: var(--left-panel-width, 0px);
9
+ margin-right: var(--right-panel-width, 0px);
10
+ transition: margin-left var(--animation_ms, 256ms), margin-right var(--animation_ms, 256ms);
15
11
  }
16
12
  main {
17
13
  max-width: var(--container_width, 90rem);
@@ -21,4 +17,4 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
21
17
  padding-left: var(--spacer);
22
18
  padding-right: var(--spacer);
23
19
  }
24
- `;updated(){super.updated(),this.style.setProperty("--panel-width",this.panelWidth),"right"===this.panelSide?this.setAttribute("panel-side","right"):this.removeAttribute("panel-side")}}window.customElements.define("k-main",Main);
20
+ `;updated(){super.updated(),this.style.setProperty("--left-panel-width",this.leftPanelWidth),this.style.setProperty("--right-panel-width",this.rightPanelWidth)}}window.customElements.define("k-main",Main);
@@ -2,7 +2,7 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
2
2
  <div id="header">
3
3
  <slot name="logo"></slot>
4
4
  <button id="toggle" @click=${this.toggleClick} aria-label="${this.collapsed?"Expand panel":"Collapse panel"}">
5
- <k-icon name="arrow-line" direction="${this.collapsed?"right":"left"}"></k-icon>
5
+ <k-icon name="arrow-line" direction="${(()=>"right"===this.side?this.collapsed?"left":"right":this.collapsed?"right":"left")()}"></k-icon>
6
6
  </button>
7
7
  </div>
8
8
  <div id="content">
@@ -13,7 +13,8 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
13
13
  --bg: var(--c_bg);
14
14
  --width-expanded: 16rem;
15
15
  --transition-duration: var(--animation_ms, 256ms);
16
- display: block;
16
+ display: flex;
17
+ flex-direction: column;
17
18
  position: fixed;
18
19
  top: 0;
19
20
  left: 0;
@@ -40,11 +41,10 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
40
41
  gap: var(--spacer_h);
41
42
  padding: var(--spacer_h);
42
43
  border-bottom: 1px solid var(--c_border);
44
+ flex-shrink: 0;
43
45
  }
44
46
  ::slotted([slot="logo"]) {
45
47
  margin-right: auto;
46
- }
47
- ::slotted([slot="logo"]) {
48
48
  flex: 1;
49
49
  min-width: 0;
50
50
  opacity: 1;
@@ -70,7 +70,8 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
70
70
  background: var(--c_bg_hover);
71
71
  }
72
72
  #content {
73
- height: calc(100% - 3rem);
73
+ flex: 1;
74
+ min-height: 0;
74
75
  overflow-y: auto;
75
76
  overflow-x: hidden;
76
77
  display: flex;
@@ -23,7 +23,7 @@
23
23
  <body>
24
24
  <!-- Left Side Panel -->
25
25
  <k-side-panel>
26
- <h3 slot="logo">Kempo</h3>
26
+ <h3 slot="logo" class="m0">Kempo</h3>
27
27
  <k-side-panel-item icon="cards" href="#" active>Dashboard</k-side-panel-item>
28
28
  <k-side-panel-item icon="check" href="#">Tasks</k-side-panel-item>
29
29
  <k-side-panel-item icon="label" href="#">Email</k-side-panel-item>
@@ -37,7 +37,7 @@
37
37
 
38
38
  <!-- Right Side Panel -->
39
39
  <k-side-panel side="right">
40
- <h3 slot="logo">Tools</h3>
40
+ <h3 slot="logo" class="m0">Tools</h3>
41
41
  <k-side-panel-item icon="notification" href="#">Notifications</k-side-panel-item>
42
42
  <k-side-panel-item icon="chat" href="#">Messages</k-side-panel-item>
43
43
  <k-side-panel-label>Recent</k-side-panel-label>
@@ -0,0 +1,90 @@
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>SidePanel Persistent State Example</title>
7
+ <link rel="stylesheet" href="../kempo-vars.css" />
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kempo-css@1.3.2/dist/kempo.min.css" />
9
+ <script>window.litDisableBundleWarning = true;</script>
10
+ <script type="module">
11
+ import theme from '../src/utils/theme.js';
12
+ theme.init();
13
+ </script>
14
+ <script type="module" src="../src/components/SidePanel.js"></script>
15
+ <script type="module" src="../src/components/Main.js"></script>
16
+ <style>
17
+ body {
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+ </style>
22
+ </head>
23
+ <body>
24
+ <k-side-panel persistent-id="demo-panel">
25
+ <h3 slot="logo" class="m0">My App</h3>
26
+ <k-side-panel-item icon="cards" href="#" active>Dashboard</k-side-panel-item>
27
+ <k-side-panel-item icon="check" href="#">Tasks</k-side-panel-item>
28
+ <k-side-panel-item icon="label" href="#">Email</k-side-panel-item>
29
+ <k-side-panel-label>Projects</k-side-panel-label>
30
+ <k-side-panel-item icon="folder" href="#">Website Redesign</k-side-panel-item>
31
+ <k-side-panel-item icon="folder" href="#">Mobile App</k-side-panel-item>
32
+ <k-side-panel-spacer></k-side-panel-spacer>
33
+ <k-side-panel-item icon="settings" href="#">Settings</k-side-panel-item>
34
+ <k-side-panel-item icon="account-circle" href="#">Profile</k-side-panel-item>
35
+ </k-side-panel>
36
+
37
+ <k-main>
38
+ <h1>Persistent State Demo</h1>
39
+ <p>This side panel has <code>persistent-id="demo-panel"</code> set, which means it saves its collapsed/expanded state to localStorage.</p>
40
+
41
+ <h2>Try It Out</h2>
42
+ <ol>
43
+ <li>Click the toggle arrow to collapse or expand the side panel</li>
44
+ <li>Refresh the page (or this iframe)</li>
45
+ <li>Notice the panel returns to the same state you left it in</li>
46
+ </ol>
47
+
48
+ <h2>How It Works</h2>
49
+ <p>When you set the <code>persistent-id</code> attribute, the component:</p>
50
+ <ul>
51
+ <li>Saves the collapsed state to <code>localStorage</code> whenever it changes</li>
52
+ <li>Restores the saved state when the component mounts</li>
53
+ <li>Uses the key format: <code>side-panel-persistent-id-{persistentId}</code></li>
54
+ </ul>
55
+
56
+ <p>This is useful for preserving user preferences across sessions, making your application feel more responsive and personalized.</p>
57
+
58
+ <div style="margin-top: 2rem; padding: 1rem; background: var(--c_bg__alt); border-radius: var(--radius);">
59
+ <h3>Technical Details</h3>
60
+ <p><strong>LocalStorage Key:</strong> <code>side-panel-persistent-id-demo-panel</code></p>
61
+ <p><strong>Stored Value:</strong> <code id="stored-value">Loading...</code></p>
62
+ <button onclick="clearStorage()" class="btn">Clear Stored State</button>
63
+ </div>
64
+ </k-main>
65
+
66
+ <script>
67
+ function updateStoredValue() {
68
+ const value = localStorage.getItem('side-panel-persistent-id-demo-panel');
69
+ document.getElementById('stored-value').textContent = value || 'null (not set)';
70
+ }
71
+
72
+ function clearStorage() {
73
+ localStorage.removeItem('side-panel-persistent-id-demo-panel');
74
+ updateStoredValue();
75
+ window.location.reload();
76
+ }
77
+
78
+ // Update on load
79
+ updateStoredValue();
80
+
81
+ // Update when storage changes
82
+ window.addEventListener('storage', updateStoredValue);
83
+
84
+ // Update when panel changes
85
+ window.addEventListener('side-panel-change', () => {
86
+ setTimeout(updateStoredValue, 100);
87
+ });
88
+ </script>
89
+ </body>
90
+ </html>
@@ -25,6 +25,7 @@
25
25
  <a href="#withMenu">With Menu</a><br />
26
26
  <a href="#independentScrolling">Independent Scrolling</a><br />
27
27
  <a href="#collapsedDefault">Collapsed by Default</a><br />
28
+ <a href="#persistent">Persistent State</a><br />
28
29
  <a href="#rightSidePanel">Right Side Panel</a><br />
29
30
  <a href="#dualPanels">Dual Side Panels</a><br />
30
31
  <a href="#customStyling">Custom Styling</a><br />
@@ -82,6 +83,22 @@
82
83
  </div>
83
84
  </div>
84
85
 
86
+ <h3 id="persistent"><a href="#persistent" class="no-link">Persistent State</a></h3>
87
+ <p>Use the <code>persistent-id</code> attribute to save the panel's collapsed/expanded state to localStorage. When the page is refreshed, the panel will remember its previous state.</p>
88
+ <div class="row -mx">
89
+ <div class="col m-span-12 px">
90
+ <k-card label="HTML">
91
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-side-panel</span> <span class="hljs-attr">persistent-id</span>=<span class="hljs-string">"main-nav"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"logo"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m0"</span>&gt;</span>My App<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-side-panel-item</span> <span class="hljs-attr">icon</span>=<span class="hljs-string">"cards"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">active</span>&gt;</span>Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">k-side-panel-item</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-side-panel-item</span> <span class="hljs-attr">icon</span>=<span class="hljs-string">"check"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Tasks<span class="hljs-tag">&lt;/<span class="hljs-name">k-side-panel-item</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-side-panel-item</span> <span class="hljs-attr">icon</span>=<span class="hljs-string">"settings"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Settings<span class="hljs-tag">&lt;/<span class="hljs-name">k-side-panel-item</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-side-panel</span>&gt;</span></code></pre>
92
+ </k-card>
93
+ </div>
94
+ <div class="col m-span-12 px">
95
+ <k-card label="Output">
96
+ <p class="info-box">Try collapsing/expanding the panel, then refresh this page. The panel will remember its state!</p>
97
+ <iframe src="./side-panel-persistent.html" style="width: 100%; height: 400px; border: 1px solid var(--c_border); border-radius: var(--radius);"></iframe>
98
+ </k-card>
99
+ </div>
100
+ </div>
101
+
85
102
  <h3 id="rightSidePanel"><a href="#rightSidePanel" class="no-link">Right Side Panel</a></h3>
86
103
  <p>Set the <code>side</code> attribute to <code>"right"</code> to position the panel on the right side of the page.</p>
87
104
  <div class="row -mx">
@@ -146,6 +163,8 @@
146
163
  <p>Whether the side panel is collapsed to icon-only view. Defaults to <code>false</code>. Syncs to <code>collapsed</code> attribute.</p>
147
164
  <h5><code>side<i>: string</i></code></h5>
148
165
  <p>The side where the panel is positioned. Can be <code>"left"</code> or <code>"right"</code>. Defaults to <code>"left"</code>. Syncs to <code>side</code> attribute.</p>
166
+ <h5><code>persistentId<i>: string</i></code></h5>
167
+ <p>A unique identifier for saving the panel's collapsed/expanded state to localStorage. When set, the panel will remember its state across page refreshes. Syncs to <code>persistent-id</code> attribute.</p>
149
168
 
150
169
  <h3 id="events"><a href="#events" class="no-link">Events</a></h3>
151
170
  <h5><code>expand</code></h5>
@@ -1,17 +1,13 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class Main extends ShadowComponent{static properties={panelWidth:{type:String,state:!0},panelSide:{type:String,state:!0}};constructor(){super(),this.panelWidth="0px",this.panelSide="left",this.handlePanelChange=this.handlePanelChange.bind(this)}connectedCallback(){super.connectedCallback(),window.addEventListener("side-panel-change",this.handlePanelChange);const e=document.querySelector("k-side-panel");e&&(this.panelWidth=e.collapsed?"3.5rem":"16rem",this.panelSide=e.side||"left")}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("side-panel-change",this.handlePanelChange)}handlePanelChange(e){this.panelWidth=e.detail.width,this.panelSide=e.detail.side}render(){return html`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class Main extends ShadowComponent{static properties={leftPanelWidth:{type:String,state:!0},rightPanelWidth:{type:String,state:!0}};constructor(){super(),this.leftPanelWidth="0px",this.rightPanelWidth="0px",this.handlePanelChange=this.handlePanelChange.bind(this)}connectedCallback(){super.connectedCallback(),window.addEventListener("side-panel-change",this.handlePanelChange);const t=document.querySelector('k-side-panel:not([side="right"])');t&&(this.leftPanelWidth=t.collapsed?"3.5rem":"16rem");const e=document.querySelector('k-side-panel[side="right"]');e&&(this.rightPanelWidth=e.collapsed?"3.5rem":"16rem")}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("side-panel-change",this.handlePanelChange)}handlePanelChange(t){"right"===t.detail.side?this.rightPanelWidth=t.detail.width:this.leftPanelWidth=t.detail.width}render(){return html`
2
2
  <main>
3
3
  <slot></slot>
4
4
  </main>
5
5
  `}static styles=css`
6
6
  :host {
7
7
  display: block;
8
- margin-left: var(--panel-width, 0px);
9
- transition: margin-left var(--animation_ms, 256ms);
10
- }
11
- :host([panel-side="right"]) {
12
- margin-left: 0;
13
- margin-right: var(--panel-width, 0px);
14
- transition: margin-right var(--animation_ms, 256ms);
8
+ margin-left: var(--left-panel-width, 0px);
9
+ margin-right: var(--right-panel-width, 0px);
10
+ transition: margin-left var(--animation_ms, 256ms), margin-right var(--animation_ms, 256ms);
15
11
  }
16
12
  main {
17
13
  max-width: var(--container_width, 90rem);
@@ -21,4 +17,4 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
21
17
  padding-left: var(--spacer);
22
18
  padding-right: var(--spacer);
23
19
  }
24
- `;updated(){super.updated(),this.style.setProperty("--panel-width",this.panelWidth),"right"===this.panelSide?this.setAttribute("panel-side","right"):this.removeAttribute("panel-side")}}window.customElements.define("k-main",Main);
20
+ `;updated(){super.updated(),this.style.setProperty("--left-panel-width",this.leftPanelWidth),this.style.setProperty("--right-panel-width",this.rightPanelWidth)}}window.customElements.define("k-main",Main);
@@ -2,7 +2,7 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
2
2
  <div id="header">
3
3
  <slot name="logo"></slot>
4
4
  <button id="toggle" @click=${this.toggleClick} aria-label="${this.collapsed?"Expand panel":"Collapse panel"}">
5
- <k-icon name="arrow-line" direction="${this.collapsed?"right":"left"}"></k-icon>
5
+ <k-icon name="arrow-line" direction="${(()=>"right"===this.side?this.collapsed?"left":"right":this.collapsed?"right":"left")()}"></k-icon>
6
6
  </button>
7
7
  </div>
8
8
  <div id="content">
@@ -13,7 +13,8 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
13
13
  --bg: var(--c_bg);
14
14
  --width-expanded: 16rem;
15
15
  --transition-duration: var(--animation_ms, 256ms);
16
- display: block;
16
+ display: flex;
17
+ flex-direction: column;
17
18
  position: fixed;
18
19
  top: 0;
19
20
  left: 0;
@@ -40,11 +41,10 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
40
41
  gap: var(--spacer_h);
41
42
  padding: var(--spacer_h);
42
43
  border-bottom: 1px solid var(--c_border);
44
+ flex-shrink: 0;
43
45
  }
44
46
  ::slotted([slot="logo"]) {
45
47
  margin-right: auto;
46
- }
47
- ::slotted([slot="logo"]) {
48
48
  flex: 1;
49
49
  min-width: 0;
50
50
  opacity: 1;
@@ -70,7 +70,8 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from".
70
70
  background: var(--c_bg_hover);
71
71
  }
72
72
  #content {
73
- height: calc(100% - 3rem);
73
+ flex: 1;
74
+ min-height: 0;
74
75
  overflow-y: auto;
75
76
  overflow-x: hidden;
76
77
  display: flex;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "kempo-ui",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
4
4
  "type": "module",
5
5
  "description": "A Lit based web-component library",
6
6
  "main": "index.js",
7
7
  "scripts": {
8
8
  "build": "node scripts/build.js",
9
9
  "prod": "node scripts/docs.js",
10
- "dev": "node scripts/docs.js --src",
10
+ "dev": "node scripts/docs.js --src --logging silent",
11
11
  "geticon": "node scripts/getIcon.js",
12
12
  "test": "npx kempo-test",
13
13
  "test:gui": "npx kempo-test --gui",
@@ -8,13 +8,16 @@ import './Icon.js';
8
8
  class SidePanel extends ShadowComponent {
9
9
  static properties = {
10
10
  collapsed: { type: Boolean, reflect: true },
11
- side: { type: String, reflect: true }
11
+ side: { type: String, reflect: true },
12
+ persistentId: { type: String, reflect: true, attribute: 'persistent-id' }
12
13
  };
13
14
 
14
15
  constructor() {
15
16
  super();
16
17
  this.collapsed = false;
17
18
  this.side = 'left';
19
+ this.persistentId = null;
20
+ this.isInitialLoad = true;
18
21
  }
19
22
 
20
23
  toggleClick = () => this.toggle();
@@ -22,7 +25,22 @@ class SidePanel extends ShadowComponent {
22
25
  updated(changedProperties) {
23
26
  super.updated(changedProperties);
24
27
 
28
+ if(changedProperties.has('persistentId') && this.persistentId && window?.localStorage) {
29
+ const key = `side-panel-persistent-id-${this.persistentId}`;
30
+ const value = window.localStorage.getItem(key);
31
+ if(value !== null) {
32
+ this.collapsed = value === 'true';
33
+ this.isInitialLoad = false;
34
+ }
35
+ }
36
+
25
37
  if(changedProperties.has('collapsed')) {
38
+ if(this.persistentId && window?.localStorage && !this.isInitialLoad) {
39
+ const key = `side-panel-persistent-id-${this.persistentId}`;
40
+ window.localStorage.setItem(key, this.collapsed.toString());
41
+ }
42
+ this.isInitialLoad = false;
43
+
26
44
  const eventName = this.collapsed ? 'collapse' : 'expand';
27
45
  this.dispatchEvent(new CustomEvent(eventName));
28
46
  this.dispatchEvent(new CustomEvent('change', { detail: eventName }));
@@ -80,7 +98,7 @@ class SidePanel extends ShadowComponent {
80
98
  transition: width var(--transition-duration);
81
99
  background: var(--bg);
82
100
  border-right: 1px solid var(--c_border);
83
- z-index: 1000;
101
+ z-index: 99;
84
102
  }
85
103
  :host([collapsed]) {
86
104
  width: auto;
@@ -102,8 +120,6 @@ class SidePanel extends ShadowComponent {
102
120
  }
103
121
  ::slotted([slot="logo"]) {
104
122
  margin-right: auto;
105
- }
106
- ::slotted([slot="logo"]) {
107
123
  flex: 1;
108
124
  min-width: 0;
109
125
  opacity: 1;