kempo-ui 0.0.40 → 0.0.41

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.
@@ -0,0 +1,123 @@
1
+ import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComponent.js";import{boolTrueFalse}from"../utils/propConverters.js";const openDropdowns=new Set;export default class Dropdown extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0},openDirection:{type:String,reflect:!0,attribute:"open-direction"},closeOnSelect:{type:Boolean,reflect:!0,attribute:"close-on-select",converter:boolTrueFalse},closeOnClickOutside:{type:Boolean,reflect:!0,attribute:"close-on-click-outside",converter:boolTrueFalse}};anchorId=`dropdown-anchor-${Math.random().toString(36).slice(2,11)}`;constructor(){super(),this.opened=!1,this.openDirection="down left",this.closeOnSelect=!0,this.closeOnClickOutside=!0}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.handleDocumentClick),document.addEventListener("keydown",this.handleKeydown)}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.handleDocumentClick),document.removeEventListener("keydown",this.handleKeydown),openDropdowns.delete(this)}updated(t){super.updated(t),t.has("opened")&&(this.opened?openDropdowns.add(this):openDropdowns.delete(this),this.dispatchEvent(new CustomEvent(this.opened?"opened":"closed",{bubbles:!0})))}handleDocumentClick=t=>{const e=t.target.closest("k-dropdown"),o=t.target.closest('[slot="trigger"]');if(e&&e!==this&&o)return void(this.opened&&this.close());if(!this.opened)return;if(e!==this)return void(this.closeOnClickOutside&&this.close());if(o)return;t.target.closest("a, button")&&this.closeOnSelect&&this.close()};handleTriggerClick=t=>{t.stopPropagation(),this.opened||openDropdowns.forEach(t=>{t!==this&&t.close()}),this.toggle()};handleMenuClick=t=>{const e=t.target.closest("a, button");if(e&&!e.hasAttribute("disabled")){const t=e.dataset?.value||e.textContent.trim();this.dispatchEvent(new CustomEvent("select",{detail:{value:t,item:e},bubbles:!0}))}};handleKeydown=t=>{if(this.opened)if("Escape"===t.key)t.preventDefault(),this.close(),this.focusTrigger();else if("ArrowDown"===t.key)t.preventDefault(),this.focusNextItem();else if("ArrowUp"===t.key)t.preventDefault(),this.focusPreviousItem();else if("Enter"===t.key||" "===t.key){const e=this.querySelector("a:focus, button:focus");e&&(t.preventDefault(),e.click())}};open(){return openDropdowns.forEach(t=>{t!==this&&t.close()}),this.opened=!0,requestAnimationFrame(()=>this.focusFirstItem()),this}close(){return this.opened=!1,this}toggle(){return this.opened?this.close():this.open()}focusTrigger(){const t=this.querySelector('[slot="trigger"]');t&&t.focus()}getMenuItems(){return[...this.querySelectorAll("a, button")].filter(t=>!t.hasAttribute("disabled")&&!t.closest('[slot="trigger"]'))}focusFirstItem(){const t=this.getMenuItems();t.length>0&&t[0].focus()}focusNextItem(){const t=this.getMenuItems(),e=document.activeElement,o=t.indexOf(e),n=t[o+1]||t[0];n&&n.focus()}focusPreviousItem(){const t=this.getMenuItems(),e=document.activeElement,o=t.indexOf(e),n=t[o-1]||t[t.length-1];n&&n.focus()}getPositionArea(){const t=this.openDirection.toLowerCase().trim().split(/\s+/),e={down:"bottom",up:"top",left:"left",right:"right",center:"center"};return t.map(t=>e[t]||t).join(" ")}getFallbacks(){this.getPositionArea().split(" ");const t=[];return t.push("flip-block"),t.push("flip-inline"),t.push("flip-block flip-inline"),t.join(", ")}static styles=css`
2
+ :host {
3
+ display: inline-block;
4
+ position: relative;
5
+ }
6
+ #trigger {
7
+ cursor: pointer;
8
+ anchor-name: --dropdown-trigger;
9
+ }
10
+ #menu {
11
+ display: none;
12
+ position: fixed;
13
+ position-anchor: --dropdown-trigger;
14
+ z-index: 1000;
15
+ min-width: anchor-size(width);
16
+ background: var(--c_bg);
17
+ border: 1px solid var(--c_border);
18
+ box-shadow: var(--drop_shadow);
19
+ margin: 0.25rem;
20
+ }
21
+ :host([opened]) #menu {
22
+ display: block;
23
+ }
24
+ /* Default: down left */
25
+ #menu {
26
+ position-area: bottom span-right;
27
+ position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
28
+ }
29
+ /* down right */
30
+ :host([open-direction="down right"]) #menu {
31
+ position-area: bottom span-left;
32
+ }
33
+ /* down center */
34
+ :host([open-direction="down center"]) #menu,
35
+ :host([open-direction="down"]) #menu {
36
+ position-area: bottom center;
37
+ }
38
+ /* up left */
39
+ :host([open-direction="up left"]) #menu {
40
+ position-area: top span-right;
41
+ }
42
+ /* up right */
43
+ :host([open-direction="up right"]) #menu {
44
+ position-area: top span-left;
45
+ }
46
+ /* up center */
47
+ :host([open-direction="up center"]) #menu,
48
+ :host([open-direction="up"]) #menu {
49
+ position-area: top center;
50
+ }
51
+ /* left up */
52
+ :host([open-direction="left up"]) #menu {
53
+ position-area: left span-top;
54
+ }
55
+ /* left down */
56
+ :host([open-direction="left down"]) #menu {
57
+ position-area: left span-bottom;
58
+ }
59
+ /* left center */
60
+ :host([open-direction="left center"]) #menu,
61
+ :host([open-direction="left"]) #menu {
62
+ position-area: left center;
63
+ }
64
+ /* right up */
65
+ :host([open-direction="right up"]) #menu {
66
+ position-area: right span-top;
67
+ }
68
+ /* right down */
69
+ :host([open-direction="right down"]) #menu {
70
+ position-area: right span-bottom;
71
+ }
72
+ /* right center */
73
+ :host([open-direction="right center"]) #menu,
74
+ :host([open-direction="right"]) #menu {
75
+ position-area: right center;
76
+ }
77
+ /* Slotted menu item styles (not trigger) */
78
+ ::slotted(a:not([slot="trigger"])),
79
+ ::slotted(button:not([slot="trigger"])) {
80
+ all: unset !important;
81
+ display: block !important;
82
+ box-sizing: border-box !important;
83
+ width: 100% !important;
84
+ padding: var(--spacer_h) var(--spacer) !important;
85
+ color: var(--tc) !important;
86
+ background: transparent !important;
87
+ border: none !important;
88
+ border-top: 1px solid var(--c_border) !important;
89
+ border-radius: 0 !important;
90
+ font: inherit !important;
91
+ text-align: left !important;
92
+ cursor: pointer !important;
93
+ white-space: nowrap !important;
94
+ transition: background var(--animation_ms) !important;
95
+ }
96
+ ::slotted(a.k-dropdown-first),
97
+ ::slotted(button.k-dropdown-first) {
98
+ border-top: none !important;
99
+ }
100
+ ::slotted(a:not([slot="trigger"]):hover),
101
+ ::slotted(a:not([slot="trigger"]):focus-visible),
102
+ ::slotted(button:not([slot="trigger"]):hover),
103
+ ::slotted(button:not([slot="trigger"]):focus-visible) {
104
+ background: var(--c_bg__alt) !important;
105
+ outline: none !important;
106
+ }
107
+ ::slotted(a:not([slot="trigger"])[disabled]),
108
+ ::slotted(button:not([slot="trigger"])[disabled]) {
109
+ opacity: 0.5 !important;
110
+ cursor: not-allowed !important;
111
+ pointer-events: none !important;
112
+ }
113
+ ::slotted(hr) {
114
+ display: none !important;
115
+ }
116
+ `;handleSlotChange=t=>{t.target.assignedElements().filter(t=>t.matches("a, button")).forEach((t,e)=>t.classList.toggle("k-dropdown-first",0===e))};render(){return html`
117
+ <div id="trigger" @click=${this.handleTriggerClick}>
118
+ <slot name="trigger"></slot>
119
+ </div>
120
+ <div id="menu" role="menu" @click=${this.handleMenuClick}>
121
+ <slot @slotchange=${this.handleSlotChange}></slot>
122
+ </div>
123
+ `}}customElements.define("k-dropdown",Dropdown);
@@ -1 +1 @@
1
- import{LitElement}from"../lit-all.min.js";export default class ShadowComponent extends LitElement{#e;connectedCallback(){super.connectedCallback(),this.#e=new MutationObserver(()=>{console.log("MutationObserver fired for",this.constructor.name),this.childrenUpdated(),this.requestUpdate()}),this.#e.observe(this,{childList:!0})}disconnectedCallback(){super.disconnectedCallback(),this.#e?.disconnect()}childrenUpdated(){}createRenderRoot(){const e=this.attachShadow({mode:"open"}),t=document.createElement("link");t.rel="stylesheet",t.href=window.kempo?.pathToStylesheet||"https://cdn.jsdelivr.net/npm/kempo-css@1.3.1/dist/kempo.min.css",e.appendChild(t);const n=this.constructor.styles;if(n){const t=document.createElement("style");Array.isArray(n)?t.textContent=n.map(e=>e.cssText||e).join("\n"):t.textContent=n.cssText||n,e.appendChild(t)}const s=document.createElement("div");return s.style.display="contents",e.appendChild(s),s}}
1
+ import{LitElement}from"../lit-all.min.js";export default class ShadowComponent extends LitElement{#e;connectedCallback(){super.connectedCallback(),this.#e=new MutationObserver(()=>{console.log("MutationObserver fired for",this.constructor.name),this.childrenUpdated(),this.requestUpdate()}),this.#e.observe(this,{childList:!0})}disconnectedCallback(){super.disconnectedCallback(),this.#e?.disconnect()}childrenUpdated(){}createRenderRoot(){const e=this.attachShadow({mode:"open"}),t=document.createElement("link");t.rel="stylesheet",t.href=window.kempo?.pathToStylesheet||"https://cdn.jsdelivr.net/npm/kempo-css@1.3.2/dist/kempo.min.css",e.appendChild(t);const n=this.constructor.styles;if(n){const t=document.createElement("style");Array.isArray(n)?t.textContent=n.map(e=>e.cssText||e).join("\n"):t.textContent=n.cssText||n,e.appendChild(t)}const s=document.createElement("div");return s.style.display="contents",e.appendChild(s),s}}
@@ -0,0 +1,113 @@
1
+ import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComponent.js";export default class Spinner extends ShadowComponent{static properties={size:{type:String,reflect:!0},variant:{type:String,reflect:!0}};constructor(){super(),this.size="md",this.variant="spinner"}static styles=css`
2
+ :host {
3
+ --spinner-size: 2rem;
4
+ --spinner-border-width: 3px;
5
+ --spinner-color: var(--c_primary);
6
+ --spinner-track-color: var(--c_border);
7
+ display: inline-flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ }
11
+ /* Sizes */
12
+ :host([size="xs"]) {
13
+ --spinner-size: 1rem;
14
+ --spinner-border-width: 2px;
15
+ }
16
+ :host([size="sm"]) {
17
+ --spinner-size: 1.5rem;
18
+ --spinner-border-width: 2px;
19
+ }
20
+ :host([size="md"]) {
21
+ --spinner-size: 2rem;
22
+ --spinner-border-width: 3px;
23
+ }
24
+ :host([size="lg"]) {
25
+ --spinner-size: 3rem;
26
+ --spinner-border-width: 4px;
27
+ }
28
+ :host([size="xl"]) {
29
+ --spinner-size: 4rem;
30
+ --spinner-border-width: 5px;
31
+ }
32
+ /* Spinner variant */
33
+ .spinner {
34
+ width: var(--spinner-size);
35
+ height: var(--spinner-size);
36
+ border: var(--spinner-border-width) solid var(--spinner-track-color);
37
+ border-top-color: var(--spinner-color);
38
+ border-radius: 50%;
39
+ animation: spin 0.8s linear infinite;
40
+ }
41
+ @keyframes spin {
42
+ to { transform: rotate(360deg); }
43
+ }
44
+ /* Dots variant */
45
+ .dots {
46
+ display: flex;
47
+ gap: calc(var(--spinner-size) * 0.2);
48
+ }
49
+ .dots span {
50
+ width: calc(var(--spinner-size) * 0.3);
51
+ height: calc(var(--spinner-size) * 0.3);
52
+ background: var(--spinner-color);
53
+ border-radius: 50%;
54
+ animation: bounce 1.4s ease-in-out infinite both;
55
+ }
56
+ .dots span:nth-child(1) { animation-delay: -0.32s; }
57
+ .dots span:nth-child(2) { animation-delay: -0.16s; }
58
+ .dots span:nth-child(3) { animation-delay: 0s; }
59
+ @keyframes bounce {
60
+ 0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
61
+ 40% { transform: scale(1); opacity: 1; }
62
+ }
63
+ /* Bars variant */
64
+ .bars {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: calc(var(--spinner-size) * 0.1);
68
+ height: var(--spinner-size);
69
+ }
70
+ .bars span {
71
+ width: calc(var(--spinner-size) * 0.15);
72
+ height: 60%;
73
+ background: var(--spinner-color);
74
+ border-radius: calc(var(--spinner-size) * 0.05);
75
+ animation: bars 1.2s ease-in-out infinite;
76
+ }
77
+ .bars span:nth-child(1) { animation-delay: -0.24s; }
78
+ .bars span:nth-child(2) { animation-delay: -0.12s; }
79
+ .bars span:nth-child(3) { animation-delay: 0s; }
80
+ .bars span:nth-child(4) { animation-delay: 0.12s; }
81
+ @keyframes bars {
82
+ 0%, 40%, 100% { height: 40%; }
83
+ 20% { height: 100%; }
84
+ }
85
+ /* Pulse variant */
86
+ .pulse {
87
+ width: var(--spinner-size);
88
+ height: var(--spinner-size);
89
+ background: var(--spinner-color);
90
+ border-radius: 50%;
91
+ animation: pulse 1.5s ease-in-out infinite;
92
+ }
93
+ @keyframes pulse {
94
+ 0% { transform: scale(0.8); opacity: 0.5; }
95
+ 50% { transform: scale(1); opacity: 1; }
96
+ 100% { transform: scale(0.8); opacity: 0.5; }
97
+ }
98
+ /* Ring variant */
99
+ .ring {
100
+ width: var(--spinner-size);
101
+ height: var(--spinner-size);
102
+ border: var(--spinner-border-width) solid var(--spinner-color);
103
+ border-radius: 50%;
104
+ animation: ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
105
+ }
106
+ @keyframes ring {
107
+ 0% { transform: rotate(0deg); border-color: var(--spinner-color) transparent transparent transparent; }
108
+ 25% { border-color: transparent var(--spinner-color) transparent transparent; }
109
+ 50% { border-color: transparent transparent var(--spinner-color) transparent; }
110
+ 75% { border-color: transparent transparent transparent var(--spinner-color); }
111
+ 100% { transform: rotate(360deg); border-color: var(--spinner-color) transparent transparent transparent; }
112
+ }
113
+ `;render(){switch(this.variant){case"dots":return html`<div class="dots"><span></span><span></span><span></span></div>`;case"bars":return html`<div class="bars"><span></span><span></span><span></span><span></span></div>`;case"pulse":return html`<div class="pulse"></div>`;case"ring":return html`<div class="ring"></div>`;default:return html`<div class="spinner"></div>`}}}customElements.define("k-spinner",Spinner);
@@ -0,0 +1,282 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Dropdown - Components - Kempo Docs - A Web Components Solution</title>
7
+ <link rel="icon" type="image/png" sizes="48x48" href="../media/icon48.png">
8
+ <link rel="manifest" href="../manifest.json" />
9
+ <link rel="stylesheet" href="../kempo-vars.css" />
10
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kempo-css@1.3.2/dist/kempo.min.css" />
11
+ <link rel="stylesheet" href="../kempo-hljs.css" />
12
+ <link rel="stylesheet" href="../styles.css" />
13
+ <script>window.litDisableBundleWarning = true;</script>
14
+ <script src="../init-1.js" type="module"></script>
15
+ </head>
16
+ <body>
17
+ <k-import src="../nav-1.inc.html"></k-import>
18
+ <main>
19
+ <h1 class="ta-center">Dropdown</h1>
20
+
21
+ <k-accordion persistent-id="toc" class="b r mb">
22
+ <k-accordion-header for-panel="toc-panel">Table of Contents</k-accordion-header>
23
+ <k-accordion-panel name="toc-panel">
24
+ <div class="m">
25
+ <h6>Examples</h6>
26
+ <a href="#basicUsage">Basic Usage</a><br />
27
+ <a href="#withIcons">With Icons</a><br />
28
+ <a href="#openDirection">Open Direction</a><br />
29
+ <a href="#disabledItems">Disabled Items</a><br />
30
+ <a href="#links">Using Links</a><br />
31
+ <a href="#events">Events</a><br />
32
+
33
+ <h6 class="mt"><a href="#jsRef" class="no-link">JavaScript Reference</a></h6>
34
+ <a href="#constructor">Constructor</a><br />
35
+ <a href="#requirements">Requirements</a><br />
36
+ <a href="#attributes">Attributes</a><br />
37
+ <a href="#methods">Methods</a><br />
38
+ <a href="#eventsRef">Events</a><br />
39
+ </div>
40
+ </k-accordion-panel>
41
+ </k-accordion>
42
+
43
+ <h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
44
+ <p>Create dropdown menus using the <code>k-dropdown</code> component. Use the <code>trigger</code> slot for the button that opens the menu, and place <code>&lt;button&gt;</code> or <code>&lt;a&gt;</code> elements as children for menu items.</p>
45
+ <div class="row -mx">
46
+ <div class="col m-span-12 px">
47
+ <k-card label="HTML">
48
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"trigger"</span>&gt;</span>Actions<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"edit"</span>&gt;</span>Edit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"duplicate"</span>&gt;</span>Duplicate<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"delete"</span>&gt;</span>Delete<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span></code></pre>
49
+ </k-card>
50
+ </div>
51
+ <div class="col m-span-12 px">
52
+ <k-card label="Output">
53
+ <k-dropdown>
54
+ <button slot="trigger">Actions</button>
55
+ <button data-value="edit">Edit</button>
56
+ <button data-value="duplicate">Duplicate</button>
57
+ <button data-value="delete">Delete</button>
58
+ </k-dropdown>
59
+ </k-card>
60
+ </div>
61
+ </div>
62
+
63
+ <h3 id="withIcons"><a href="#withIcons" class="no-link">With Icons</a></h3>
64
+ <p>Add icons to your dropdown items for better visual cues.</p>
65
+ <div class="row -mx">
66
+ <div class="col m-span-12 px">
67
+ <k-card label="HTML">
68
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"trigger"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-icon</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"more_vert"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-icon</span>&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"edit"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-icon</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"edit"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-icon</span>&gt;</span> Edit<br /> <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"copy"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-icon</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"content_copy"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-icon</span>&gt;</span> Copy<br /> <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"delete"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tc-danger"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">k-icon</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"delete"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">k-icon</span>&gt;</span> Delete<br /> <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span></code></pre>
69
+ </k-card>
70
+ </div>
71
+ <div class="col m-span-12 px">
72
+ <k-card label="Output">
73
+ <k-dropdown>
74
+ <button slot="trigger">
75
+ <k-icon name="more_vert"></k-icon>
76
+ </button>
77
+ <button data-value="edit">
78
+ <k-icon name="edit"></k-icon> Edit
79
+ </button>
80
+ <button data-value="copy">
81
+ <k-icon name="content_copy"></k-icon> Copy
82
+ </button>
83
+ <button data-value="delete" class="tc-danger">
84
+ <k-icon name="delete"></k-icon> Delete
85
+ </button>
86
+ </k-dropdown>
87
+ </k-card>
88
+ </div>
89
+ </div>
90
+
91
+ <h3 id="openDirection"><a href="#openDirection" class="no-link">Open Direction</a></h3>
92
+ <p>Control where the menu appears using the <code>open-direction</code> attribute. Specify a direction like <code>down left</code>, <code>up right</code>, <code>left up</code>, etc. The browser automatically handles fallback positioning if the menu would overflow the viewport using CSS Anchor Positioning.</p>
93
+ <div class="row -mx">
94
+ <div class="col m-span-12 px">
95
+ <k-card label="HTML">
96
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span> <span class="hljs-attr">open-direction</span>=<span class="hljs-string">"down left"</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span> <span class="hljs-attr">open-direction</span>=<span class="hljs-string">"down right"</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span> <span class="hljs-attr">open-direction</span>=<span class="hljs-string">"up left"</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span> <span class="hljs-attr">open-direction</span>=<span class="hljs-string">"right down"</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span></code></pre>
97
+ </k-card>
98
+ </div>
99
+ <div class="col m-span-12 px">
100
+ <k-card label="Output">
101
+ <div class="d-f gap fw-w">
102
+ <k-dropdown open-direction="down left">
103
+ <button slot="trigger">Down Left</button>
104
+ <button data-value="1">Item 1</button>
105
+ <button data-value="2">Item 2</button>
106
+ </k-dropdown>
107
+ <k-dropdown open-direction="down right">
108
+ <button slot="trigger">Down Right</button>
109
+ <button data-value="1">Item 1</button>
110
+ <button data-value="2">Item 2</button>
111
+ </k-dropdown>
112
+ <k-dropdown open-direction="up left">
113
+ <button slot="trigger">Up Left</button>
114
+ <button data-value="1">Item 1</button>
115
+ <button data-value="2">Item 2</button>
116
+ </k-dropdown>
117
+ <k-dropdown open-direction="up right">
118
+ <button slot="trigger">Up Right</button>
119
+ <button data-value="1">Item 1</button>
120
+ <button data-value="2">Item 2</button>
121
+ </k-dropdown>
122
+ </div>
123
+ <div class="d-f gap fw-w mt">
124
+ <k-dropdown open-direction="right down">
125
+ <button slot="trigger">Right Down</button>
126
+ <button data-value="1">Item 1</button>
127
+ <button data-value="2">Item 2</button>
128
+ </k-dropdown>
129
+ <k-dropdown open-direction="right up">
130
+ <button slot="trigger">Right Up</button>
131
+ <button data-value="1">Item 1</button>
132
+ <button data-value="2">Item 2</button>
133
+ </k-dropdown>
134
+ <k-dropdown open-direction="left down">
135
+ <button slot="trigger">Left Down</button>
136
+ <button data-value="1">Item 1</button>
137
+ <button data-value="2">Item 2</button>
138
+ </k-dropdown>
139
+ <k-dropdown open-direction="left up">
140
+ <button slot="trigger">Left Up</button>
141
+ <button data-value="1">Item 1</button>
142
+ <button data-value="2">Item 2</button>
143
+ </k-dropdown>
144
+ </div>
145
+ </k-card>
146
+ </div>
147
+ </div>
148
+
149
+ <h3 id="disabledItems"><a href="#disabledItems" class="no-link">Disabled Items</a></h3>
150
+ <p>Add the <code>disabled</code> attribute to disable specific items.</p>
151
+ <div class="row -mx">
152
+ <div class="col m-span-12 px">
153
+ <k-card label="HTML">
154
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"trigger"</span>&gt;</span>Actions<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"edit"</span>&gt;</span>Edit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"copy"</span> <span class="hljs-attr">disabled</span>&gt;</span>Copy (disabled)<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"delete"</span>&gt;</span>Delete<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span></code></pre>
155
+ </k-card>
156
+ </div>
157
+ <div class="col m-span-12 px">
158
+ <k-card label="Output">
159
+ <k-dropdown>
160
+ <button slot="trigger">Actions</button>
161
+ <button data-value="edit">Edit</button>
162
+ <button data-value="copy" disabled>Copy (disabled)</button>
163
+ <button data-value="delete">Delete</button>
164
+ </k-dropdown>
165
+ </k-card>
166
+ </div>
167
+ </div>
168
+
169
+ <h3 id="links"><a href="#links" class="no-link">Using Links</a></h3>
170
+ <p>You can use <code>&lt;a&gt;</code> elements for navigation items.</p>
171
+ <div class="row -mx">
172
+ <div class="col m-span-12 px">
173
+ <k-card label="HTML">
174
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"trigger"</span>&gt;</span>Navigate<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#home"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span></code></pre>
175
+ </k-card>
176
+ </div>
177
+ <div class="col m-span-12 px">
178
+ <k-card label="Output">
179
+ <k-dropdown>
180
+ <button slot="trigger">Navigate</button>
181
+ <a href="#home">Home</a>
182
+ <a href="#about">About</a>
183
+ <a href="#contact">Contact</a>
184
+ </k-dropdown>
185
+ </k-card>
186
+ </div>
187
+ </div>
188
+
189
+ <h3 id="events"><a href="#events" class="no-link">Events</a></h3>
190
+ <p>Listen for the <code>select</code> event to handle item selection.</p>
191
+ <div class="row -mx">
192
+ <div class="col m-span-12 px">
193
+ <k-card label="HTML">
194
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">k-dropdown</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"eventDropdown"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">"trigger"</span>&gt;</span>Select an option<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"option1"</span>&gt;</span>Option 1<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"option2"</span>&gt;</span>Option 2<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">data-value</span>=<span class="hljs-string">"option3"</span>&gt;</span>Option 3<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">k-dropdown</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"selectedValue"</span>&gt;</span>Selected: none<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"><br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'eventDropdown'</span>)<br /> .addEventListener(<span class="hljs-string">'select'</span>, <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {<br /> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'selectedValue'</span>)<br /> .textContent = <span class="hljs-string">'Selected: '</span> + e.detail.value;<br /> });<br /></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre>
195
+ </k-card>
196
+ </div>
197
+ <div class="col m-span-12 px">
198
+ <k-card label="Output">
199
+ <k-dropdown id="eventDropdown">
200
+ <button slot="trigger">Select an option</button>
201
+ <button data-value="option1">Option 1</button>
202
+ <button data-value="option2">Option 2</button>
203
+ <button data-value="option3">Option 3</button>
204
+ </k-dropdown>
205
+ <p id="selectedValue">Selected: none</p>
206
+ <script>
207
+ document.getElementById('eventDropdown')
208
+ .addEventListener('select', e => {
209
+ document.getElementById('selectedValue')
210
+ .textContent = 'Selected: ' + e.detail.value;
211
+ });
212
+ </script>
213
+ </k-card>
214
+ </div>
215
+ </div>
216
+
217
+ <h2 id="jsRef"><a href="#jsRef" class="no-link">JavaScript Reference</a></h2>
218
+
219
+ <h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
220
+ <h6>Extends <a href="./shadow-component.html">ShadowComponent</a></h6>
221
+ <h5><code>new Dropdown()</code></h5>
222
+
223
+ <h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
224
+ <ul>
225
+ <li><a href="./shadow-component.html">ShadowComponent</a></li>
226
+ <li><a href="./icon.html">Icon</a> (optional, for icons in items)</li>
227
+ </ul>
228
+
229
+ <h3 id="attributes"><a href="#attributes" class="no-link">Attributes</a></h3>
230
+ <h5><code>opened<i>: boolean</i></code></h5>
231
+ <p>Whether the dropdown menu is currently open.</p>
232
+
233
+ <h5><code>open-direction<i>: string</i></code></h5>
234
+ <p>Where the menu appears relative to the trigger. Specify two directions like <code>down left</code>, <code>up right</code>, <code>left up</code>, <code>right down</code>, etc. The browser will automatically flip the position if the menu would overflow the viewport using CSS Anchor Positioning. Default is <code>down left</code>.</p>
235
+
236
+ <h5><code>close-on-select<i>: boolean</i></code></h5>
237
+ <p>Whether the dropdown closes when an item is selected. Default is <code>true</code>.</p>
238
+
239
+ <h5><code>close-on-click-outside<i>: boolean</i></code></h5>
240
+ <p>Whether the dropdown closes when clicking outside of it. Default is <code>true</code>.</p>
241
+
242
+ <h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
243
+ <h5><code>open()<i>: this</i></code></h5>
244
+ <p>Opens the dropdown menu and focuses the first item.</p>
245
+
246
+ <h5><code>close()<i>: this</i></code></h5>
247
+ <p>Closes the dropdown menu.</p>
248
+
249
+ <h5><code>toggle()<i>: this</i></code></h5>
250
+ <p>Toggles the dropdown menu open or closed.</p>
251
+
252
+ <h3 id="eventsRef"><a href="#eventsRef" class="no-link">Events</a></h3>
253
+ <h5><code>select</code></h5>
254
+ <p>Fired when an item is selected. The event detail contains:</p>
255
+ <ul>
256
+ <li><code>value</code>: The value of the selected item (from <code>data-value</code> attribute or text content)</li>
257
+ <li><code>item</code>: The DOM element that was selected</li>
258
+ </ul>
259
+
260
+ <h5><code>opened</code></h5>
261
+ <p>Fired when the dropdown menu is opened.</p>
262
+
263
+ <h5><code>closed</code></h5>
264
+ <p>Fired when the dropdown menu is closed.</p>
265
+
266
+ <h4>Keyboard Navigation</h4>
267
+ <ul>
268
+ <li><code>Enter</code> / <code>Space</code> - Select the focused item</li>
269
+ <li><code>Arrow Down</code> - Move focus to the next item</li>
270
+ <li><code>Arrow Up</code> - Move focus to the previous item</li>
271
+ <li><code>Escape</code> - Close the dropdown</li>
272
+ </ul>
273
+
274
+ </main>
275
+ <div style="height:33vh"></div>
276
+ <script type="module" src="../src/components/Dropdown.js"></script>
277
+ <script type="module" src="../src/components/Import.js"></script>
278
+ <script type="module" src="../src/components/Accordion.js"></script>
279
+ <script type="module" src="../src/components/Card.js"></script>
280
+ <script type="module" src="../src/components/Icon.js"></script>
281
+ </body>
282
+ </html>