kempo-ui 0.3.14 → 0.3.16

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 (82) hide show
  1. package/dist/components/Combobox.js +97 -0
  2. package/dist/components/Option.js +1 -0
  3. package/dist/components/ThemeSwitcher.js +77 -14
  4. package/docs/components/accordion.html +282 -94
  5. package/docs/components/aside.html +282 -94
  6. package/docs/components/card.html +282 -94
  7. package/docs/components/code-editor.html +282 -94
  8. package/docs/components/color-picker.html +282 -94
  9. package/docs/components/combobox.html +798 -0
  10. package/docs/components/content-slider.html +282 -94
  11. package/docs/components/context.html +282 -94
  12. package/docs/components/dialog.html +282 -94
  13. package/docs/components/dropdown.html +282 -94
  14. package/docs/components/filter-list.html +282 -94
  15. package/docs/components/focus-capture.html +282 -94
  16. package/docs/components/html-editor.html +282 -94
  17. package/docs/components/hybrid-component.html +282 -94
  18. package/docs/components/icon.html +282 -94
  19. package/docs/components/import.html +282 -94
  20. package/docs/components/light-component.html +282 -94
  21. package/docs/components/nav-spacer.html +282 -94
  22. package/docs/components/nav.html +282 -94
  23. package/docs/components/photo-viewer.html +282 -94
  24. package/docs/components/resize.html +282 -94
  25. package/docs/components/shadow-component.html +282 -94
  26. package/docs/components/show-more.html +282 -94
  27. package/docs/components/sortable.html +282 -94
  28. package/docs/components/spinner.html +282 -94
  29. package/docs/components/split.html +282 -94
  30. package/docs/components/table.html +282 -94
  31. package/docs/components/tableControls.html +282 -94
  32. package/docs/components/tableCustomFields.html +282 -94
  33. package/docs/components/tableFetchRecords.html +282 -94
  34. package/docs/components/tableFieldSortHide.html +282 -94
  35. package/docs/components/tablePagination.html +282 -94
  36. package/docs/components/tablePlaceholder.html +282 -94
  37. package/docs/components/tableRecordEditing.html +282 -94
  38. package/docs/components/tableRecordFiltering.html +282 -94
  39. package/docs/components/tableRecordHiding.html +282 -94
  40. package/docs/components/tableRecordSearching.html +282 -94
  41. package/docs/components/tableRecordSelection.html +282 -94
  42. package/docs/components/tableRowControls.html +282 -94
  43. package/docs/components/tableServerSync.html +282 -94
  44. package/docs/components/tableSorting.html +282 -94
  45. package/docs/components/tabs.html +282 -94
  46. package/docs/components/tags.html +282 -94
  47. package/docs/components/theme-select.html +282 -94
  48. package/docs/components/theme-switcher-aside.html +47 -0
  49. package/docs/components/theme-switcher.html +441 -101
  50. package/docs/components/timestamp.html +282 -94
  51. package/docs/components/toast.html +282 -94
  52. package/docs/components/toggle.html +282 -94
  53. package/docs/components/tree.html +282 -94
  54. package/docs/index.html +288 -94
  55. package/docs/src/components/Combobox.js +97 -0
  56. package/docs/src/components/Option.js +1 -0
  57. package/docs/src/components/ThemeSwitcher.js +77 -14
  58. package/docs/utils/context.html +282 -94
  59. package/docs/utils/cookie.html +282 -94
  60. package/docs/utils/debounce.html +282 -94
  61. package/docs/utils/drag.html +282 -94
  62. package/docs/utils/elevation.html +282 -94
  63. package/docs/utils/formatTimestamp.html +282 -94
  64. package/docs/utils/object.html +282 -94
  65. package/docs/utils/propConverters.html +282 -94
  66. package/docs/utils/string.html +282 -94
  67. package/docs/utils/theme.html +282 -94
  68. package/docs/utils/toTitleCase.html +282 -94
  69. package/docs/utils/type.html +282 -94
  70. package/docs/utils/wait.html +282 -94
  71. package/docs-src/components/combobox.page.html +358 -0
  72. package/docs-src/components/theme-switcher-aside.page.html +35 -0
  73. package/docs-src/components/theme-switcher.page.html +159 -7
  74. package/docs-src/index.page.html +6 -0
  75. package/docs-src/nav.fragment.html +283 -95
  76. package/llms.txt +2 -1
  77. package/package.json +1 -1
  78. package/src/components/Combobox.js +338 -0
  79. package/src/components/Option.js +19 -0
  80. package/src/components/ThemeSwitcher.js +171 -24
  81. package/tests/components/Combobox.browser-test.js +790 -0
  82. package/tests/components/ThemeSwitcher.browser-test.js +361 -33
@@ -0,0 +1,97 @@
1
+ import{html as e,css as t}from"../lit-all.min.js";import s from"./ShadowComponent.js";import i from"../utils/debounce.js";import"./Option.js";export default class o extends s{static formAssociated=!0;static properties={value:{type:String,reflect:!0},name:{type:String,reflect:!0},placeholder:{type:String},opened:{type:Boolean,reflect:!0},searching:{type:Boolean,reflect:!0},required:{type:Boolean,reflect:!0},requireMatch:{type:Boolean,reflect:!0,attribute:"require-match"},disabled:{type:Boolean,reflect:!0},debounceMs:{type:Number,attribute:"debounce-ms"},maxVisible:{type:Number,attribute:"max-visible"}};#e=-1;#t=[];constructor(){super(),this.internals=this.attachInternals(),this.value="",this.name="",this.placeholder="",this.opened=!1,this.searching=!1,this.required=!1,this.requireMatch=!1,this.disabled=!1,this.debounceMs=300,this.maxVisible=8,this.#s(300)}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.handleDocumentClick),this.#i()}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.handleDocumentClick)}childrenUpdated(){this.#i()}updated(e){super.updated(e),e.has("debounceMs")&&this.#s(this.debounceMs),(e.has("value")||e.has("required")||e.has("requireMatch"))&&this.#o()}formResetCallback(){this.value="",this.#o()}formStateRestoreCallback(e){this.value=e}handleInput=e=>{this.value=e.target.value,this.opened=!0,this.#e=-1,this.#a(this.value)};handleKeydown=e=>{const t=this.#n;if("ArrowDown"===e.key){if(e.preventDefault(),!this.opened)return void(this.opened=!0);this.#e=Math.min(this.#e+1,t.length-1),this.requestUpdate(),this.#r()}else"ArrowUp"===e.key?(e.preventDefault(),this.#e=Math.max(this.#e-1,0),this.requestUpdate(),this.#r()):"Enter"===e.key?(e.preventDefault(),this.#e>=0&&this.#e<t.length&&this.#l(t[this.#e])):"Escape"===e.key?(e.preventDefault(),this.opened=!1,this.#e=-1):"Tab"===e.key&&(this.opened=!1,this.#e=-1)};handleDocumentClick=e=>{e.composedPath().includes(this)||(this.opened=!1,this.#e=-1)};handleFocus=()=>{this.opened=!0};handleOptionClick=e=>{const t=Number(e.currentTarget.dataset.index);this.#l(this.#n[t])};setOptions(e){return[...this.querySelectorAll("k-option")].forEach(e=>e.remove()),e.forEach(e=>{const t=document.createElement("k-option");"object"==typeof e?(t.setAttribute("value",e.value??e.label),t.textContent=e.label):t.textContent=e,this.appendChild(t)}),this}clear(){return this.value="",this.opened=!1,this.#e=-1,this.#o(),this}#s(e){this.#a=i(e=>{this.dispatchEvent(new CustomEvent("search",{detail:{value:e},bubbles:!0,composed:!0}))},e)}#a=()=>{};#i(){this.#t=[...this.querySelectorAll("k-option")].map(e=>({label:e.label,value:e.value})),this.requestUpdate()}#l(e){this.value=e.label,this.opened=!1,this.#e=-1,this.#o(),this.dispatchEvent(new CustomEvent("select",{detail:{value:e.value,label:e.label},bubbles:!0,composed:!0})),this.dispatchEvent(new CustomEvent("change",{bubbles:!0}))}#o(){const e=this.#t.find(e=>e.label===this.value);this.internals.setFormValue(e?e.value:this.value),this.required&&!this.value?this.internals.setValidity({valueMissing:!0},"Please select a value.",this.shadowRoot?.querySelector("input")):this.requireMatch&&this.value&&!e?this.internals.setValidity({customError:!0},"Please select a valid option.",this.shadowRoot?.querySelector("input")):this.internals.setValidity({})}#r(){this.updateComplete.then(()=>{this.shadowRoot?.querySelector(".option.focused")?.scrollIntoView({block:"nearest"})})}get#d(){const e=(this.value||"").toLowerCase();return e?this.#t.filter(t=>t.label.toLowerCase().includes(e)):this.#t}get#n(){return this.#d.slice(0,this.maxVisible)}render(){const t=this.#n,s=this.#d.length>this.maxVisible;return e`
2
+ <slot style="display:none"></slot>
3
+ <input
4
+ type="text"
5
+ .value=${this.value}
6
+ placeholder=${this.placeholder}
7
+ ?disabled=${this.disabled}
8
+ @input=${this.handleInput}
9
+ @keydown=${this.handleKeydown}
10
+ @focus=${this.handleFocus}
11
+ autocomplete="off"
12
+ />
13
+ ${this.opened?e`
14
+ <div id="menu">
15
+ ${t.map((t,s)=>e`
16
+ <div
17
+ class="option ${s===this.#e?"focused":""}"
18
+ data-index=${s}
19
+ @click=${this.handleOptionClick}
20
+ >${t.label}</div>
21
+ `)}
22
+ ${s&&!this.searching?e`
23
+ <div class="more">${this.#d.length-this.maxVisible} more...</div>
24
+ `:""}
25
+ ${0!==t.length||this.searching?"":e`
26
+ <div class="no-results">No matches</div>
27
+ `}
28
+ ${this.searching?e`
29
+ <div class="searching">
30
+ <k-spinner size="xs"></k-spinner>
31
+ <span>Searching...</span>
32
+ </div>
33
+ `:""}
34
+ </div>
35
+ `:""}
36
+ `}static styles=t`
37
+ :host {
38
+ display: block;
39
+ position: relative;
40
+ }
41
+ :host([disabled]) {
42
+ opacity: 0.5;
43
+ pointer-events: none;
44
+ }
45
+ input {
46
+ width: 100%;
47
+ box-sizing: border-box;
48
+ padding: var(--spacer_h) var(--spacer);
49
+ border: 1px solid var(--c_border);
50
+ border-radius: var(--radius);
51
+ background: var(--c_bg);
52
+ color: var(--tc);
53
+ font: inherit;
54
+ outline: none;
55
+ transition: border-color var(--animation_ms);
56
+ }
57
+ input:focus {
58
+ border-color: var(--c_primary);
59
+ }
60
+ #menu {
61
+ position: absolute;
62
+ top: 100%;
63
+ left: 0;
64
+ right: 0;
65
+ z-index: 70;
66
+ max-height: 20rem;
67
+ overflow-y: auto;
68
+ background: var(--c_bg);
69
+ border: 1px solid var(--c_border);
70
+ border-radius: var(--radius);
71
+ box-shadow: var(--drop_shadow);
72
+ margin-top: 0.25rem;
73
+ }
74
+ .option {
75
+ padding: var(--spacer_h) var(--spacer);
76
+ cursor: pointer;
77
+ transition: background var(--animation_ms);
78
+ }
79
+ .option:hover,
80
+ .option.focused {
81
+ background: var(--c_bg__alt);
82
+ }
83
+ .no-results,
84
+ .more {
85
+ padding: var(--spacer_h) var(--spacer);
86
+ color: var(--tc_muted);
87
+ font-style: italic;
88
+ }
89
+ .searching {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 0.5rem;
93
+ padding: var(--spacer_h) var(--spacer);
94
+ color: var(--tc_muted);
95
+ border-top: 1px solid var(--c_border);
96
+ }
97
+ `}customElements.define("k-combobox",o);
@@ -0,0 +1 @@
1
+ export default class t extends HTMLElement{static get observedAttributes(){return["value"]}get value(){return this.getAttribute("value")??this.textContent.trim()}set value(t){this.setAttribute("value",t)}get label(){return this.textContent.trim()}}customElements.define("k-option",t);
@@ -1,24 +1,87 @@
1
- import t from"./ShadowComponent.js";import{html as e,css as r}from"../lit-all.min.js";import s from"../utils/theme.js";import"./Icon.js";export default class n extends t{static properties={currentTheme:{type:String,reflect:!0,attribute:"current-theme"}};constructor(){super(),this.currentTheme=s.get()}handleClick=()=>{const t=s.get();"auto"===t&&s.set("light"),"light"===t&&s.set("dark"),"dark"===t&&s.set("auto")};connectedCallback(){super.connectedCallback(),this.unsubscribe=s.subscribe(t=>{this.currentTheme=t})}disconnectedCallback(){super.disconnectedCallback(),this.unsubscribe&&this.unsubscribe()}static styles=r`
1
+ import e from"./ShadowComponent.js";import{html as t,css as s,nothing as r}from"../lit-all.min.js";import i from"../utils/theme.js";import"./Icon.js";const a={auto:"mode-auto",light:"mode-light",dark:"mode-dark"},o=e=>e.split(",").map(e=>e.trim()).filter(Boolean),n=e=>e.charAt(0).toUpperCase()+e.slice(1);export default class d extends e{static properties={currentTheme:{type:String,reflect:!0,attribute:"current-theme"},mode:{type:String,reflect:!0},options:{type:String,reflect:!0},labels:{type:String,reflect:!0},resolvedMode:{type:String,state:!0},resolvedLabels:{type:Array,state:!0}};constructor(){super(),this.currentTheme=i.get(),this.mode="auto",this.options="light, auto, dark",this.labels=null,this.resolvedMode="segmented",this.resolvedLabels=null}#e=null;handleAsideStateChange=e=>{"auto"===this.mode&&(this.resolvedMode="collapsed"===e.detail.state?"toggle":"segmented"),this.resolveLabels()};resolveMode=()=>{"toggle"!==this.mode&&"segmented"!==this.mode?this.#e?this.resolvedMode="collapsed"===this.#e.state?"toggle":"segmented":this.resolvedMode="segmented":this.resolvedMode=this.mode};resolveLabels=()=>{if(null===this.labels||void 0===this.labels||this.#e&&"collapsed"===this.#e.state)return void(this.resolvedLabels=null);const e=o(this.options);if(""===this.labels)this.resolvedLabels=e.map(n);else{const t=o(this.labels);this.resolvedLabels=e.map((e,s)=>t[s]??n(e))}};handleClick=()=>{const e=o(this.options);if(0===e.length)return;const t=e.indexOf(i.get());i.set(e[(t+1)%e.length])};handleSegmentClick=e=>{i.set(e)};connectedCallback(){super.connectedCallback(),this.unsubscribe=i.subscribe(e=>{this.currentTheme=e}),this.#e=this.closest("k-aside"),this.#e&&this.#e.addEventListener("aside_state_change",this.handleAsideStateChange),this.resolveMode(),this.resolveLabels()}disconnectedCallback(){super.disconnectedCallback(),this.unsubscribe&&this.unsubscribe(),this.#e&&(this.#e.removeEventListener("aside_state_change",this.handleAsideStateChange),this.#e=null)}updated(e){super.updated(e),e.has("mode")&&this.resolveMode(),(e.has("labels")||e.has("options"))&&this.resolveLabels()}static styles=s`
2
2
  :host {
3
- --padding: var(--spacer);
3
+ --padding: var(--spacer, 1rem);
4
+ --c_inactive: transparent;
5
+ --tc_inactive: inherit;
6
+ --c_inactive__hover: var(--c_bg__alt);
7
+ --tc_inactive__hover: inherit;
8
+ --c_active: var(--c_primary);
9
+ --tc_active: var(--tc_light);
10
+ --c_active__hover: var(--c_active);
11
+ --tc_active__hover: var(--tc_active);
12
+ --border: 1px solid var(--c_border);
13
+ }
14
+ button.no-btn {
15
+ padding: var(--padding);
16
+ border-radius: var(--radius);
4
17
  display: flex;
18
+ align-items: center;
19
+ gap: calc(var(--spacer, 1rem) * 0.35);
20
+ color: var(--tc_inactive);
21
+ }
22
+ button.no-btn:hover {
23
+ color: var(--tc_inactive__hover);
5
24
  }
6
- button {
25
+ .segmented {
26
+ display: inline-flex;
27
+ }
28
+ .segmented button {
7
29
  padding: var(--padding);
30
+ background: var(--c_inactive);
31
+ cursor: pointer;
32
+ color: var(--tc_inactive);
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ gap: calc(var(--spacer, 1rem) * 0.35);
37
+ border: var(--border);
38
+ border-right: none;
39
+ }
40
+ .segmented button:first-child {
41
+ border-radius: var(--radius) 0 0 var(--radius);
42
+ }
43
+ .segmented button:last-child {
44
+ border-radius: 0 var(--radius) var(--radius) 0;
45
+ border-right: var(--border);
46
+ }
47
+ .segmented button:not(:first-child):not(:last-child) {
48
+ border-radius: 0;
8
49
  }
9
- `;render(){const t="auto"===this.currentTheme?"mode-auto":"light"===this.currentTheme?"mode-light":"mode-dark";return e`
50
+ .segmented button.active {
51
+ background: var(--c_active);
52
+ color: var(--tc_active);
53
+ border-color: var(--c_active);
54
+ }
55
+ .segmented button.active + button {
56
+ border-left-color: var(--c_active);
57
+ }
58
+ .segmented button:not(.active):hover {
59
+ background: var(--c_inactive__hover);
60
+ color: var(--tc_inactive__hover);
61
+ }
62
+ .segmented button.active:hover {
63
+ background: var(--c_active__hover);
64
+ color: var(--tc_active__hover);
65
+ }
66
+ `;renderToggle(){return t`
10
67
  <button
11
68
  class="no-btn"
12
69
  @click=${this.handleClick}
13
70
  >
14
- <k-icon name=${t}></k-icon>
71
+ <k-icon name=${a[this.currentTheme]||"mode-auto"}></k-icon>
72
+ ${this.resolvedLabels?t`<span>${this.resolvedLabels[o(this.options).indexOf(this.currentTheme)]??n(this.currentTheme)}</span>`:r}
15
73
  </button>
16
- `}static styles=r`
17
- :host {
18
- --padding: var(--spacer, 1rem);
19
- }
20
- button.no-btn {
21
- padding: var(--padding);
22
- border-radius: var(--radius);
23
- }
24
- `;static setTheme(t){s.set(t)}static getCurrentTheme(){return s.get()}static getCalculatedCurrentTheme(){return s.getCalculated()}}customElements.define("k-theme-switcher",n);
74
+ `}renderSegmented(){const e=o(this.options);return t`
75
+ <div class="segmented">
76
+ ${e.map((e,s)=>t`
77
+ <button
78
+ class="no-style ${e===this.currentTheme?"active":""}"
79
+ @click=${()=>this.handleSegmentClick(e)}
80
+ title=${e}
81
+ >
82
+ <k-icon name=${a[e]||"mode-auto"}></k-icon>
83
+ ${this.resolvedLabels?t`<span>${this.resolvedLabels[s]}</span>`:r}
84
+ </button>
85
+ `)}
86
+ </div>
87
+ `}render(){return"toggle"===this.resolvedMode?this.renderToggle():this.renderSegmented()}static setTheme(e){i.set(e)}static getCurrentTheme(){return i.get()}static getCalculatedCurrentTheme(){return i.getCalculated()}}customElements.define("k-theme-switcher",d);