kempo-ui 0.0.2 → 0.0.3

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 (193) hide show
  1. package/.github/copilot-instructions.md +149 -149
  2. package/.github/workflows/publish-npm.yml +44 -44
  3. package/CONTRIBUTING.md +149 -149
  4. package/README.md +0 -0
  5. package/dist/kempo-vars.css +0 -0
  6. package/dist/src/components/FocusCapture.js +7 -0
  7. package/dist/src/components/Icon.js +10 -0
  8. package/dist/src/components/Import.js +1 -0
  9. package/dist/src/components/ShadowComponent.js +1 -0
  10. package/dist/src/components/SideMenu.js +97 -0
  11. package/dist/src/lit-all.min.js +120 -0
  12. package/docs/components/accordion.html +122 -0
  13. package/docs/components/card.html +91 -0
  14. package/docs/components/component.html +90 -0
  15. package/docs/components/content-slider.html +193 -0
  16. package/docs/components/dialog.html +245 -0
  17. package/docs/components/directory-viewer.html +90 -0
  18. package/docs/components/focus-capture.html +68 -0
  19. package/docs/components/icon.html +161 -0
  20. package/docs/components/import.html +78 -0
  21. package/docs/components/lazy-component.html +84 -0
  22. package/docs/components/reactive-component.html +69 -0
  23. package/docs/components/reactive-lazy-component.html +69 -0
  24. package/docs/components/resize.html +152 -0
  25. package/docs/components/search.html +102 -0
  26. package/docs/components/show-more.html +123 -0
  27. package/docs/components/side-menu.html +168 -0
  28. package/docs/components/sortable.html +96 -0
  29. package/docs/components/split.html +126 -0
  30. package/docs/components/table.html +312 -0
  31. package/docs/components/tableControls.html +77 -0
  32. package/docs/components/tableCustomFields.html +113 -0
  33. package/docs/components/tableFetchRecords.html +114 -0
  34. package/docs/components/tableFieldSortHide.html +75 -0
  35. package/docs/components/tablePagination.html +100 -0
  36. package/docs/components/tableRecordEditing.html +144 -0
  37. package/docs/components/tableRecordFiltering.html +75 -0
  38. package/docs/components/tableRecordHiding.html +80 -0
  39. package/docs/components/tableRecordSearching.html +75 -0
  40. package/docs/components/tableRecordSelection.html +75 -0
  41. package/docs/components/tableRowControls.html +78 -0
  42. package/docs/components/tableSorting.html +73 -0
  43. package/docs/components/tabs.html +163 -0
  44. package/docs/components/tags.html +94 -0
  45. package/docs/components/theme-switcher.html +129 -0
  46. package/docs/components/timestamp.html +86 -0
  47. package/docs/components/toast.html +328 -0
  48. package/docs/components/toggle.html +155 -0
  49. package/docs/dev.config.json +20 -0
  50. package/docs/icons/add.svg +1 -0
  51. package/docs/icons/arrow-back.svg +1 -0
  52. package/docs/icons/arrow-down-double.svg +1 -0
  53. package/docs/icons/arrow-down.svg +1 -0
  54. package/docs/icons/arrow-forward.svg +1 -0
  55. package/docs/icons/arrow-up-double.svg +1 -0
  56. package/docs/icons/arrow-up.svg +1 -0
  57. package/docs/icons/cards.svg +1 -0
  58. package/docs/icons/check.svg +1 -0
  59. package/docs/icons/chevron-left.svg +1 -0
  60. package/docs/icons/chevron-right.svg +1 -0
  61. package/docs/icons/close.svg +2 -0
  62. package/docs/icons/compare.svg +1 -0
  63. package/docs/icons/delete.svg +1 -0
  64. package/docs/icons/drag-handle.svg +1 -0
  65. package/docs/icons/drawing2.svg +124 -0
  66. package/docs/icons/edit.svg +1 -0
  67. package/docs/icons/error.svg +1 -0
  68. package/docs/icons/export-file.svg +1 -0
  69. package/docs/icons/file-text.svg +1 -0
  70. package/docs/icons/file.svg +1 -0
  71. package/docs/icons/filter-off.svg +1 -0
  72. package/docs/icons/filter.svg +1 -0
  73. package/docs/icons/first.svg +1 -0
  74. package/docs/icons/folder-clear.svg +1 -0
  75. package/docs/icons/folder-create.svg +1 -0
  76. package/docs/icons/folder-open.svg +1 -0
  77. package/docs/icons/folder.svg +1 -0
  78. package/docs/icons/hide.svg +1 -0
  79. package/docs/icons/image.svg +1 -0
  80. package/docs/icons/label-add.svg +1 -0
  81. package/docs/icons/label.svg +1 -0
  82. package/docs/icons/last.svg +1 -0
  83. package/docs/icons/menu.svg +1 -0
  84. package/docs/icons/mode-auto.svg +1 -0
  85. package/docs/icons/mode-dark.svg +1 -0
  86. package/docs/icons/mode-light.svg +1 -0
  87. package/docs/icons/open-in-browser.svg +1 -0
  88. package/docs/icons/pause.svg +1 -0
  89. package/docs/icons/play.svg +1 -0
  90. package/docs/icons/refresh.svg +1 -0
  91. package/docs/icons/remove.svg +1 -0
  92. package/docs/icons/save.svg +1 -0
  93. package/docs/icons/search.svg +1 -0
  94. package/docs/icons/settings.svg +1 -0
  95. package/docs/icons/show.svg +1 -0
  96. package/docs/icons/storage.svg +1 -0
  97. package/docs/icons/table-visibility.svg +1 -0
  98. package/docs/icons/tag.svg +1 -0
  99. package/docs/icons/tools.svg +1 -0
  100. package/docs/icons/trash-x.svg +1 -0
  101. package/docs/icons/warning.svg +1 -0
  102. package/docs/index.html +28 -21
  103. package/docs/nav.inc.html +73 -0
  104. package/docs/nav.inc.js +4 -0
  105. package/{config/production.json → docs/prod.config.json} +20 -16
  106. package/docs/src/components/FocusCapture.js +7 -0
  107. package/docs/src/components/HybridComponent.js +1 -0
  108. package/docs/src/components/Icon.js +10 -0
  109. package/docs/src/components/Import.js +1 -0
  110. package/docs/src/components/LightComponent.js +1 -0
  111. package/docs/src/components/ShadowComponent.js +1 -0
  112. package/docs/src/components/SideMenu.js +97 -0
  113. package/docs/src/lit-all.min.js +120 -0
  114. package/docs/src/utils/cli.js +1 -0
  115. package/docs/src/utils/fs-utils.js +1 -0
  116. package/docs/styles.css +6 -0
  117. package/icons/add.svg +1 -0
  118. package/icons/arrow-back.svg +1 -0
  119. package/icons/arrow-down-double.svg +1 -0
  120. package/icons/arrow-down.svg +1 -0
  121. package/icons/arrow-forward.svg +1 -0
  122. package/icons/arrow-up-double.svg +1 -0
  123. package/icons/arrow-up.svg +1 -0
  124. package/icons/cards.svg +1 -0
  125. package/icons/check.svg +1 -0
  126. package/icons/chevron-left.svg +1 -0
  127. package/icons/chevron-right.svg +1 -0
  128. package/icons/close.svg +2 -0
  129. package/icons/compare.svg +1 -0
  130. package/icons/delete.svg +1 -0
  131. package/icons/drag-handle.svg +1 -0
  132. package/icons/drawing2.svg +124 -0
  133. package/icons/edit.svg +1 -0
  134. package/icons/error.svg +1 -0
  135. package/icons/export-file.svg +1 -0
  136. package/icons/file-text.svg +1 -0
  137. package/icons/file.svg +1 -0
  138. package/icons/filter-off.svg +1 -0
  139. package/icons/filter.svg +1 -0
  140. package/icons/first.svg +1 -0
  141. package/icons/folder-clear.svg +1 -0
  142. package/icons/folder-create.svg +1 -0
  143. package/icons/folder-open.svg +1 -0
  144. package/icons/folder.svg +1 -0
  145. package/icons/hide.svg +1 -0
  146. package/icons/image.svg +1 -0
  147. package/icons/label-add.svg +1 -0
  148. package/icons/label.svg +1 -0
  149. package/icons/last.svg +1 -0
  150. package/icons/menu.svg +1 -0
  151. package/icons/mode-auto.svg +1 -0
  152. package/icons/mode-dark.svg +1 -0
  153. package/icons/mode-light.svg +1 -0
  154. package/icons/open-in-browser.svg +1 -0
  155. package/icons/pause.svg +1 -0
  156. package/icons/play.svg +1 -0
  157. package/icons/refresh.svg +1 -0
  158. package/icons/remove.svg +1 -0
  159. package/icons/save.svg +1 -0
  160. package/icons/search.svg +1 -0
  161. package/icons/settings.svg +1 -0
  162. package/icons/show.svg +1 -0
  163. package/icons/storage.svg +1 -0
  164. package/icons/table-visibility.svg +1 -0
  165. package/icons/tag.svg +1 -0
  166. package/icons/tools.svg +1 -0
  167. package/icons/trash-x.svg +1 -0
  168. package/icons/warning.svg +1 -0
  169. package/package.json +2 -2
  170. package/scripts/build.js +109 -138
  171. package/scripts/docs.js +60 -60
  172. package/src/components/Card.js +66 -0
  173. package/src/components/FocusCapture.js +23 -0
  174. package/src/components/HybridComponent.js +40 -40
  175. package/src/components/Icon.js +155 -0
  176. package/src/components/Import.js +104 -0
  177. package/src/components/LightComponent.js +32 -32
  178. package/src/components/ShadowComponent.js +30 -17
  179. package/src/components/SideMenu.js +167 -0
  180. package/src/components/ThemeSwitcher.js +108 -0
  181. package/src/kempo-hljs.css +146 -0
  182. package/src/utils/cli.js +43 -43
  183. package/src/utils/fs-utils.js +41 -41
  184. package/src/utils/propConverters.js +11 -0
  185. package/tests/HybridComponent.browser-test.js +214 -214
  186. package/tests/LightComponent.browser-test.js +169 -169
  187. package/tests/ShadowComponent.browser-test.js +130 -130
  188. package/config/development.json +0 -14
  189. package/docs/components/ShadowComponent.js +0 -1
  190. /package/{docs → dist/src}/components/HybridComponent.js +0 -0
  191. /package/{docs → dist/src}/components/LightComponent.js +0 -0
  192. /package/{docs → dist/src}/utils/cli.js +0 -0
  193. /package/{docs → dist/src}/utils/fs-utils.js +0 -0
@@ -1,40 +1,40 @@
1
- import { html, render } from '../lit-all.min.js';
2
- import ShadowComponent from './ShadowComponent.js';
3
-
4
- export default class HybridComponent extends ShadowComponent {
5
-
6
- /*
7
- Shadow DOM Setup
8
- */
9
-
10
- createRenderRoot() {
11
- const renderContainer = super.createRenderRoot();
12
-
13
- this.lightRoot = document.createElement('div');
14
- this.lightRoot.style.display = 'contents';
15
- this.lightRoot.slot = 'lightRoot';
16
- this.appendChild(this.lightRoot);
17
-
18
- return renderContainer;
19
- }
20
-
21
- /*
22
- Lifecycle Callbacks
23
- */
24
-
25
- updated() {
26
- render(this.renderLightDom(), this.lightRoot);
27
- }
28
-
29
- /*
30
- Rendering
31
- */
32
-
33
- renderLightDom() {
34
- return html``;
35
- }
36
-
37
- render() {
38
- return html`<slot name="lightRoot"></slot>`;
39
- }
40
- }
1
+ import { html, render } from '../lit-all.min.js';
2
+ import ShadowComponent from './ShadowComponent.js';
3
+
4
+ export default class HybridComponent extends ShadowComponent {
5
+
6
+ /*
7
+ Shadow DOM Setup
8
+ */
9
+
10
+ createRenderRoot() {
11
+ const renderContainer = super.createRenderRoot();
12
+
13
+ this.lightRoot = document.createElement('div');
14
+ this.lightRoot.style.display = 'contents';
15
+ this.lightRoot.slot = 'lightRoot';
16
+ this.appendChild(this.lightRoot);
17
+
18
+ return renderContainer;
19
+ }
20
+
21
+ /*
22
+ Lifecycle Callbacks
23
+ */
24
+
25
+ updated() {
26
+ render(this.renderLightDom(), this.lightRoot);
27
+ }
28
+
29
+ /*
30
+ Rendering
31
+ */
32
+
33
+ renderLightDom() {
34
+ return html``;
35
+ }
36
+
37
+ render() {
38
+ return html`<slot name="lightRoot"></slot>`;
39
+ }
40
+ }
@@ -0,0 +1,155 @@
1
+ import ShadowComponent from './ShadowComponent.js';
2
+ import { html, css, unsafeHTML } from '../lit-all.min.js';
3
+
4
+ const cache = {};
5
+
6
+ const getIconByPath = async (path) => {
7
+ if (!cache[path]) {
8
+ cache[path] = new Promise(async (resolve, reject) => {
9
+ const controller = new AbortController();
10
+ const signal = controller.signal;
11
+ try {
12
+ const response = await fetch(path, { signal });
13
+ if (response.status === 200) {
14
+ resolve(await response.text());
15
+ } else if (response.status === 404) {
16
+ resolve(null);
17
+ }
18
+ } catch (e) {
19
+ if (e.name !== 'AbortError') {
20
+ reject(e);
21
+ }
22
+ }
23
+ }).catch(() => null);
24
+ }
25
+ return await cache[path];
26
+ };
27
+
28
+ const getIconByName = (name) => {
29
+ const tryDir = async (dir) => getIconByPath(`${dir}/${name}.svg`);
30
+
31
+ return new Promise(async (resolve, reject) => {
32
+ let svg;
33
+ for(let i = 0; i < Icon.pathToIcons.length && !svg; i++){
34
+ try {
35
+ svg = await tryDir(Icon.pathToIcons[i]);
36
+ } catch(e){}
37
+ }
38
+ if(svg){
39
+ resolve(svg);
40
+ } else {
41
+ resolve(null);
42
+ }
43
+ });
44
+ };
45
+
46
+ export default class Icon extends ShadowComponent {
47
+ /*
48
+ Properties
49
+ */
50
+ static properties = {
51
+ src: { type: String, reflect: true },
52
+ name: { type: String, reflect: true },
53
+ iconContent: { type: String }
54
+ };
55
+
56
+ constructor(name = '') {
57
+ super();
58
+ this.src = '';
59
+ this.name = name;
60
+ this.iconContent = '';
61
+ }
62
+
63
+ /*
64
+ Lifecycle Callbacks
65
+ */
66
+ updated(changedProperties) {
67
+ super.updated();
68
+
69
+ if (changedProperties.has('src') || changedProperties.has('name')) {
70
+ this.loadIcon();
71
+ }
72
+
73
+ if (changedProperties.has('iconContent')) {
74
+ this.fixSVG();
75
+ }
76
+ }
77
+
78
+ /*
79
+ Methods
80
+ */
81
+ async loadIcon() {
82
+ let svg;
83
+
84
+ if (this.src) {
85
+ svg = await getIconByPath(this.src);
86
+ } else if (this.name) {
87
+ svg = await getIconByName(this.name);
88
+ }
89
+
90
+ if (svg) {
91
+ this.iconContent = svg;
92
+ } else {
93
+ // Check for slotted fallback content first
94
+ const slottedContent = this.innerHTML.trim();
95
+ if (slottedContent) {
96
+ this.iconContent = slottedContent;
97
+ } else {
98
+ this.iconContent = Icon.fallback;
99
+ }
100
+ }
101
+ }
102
+
103
+ fixSVG() {
104
+ // Use setTimeout to ensure DOM is updated
105
+ setTimeout(() => {
106
+ const $svg = this.querySelector('svg');
107
+ if ($svg) {
108
+ $svg.removeAttribute('width');
109
+ $svg.removeAttribute('height');
110
+ $svg.querySelectorAll('path, rect, circle').forEach($path => {
111
+ $path.setAttribute('fill', 'currentColor');
112
+ });
113
+ }
114
+ }, 0);
115
+ }
116
+
117
+ /*
118
+ Styles
119
+ */
120
+ static styles = css`
121
+ :host {
122
+ display: inline-block;
123
+ vertical-align: bottom;
124
+ }
125
+ ::slotted(svg), svg {
126
+ height: 1.35em;
127
+ vertical-align: middle;
128
+ }
129
+ `;
130
+
131
+ /*
132
+ Rendering
133
+ */
134
+ render() {
135
+ if (!this.iconContent) {
136
+ // Show slotted content while loading
137
+ return html`<slot></slot>`;
138
+ }
139
+
140
+ return html`${unsafeHTML(this.iconContent)}`;
141
+ }
142
+
143
+ /*
144
+ Static Properties
145
+ */
146
+ static pathToIcons = [
147
+ '/icons'
148
+ ];
149
+
150
+ static fallback = `
151
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M480-79q-16 0-30.5-6T423-102L102-423q-11-12-17-26.5T79-480q0-16 6-31t17-26l321-321q12-12 26.5-17.5T480-881q16 0 31 5.5t26 17.5l321 321q12 11 17.5 26t5.5 31q0 16-5.5 30.5T858-423L537-102q-11 11-26 17t-31 6Zm0-80 321-321-321-321-321 321 321 321Zm-40-281h80v-240h-80v240Zm40 120q17 0 28.5-11.5T520-360q0-17-11.5-28.5T480-400q-17 0-28.5 11.5T440-360q0 17 11.5 28.5T480-320Zm0-160Z"/></svg>
152
+ `;
153
+ }
154
+
155
+ window.customElements.define('k-icon', Icon);
@@ -0,0 +1,104 @@
1
+ import LightComponent from './LightComponent.js';
2
+ import { html, render, unsafeHTML } from '../lit-all.min.js';
3
+
4
+ export default class Import extends LightComponent {
5
+ /*
6
+ Properties
7
+ */
8
+ static properties = {
9
+ src: {
10
+ type: String,
11
+ reflect: true
12
+ },
13
+ content: {
14
+ type: String
15
+ },
16
+ scripts: {
17
+ type: Array
18
+ }
19
+ }
20
+
21
+ static replacements = {}
22
+ constructor(){
23
+ super();
24
+ this.src = '';
25
+ this.content = '';
26
+ this.scripts = [];
27
+ }
28
+
29
+ /*
30
+ Lifecycle Callbacks
31
+ */
32
+ updated(changedProperties){
33
+ super.updated(); // Important: call super for LightComponent
34
+
35
+ if(changedProperties.has('src')){
36
+ this.fetch();
37
+ }
38
+
39
+ if(changedProperties.has('content') && this.scripts.length > 0){
40
+ // Execute scripts after DOM content is rendered
41
+ setTimeout(() => this.executeScripts(), 0);
42
+ }
43
+ }
44
+
45
+ /*
46
+ Methods
47
+ */
48
+ async fetch(){
49
+ let contents = await (await fetch(this.src)).text();
50
+ for (const [name, value] of Object.entries(Import.replacements)) {
51
+ contents = contents.replace(new RegExp(`%%${name}%%`, 'g'), value);
52
+ }
53
+
54
+ // Parse the HTML to extract scripts
55
+ const parser = new DOMParser();
56
+ const doc = parser.parseFromString(contents, 'text/html');
57
+ const scriptElements = doc.querySelectorAll('script');
58
+
59
+ // Extract script sources and inline code
60
+ this.scripts = Array.from(scriptElements).map(script => ({
61
+ src: script.src,
62
+ text: script.textContent,
63
+ type: script.type || 'text/javascript'
64
+ }));
65
+
66
+ // Remove script tags from the content
67
+ scriptElements.forEach(script => script.remove());
68
+
69
+ // Serialize back to string without scripts - use the entire body content
70
+ this.content = doc.body ? doc.body.innerHTML : contents;
71
+ }
72
+
73
+ executeScripts(){
74
+ this.scripts.forEach(script => {
75
+ const scriptElement = document.createElement('script');
76
+ scriptElement.type = script.type;
77
+
78
+ if (script.src) {
79
+ scriptElement.src = script.src;
80
+ } else if (script.text) {
81
+ scriptElement.textContent = script.text;
82
+ }
83
+
84
+ // Append to head to execute
85
+ document.head.appendChild(scriptElement);
86
+ });
87
+
88
+ // Clear scripts after execution to avoid re-execution
89
+ this.scripts = [];
90
+ }
91
+
92
+ /*
93
+ Rendering
94
+ */
95
+ renderLightDom() {
96
+ if (!this.content) {
97
+ return html``;
98
+ }
99
+
100
+ // Use unsafeHTML to render the actual HTML content
101
+ return html`${unsafeHTML(this.content)}`;
102
+ }
103
+ }
104
+ window.customElements.define('k-import', Import);
@@ -1,32 +1,32 @@
1
- import { LitElement, html, render } from '../lit-all.min.js';
2
-
3
- export default class LightComponent extends LitElement {
4
-
5
- /*
6
- Light DOM Setup
7
- */
8
-
9
- createRenderRoot() {
10
- this.lightRoot = document.createElement('div');
11
- this.lightRoot.style.display = 'contents';
12
- this.appendChild(this.lightRoot);
13
-
14
- return this;
15
- }
16
-
17
- /*
18
- Lifecycle Callbacks
19
- */
20
-
21
- updated() {
22
- render(this.renderLightDom(), this.lightRoot);
23
- }
24
-
25
- /*
26
- Rendering
27
- */
28
-
29
- renderLightDom() {
30
- return html``;
31
- }
32
- }
1
+ import { LitElement, html, render } from '../lit-all.min.js';
2
+
3
+ export default class LightComponent extends LitElement {
4
+
5
+ /*
6
+ Light DOM Setup
7
+ */
8
+
9
+ createRenderRoot() {
10
+ this.lightRoot = document.createElement('div');
11
+ this.lightRoot.style.display = 'contents';
12
+ this.appendChild(this.lightRoot);
13
+
14
+ return this;
15
+ }
16
+
17
+ /*
18
+ Lifecycle Callbacks
19
+ */
20
+
21
+ updated() {
22
+ render(this.renderLightDom(), this.lightRoot);
23
+ }
24
+
25
+ /*
26
+ Rendering
27
+ */
28
+
29
+ renderLightDom() {
30
+ return html``;
31
+ }
32
+ }
@@ -1,17 +1,30 @@
1
- import { LitElement } from '../lit-all.min.js';
2
-
3
- export default class ShadowComponent extends LitElement {
4
- createRenderRoot() {
5
- const shadowRoot = this.attachShadow({ mode: 'open' });
6
-
7
- const link = document.createElement('link');
8
- link.rel = 'stylesheet';
9
- link.href = '/kempo.min.css';
10
- shadowRoot.appendChild(link);
11
-
12
- const renderContainer = document.createElement('div');
13
- shadowRoot.appendChild(renderContainer);
14
-
15
- return renderContainer;
16
- }
17
- }
1
+ import { LitElement } from '../lit-all.min.js';
2
+
3
+ export default class ShadowComponent extends LitElement {
4
+ createRenderRoot() {
5
+ const shadowRoot = this.attachShadow({ mode: 'open' });
6
+
7
+ const link = document.createElement('link');
8
+ link.rel = 'stylesheet';
9
+ link.href = '/kempo.min.css';
10
+ shadowRoot.appendChild(link);
11
+
12
+ // Inject component styles if they exist
13
+ const styles = this.constructor.styles;
14
+ if (styles) {
15
+ const styleEl = document.createElement('style');
16
+ if (Array.isArray(styles)) {
17
+ styleEl.textContent = styles.map(s => s.cssText || s).join('\n');
18
+ } else {
19
+ styleEl.textContent = styles.cssText || styles;
20
+ }
21
+ shadowRoot.appendChild(styleEl);
22
+ }
23
+
24
+ const renderContainer = document.createElement('div');
25
+ renderContainer.style.display = 'contents';
26
+ shadowRoot.appendChild(renderContainer);
27
+
28
+ return renderContainer;
29
+ }
30
+ }
@@ -0,0 +1,167 @@
1
+ import ShadowComponent from './ShadowComponent.js';
2
+ import { html, css } from '../lit-all.min.js';
3
+ import { boolTrueFalse } from '../utils/propConverters.js';
4
+ import './FocusCapture.js';
5
+
6
+ export default class SideMenu extends ShadowComponent {
7
+ /*
8
+ Properties
9
+ */
10
+ static properties = {
11
+ opened: { type: Boolean, reflect: true },
12
+ overlayClose: { type: Boolean, reflect: true, attribute: 'overlay-close', converter: boolTrueFalse },
13
+ side: { type: String, reflect: true }
14
+ };
15
+
16
+ constructor() {
17
+ super();
18
+ this.opened = false;
19
+ this.overlayClose = true;
20
+ this.side = 'left';
21
+ }
22
+
23
+ /*
24
+ Event Handlers
25
+ */
26
+ overlayClick = () => {
27
+ if(this.overlayClose) this.close();
28
+ }
29
+
30
+ /*
31
+ Lifecycle Callbacks
32
+ */
33
+ updated(changedProperties) {
34
+ super.updated(changedProperties);
35
+
36
+ if(changedProperties.has('opened')) {
37
+ if(this.opened) {
38
+ this.dispatchEvent(new CustomEvent('change', { detail: 'open' }));
39
+ this.dispatchEvent(new CustomEvent('open'));
40
+ } else {
41
+ this.dispatchEvent(new CustomEvent('change', { detail: 'close' }));
42
+ this.dispatchEvent(new CustomEvent('close'));
43
+ }
44
+ }
45
+ }
46
+
47
+ /*
48
+ Methods
49
+ */
50
+ open() {
51
+ this.opened = true;
52
+ }
53
+
54
+ close() {
55
+ this.opened = false;
56
+ }
57
+
58
+ toggle() {
59
+ this.opened ? this.close() : this.open();
60
+ this.dispatchEvent(new CustomEvent('toggle'));
61
+ }
62
+
63
+ /*
64
+ Rendering
65
+ */
66
+ render() {
67
+ return html`
68
+ <k-focus-capture>
69
+ <div id="container">
70
+ <button id="overlay" @click=${this.overlayClick}>
71
+ <div id="overlay-x">✕</div>
72
+ </button>
73
+ <div id="menu">
74
+ <slot></slot>
75
+ </div>
76
+ </div>
77
+ </k-focus-capture>
78
+ `;
79
+ }
80
+ static styles = css`
81
+ :host {
82
+ --bg: var(--c_bg);
83
+ --width: 20rem;
84
+
85
+ position: fixed;
86
+ top: 0;
87
+ left: 0;
88
+ width: 100vw;
89
+ max-width: 100%;
90
+ height: 100vh;
91
+ z-index: 100;
92
+ pointer-events: none;
93
+ }
94
+ :host([opened]) {
95
+ pointer-events: auto;
96
+ }
97
+ k-focus-capture {
98
+ width: 100%;
99
+ height: 100%;
100
+ }
101
+ #container {
102
+ position: relative;
103
+ width: 100%;
104
+ height: 100%;
105
+ opacity: 0;
106
+ transition: opacity var(--animation_ms, 256ms);
107
+ }
108
+ :host([opened]) #container {
109
+ opacity: 1;
110
+ }
111
+ #overlay {
112
+ position: absolute;
113
+ width: 100%;
114
+ height: 100%;
115
+ left: 0;
116
+ top: 0;
117
+ background: var(--overlay, rgba(0, 0, 0, 0.5));
118
+ border: none;
119
+ padding: 0;
120
+ cursor: pointer;
121
+ z-index: 1;
122
+ }
123
+ #overlay-x {
124
+ position: absolute;
125
+ top: var(--spacer_h);
126
+ right: var(--spacer_h);
127
+ font-size: 1.75rem;
128
+ cursor: pointer;
129
+ color: var(--tc_light);
130
+ }
131
+ :host([overlay-close="false"]) #overlay-x {
132
+ display: none;
133
+ }
134
+ :host([overlay-close="false"]) #overlay {
135
+ cursor: default;
136
+ }
137
+ #menu {
138
+ position: absolute;
139
+ width: var(--width);
140
+ max-width: calc(100vw - 6rem);
141
+ height: 100vh;
142
+ overflow-y: auto;
143
+ left: calc(var(--width) * -1);
144
+ top: 0;
145
+ background: var(--bg);
146
+ transition: left var(--animation_ms, 256ms);
147
+ padding: var(--menu_padding, var(--spacer));
148
+ z-index: 2;
149
+ }
150
+ :host([opened]) #menu {
151
+ left: 0;
152
+ }
153
+ :host([side="right"]) #menu {
154
+ left: auto;
155
+ transition: right var(--animation_ms, 256ms);
156
+ right: calc(var(--width) * -1);
157
+ }
158
+ :host([opened][side="right"]) #menu {
159
+ right: 0;
160
+ }
161
+ :host([side="right"]) #overlay-x {
162
+ right: auto;
163
+ left: var(--spacer_h);
164
+ }
165
+ `;
166
+ }
167
+ window.customElements.define('k-side-menu', SideMenu);