kempo 3.1.0 → 3.2.1
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.
- package/dist/components/LazyComponent.js +1 -1
- package/dist/components/Tabs.js +1 -1
- package/docs/components/lazy-component.html +8 -0
- package/docs/components/tabs.html +24 -0
- package/docs/icons/open-in-browser.svg +1 -0
- package/docs/icons/remove.svg +1 -0
- package/docs/icons/save.svg +1 -0
- package/docs/kempo/components/LazyComponent.js +1 -1
- package/docs/kempo/components/Tabs.js +1 -1
- package/icons/open-in-browser.svg +1 -0
- package/icons/remove.svg +1 -0
- package/icons/save.svg +1 -0
- package/package.json +2 -2
- package/scripts/test.js +31 -0
- package/src/components/Component.js +1 -0
- package/src/components/LazyComponent.js +27 -5
- package/src/components/Tabs.js +136 -126
|
@@ -1 +1 @@
|
|
|
1
|
-
import Component from"./Component.js";import{isInView}from"../utils/element.js";import raf from"../utils/raf.js";export default class LazyComponent extends Component{constructor(e){super(e),new IntersectionObserver((([e])=>{e.intersectionRatio>0&&!this.rendered?this.inViewCallback():this.outOfViewCallback()})).observe(this),this.
|
|
1
|
+
import Component from"./Component.js";import{isInView}from"../utils/element.js";import raf from"../utils/raf.js";const inViewIntervalId=Symbol();export default class LazyComponent extends Component{constructor(e){super(e),this[inViewIntervalId]=null,new IntersectionObserver((([e])=>{e.intersectionRatio>0&&!this.rendered?this.inViewCallback():this.outOfViewCallback()})).observe(this),this.registerAttributes({unrender:!1,inViewInterval:0})}connectedCallback(){this.renderSkelton(),raf((()=>{isInView(this)?this.inViewCallback():this.outOfViewCallback()}),2)}async inViewCallback(){return clearInterval(this[inViewIntervalId]),await this.render()}async outOfViewCallback(){return clearInterval(this[inViewIntervalId]),this[inViewIntervalId]=setInterval((async()=>{await isInView(this)&&this.inViewCallback()}),this.inViewInterval||this.constructor.inViewIntervalTimeout),!!this.unrender&&(await this.renderSkelton(),!0)}async renderSkelton(){return this.shadowRoot.innerHTML=`<link rel="stylesheet" href="${Component.pathToKempo}/kempo-styles.css" />${this.skeletonTemplate}<style>${this.shadowStyles}</style>`,this.rendered=!1,!0}get skeletonTemplate(){return'<div style="height: var(--skeleton_height, 1rem)"></div>'}static inViewIntervalTimeout=1e3}
|
package/dist/components/Tabs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import Component from"./Component.js";import{dispatchEvent}from"../utils/element.js";
|
|
1
|
+
import Component from"./Component.js";import{dispatchEvent}from"../utils/element.js";export class Tabs extends Component{constructor(){super(),this.registerAttributes({active:"",fixedHeight:!1})}async connectedCallback(){await super.connectedCallback(),this.active||(this.active=`${this.querySelectorAll("k-tab-content")[0].name}`);const t=this.shadowRoot.getElementById("tabs");t.addEventListener("scroll",this.updateScrollIndicators.bind(this)),this.updateScrollIndicators(),new ResizeObserver((()=>this.updateScrollIndicators())).observe(t);const e=this.shadowRoot.getElementById("scroll-left"),n=this.shadowRoot.getElementById("scroll-right");e.addEventListener("click",(()=>{t.scrollBy({left:-200,behavior:"smooth"})})),n.addEventListener("click",(()=>{t.scrollBy({left:200,behavior:"smooth"})}))}updateScrollIndicators(){const t=this.shadowRoot.getElementById("tabs"),e=this.shadowRoot.getElementById("scroll-left"),n=this.shadowRoot.getElementById("scroll-right"),o=t.scrollLeft>0,s=t.scrollLeft<t.scrollWidth-t.clientWidth;e.classList.toggle("visible",o),n.classList.toggle("visible",s)}attributeChangedCallback(t,e,n){if("active"===t){const t=this.getActiveTab();t&&(t.active=!1);const e=this.getActiveContent();e&&(e.active=!1);const o=this.getTab(n);o&&(o.active=!0);const s=this.getContent(n);s&&(s.active=!0),dispatchEvent(this,"tab",{tab:n})}}get contents(){return[...this.querySelectorAll(":scope > k-tab-content")]}get tabs(){return[...this.querySelectorAll(":scope > k-tab")]}getTab(t){let e;if("string"==typeof t&&(e=this.querySelector(`k-tab[for="${t}"]`)),!e){let n=parseInt(t);n||(n=0),e=this.querySelectorAll("k-tab")[n]}return e}getActiveTab(){return this.querySelector(':scope > k-tab[active="true"]')}getContent(t){let e;if("string"==typeof t&&(e=this.querySelector(`k-tab-content[name="${t}"]`)),!e){let n=parseInt(t);n||(n=0),e=this.querySelectorAll("k-tab-content")[n]}return e}getActiveContent(){return this.querySelector(':scope > k-tab-content[active="true"]')}get shadowTemplate(){return`\n <div id="wrapper">\n <div id="tabs-container">\n <div id="scroll-left" class="scroll-indicator">\n <svg class="arrow" width="20" height="20" viewBox="0 0 20 20" fill="none">\n <path d="M12 15L7 10L12 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>\n </svg>\n </div>\n <div id="tabs">\n <slot name="tabs"></slot>\n </div>\n <div id="scroll-right" class="scroll-indicator">\n <svg class="arrow" width="20" height="20" viewBox="0 0 20 20" fill="none">\n <path d="M8 15L13 10L8 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>\n </svg>\n </div>\n </div>\n <div id="contents">\n ${super.shadowTemplate}\n </div>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n }\n #wrapper {\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n }\n #tabs-container {\n position: relative;\n border-bottom: 1px solid var(--c_border);\n }\n #tabs {\n display: flex;\n overflow-x: auto;\n overflow-y: hidden;\n }\n #tabs ::slotted(*) {\n flex: 0 0 auto;\n }\n .scroll-indicator {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 72px;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n padding-bottom: 2px;\n }\n .scroll-indicator .arrow {\n color: var(--tc_base);\n z-index: 1;\n }\n .scroll-indicator.visible {\n opacity: 1;\n }\n #scroll-left {\n left: 0;\n background: linear-gradient(90deg, \n var(--c_bg) 0%,\n var(--c_bg) 30%,\n transparent 100%\n );\n }\n #scroll-right {\n right: 0;\n justify-content: flex-end;\n background: linear-gradient(-90deg, \n var(--c_bg) 0%,\n var(--c_bg) 30%,\n transparent 100%\n );\n }\n :host([fixed-height="true"]) #wrapper {\n height: 100%;\n }\n :host([fixed-height="true"]) #contents {\n height: 100%;\n flex: 1;\n min-height: 1.35rem;\n overflow: auto;\n }\n `}static observedAttributes=[...super.observedAttributes,"active"]}window.customElements.define("k-tabs",Tabs);const clickHandler=Symbol();export class Tab extends Component{constructor(){super(),this[clickHandler]=()=>{this.active||(this.tabs.active=this.for)},this.slot="tabs",this.registerAttribute("active",!1)}connectedCallback(){super.connectedCallback(),this.shadowRoot.getElementById("button").addEventListener("click",this[clickHandler])}get for(){const t=this.getAttribute("for");return t||this?.tabs.tabs.indexOf(this)}set for(t){t?this.setAttribute("for",t):this.removeAttribute("for")}get tabs(){return"K-TABS"===this.parentElement.tagName?this.parentElement:null}get shadowTemplate(){return`\n <button id="button">\n ${super.shadowTemplate}\n </button>\n `}get shadowStyles(){return"\n :host {\n margin-bottom: -1px;\n flex: 0 0 auto; /* Important: prevent shrinking and growing */\n }\n #button {\n padding: var(--spacer_h);\n background-color: transparent;\n border: none;\n cursor: inherit;\n box-shadow: none;\n color: inherit;\n white-space: nowrap; /* Prevent text wrapping */\n }\n :host(:not([active])) #button {\n cursor: pointer;\n }\n :host([active]){\n border-bottom: 2px solid var(--c_primary);\n margin-bottom: -1px;\n }\n :host([active]) #button {\n color: var(--tc_primary);\n }\n "}}window.customElements.define("k-tab",Tab);export class TabContent extends Component{constructor(){super(),this.registerAttribute("active",!1)}get tabs(){return"K-TABS"===this.parentElement.tagName?this.parentElement:null}get name(){const t=this.getAttribute("name");return t||this?.tabs.contents.indexOf(this)}set name(t){t?this.setAttribute("name",t):this.removeAttribute("name")}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n height: 100%;\n max-height: 100%;\n flex: 1 1 auto;\n overflow: auto;\n }\n :host([active]) {\n display: block;\n }\n :host(:not([active])) {\n display: none;\n }\n `}}window.customElements.define("k-tab-content",TabContent);export class TabSpacer extends Component{constructor(){super(),this.slot="tabs"}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n flex: 1 1 auto !important;\n height: 1px;\n }\n `}}window.customElements.define("k-tab-spacer",TabSpacer);export default{Tab:Tab,TabContent:TabContent,Tabs:Tabs,TabSpacer:TabSpacer};
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
<p>Extend the <code>LazyComponent</code> class to create a lazy-loaded component. The component will render its content only when it becomes visible in the viewport.</p>
|
|
42
42
|
<pre><code class="hljs javascript"><span class="hljs-keyword">import</span> LazyComponent <span class="hljs-keyword">from</span> <span class="hljs-string">'kempo'</span>;<br /><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyLazyComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">LazyComponent</span> </span>{<br /> <span class="hljs-keyword">constructor</span>(){<br /> <span class="hljs-keyword">super</span>();<br /> <span class="hljs-comment">// Your Constructor</span><br /> }<br /> <span class="hljs-keyword">async</span> render(force){<br /> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">await</span> <span class="hljs-keyword">super</span>.render(force)){<br /> <span class="hljs-comment">// Your render logic</span><br /> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br /> }<br /> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br /> }<br />}<br /><span class="hljs-built_in">window</span>.customElements.define(<span class="hljs-string">'my-lazy-component'</span>, MyLazyComponent);</code></pre>
|
|
43
43
|
|
|
44
|
+
<h3 id="basicUsage"><a href="#interval" class="no-link">In-View Interval Timeout</a></h3>
|
|
45
|
+
<p>In addition to using intersection observes to watch for components being scrolled into view, an interval is used to check if the component suddenly become visible via a display style change to itself or a parent. The timeout of the interval can be set for all instances of your LazyComponent by chaning the static member <code>inViewIntervalTimeout</code>.</p>
|
|
46
|
+
<pre><code class="hljs javascript"><span class="hljs-keyword">import</span> LazyComponent <span class="hljs-keyword">from</span> <span class="hljs-string">'kempo'</span>;<br /><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyLazyComponent</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">LazyComponent</span> </span>{<br /> <span class="hljs-keyword">constructor</span>(){<br /> <span class="hljs-keyword">super</span>();<br /> <span class="hljs-comment">// Your Constructor</span><br /> }<br /> <span class="hljs-keyword">async</span> render(force){<br /> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">await</span> <span class="hljs-keyword">super</span>.render(force)){<br /> <span class="hljs-comment">// Your render logic</span><br /> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br /> }<br /> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br /> }<br /><br /> <span class="hljs-keyword">static</span> inViewIntervalTimeout = <span class="hljs-number">200</span>; <span class="hljs-comment">// Change the inView checking interval to 200ms instead of the default 1000</span><br />}<br /><span class="hljs-built_in">window</span>.customElements.define(<span class="hljs-string">'my-lazy-component'</span>, MyLazyComponent);</code></pre>
|
|
47
|
+
|
|
48
|
+
<h5>Individal Component interval timer overwrites</h5>
|
|
49
|
+
<p>You can overwrite this interval timeout for individual elements by setting the <code>in-view-interval</code> attribute.</p>
|
|
50
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">my-lazy-component</span> <span class="hljs-attr">in-view-interval</span>=<span class="hljs-string">"5000"</span>></span><br /> <span class="hljs-comment"><!-- The interval to check if it is in-view (due to element/parent visibility change) is now set to check every 5 seconds --></span><br /><span class="hljs-tag"></<span class="hljs-name">my-lazy-component</span>></span></code></pre>
|
|
51
|
+
|
|
44
52
|
<h2 id="jsRef">JavaScript Reference</h2>
|
|
45
53
|
|
|
46
54
|
<h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
<a href="#basicUsage">Basic Usage</a><br />
|
|
27
27
|
<a href="#tabSpacers">Tab Spacers</a><br />
|
|
28
28
|
<a href="#scrollingTabs">Scrolling Tabs</a><br />
|
|
29
|
+
<a href="#fixedHeight">Fixed Height</a><br />
|
|
29
30
|
|
|
30
31
|
<h6 class="mt">JavaScript Reference</h6>
|
|
31
32
|
<a href="#constructor">Constructor</a><br />
|
|
@@ -113,6 +114,27 @@
|
|
|
113
114
|
<k-tab-content name="tab10"><p>Content 10</p></k-tab-content>
|
|
114
115
|
</k-tabs>
|
|
115
116
|
|
|
117
|
+
<h3 id="fixedHeight"><a href="#fixedHeight" class="no-link">Fixed Height</a></h3>
|
|
118
|
+
<p>Use the <code>fixed-height</code> attribute to make the tabs component fill its container height and enable content scrolling.</p>
|
|
119
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-tabs</span> <span class="hljs-attr">fixed-height</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"height: 200px"</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tab</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"fixed1"</span>></span>Tab 1<span class="hljs-tag"></<span class="hljs-name">k-tab</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tab</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"fixed2"</span>></span>Tab 2<span class="hljs-tag"></<span class="hljs-name">k-tab</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tab-content</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"fixed1"</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>This content will scroll if it's too long.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"></<span class="hljs-name">k-tab-content</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">k-tab-content</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"fixed2"</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Another tab that is not tall enough.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /> <span class="hljs-tag"></<span class="hljs-name">k-tab-content</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">k-tabs</span>></span></code></pre>
|
|
120
|
+
<k-tabs fixed-height="true" style="height: 200px">
|
|
121
|
+
<k-tab for="fixed1">Tab 1</k-tab>
|
|
122
|
+
<k-tab for="fixed2">Tab 2</k-tab>
|
|
123
|
+
<k-tab-content name="fixed1">
|
|
124
|
+
<p>This content will scroll if it's too long.</p>
|
|
125
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
|
|
126
|
+
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
|
127
|
+
<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
|
|
128
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
|
|
129
|
+
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
|
130
|
+
<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
|
|
131
|
+
</k-tab-content>
|
|
132
|
+
<k-tab-content name="fixed2">
|
|
133
|
+
<p>Another tab that is not tall enough.</p>
|
|
134
|
+
<p>Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</p>
|
|
135
|
+
</k-tab-content>
|
|
136
|
+
</k-tabs>
|
|
137
|
+
|
|
116
138
|
<h2 id="jsRef">JavaScript Reference</h2>
|
|
117
139
|
<h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
|
|
118
140
|
<h6>Extends <a href="./component.html">Component</a></h6>
|
|
@@ -124,6 +146,8 @@
|
|
|
124
146
|
<h3 id="attributes"><a href="#attributes" class="no-link">Attributes</a></h3>
|
|
125
147
|
<h5><code>active<i>: string</i></code></h5>
|
|
126
148
|
<p>The name of the active tab.</p>
|
|
149
|
+
<h5><code>fixed-height<i>: boolean</i></code></h5>
|
|
150
|
+
<p>When true, the tabs component will fill its container height and enable content scrolling.</p>
|
|
127
151
|
<h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
|
|
128
152
|
<h5><code>getActiveTab()<i>: Tab</i></code></h5>
|
|
129
153
|
<p>Returns the currently active tab.</p>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H600v-80h160v-480H200v480h160v80H200Zm240 0v-246l-64 64-56-58 160-160 160 160-56 58-64-64v246h-80Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M200-440v-80h560v80H200Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z"/></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import Component from"./Component.js";import{isInView}from"../utils/element.js";import raf from"../utils/raf.js";export default class LazyComponent extends Component{constructor(e){super(e),new IntersectionObserver((([e])=>{e.intersectionRatio>0&&!this.rendered?this.inViewCallback():this.outOfViewCallback()})).observe(this),this.
|
|
1
|
+
import Component from"./Component.js";import{isInView}from"../utils/element.js";import raf from"../utils/raf.js";const inViewIntervalId=Symbol();export default class LazyComponent extends Component{constructor(e){super(e),this[inViewIntervalId]=null,new IntersectionObserver((([e])=>{e.intersectionRatio>0&&!this.rendered?this.inViewCallback():this.outOfViewCallback()})).observe(this),this.registerAttributes({unrender:!1,inViewInterval:0})}connectedCallback(){this.renderSkelton(),raf((()=>{isInView(this)?this.inViewCallback():this.outOfViewCallback()}),2)}async inViewCallback(){return clearInterval(this[inViewIntervalId]),await this.render()}async outOfViewCallback(){return clearInterval(this[inViewIntervalId]),this[inViewIntervalId]=setInterval((async()=>{await isInView(this)&&this.inViewCallback()}),this.inViewInterval||this.constructor.inViewIntervalTimeout),!!this.unrender&&(await this.renderSkelton(),!0)}async renderSkelton(){return this.shadowRoot.innerHTML=`<link rel="stylesheet" href="${Component.pathToKempo}/kempo-styles.css" />${this.skeletonTemplate}<style>${this.shadowStyles}</style>`,this.rendered=!1,!0}get skeletonTemplate(){return'<div style="height: var(--skeleton_height, 1rem)"></div>'}static inViewIntervalTimeout=1e3}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import Component from"./Component.js";import{dispatchEvent}from"../utils/element.js";
|
|
1
|
+
import Component from"./Component.js";import{dispatchEvent}from"../utils/element.js";export class Tabs extends Component{constructor(){super(),this.registerAttributes({active:"",fixedHeight:!1})}async connectedCallback(){await super.connectedCallback(),this.active||(this.active=`${this.querySelectorAll("k-tab-content")[0].name}`);const t=this.shadowRoot.getElementById("tabs");t.addEventListener("scroll",this.updateScrollIndicators.bind(this)),this.updateScrollIndicators(),new ResizeObserver((()=>this.updateScrollIndicators())).observe(t);const e=this.shadowRoot.getElementById("scroll-left"),n=this.shadowRoot.getElementById("scroll-right");e.addEventListener("click",(()=>{t.scrollBy({left:-200,behavior:"smooth"})})),n.addEventListener("click",(()=>{t.scrollBy({left:200,behavior:"smooth"})}))}updateScrollIndicators(){const t=this.shadowRoot.getElementById("tabs"),e=this.shadowRoot.getElementById("scroll-left"),n=this.shadowRoot.getElementById("scroll-right"),o=t.scrollLeft>0,s=t.scrollLeft<t.scrollWidth-t.clientWidth;e.classList.toggle("visible",o),n.classList.toggle("visible",s)}attributeChangedCallback(t,e,n){if("active"===t){const t=this.getActiveTab();t&&(t.active=!1);const e=this.getActiveContent();e&&(e.active=!1);const o=this.getTab(n);o&&(o.active=!0);const s=this.getContent(n);s&&(s.active=!0),dispatchEvent(this,"tab",{tab:n})}}get contents(){return[...this.querySelectorAll(":scope > k-tab-content")]}get tabs(){return[...this.querySelectorAll(":scope > k-tab")]}getTab(t){let e;if("string"==typeof t&&(e=this.querySelector(`k-tab[for="${t}"]`)),!e){let n=parseInt(t);n||(n=0),e=this.querySelectorAll("k-tab")[n]}return e}getActiveTab(){return this.querySelector(':scope > k-tab[active="true"]')}getContent(t){let e;if("string"==typeof t&&(e=this.querySelector(`k-tab-content[name="${t}"]`)),!e){let n=parseInt(t);n||(n=0),e=this.querySelectorAll("k-tab-content")[n]}return e}getActiveContent(){return this.querySelector(':scope > k-tab-content[active="true"]')}get shadowTemplate(){return`\n <div id="wrapper">\n <div id="tabs-container">\n <div id="scroll-left" class="scroll-indicator">\n <svg class="arrow" width="20" height="20" viewBox="0 0 20 20" fill="none">\n <path d="M12 15L7 10L12 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>\n </svg>\n </div>\n <div id="tabs">\n <slot name="tabs"></slot>\n </div>\n <div id="scroll-right" class="scroll-indicator">\n <svg class="arrow" width="20" height="20" viewBox="0 0 20 20" fill="none">\n <path d="M8 15L13 10L8 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>\n </svg>\n </div>\n </div>\n <div id="contents">\n ${super.shadowTemplate}\n </div>\n </div>\n `}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n width: 100%;\n }\n #wrapper {\n display: flex;\n flex-direction: column;\n width: 100%;\n min-width: 0;\n }\n #tabs-container {\n position: relative;\n border-bottom: 1px solid var(--c_border);\n }\n #tabs {\n display: flex;\n overflow-x: auto;\n overflow-y: hidden;\n }\n #tabs ::slotted(*) {\n flex: 0 0 auto;\n }\n .scroll-indicator {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 72px;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n padding-bottom: 2px;\n }\n .scroll-indicator .arrow {\n color: var(--tc_base);\n z-index: 1;\n }\n .scroll-indicator.visible {\n opacity: 1;\n }\n #scroll-left {\n left: 0;\n background: linear-gradient(90deg, \n var(--c_bg) 0%,\n var(--c_bg) 30%,\n transparent 100%\n );\n }\n #scroll-right {\n right: 0;\n justify-content: flex-end;\n background: linear-gradient(-90deg, \n var(--c_bg) 0%,\n var(--c_bg) 30%,\n transparent 100%\n );\n }\n :host([fixed-height="true"]) #wrapper {\n height: 100%;\n }\n :host([fixed-height="true"]) #contents {\n height: 100%;\n flex: 1;\n min-height: 1.35rem;\n overflow: auto;\n }\n `}static observedAttributes=[...super.observedAttributes,"active"]}window.customElements.define("k-tabs",Tabs);const clickHandler=Symbol();export class Tab extends Component{constructor(){super(),this[clickHandler]=()=>{this.active||(this.tabs.active=this.for)},this.slot="tabs",this.registerAttribute("active",!1)}connectedCallback(){super.connectedCallback(),this.shadowRoot.getElementById("button").addEventListener("click",this[clickHandler])}get for(){const t=this.getAttribute("for");return t||this?.tabs.tabs.indexOf(this)}set for(t){t?this.setAttribute("for",t):this.removeAttribute("for")}get tabs(){return"K-TABS"===this.parentElement.tagName?this.parentElement:null}get shadowTemplate(){return`\n <button id="button">\n ${super.shadowTemplate}\n </button>\n `}get shadowStyles(){return"\n :host {\n margin-bottom: -1px;\n flex: 0 0 auto; /* Important: prevent shrinking and growing */\n }\n #button {\n padding: var(--spacer_h);\n background-color: transparent;\n border: none;\n cursor: inherit;\n box-shadow: none;\n color: inherit;\n white-space: nowrap; /* Prevent text wrapping */\n }\n :host(:not([active])) #button {\n cursor: pointer;\n }\n :host([active]){\n border-bottom: 2px solid var(--c_primary);\n margin-bottom: -1px;\n }\n :host([active]) #button {\n color: var(--tc_primary);\n }\n "}}window.customElements.define("k-tab",Tab);export class TabContent extends Component{constructor(){super(),this.registerAttribute("active",!1)}get tabs(){return"K-TABS"===this.parentElement.tagName?this.parentElement:null}get name(){const t=this.getAttribute("name");return t||this?.tabs.contents.indexOf(this)}set name(t){t?this.setAttribute("name",t):this.removeAttribute("name")}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n display: block;\n height: 100%;\n max-height: 100%;\n flex: 1 1 auto;\n overflow: auto;\n }\n :host([active]) {\n display: block;\n }\n :host(:not([active])) {\n display: none;\n }\n `}}window.customElements.define("k-tab-content",TabContent);export class TabSpacer extends Component{constructor(){super(),this.slot="tabs"}get shadowStyles(){return`\n ${super.shadowStyles}\n :host {\n flex: 1 1 auto !important;\n height: 1px;\n }\n `}}window.customElements.define("k-tab-spacer",TabSpacer);export default{Tab:Tab,TabContent:TabContent,Tabs:Tabs,TabSpacer:TabSpacer};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H600v-80h160v-480H200v480h160v80H200Zm240 0v-246l-64 64-56-58 160-160 160 160-56 58-64-64v246h-80Z"/></svg>
|
package/icons/remove.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M200-440v-80h560v80H200Z"/></svg>
|
package/icons/save.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z"/></svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kempo",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A web component library",
|
|
6
6
|
"exports": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"./components/*": "./dist/components/*.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "node ./
|
|
14
|
+
"test": "node ./scripts/test.js",
|
|
15
15
|
"build": "node ./scripts/build.js",
|
|
16
16
|
"docs": "node ./scripts/docs.js",
|
|
17
17
|
"docs-src": "node ./scripts/docs.js -s"
|
package/scripts/test.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import {
|
|
5
|
+
getArgs,
|
|
6
|
+
runChildNodeProcess
|
|
7
|
+
} from '../src/utils/cli.js';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
|
|
12
|
+
const options = getArgs({
|
|
13
|
+
p: 'port',
|
|
14
|
+
b: 'build',
|
|
15
|
+
s: 'src'
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
port = 8084,
|
|
20
|
+
build = true
|
|
21
|
+
} = options;
|
|
22
|
+
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
app.use('/kempo', express.static('src'));
|
|
26
|
+
app.use('/icons', express.static('icons'));
|
|
27
|
+
app.use(express.static('test'));
|
|
28
|
+
|
|
29
|
+
app.listen(port, () => {
|
|
30
|
+
console.log(`Docs Running on: http://localhost:${port}`);
|
|
31
|
+
});
|
|
@@ -107,3 +107,4 @@ export default class Component extends HTMLElement {
|
|
|
107
107
|
static observedAttributes = ['rendered'];
|
|
108
108
|
static pathToKempo = import.meta.url.substring(0, import.meta.url.indexOf('/components/Component.js') );
|
|
109
109
|
}
|
|
110
|
+
window.customElements.define('k-component', Component);
|
|
@@ -2,9 +2,14 @@ import Component from './Component.js';
|
|
|
2
2
|
import { isInView } from '../utils/element.js';
|
|
3
3
|
import raf from '../utils/raf.js';
|
|
4
4
|
|
|
5
|
+
const inViewIntervalId = Symbol();
|
|
5
6
|
export default class LazyComponent extends Component {
|
|
6
7
|
constructor(shadowDetails){
|
|
7
8
|
super(shadowDetails);
|
|
9
|
+
|
|
10
|
+
/* Private Members */
|
|
11
|
+
this[inViewIntervalId] = null;
|
|
12
|
+
|
|
8
13
|
(new IntersectionObserver(([comp]) => {
|
|
9
14
|
if(comp.intersectionRatio > 0 && !this.rendered){
|
|
10
15
|
this.inViewCallback();
|
|
@@ -13,19 +18,33 @@ export default class LazyComponent extends Component {
|
|
|
13
18
|
}
|
|
14
19
|
})).observe(this);
|
|
15
20
|
|
|
16
|
-
this.
|
|
21
|
+
this.registerAttributes({
|
|
22
|
+
unrender: false,
|
|
23
|
+
inViewInterval: 0
|
|
24
|
+
});
|
|
17
25
|
}
|
|
18
|
-
|
|
26
|
+
connectedCallback(){
|
|
19
27
|
// Do not do super.connectedCallback() because it will render the component prematurely
|
|
20
|
-
|
|
21
|
-
raf(
|
|
22
|
-
if(
|
|
28
|
+
this.renderSkelton(); // so that the skeleton height is rendered, which will give it the propert height so potentional LazyComponents below it will get out of the viewport as they normally would, before we then check if it is in View (which is delayed a few frames.
|
|
29
|
+
raf(()=>{
|
|
30
|
+
if(isInView(this)){
|
|
31
|
+
this.inViewCallback();
|
|
32
|
+
} else {
|
|
33
|
+
this.outOfViewCallback();
|
|
34
|
+
}
|
|
23
35
|
}, 2);
|
|
24
36
|
}
|
|
25
37
|
async inViewCallback(){
|
|
38
|
+
clearInterval(this[inViewIntervalId]);
|
|
26
39
|
return await this.render();
|
|
27
40
|
}
|
|
28
41
|
async outOfViewCallback(){
|
|
42
|
+
clearInterval(this[inViewIntervalId]);
|
|
43
|
+
this[inViewIntervalId] = setInterval(async ()=>{
|
|
44
|
+
if(await isInView(this)){
|
|
45
|
+
this.inViewCallback();
|
|
46
|
+
}
|
|
47
|
+
}, this.inViewInterval || LazyComponent.inViewIntervalTimeout);
|
|
29
48
|
if(this.unrender){
|
|
30
49
|
await this.renderSkelton();
|
|
31
50
|
return true;
|
|
@@ -40,4 +59,7 @@ export default class LazyComponent extends Component {
|
|
|
40
59
|
get skeletonTemplate(){
|
|
41
60
|
return /*html*/`<div style="height: var(--skeleton_height, 1rem)"></div>`;
|
|
42
61
|
}
|
|
62
|
+
|
|
63
|
+
/* Static Members */
|
|
64
|
+
static inViewIntervalTimeout = 1000;
|
|
43
65
|
}
|
package/src/components/Tabs.js
CHANGED
|
@@ -3,131 +3,14 @@ import {
|
|
|
3
3
|
dispatchEvent
|
|
4
4
|
} from '../utils/element.js';
|
|
5
5
|
|
|
6
|
-
const clickHandler = Symbol();
|
|
7
|
-
export class Tab extends Component {
|
|
8
|
-
constructor(){
|
|
9
|
-
super();
|
|
10
|
-
|
|
11
|
-
/* Private Methods */
|
|
12
|
-
this[clickHandler] = () => {
|
|
13
|
-
if(!this.active){
|
|
14
|
-
this.tabs.active = this.for;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/* Init */
|
|
19
|
-
this.slot = 'tabs';
|
|
20
|
-
this.registerAttribute('active', false);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
connectedCallback(){
|
|
24
|
-
super.connectedCallback();
|
|
25
|
-
this.shadowRoot.getElementById('button').addEventListener('click', this[clickHandler]);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
get for(){
|
|
29
|
-
const forValue = this.getAttribute('for');
|
|
30
|
-
if(forValue) return forValue;
|
|
31
|
-
return this?.tabs.tabs.indexOf(this);
|
|
32
|
-
}
|
|
33
|
-
set for(v){
|
|
34
|
-
if(v){
|
|
35
|
-
this.setAttribute('for', v);
|
|
36
|
-
} else {
|
|
37
|
-
this.removeAttribute('for');
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
get tabs(){
|
|
42
|
-
return this.parentElement.tagName === 'K-TABS'?this.parentElement:null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get shadowTemplate(){
|
|
46
|
-
return /*html*/`
|
|
47
|
-
<button id="button">
|
|
48
|
-
${super.shadowTemplate}
|
|
49
|
-
</button>
|
|
50
|
-
`;
|
|
51
|
-
}
|
|
52
|
-
get shadowStyles(){
|
|
53
|
-
return /*css*/`
|
|
54
|
-
:host {
|
|
55
|
-
margin-bottom: -1px;
|
|
56
|
-
flex: 0 0 auto; /* Important: prevent shrinking and growing */
|
|
57
|
-
}
|
|
58
|
-
#button {
|
|
59
|
-
padding: var(--spacer_h);
|
|
60
|
-
background-color: transparent;
|
|
61
|
-
border: none;
|
|
62
|
-
cursor: inherit;
|
|
63
|
-
box-shadow: none;
|
|
64
|
-
color: inherit;
|
|
65
|
-
white-space: nowrap; /* Prevent text wrapping */
|
|
66
|
-
}
|
|
67
|
-
:host(:not([active])) #button {
|
|
68
|
-
cursor: pointer;
|
|
69
|
-
}
|
|
70
|
-
:host([active]){
|
|
71
|
-
border-bottom: 2px solid var(--c_primary);
|
|
72
|
-
margin-bottom: -1px;
|
|
73
|
-
}
|
|
74
|
-
:host([active]) #button {
|
|
75
|
-
color: var(--tc_primary);
|
|
76
|
-
}
|
|
77
|
-
`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
window.customElements.define('k-tab', Tab);
|
|
82
|
-
|
|
83
|
-
export class TabContent extends Component {
|
|
84
|
-
constructor(){
|
|
85
|
-
super();
|
|
86
|
-
|
|
87
|
-
this.registerAttribute('active', false);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
get tabs(){
|
|
91
|
-
return this.parentElement.tagName === 'K-TABS'?this.parentElement:null;
|
|
92
|
-
}
|
|
93
|
-
get name(){
|
|
94
|
-
const name = this.getAttribute('name');
|
|
95
|
-
if(name) return name;
|
|
96
|
-
return this?.tabs.contents.indexOf(this);
|
|
97
|
-
}
|
|
98
|
-
set name(v){
|
|
99
|
-
if(v){
|
|
100
|
-
this.setAttribute('name', v);
|
|
101
|
-
} else {
|
|
102
|
-
this.removeAttribute('name');
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
get shadowStyles(){
|
|
107
|
-
return /*css*/`
|
|
108
|
-
${super.shadowStyles}
|
|
109
|
-
:host {
|
|
110
|
-
display: block;
|
|
111
|
-
height: 100%;
|
|
112
|
-
max-height: 100%;
|
|
113
|
-
flex: 1 1 auto;
|
|
114
|
-
overflow: hidden;
|
|
115
|
-
}
|
|
116
|
-
:host([active]) {
|
|
117
|
-
display: block;
|
|
118
|
-
}
|
|
119
|
-
:host(:not([active])) {
|
|
120
|
-
display: none;
|
|
121
|
-
}
|
|
122
|
-
`;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
window.customElements.define('k-tab-content', TabContent);
|
|
126
6
|
|
|
127
7
|
export class Tabs extends Component {
|
|
128
8
|
constructor(){
|
|
129
9
|
super();
|
|
130
|
-
this.
|
|
10
|
+
this.registerAttributes({
|
|
11
|
+
active: '',
|
|
12
|
+
fixedHeight: false
|
|
13
|
+
});
|
|
131
14
|
}
|
|
132
15
|
|
|
133
16
|
async connectedCallback(){
|
|
@@ -136,12 +19,10 @@ export class Tabs extends Component {
|
|
|
136
19
|
this.active = `${this.querySelectorAll('k-tab-content')[0].name}`;
|
|
137
20
|
}
|
|
138
21
|
|
|
139
|
-
// Add scroll listeners for indicators
|
|
140
22
|
const tabsContainer = this.shadowRoot.getElementById('tabs');
|
|
141
23
|
tabsContainer.addEventListener('scroll', this.updateScrollIndicators.bind(this));
|
|
142
24
|
this.updateScrollIndicators();
|
|
143
25
|
|
|
144
|
-
// Update indicators on resize
|
|
145
26
|
new ResizeObserver(() => this.updateScrollIndicators()).observe(tabsContainer);
|
|
146
27
|
|
|
147
28
|
const leftButton = this.shadowRoot.getElementById('scroll-left');
|
|
@@ -217,8 +98,7 @@ export class Tabs extends Component {
|
|
|
217
98
|
}
|
|
218
99
|
getActiveContent(){
|
|
219
100
|
return this.querySelector(':scope > k-tab-content[active="true"]');
|
|
220
|
-
}
|
|
221
|
-
|
|
101
|
+
}
|
|
222
102
|
|
|
223
103
|
get shadowTemplate(){
|
|
224
104
|
return /*html*/`
|
|
@@ -306,6 +186,15 @@ export class Tabs extends Component {
|
|
|
306
186
|
transparent 100%
|
|
307
187
|
);
|
|
308
188
|
}
|
|
189
|
+
:host([fixed-height="true"]) #wrapper {
|
|
190
|
+
height: 100%;
|
|
191
|
+
}
|
|
192
|
+
:host([fixed-height="true"]) #contents {
|
|
193
|
+
height: 100%;
|
|
194
|
+
flex: 1;
|
|
195
|
+
min-height: 1.35rem;
|
|
196
|
+
overflow: auto;
|
|
197
|
+
}
|
|
309
198
|
`;
|
|
310
199
|
}
|
|
311
200
|
|
|
@@ -313,17 +202,138 @@ export class Tabs extends Component {
|
|
|
313
202
|
}
|
|
314
203
|
window.customElements.define('k-tabs', Tabs);
|
|
315
204
|
|
|
316
|
-
|
|
205
|
+
const clickHandler = Symbol();
|
|
206
|
+
export class Tab extends Component {
|
|
317
207
|
constructor(){
|
|
318
208
|
super();
|
|
209
|
+
|
|
210
|
+
/* Private Methods */
|
|
211
|
+
this[clickHandler] = () => {
|
|
212
|
+
if(!this.active){
|
|
213
|
+
this.tabs.active = this.for;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Init */
|
|
319
218
|
this.slot = 'tabs';
|
|
219
|
+
this.registerAttribute('active', false);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
connectedCallback(){
|
|
223
|
+
super.connectedCallback();
|
|
224
|
+
this.shadowRoot.getElementById('button').addEventListener('click', this[clickHandler]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
get for(){
|
|
228
|
+
const forValue = this.getAttribute('for');
|
|
229
|
+
if(forValue) return forValue;
|
|
230
|
+
return this?.tabs.tabs.indexOf(this);
|
|
231
|
+
}
|
|
232
|
+
set for(v){
|
|
233
|
+
if(v){
|
|
234
|
+
this.setAttribute('for', v);
|
|
235
|
+
} else {
|
|
236
|
+
this.removeAttribute('for');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
get tabs(){
|
|
241
|
+
return this.parentElement.tagName === 'K-TABS'?this.parentElement:null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
get shadowTemplate(){
|
|
245
|
+
return /*html*/`
|
|
246
|
+
<button id="button">
|
|
247
|
+
${super.shadowTemplate}
|
|
248
|
+
</button>
|
|
249
|
+
`;
|
|
250
|
+
}
|
|
251
|
+
get shadowStyles(){
|
|
252
|
+
return /*css*/`
|
|
253
|
+
:host {
|
|
254
|
+
margin-bottom: -1px;
|
|
255
|
+
flex: 0 0 auto; /* Important: prevent shrinking and growing */
|
|
256
|
+
}
|
|
257
|
+
#button {
|
|
258
|
+
padding: var(--spacer_h);
|
|
259
|
+
background-color: transparent;
|
|
260
|
+
border: none;
|
|
261
|
+
cursor: inherit;
|
|
262
|
+
box-shadow: none;
|
|
263
|
+
color: inherit;
|
|
264
|
+
white-space: nowrap; /* Prevent text wrapping */
|
|
265
|
+
}
|
|
266
|
+
:host(:not([active])) #button {
|
|
267
|
+
cursor: pointer;
|
|
268
|
+
}
|
|
269
|
+
:host([active]){
|
|
270
|
+
border-bottom: 2px solid var(--c_primary);
|
|
271
|
+
margin-bottom: -1px;
|
|
272
|
+
}
|
|
273
|
+
:host([active]) #button {
|
|
274
|
+
color: var(--tc_primary);
|
|
275
|
+
}
|
|
276
|
+
`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
}
|
|
280
|
+
window.customElements.define('k-tab', Tab);
|
|
281
|
+
|
|
282
|
+
export class TabContent extends Component {
|
|
283
|
+
constructor(){
|
|
284
|
+
super();
|
|
285
|
+
|
|
286
|
+
this.registerAttribute('active', false);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
get tabs(){
|
|
290
|
+
return this.parentElement.tagName === 'K-TABS'?this.parentElement:null;
|
|
291
|
+
}
|
|
292
|
+
get name(){
|
|
293
|
+
const name = this.getAttribute('name');
|
|
294
|
+
if(name) return name;
|
|
295
|
+
return this?.tabs.contents.indexOf(this);
|
|
296
|
+
}
|
|
297
|
+
set name(v){
|
|
298
|
+
if(v){
|
|
299
|
+
this.setAttribute('name', v);
|
|
300
|
+
} else {
|
|
301
|
+
this.removeAttribute('name');
|
|
302
|
+
}
|
|
320
303
|
}
|
|
321
304
|
|
|
322
305
|
get shadowStyles(){
|
|
323
306
|
return /*css*/`
|
|
324
307
|
${super.shadowStyles}
|
|
325
308
|
:host {
|
|
309
|
+
display: block;
|
|
310
|
+
height: 100%;
|
|
311
|
+
max-height: 100%;
|
|
326
312
|
flex: 1 1 auto;
|
|
313
|
+
overflow: auto;
|
|
314
|
+
}
|
|
315
|
+
:host([active]) {
|
|
316
|
+
display: block;
|
|
317
|
+
}
|
|
318
|
+
:host(:not([active])) {
|
|
319
|
+
display: none;
|
|
320
|
+
}
|
|
321
|
+
`;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
window.customElements.define('k-tab-content', TabContent);
|
|
325
|
+
|
|
326
|
+
export class TabSpacer extends Component {
|
|
327
|
+
constructor(){
|
|
328
|
+
super();
|
|
329
|
+
this.slot = 'tabs';
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
get shadowStyles(){
|
|
333
|
+
return /*css*/`
|
|
334
|
+
${super.shadowStyles}
|
|
335
|
+
:host {
|
|
336
|
+
flex: 1 1 auto !important;
|
|
327
337
|
height: 1px;
|
|
328
338
|
}
|
|
329
339
|
`;
|