kempo-ui 0.0.67 → 0.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/components/ShadowComponent.js +1 -1
  2. package/dist/components/Tree.js +10 -16
  3. package/docs/components/accordion.html +1 -1
  4. package/docs/components/card.html +1 -1
  5. package/docs/components/color-picker.html +1 -1
  6. package/docs/components/content-slider.html +1 -1
  7. package/docs/components/dialog.html +1 -1
  8. package/docs/components/dropdown.html +1 -1
  9. package/docs/components/focus-capture.html +1 -1
  10. package/docs/components/html-editor.html +1 -1
  11. package/docs/components/hybrid-component.html +1 -1
  12. package/docs/components/icon.html +1 -1
  13. package/docs/components/import.html +1 -1
  14. package/docs/components/light-component.html +1 -1
  15. package/docs/components/photo-viewer.html +1 -1
  16. package/docs/components/resize.html +1 -1
  17. package/docs/components/shadow-component.html +1 -1
  18. package/docs/components/show-more.html +1 -1
  19. package/docs/components/side-menu.html +1 -1
  20. package/docs/components/side-panel-basic.html +1 -1
  21. package/docs/components/side-panel-dual.html +1 -1
  22. package/docs/components/side-panel-menu.html +1 -1
  23. package/docs/components/side-panel-persistent.html +1 -1
  24. package/docs/components/side-panel-scroll.html +1 -1
  25. package/docs/components/side-panel.html +1 -1
  26. package/docs/components/sortable.html +1 -1
  27. package/docs/components/spinner.html +1 -1
  28. package/docs/components/split.html +1 -1
  29. package/docs/components/table.html +1 -1
  30. package/docs/components/tableControls.html +1 -1
  31. package/docs/components/tableCustomFields.html +1 -1
  32. package/docs/components/tableFetchRecords.html +1 -1
  33. package/docs/components/tableFieldSortHide.html +1 -1
  34. package/docs/components/tablePagination.html +1 -1
  35. package/docs/components/tableRecordEditing.html +1 -1
  36. package/docs/components/tableRecordFiltering.html +1 -1
  37. package/docs/components/tableRecordHiding.html +1 -1
  38. package/docs/components/tableRecordSearching.html +1 -1
  39. package/docs/components/tableRecordSelection.html +1 -1
  40. package/docs/components/tableRowControls.html +1 -1
  41. package/docs/components/tableSorting.html +1 -1
  42. package/docs/components/tabs.html +1 -1
  43. package/docs/components/tags.html +1 -1
  44. package/docs/components/theme-switcher.html +1 -1
  45. package/docs/components/timestamp.html +1 -1
  46. package/docs/components/toast.html +1 -1
  47. package/docs/components/toggle.html +19 -1
  48. package/docs/components/tree.html +197 -30
  49. package/docs/index.html +1 -1
  50. package/docs/src/components/ShadowComponent.js +1 -1
  51. package/docs/src/components/Tree.js +10 -16
  52. package/docs/utils/context.html +1 -1
  53. package/docs/utils/cookie.html +1 -1
  54. package/docs/utils/debounce.html +1 -1
  55. package/docs/utils/drag.html +1 -1
  56. package/docs/utils/formatTimestamp.html +1 -1
  57. package/docs/utils/object.html +1 -1
  58. package/docs/utils/propConverters.html +1 -1
  59. package/docs/utils/string.html +1 -1
  60. package/docs/utils/theme.html +1 -1
  61. package/docs/utils/toTitleCase.html +1 -1
  62. package/docs/utils/type.html +1 -1
  63. package/docs/utils/wait.html +1 -1
  64. package/package.json +5 -1
  65. package/src/components/ShadowComponent.js +1 -1
  66. package/src/components/Toggle.js +2 -0
  67. package/src/components/Tree.js +95 -137
  68. package/tests/components/Toggle.browser-test.js +31 -0
  69. package/tests/components/Tree.browser-test.js +161 -161
  70. package/tools/highlight.js +51 -0
@@ -1,15 +1,17 @@
1
- import ShadowComponent from './ShadowComponent.js';
2
- import { html, css, render } from '../lit-all.min.js';
1
+ import ShadowComponent from './ShadowComponent.js';
2
+ import { html, css } from '../lit-all.min.js';
3
3
  import { boolExists } from '../utils/propConverters.js';
4
4
  import './Icon.js';
5
5
 
6
+ let nodeTagCounter = 0;
7
+
6
8
  export default class Tree extends ShadowComponent {
7
9
  /* Properties */
8
10
  static properties = {
9
11
  data: { type: Object },
10
12
  depth: { type: Number, reflect: true },
11
13
  editable: { type: Boolean, converter: boolExists, attribute: 'editable', reflect: true }
12
- }
14
+ };
13
15
 
14
16
  constructor(){
15
17
  super();
@@ -20,129 +22,129 @@ export default class Tree extends ShadowComponent {
20
22
 
21
23
  /* Rendering */
22
24
  render(){
23
- if(!this.data){
24
- return html`<slot></slot>`;
25
- }
26
-
27
- if(typeof this.data === 'object' && this.data !== null){
25
+ if(this.data === null || this.data === undefined) return html`<slot></slot>`;
26
+ if(Tree.nodes.find(n => n.detect(this.data))){
28
27
  return html`
29
28
  <div class="tree-root">
30
- ${(Array.isArray(this.data)
31
- ? this.data.map((v, i) => [i, v])
32
- : Object.entries(this.data)
33
- ).map(([key, value]) => Tree.renderValue(value, key, 1, this.depth))}
29
+ ${Tree.renderValue(this.data, null, 0, this.depth)}
34
30
  </div>
35
31
  `;
36
32
  }
37
-
33
+ const entries = Array.isArray(this.data)
34
+ ? this.data.map((v, i) => [i, v])
35
+ : typeof this.data === 'object'
36
+ ? Object.entries(this.data)
37
+ : null;
38
38
  return html`
39
39
  <div class="tree-root">
40
- ${Tree.renderValue(this.data, null, 0, this.depth)}
40
+ ${entries
41
+ ? entries.map(([key, value]) => Tree.renderValue(value, key, 1, this.depth))
42
+ : Tree.renderValue(this.data, null, 0, this.depth)}
41
43
  </div>
42
44
  `;
43
45
  }
44
46
 
45
- /* Leaf Definitions */
46
- static leafs = [];
47
+ /* Node Registry */
48
+ static nodes = [];
47
49
 
48
- static addLeaf = (...leafs) => {
49
- leafs.forEach(leaf => Tree.leafs.unshift(leaf));
50
+ static addNode = (...nodes) => {
51
+ [...nodes].reverse().forEach(node => {
52
+ if(!Object.prototype.hasOwnProperty.call(node, 'nodeTag')){
53
+ node.nodeTag = `k-tree-node-${nodeTagCounter++}`;
54
+ window.customElements.define(node.nodeTag, node);
55
+ }
56
+ Tree.nodes.unshift(node);
57
+ });
50
58
  };
51
59
 
52
60
  /* Static Methods */
53
- static renderValue(value, key = null, currentDepth = 0, maxDepth = 0){
54
- const LeafClass = Tree.leafs.find(leaf => leaf.detect(value));
55
-
56
- if(LeafClass){
57
- const wrapper = document.createElement('span');
58
- wrapper.className = 'd-b';
59
- if(key !== null){
60
- const keyLabel = document.createElement('span');
61
- keyLabel.className = typeof key === 'number' ? 'tc-muted' : '';
62
- keyLabel.textContent = `${key}: `;
63
- wrapper.appendChild(keyLabel);
64
- }
65
- const leaf = new LeafClass(value);
66
- const rendered = leaf.render();
67
- if(rendered instanceof Node){
68
- wrapper.appendChild(rendered);
69
- } else {
70
- const span = document.createElement('span');
71
- render(rendered, span);
72
- wrapper.appendChild(span);
73
- }
74
- return wrapper;
75
- }
76
-
77
- if(typeof value === 'object' && value !== null){
78
- const branch = new TreeBranch();
79
- branch.value = value;
80
- branch.key = key;
81
- branch.currentDepth = currentDepth;
82
- branch.maxDepth = maxDepth;
83
- return branch;
84
- }
85
-
86
- return html`<span class="d-b primitive">${key !== null ? html`<span class="${typeof key === 'number' ? 'tc-muted' : ''}">${key}: </span>` : ''}${value}</span>`;
61
+ static renderValue(value, key = null, depth = 0, maxDepth = 0){
62
+ const NodeClass = Tree.nodes.find(n => n.detect(value)) ?? TreeNode;
63
+ const el = document.createElement(NodeClass.nodeTag);
64
+ el.value = value;
65
+ el.key = key;
66
+ el.depth = depth;
67
+ el.maxDepth = maxDepth;
68
+ return el;
87
69
  }
88
70
  }
89
71
 
90
72
  window.customElements.define('k-tree', Tree);
91
73
 
92
- export class TreeBranch extends ShadowComponent {
74
+ export class TreeNode extends ShadowComponent {
75
+ static nodeTag = 'k-tree-node';
76
+
93
77
  /* Properties */
94
78
  static properties = {
95
79
  value: { type: Object },
96
- key: { type: String },
97
- currentDepth: { type: Number },
80
+ key: { attribute: false },
81
+ depth: { type: Number },
98
82
  maxDepth: { type: Number },
99
- opened: { type: Boolean, converter: boolExists, reflect: true }
83
+ opened: { type: Boolean, converter: boolExists, reflect: true },
84
+ icon: { type: String }
100
85
  };
101
86
 
102
87
  constructor(){
103
88
  super();
104
89
  this.value = null;
105
90
  this.key = null;
106
- this.currentDepth = 0;
91
+ this.depth = 0;
107
92
  this.maxDepth = 0;
108
93
  this.opened = false;
94
+ this.icon = null;
109
95
  }
110
96
 
111
97
  /* Lifecycle */
112
98
  connectedCallback(){
113
99
  super.connectedCallback();
114
- if(this.currentDepth <= this.maxDepth){
115
- this.opened = true;
116
- }
100
+ if(this.depth <= this.maxDepth) this.opened = true;
117
101
  }
118
102
 
119
103
  /* Members */
120
- get tree(){
121
- return this.closest('k-tree');
122
- }
104
+ get tree(){ return this.closest('k-tree'); }
123
105
 
124
106
  /* Event Handlers */
125
- toggle = () => {
126
- this.opened = !this.opened;
127
- };
107
+ toggle = () => { this.opened = !this.opened; };
128
108
 
129
109
  /* Rendering */
110
+ renderLabel(){
111
+ if(typeof this.value === 'object' && this.value !== null){
112
+ return html`${Array.isArray(this.value) ? 'Array' : 'Object'}`;
113
+ }
114
+ return html`${this.value}`;
115
+ }
116
+
117
+ getChildren(){
118
+ if(typeof this.value !== 'object' || this.value === null) return null;
119
+ return Array.isArray(this.value)
120
+ ? this.value.map((v, i) => [i, v])
121
+ : Object.entries(this.value);
122
+ }
123
+
124
+ renderIcon(){
125
+ if(this.icon) return html`<k-icon name="${this.icon}"></k-icon>`;
126
+ return html`<k-icon name="chevron" class="toggle-icon" direction="${this.opened ? 'down' : 'right'}"></k-icon>`;
127
+ }
128
+
130
129
  render(){
131
- const label = this.key !== null ? `${this.key}: ` : '';
132
- const type = Array.isArray(this.value) ? 'Array' : 'Object';
130
+ const children = this.getChildren();
131
+ const keyLabel = this.key !== null
132
+ ? html`<span class="${typeof this.key === 'number' ? 'tc-muted' : ''}">${this.key}: </span>`
133
+ : '';
134
+
135
+ if(!children){
136
+ return html`<span class="d-b">${keyLabel}${this.renderLabel()}</span>`;
137
+ }
133
138
 
134
139
  return html`
135
140
  <div>
136
141
  <button class="branch-label no-btn" @click=${this.toggle} aria-expanded="${this.opened}">
137
- <k-icon name="chevron-right" class="toggle-icon ${this.opened ? 'opened' : ''}"></k-icon>
138
- ${label}${type}
142
+ ${this.renderIcon()}
143
+ ${keyLabel}${this.renderLabel()}
139
144
  </button>
140
145
  ${this.opened ? html`
141
146
  <div class="pl">
142
- ${this.value ? (Array.isArray(this.value)
143
- ? this.value.map((v, i) => [i, v])
144
- : Object.entries(this.value)
145
- ).map(([key, value]) => Tree.renderValue(value, key, this.currentDepth + 1, this.maxDepth)) : ''}
147
+ ${children.map(([k, v]) => Tree.renderValue(v, k, this.depth + 1, this.maxDepth))}
146
148
  </div>
147
149
  ` : ''}
148
150
  </div>
@@ -164,81 +166,37 @@ export class TreeBranch extends ShadowComponent {
164
166
  .branch-label:focus-visible{
165
167
  box-shadow: var(--focus_shadow);
166
168
  }
167
- .toggle-icon{
168
- transition: transform var(--animation_ms, 200ms);
169
- }
170
- .toggle-icon.opened{
171
- transform: rotate(90deg);
172
- }
173
169
  `;
174
- }
175
170
 
176
- window.customElements.define('k-tree-branch', TreeBranch);
177
-
178
- export class TreeLeaf {
179
- constructor(value = null){
180
- this.value = value;
181
- }
182
-
183
- /* Rendering */
184
- render(){
185
- return html`${this.value}`;
186
- }
187
-
188
- /* Static Methods */
189
- static detect = () => {
190
- return false;
191
- };
171
+ static detect = () => false;
192
172
  }
193
173
 
194
- export class StringLeaf extends TreeLeaf {
195
- /* Rendering */
196
- render(){
197
- return html`<span class="tc-success">"${this.value}"</span>`;
198
- }
174
+ window.customElements.define('k-tree-node', TreeNode);
199
175
 
200
- /* Static Methods */
201
- static detect = value => typeof value === 'string';
176
+ export class StringNode extends TreeNode {
177
+ renderLabel(){ return html`<span class="tc-success">"${this.value}"</span>`; }
178
+ static detect = v => typeof v === 'string';
202
179
  }
203
180
 
204
- export class NumberLeaf extends TreeLeaf {
205
- /* Rendering */
206
- render(){
207
- return html`<span class="tc-primary">${this.value}</span>`;
208
- }
209
-
210
- /* Static Methods */
211
- static detect = value => typeof value === 'number';
181
+ export class NumberNode extends TreeNode {
182
+ renderLabel(){ return html`<span class="tc-primary">${this.value}</span>`; }
183
+ static detect = v => typeof v === 'number';
212
184
  }
213
185
 
214
- export class BooleanLeaf extends TreeLeaf {
215
- /* Rendering */
216
- render(){
217
- return html`<span class="${this.value ? 'tc-success' : 'tc-danger'}">${this.value}</span>`;
218
- }
219
-
220
- /* Static Methods */
221
- static detect = value => typeof value === 'boolean';
186
+ export class BooleanNode extends TreeNode {
187
+ renderLabel(){ return html`<span class="${this.value ? 'tc-success' : 'tc-danger'}">${this.value}</span>`; }
188
+ static detect = v => typeof v === 'boolean';
222
189
  }
223
190
 
224
- export class NullLeaf extends TreeLeaf {
225
- /* Rendering */
226
- render(){
227
- return html`<span class="tc-muted">null</span>`;
228
- }
229
-
230
- /* Static Methods */
231
- static detect = value => value === null;
191
+ export class NullNode extends TreeNode {
192
+ renderLabel(){ return html`<span class="tc-muted">null</span>`; }
193
+ static detect = v => v === null;
232
194
  }
233
195
 
234
- export class UndefinedLeaf extends TreeLeaf {
235
- /* Rendering */
236
- render(){
237
- return html`<span class="tc-muted">undefined</span>`;
238
- }
239
-
240
- /* Static Methods */
241
- static detect = value => value === undefined;
196
+ export class UndefinedNode extends TreeNode {
197
+ renderLabel(){ return html`<span class="tc-muted">undefined</span>`; }
198
+ static detect = v => v === undefined;
242
199
  }
243
200
 
244
- Tree.addLeaf(UndefinedLeaf, NullLeaf, BooleanLeaf, NumberLeaf, StringLeaf);
201
+ Tree.addNode(StringNode, NumberNode, BooleanNode, NullNode, UndefinedNode);
202
+
@@ -512,6 +512,37 @@ export default {
512
512
  pass('Label content projected');
513
513
  },
514
514
 
515
+ 'switch should not shrink in constrained container': async ({pass, fail}) => {
516
+ const { container, toggle } = await createToggle();
517
+ container.style.width = '100px';
518
+ await toggle.updateComplete;
519
+ const switchEl = toggle.shadowRoot.querySelector('#switch');
520
+ const style = window.getComputedStyle(switchEl);
521
+ if(style.flexShrink !== '0'){
522
+ cleanup(container);
523
+ return fail(`Expected switch flex-shrink to be 0, got ${style.flexShrink}`);
524
+ }
525
+ if(style.flexGrow !== '0'){
526
+ cleanup(container);
527
+ return fail(`Expected switch flex-grow to be 0, got ${style.flexGrow}`);
528
+ }
529
+ cleanup(container);
530
+ pass('Switch does not shrink in constrained container');
531
+ },
532
+
533
+ 'label should shrink in constrained container': async ({pass, fail}) => {
534
+ const { container, toggle } = await createToggle();
535
+ await toggle.updateComplete;
536
+ const label = toggle.shadowRoot.querySelector('#label');
537
+ const style = window.getComputedStyle(label);
538
+ if(style.flexGrow !== '1'){
539
+ cleanup(container);
540
+ return fail(`Expected label flex-grow to be 1, got ${style.flexGrow}`);
541
+ }
542
+ cleanup(container);
543
+ pass('Label grows to fill remaining space');
544
+ },
545
+
515
546
  /*
516
547
  Method Chaining
517
548
  */