@kodaris/krubble-components 1.0.20 → 1.0.22

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.
@@ -474,7 +474,7 @@
474
474
  {
475
475
  "kind": "variable",
476
476
  "name": "Pe",
477
- "default": "class extends ne{constructor(){super(...arguments),this.language=\"html\",this.code=\"\",this.activeTab=\"preview\",this.copied=!1}setTab(e){this.activeTab=e}getHighlightedCode(){return this.code?window.Prism&&window.Prism.languages[this.language]?window.Prism.highlight(this.code,window.Prism.languages[this.language],this.language):this.escapeHtml(this.code):\"\"}escapeHtml(e){const t=document.createElement(\"div\");return t.textContent=e,t.innerHTML}async copyCode(){if(this.code)try{await navigator.clipboard.writeText(this.code),this.copied=!0,setTimeout(()=>{this.copied=!1},2e3)}catch(e){console.error(\"Failed to copy code:\",e)}}render(){return U` <div class=\"tabs\"> <button class=${we({tab:!0,\"tab--active\":\"preview\"===this.activeTab})} @click=${()=>this.setTab(\"preview\")} > Preview </button> <button class=${we({tab:!0,\"tab--active\":\"code\"===this.activeTab})} @click=${()=>this.setTab(\"code\")} > Code </button> </div> <div class=${we({panel:!0,\"panel--active\":\"preview\"===this.activeTab,preview:!0})}> <slot name=\"preview\"></slot> </div> <div class=${we({panel:!0,\"panel--active\":\"code\"===this.activeTab,\"code-container\":!0})}> <button class=${we({\"copy-btn\":!0,\"copy-btn--copied\":this.copied})} @click=${this.copyCode} > ${this.copied?\"Copied!\":\"Copy\"} </button> <pre class=\"code\"><code>${ze(this.getHighlightedCode())}</code></pre> </div> `}}"
477
+ "default": "class extends ne{constructor(){super(...arguments),this.language=\"html\",this.code=\"\",this.activeTab=\"preview\",this.copied=!1}setTab(e){this.activeTab=e}getHighlightedCode(){return this.code?window.Prism&&window.Prism.languages[this.language]?window.Prism.highlight(this.code,window.Prism.languages[this.language],this.language):this.escapeHtml(this.code):\"\"}escapeHtml(e){const t=document.createElement(\"div\");return t.textContent=e,t.innerHTML}async copyCode(){if(this.code)try{await navigator.clipboard.writeText(this.code),this.copied=!0,setTimeout(()=>{this.copied=!1},2e3)}catch(e){console.error(\"Failed to copy code:\",e)}}render(){return U` <div class=\"tabs\"> <button class=${we({tab:!0,\"tab--active\":\"preview\"===this.activeTab})} @click=${()=>this.setTab(\"preview\")} > Preview </button> <button class=${we({tab:!0,\"tab--active\":\"code\"===this.activeTab})} @click=${()=>this.setTab(\"code\")} > Code </button> </div> <div class=${we({panel:!0,\"panel--active\":\"preview\"===this.activeTab,preview:!0})}> <slot name=\"preview\"></slot> </div> <div class=${we({panel:!0,\"panel--active\":\"code\"===this.activeTab,\"code-container\":!0})}> <button class=${we({\"copy-btn\":!0,\"copy-btn--copied\":this.copied})} @click=${this.copyCode} > ${this.copied?\"Copied!\":\"Copy\"} </button> <pre class=\"code\"><code>${Oe(this.getHighlightedCode())}</code></pre> </div> `}}"
478
478
  },
479
479
  {
480
480
  "kind": "variable",
@@ -1031,88 +1031,88 @@
1031
1031
  },
1032
1032
  {
1033
1033
  "kind": "javascript-module",
1034
- "path": "dist/button/button.js",
1034
+ "path": "dist/alert/alert.js",
1035
1035
  "declarations": [
1036
1036
  {
1037
1037
  "kind": "variable",
1038
- "name": "KRButton",
1039
- "default": "class KRButton extends LitElement { constructor() { super(...arguments); /** * The button variant (shape) */ this.variant = 'flat'; /** * The button color */ this.color = 'primary'; /** * The button size */ this.size = 'medium'; /** * Whether the button is disabled */ this.disabled = false; /** * Dropdown options - when provided, button becomes a dropdown */ this.options = []; this._state = 'idle'; this._stateText = ''; this._dropdownOpened = false; this._dropdownAlignRight = false; this._handleHostClick = (e) => { if (this.options.length) { e.stopPropagation(); this._toggleDropdown(); } }; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (this.options.length) { this._toggleDropdown(); } else { this.click(); } } if (e.key === 'Escape' && this._dropdownOpened) { this._dropdownOpened = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpened && !this.contains(e.target)) { this._dropdownOpened = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', this.href ? 'link' : 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); this.addEventListener('click', this._handleHostClick); document.addEventListener('click', this._handleClickOutside); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); this.removeEventListener('click', this._handleHostClick); document.removeEventListener('click', this._handleClickOutside); } _toggleDropdown() { this._dropdownOpened = !this._dropdownOpened; if (this._dropdownOpened) { // Check if dropdown would overflow viewport after render requestAnimationFrame(() => { const dropdown = this.shadowRoot?.querySelector('.dropdown'); if (dropdown) { const rect = dropdown.getBoundingClientRect(); this._dropdownAlignRight = rect.right > window.innerWidth; } }); } } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpened = false; this.dispatchEvent(new CustomEvent('option-select', { detail: { id: option.id, label: option.label }, bubbles: true, composed: true })); } /** * Shows a loading spinner and disables the button. */ showLoading() { this._clearStateTimeout(); this._state = 'loading'; this._stateText = ''; } /** * Shows a success state with optional custom text. * @param text - Text to display (default: \"Saved\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showSuccess(text = 'Success', duration = 2000) { this._clearStateTimeout(); this._state = 'success'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Shows an error state with optional custom text. * @param text - Text to display (default: \"Error\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showError(text = 'Error', duration = 2000) { this._clearStateTimeout(); this._state = 'error'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Resets the button to its idle state. */ reset() { this._clearStateTimeout(); this._state = 'idle'; this._stateText = ''; } _clearStateTimeout() { if (this._stateTimeout) { clearTimeout(this._stateTimeout); this._stateTimeout = undefined; } } updated(changedProperties) { // Reflect state classes to host this.classList.toggle('kr-button--loading', this._state === 'loading'); this.classList.toggle('kr-button--success', this._state === 'success'); this.classList.toggle('kr-button--error', this._state === 'error'); this.classList.toggle(`kr-button--${this.variant}`, true); this.classList.toggle(`kr-button--${this.color}`, true); this.classList.toggle('kr-button--small', this.size === 'small'); this.classList.toggle('kr-button--large', this.size === 'large'); } render() { const content = html ` <slot></slot> ${this.options.length ? html `<svg class=\"caret\" xmlns=\"http://www.w3.org/2000/svg\" height=\"20\" width=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z\"/></svg>` : nothing} ${this._state !== 'idle' ? html `<span class=\"state-overlay\"> ${this._state === 'loading' ? html `<span class=\"spinner\"></span>` : this._stateText} </span>` : nothing} ${this.options.length ? html ` <div class=\"dropdown ${this._dropdownOpened ? 'dropdown--opened' : ''} ${this._dropdownAlignRight ? 'dropdown--align-right' : ''}\"> ${this.options.map(option => html ` <button class=\"dropdown-item\" @click=${(e) => this._handleOptionClick(option, e)} >${option.label}</button> `)} </div> ` : nothing} `; return this.href ? html `<a class=\"link\" href=${this.href} target=${this.target || nothing}>${content}</a>` : content; } }",
1040
- "description": "A customizable button component."
1038
+ "name": "KRAlert",
1039
+ "default": "class KRAlert extends LitElement { constructor() { super(...arguments); /** * The alert type/severity */ this.type = 'info'; /** * Whether the alert can be dismissed */ this.dismissible = false; /** * Whether the alert is visible */ this.visible = true; } /** Handles dismiss button click */ _handleDismiss() { this.visible = false; this.dispatchEvent(new CustomEvent('dismiss', { bubbles: true, composed: true })); } render() { const icons = { info: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z\" clip-rule=\"evenodd\"/></svg>`, success: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\" clip-rule=\"evenodd\"/></svg>`, warning: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\" clip-rule=\"evenodd\"/></svg>`, error: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z\" clip-rule=\"evenodd\"/></svg>`, }; return html ` <div class=${classMap({ 'alert': true, ['alert--' + this.type]: true, 'alert--hidden': !this.visible })} role=\"alert\" > ${icons[this.type]} <div class=\"content\"> ${this.header ? html `<h4 class=\"header\">${this.header}</h4>` : nothing} <div class=\"message\"> <slot></slot> </div> </div> ${this.dismissible ? html ` <button class=\"dismiss\" type=\"button\" aria-label=\"Dismiss alert\" @click=${this._handleDismiss} > <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" width=\"16\" height=\"16\"> <path fill-rule=\"evenodd\" d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\" clip-rule=\"evenodd\"/> </svg> </button> ` : nothing} </div> `; } }",
1040
+ "description": "A customizable alert component for displaying important information to users."
1041
1041
  }
1042
1042
  ],
1043
1043
  "exports": [
1044
1044
  {
1045
1045
  "kind": "js",
1046
- "name": "KRButton",
1046
+ "name": "KRAlert",
1047
1047
  "declaration": {
1048
- "name": "KRButton",
1049
- "module": "dist/button/button.js"
1048
+ "name": "KRAlert",
1049
+ "module": "dist/alert/alert.js"
1050
1050
  }
1051
1051
  }
1052
1052
  ]
1053
1053
  },
1054
1054
  {
1055
1055
  "kind": "javascript-module",
1056
- "path": "dist/code-demo/code-demo.js",
1056
+ "path": "dist/button/button.js",
1057
1057
  "declarations": [
1058
1058
  {
1059
1059
  "kind": "variable",
1060
- "name": "KRCodeDemo",
1061
- "default": "class KRCodeDemo extends LitElement { constructor() { super(...arguments); this.language = 'html'; this.code = ''; this.activeTab = 'preview'; this.copied = false; } setTab(tab) { this.activeTab = tab; } getHighlightedCode() { if (!this.code) return ''; if (window.Prism && window.Prism.languages[this.language]) { return window.Prism.highlight(this.code, window.Prism.languages[this.language], this.language); } // Fallback: escape HTML and return plain text return this.escapeHtml(this.code); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async copyCode() { if (!this.code) return; try { await navigator.clipboard.writeText(this.code); this.copied = true; setTimeout(() => { this.copied = false; }, 2000); } catch (err) { console.error('Failed to copy code:', err); } } render() { return html ` <div class=\"tabs\"> <button class=${classMap({ tab: true, 'tab--active': this.activeTab === 'preview' })} @click=${() => this.setTab('preview')} > Preview </button> <button class=${classMap({ tab: true, 'tab--active': this.activeTab === 'code' })} @click=${() => this.setTab('code')} > Code </button> </div> <div class=${classMap({ panel: true, 'panel--active': this.activeTab === 'preview', preview: true })}> <slot name=\"preview\"></slot> </div> <div class=${classMap({ panel: true, 'panel--active': this.activeTab === 'code', 'code-container': true })}> <button class=${classMap({ 'copy-btn': true, 'copy-btn--copied': this.copied })} @click=${this.copyCode} > ${this.copied ? 'Copied!' : 'Copy'} </button> <pre class=\"code\"><code>${unsafeHTML(this.getHighlightedCode())}</code></pre> </div> `; } }",
1062
- "description": "Code demo component with Preview/Code tabs and syntax highlighting.\n\nUsage:\n```html\n<kr-code-demo language=\"html\">\n <div slot=\"preview\">\n <kr-button>Click me</kr-button>\n </div>\n <script slot=\"code\" type=\"text/plain\">\n <kr-button>Click me</kr-button>\n </script>\n</kr-code-demo>\n```\n\nRequires Prism.js to be loaded globally for syntax highlighting."
1060
+ "name": "KRButton",
1061
+ "default": "class KRButton extends LitElement { constructor() { super(...arguments); /** * The button variant (shape) */ this.variant = 'flat'; /** * The button color */ this.color = 'primary'; /** * The button size */ this.size = 'medium'; /** * Whether the button is disabled */ this.disabled = false; /** * Dropdown options - when provided, button becomes a dropdown */ this.options = []; this._state = 'idle'; this._stateText = ''; this._dropdownOpened = false; this._dropdownAlignRight = false; this._handleHostClick = (e) => { if (this.options.length) { e.stopPropagation(); this._toggleDropdown(); } }; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (this.options.length) { this._toggleDropdown(); } else { this.click(); } } if (e.key === 'Escape' && this._dropdownOpened) { this._dropdownOpened = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpened && !this.contains(e.target)) { this._dropdownOpened = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', this.href ? 'link' : 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); this.addEventListener('click', this._handleHostClick); document.addEventListener('click', this._handleClickOutside); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); this.removeEventListener('click', this._handleHostClick); document.removeEventListener('click', this._handleClickOutside); } _toggleDropdown() { this._dropdownOpened = !this._dropdownOpened; if (this._dropdownOpened) { // Check if dropdown would overflow viewport after render requestAnimationFrame(() => { const dropdown = this.shadowRoot?.querySelector('.dropdown'); if (dropdown) { const rect = dropdown.getBoundingClientRect(); this._dropdownAlignRight = rect.right > window.innerWidth; } }); } } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpened = false; this.dispatchEvent(new CustomEvent('option-select', { detail: { id: option.id, label: option.label }, bubbles: true, composed: true })); } /** * Shows a loading spinner and disables the button. */ showLoading() { this._clearStateTimeout(); this._state = 'loading'; this._stateText = ''; } /** * Shows a success state with optional custom text. * @param text - Text to display (default: \"Saved\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showSuccess(text = 'Success', duration = 2000) { this._clearStateTimeout(); this._state = 'success'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Shows an error state with optional custom text. * @param text - Text to display (default: \"Error\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showError(text = 'Error', duration = 2000) { this._clearStateTimeout(); this._state = 'error'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Resets the button to its idle state. */ reset() { this._clearStateTimeout(); this._state = 'idle'; this._stateText = ''; } _clearStateTimeout() { if (this._stateTimeout) { clearTimeout(this._stateTimeout); this._stateTimeout = undefined; } } updated(changedProperties) { // Reflect state classes to host this.classList.toggle('kr-button--loading', this._state === 'loading'); this.classList.toggle('kr-button--success', this._state === 'success'); this.classList.toggle('kr-button--error', this._state === 'error'); this.classList.toggle(`kr-button--${this.variant}`, true); this.classList.toggle(`kr-button--${this.color}`, true); this.classList.toggle('kr-button--small', this.size === 'small'); this.classList.toggle('kr-button--large', this.size === 'large'); } render() { const content = html ` <slot></slot> ${this.options.length ? html `<svg class=\"caret\" xmlns=\"http://www.w3.org/2000/svg\" height=\"20\" width=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z\"/></svg>` : nothing} ${this._state !== 'idle' ? html `<span class=\"state-overlay\"> ${this._state === 'loading' ? html `<span class=\"spinner\"></span>` : this._stateText} </span>` : nothing} ${this.options.length ? html ` <div class=\"dropdown ${this._dropdownOpened ? 'dropdown--opened' : ''} ${this._dropdownAlignRight ? 'dropdown--align-right' : ''}\"> ${this.options.map(option => html ` <button class=\"dropdown-item\" @click=${(e) => this._handleOptionClick(option, e)} >${option.label}</button> `)} </div> ` : nothing} `; return this.href ? html `<a class=\"link\" href=${this.href} target=${this.target || nothing}>${content}</a>` : content; } }",
1062
+ "description": "A customizable button component."
1063
1063
  }
1064
1064
  ],
1065
1065
  "exports": [
1066
1066
  {
1067
1067
  "kind": "js",
1068
- "name": "KRCodeDemo",
1068
+ "name": "KRButton",
1069
1069
  "declaration": {
1070
- "name": "KRCodeDemo",
1071
- "module": "dist/code-demo/code-demo.js"
1070
+ "name": "KRButton",
1071
+ "module": "dist/button/button.js"
1072
1072
  }
1073
1073
  }
1074
1074
  ]
1075
1075
  },
1076
1076
  {
1077
1077
  "kind": "javascript-module",
1078
- "path": "dist/alert/alert.js",
1078
+ "path": "dist/context-menu/context-menu.js",
1079
1079
  "declarations": [
1080
1080
  {
1081
1081
  "kind": "variable",
1082
- "name": "KRAlert",
1083
- "default": "class KRAlert extends LitElement { constructor() { super(...arguments); /** * The alert type/severity */ this.type = 'info'; /** * Whether the alert can be dismissed */ this.dismissible = false; /** * Whether the alert is visible */ this.visible = true; } /** Handles dismiss button click */ _handleDismiss() { this.visible = false; this.dispatchEvent(new CustomEvent('dismiss', { bubbles: true, composed: true })); } render() { const icons = { info: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z\" clip-rule=\"evenodd\"/></svg>`, success: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\" clip-rule=\"evenodd\"/></svg>`, warning: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\" clip-rule=\"evenodd\"/></svg>`, error: html `<svg class=\"icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z\" clip-rule=\"evenodd\"/></svg>`, }; return html ` <div class=${classMap({ 'alert': true, ['alert--' + this.type]: true, 'alert--hidden': !this.visible })} role=\"alert\" > ${icons[this.type]} <div class=\"content\"> ${this.header ? html `<h4 class=\"header\">${this.header}</h4>` : nothing} <div class=\"message\"> <slot></slot> </div> </div> ${this.dismissible ? html ` <button class=\"dismiss\" type=\"button\" aria-label=\"Dismiss alert\" @click=${this._handleDismiss} > <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" width=\"16\" height=\"16\"> <path fill-rule=\"evenodd\" d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\" clip-rule=\"evenodd\"/> </svg> </button> ` : nothing} </div> `; } }",
1084
- "description": "A customizable alert component for displaying important information to users."
1082
+ "name": "KRContextMenu",
1083
+ "default": "class KRContextMenu extends LitElement { constructor() { super(...arguments); this.items = []; this.resolvePromise = null; this.boundHandleOutsideClick = this.handleOutsideClick.bind(this); this.boundHandleKeyDown = this.handleKeyDown.bind(this); } static async open(options) { // Remove any existing context menu const existing = document.querySelector('kr-context-menu'); if (existing) { existing.remove(); } // Create and position the menu const menu = document.createElement('kr-context-menu'); document.body.appendChild(menu); return menu.show(options); } async show(options) { this.items = options.items; this.style.left = `${options.x}px`; this.style.top = `${options.y}px`; // Adjust position if menu would go off screen await this.updateComplete; const rect = this.getBoundingClientRect(); if (rect.right > window.innerWidth) { this.style.left = `${options.x - rect.width}px`; } if (rect.bottom > window.innerHeight) { this.style.top = `${options.y - rect.height}px`; } // Add event listeners after a microtask to avoid the current event triggering close requestAnimationFrame(() => { document.addEventListener('click', this.boundHandleOutsideClick); document.addEventListener('contextmenu', this.boundHandleOutsideClick); document.addEventListener('keydown', this.boundHandleKeyDown); }); return new Promise((resolve) => { this.resolvePromise = resolve; }); } handleOutsideClick(e) { if (!this.contains(e.target)) { this.close(null); } } handleKeyDown(e) { if (e.key === 'Escape') { this.close(null); } } handleItemClick(item) { if (!item.disabled && !item.divider) { this.close(item); } } close(result) { document.removeEventListener('click', this.boundHandleOutsideClick); document.removeEventListener('contextmenu', this.boundHandleOutsideClick); document.removeEventListener('keydown', this.boundHandleKeyDown); if (this.resolvePromise) { this.resolvePromise(result); this.resolvePromise = null; } this.remove(); } render() { return html ` <div class=\"menu\"> ${this.items.map(item => item.divider ? html `<div class=\"menu__divider\"></div>` : html ` <button class=\"menu__item\" ?disabled=${item.disabled} @click=${() => this.handleItemClick(item)} > ${item.icon ? html `<span class=\"menu__item-icon\">${item.icon}</span>` : null} ${item.label} </button> `)} </div> `; } }",
1084
+ "description": "Context menu component that can be opened programmatically.\n\nUsage:\n```ts\nconst result = await ContextMenu.open({\n x: event.clientX,\n y: event.clientY,\n items: [\n { id: 'edit', label: 'Edit Item' },\n { id: 'divider', label: '', divider: true },\n { id: 'add-above', label: 'Add Item Above' },\n { id: 'add-below', label: 'Add Item Below' },\n ]\n});\n\nif (result) {\n console.log('Selected:', result.id);\n}\n```"
1085
1085
  }
1086
1086
  ],
1087
1087
  "exports": [
1088
1088
  {
1089
1089
  "kind": "js",
1090
- "name": "KRAlert",
1090
+ "name": "KRContextMenu",
1091
1091
  "declaration": {
1092
- "name": "KRAlert",
1093
- "module": "dist/alert/alert.js"
1092
+ "name": "KRContextMenu",
1093
+ "module": "dist/context-menu/context-menu.js"
1094
1094
  }
1095
1095
  }
1096
1096
  ]
1097
1097
  },
1098
1098
  {
1099
1099
  "kind": "javascript-module",
1100
- "path": "dist/context-menu/context-menu.js",
1100
+ "path": "dist/code-demo/code-demo.js",
1101
1101
  "declarations": [
1102
1102
  {
1103
1103
  "kind": "variable",
1104
- "name": "KRContextMenu",
1105
- "default": "class KRContextMenu extends LitElement { constructor() { super(...arguments); this.items = []; this.resolvePromise = null; this.boundHandleOutsideClick = this.handleOutsideClick.bind(this); this.boundHandleKeyDown = this.handleKeyDown.bind(this); } static async open(options) { // Remove any existing context menu const existing = document.querySelector('kr-context-menu'); if (existing) { existing.remove(); } // Create and position the menu const menu = document.createElement('kr-context-menu'); document.body.appendChild(menu); return menu.show(options); } async show(options) { this.items = options.items; this.style.left = `${options.x}px`; this.style.top = `${options.y}px`; // Adjust position if menu would go off screen await this.updateComplete; const rect = this.getBoundingClientRect(); if (rect.right > window.innerWidth) { this.style.left = `${options.x - rect.width}px`; } if (rect.bottom > window.innerHeight) { this.style.top = `${options.y - rect.height}px`; } // Add event listeners after a microtask to avoid the current event triggering close requestAnimationFrame(() => { document.addEventListener('click', this.boundHandleOutsideClick); document.addEventListener('contextmenu', this.boundHandleOutsideClick); document.addEventListener('keydown', this.boundHandleKeyDown); }); return new Promise((resolve) => { this.resolvePromise = resolve; }); } handleOutsideClick(e) { if (!this.contains(e.target)) { this.close(null); } } handleKeyDown(e) { if (e.key === 'Escape') { this.close(null); } } handleItemClick(item) { if (!item.disabled && !item.divider) { this.close(item); } } close(result) { document.removeEventListener('click', this.boundHandleOutsideClick); document.removeEventListener('contextmenu', this.boundHandleOutsideClick); document.removeEventListener('keydown', this.boundHandleKeyDown); if (this.resolvePromise) { this.resolvePromise(result); this.resolvePromise = null; } this.remove(); } render() { return html ` <div class=\"menu\"> ${this.items.map(item => item.divider ? html `<div class=\"menu__divider\"></div>` : html ` <button class=\"menu__item\" ?disabled=${item.disabled} @click=${() => this.handleItemClick(item)} > ${item.icon ? html `<span class=\"menu__item-icon\">${item.icon}</span>` : null} ${item.label} </button> `)} </div> `; } }",
1106
- "description": "Context menu component that can be opened programmatically.\n\nUsage:\n```ts\nconst result = await ContextMenu.open({\n x: event.clientX,\n y: event.clientY,\n items: [\n { id: 'edit', label: 'Edit Item' },\n { id: 'divider', label: '', divider: true },\n { id: 'add-above', label: 'Add Item Above' },\n { id: 'add-below', label: 'Add Item Below' },\n ]\n});\n\nif (result) {\n console.log('Selected:', result.id);\n}\n```"
1104
+ "name": "KRCodeDemo",
1105
+ "default": "class KRCodeDemo extends LitElement { constructor() { super(...arguments); this.language = 'html'; this.code = ''; this.activeTab = 'preview'; this.copied = false; } setTab(tab) { this.activeTab = tab; } getHighlightedCode() { if (!this.code) return ''; if (window.Prism && window.Prism.languages[this.language]) { return window.Prism.highlight(this.code, window.Prism.languages[this.language], this.language); } // Fallback: escape HTML and return plain text return this.escapeHtml(this.code); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } async copyCode() { if (!this.code) return; try { await navigator.clipboard.writeText(this.code); this.copied = true; setTimeout(() => { this.copied = false; }, 2000); } catch (err) { console.error('Failed to copy code:', err); } } render() { return html ` <div class=\"tabs\"> <button class=${classMap({ tab: true, 'tab--active': this.activeTab === 'preview' })} @click=${() => this.setTab('preview')} > Preview </button> <button class=${classMap({ tab: true, 'tab--active': this.activeTab === 'code' })} @click=${() => this.setTab('code')} > Code </button> </div> <div class=${classMap({ panel: true, 'panel--active': this.activeTab === 'preview', preview: true })}> <slot name=\"preview\"></slot> </div> <div class=${classMap({ panel: true, 'panel--active': this.activeTab === 'code', 'code-container': true })}> <button class=${classMap({ 'copy-btn': true, 'copy-btn--copied': this.copied })} @click=${this.copyCode} > ${this.copied ? 'Copied!' : 'Copy'} </button> <pre class=\"code\"><code>${unsafeHTML(this.getHighlightedCode())}</code></pre> </div> `; } }",
1106
+ "description": "Code demo component with Preview/Code tabs and syntax highlighting.\n\nUsage:\n```html\n<kr-code-demo language=\"html\">\n <div slot=\"preview\">\n <kr-button>Click me</kr-button>\n </div>\n <script slot=\"code\" type=\"text/plain\">\n <kr-button>Click me</kr-button>\n </script>\n</kr-code-demo>\n```\n\nRequires Prism.js to be loaded globally for syntax highlighting."
1107
1107
  }
1108
1108
  ],
1109
1109
  "exports": [
1110
1110
  {
1111
1111
  "kind": "js",
1112
- "name": "KRContextMenu",
1112
+ "name": "KRCodeDemo",
1113
1113
  "declaration": {
1114
- "name": "KRContextMenu",
1115
- "module": "dist/context-menu/context-menu.js"
1114
+ "name": "KRCodeDemo",
1115
+ "module": "dist/code-demo/code-demo.js"
1116
1116
  }
1117
1117
  }
1118
1118
  ]
@@ -1224,6 +1224,14 @@
1224
1224
  "name": "KRTextareaField",
1225
1225
  "module": "./textarea-field/textarea-field.js"
1226
1226
  }
1227
+ },
1228
+ {
1229
+ "kind": "js",
1230
+ "name": "KRDetailField",
1231
+ "declaration": {
1232
+ "name": "KRDetailField",
1233
+ "module": "./detail-field/detail-field.js"
1234
+ }
1227
1235
  }
1228
1236
  ]
1229
1237
  },
@@ -1248,6 +1256,28 @@
1248
1256
  }
1249
1257
  ]
1250
1258
  },
1259
+ {
1260
+ "kind": "javascript-module",
1261
+ "path": "dist/style/base.js",
1262
+ "declarations": [
1263
+ {
1264
+ "kind": "variable",
1265
+ "name": "krBaseCSS",
1266
+ "default": "css ` :host, *, *::before, *::after { box-sizing: border-box; } :host { /* Primary */ --kr-primary: rgb(22, 48, 82); --kr-primary-hover: rgb(16, 36, 62); --kr-primary-text: #ffffff; /* Accent */ --kr-accent: #BEEA4E; --kr-accent-hover: #a8d43a; --kr-accent-text: #000000; /* Text */ --kr-text: #000000; --kr-text-muted: #4b5563; --kr-text-disabled: #9ca3af; /* Borders */ --kr-border: #e5e7eb; } `",
1267
+ "description": "Base theme styles with CSS custom properties.\nImport into components: static styles = [krBaseCSS, css`...`]"
1268
+ }
1269
+ ],
1270
+ "exports": [
1271
+ {
1272
+ "kind": "js",
1273
+ "name": "krBaseCSS",
1274
+ "declaration": {
1275
+ "name": "krBaseCSS",
1276
+ "module": "dist/style/base.js"
1277
+ }
1278
+ }
1279
+ ]
1280
+ },
1251
1281
  {
1252
1282
  "kind": "javascript-module",
1253
1283
  "path": "dist/table/table.js",
@@ -1313,28 +1343,6 @@
1313
1343
  }
1314
1344
  ]
1315
1345
  },
1316
- {
1317
- "kind": "javascript-module",
1318
- "path": "dist/style/base.js",
1319
- "declarations": [
1320
- {
1321
- "kind": "variable",
1322
- "name": "krBaseCSS",
1323
- "default": "css ` :host, *, *::before, *::after { box-sizing: border-box; } :host { /* Primary */ --kr-primary: rgb(22, 48, 82); --kr-primary-hover: rgb(16, 36, 62); --kr-primary-text: #ffffff; /* Accent */ --kr-accent: #BEEA4E; --kr-accent-hover: #a8d43a; --kr-accent-text: #000000; /* Text */ --kr-text: #000000; --kr-text-muted: #4b5563; --kr-text-disabled: #9ca3af; /* Borders */ --kr-border: #e5e7eb; } `",
1324
- "description": "Base theme styles with CSS custom properties.\nImport into components: static styles = [krBaseCSS, css`...`]"
1325
- }
1326
- ],
1327
- "exports": [
1328
- {
1329
- "kind": "js",
1330
- "name": "krBaseCSS",
1331
- "declaration": {
1332
- "name": "krBaseCSS",
1333
- "module": "dist/style/base.js"
1334
- }
1335
- }
1336
- ]
1337
- },
1338
1346
  {
1339
1347
  "kind": "javascript-module",
1340
1348
  "path": "src/accordion/accordion.ts",
@@ -2381,6 +2389,14 @@
2381
2389
  "name": "KRTextareaField",
2382
2390
  "module": "./textarea-field/textarea-field.js"
2383
2391
  }
2392
+ },
2393
+ {
2394
+ "kind": "js",
2395
+ "name": "KRDetailField",
2396
+ "declaration": {
2397
+ "name": "KRDetailField",
2398
+ "module": "./detail-field/detail-field.js"
2399
+ }
2384
2400
  }
2385
2401
  ]
2386
2402
  },
@@ -3505,22 +3521,66 @@
3505
3521
  },
3506
3522
  {
3507
3523
  "kind": "javascript-module",
3508
- "path": "dist/form/textarea-field/textarea-field.js",
3524
+ "path": "dist/form/detail-field/detail-field.js",
3509
3525
  "declarations": [
3510
3526
  {
3511
3527
  "kind": "variable",
3512
- "name": "KRTextareaField",
3513
- "default": "class KRTextareaField extends LitElement { constructor() { super(); /** * The textarea label text */ this.label = ''; /** * The textarea name for form submission */ this.name = ''; /** * The current value */ this.value = ''; /** * Placeholder text */ this.placeholder = ''; /** * Whether the field is required */ this.required = false; /** * Whether the field is disabled */ this.disabled = false; /** * Whether the field is readonly */ this.readonly = false; /** * Number of visible text lines */ this.rows = 3; /** * Autocomplete attribute value */ this.autocomplete = ''; /** * Helper text shown below the textarea */ this.hint = ''; this._touched = false; this._dirty = false; this._handleInvalid = (e) => { e.preventDefault(); this._touched = true; }; this._internals = this.attachInternals(); } // Form-associated custom element callbacks get form() { return this._internals.form; } get validity() { return this._internals.validity; } get validationMessage() { return this._internals.validationMessage; } get willValidate() { return this._internals.willValidate; } checkValidity() { return this._internals.checkValidity(); } reportValidity() { return this._internals.reportValidity(); } formResetCallback() { this.value = ''; this._touched = false; this._dirty = false; this._internals.setFormValue(''); this._internals.setValidity({}); } formStateRestoreCallback(state) { this.value = state; } connectedCallback() { super.connectedCallback(); this.addEventListener('invalid', this._handleInvalid); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('invalid', this._handleInvalid); } firstUpdated() { this._updateValidity(); } updated(changedProperties) { if (changedProperties.has('required') || changedProperties.has('value')) { this._updateValidity(); } } _updateValidity() { if (this._textarea) { this._internals.setValidity(this._textarea.validity, this._textarea.validationMessage); } } _handleInput(e) { this.value = e.target.value; this._dirty = true; this._internals.setFormValue(this.value); this._internals.setValidity(this._textarea.validity, this._textarea.validationMessage); } _handleChange(e) { this.value = e.target.value; this._internals.setFormValue(this.value); } _handleBlur() { this._touched = true; this._internals.setValidity(this._textarea.validity, this._textarea.validationMessage); } render() { let validationMessage = ''; if (this._touched && this._textarea && !this._textarea.validity.valid) { validationMessage = this._textarea.validationMessage; } return html ` <div class=\"wrapper\"> ${this.label ? html ` <label for=\"textarea\"> ${this.label} ${this.required ? html `<span class=\"required\" aria-hidden=\"true\">*</span>` : nothing} </label> ` : nothing} <textarea id=\"textarea\" class=${classMap({ 'textarea--invalid': this._touched && this._textarea && !this._textarea.validity.valid })} name=${this.name} .value=${live(this.value)} placeholder=${this.placeholder} ?required=${this.required} ?disabled=${this.disabled} ?readonly=${this.readonly} rows=${this.rows} cols=${ifDefined(this.cols)} minlength=${ifDefined(this.minlength)} maxlength=${ifDefined(this.maxlength)} autocomplete=${ifDefined(this.autocomplete || undefined)} @input=${this._handleInput} @change=${this._handleChange} @blur=${this._handleBlur} ></textarea> ${validationMessage ? html `<div class=\"validation-message\">${validationMessage}</div>` : this.hint ? html `<div class=\"hint\">${this.hint}</div>` : nothing} </div> `; } // Public methods for programmatic control focus() { this._textarea?.focus(); } blur() { this._textarea?.blur(); } select() { this._textarea?.select(); } }",
3514
- "description": "A textarea component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset.\n\nNative input and change events bubble up from the inner textarea element."
3528
+ "name": "KRDetailField",
3529
+ "default": "class KRDetailField extends LitElement { constructor() { super(...arguments); /** * The field label text */ this.label = ''; /** * The value to display */ this.value = ''; } render() { return html ` <div class=\"wrapper\"> ${this.label ? html `<label>${this.label}</label>` : nothing} <div class=\"value\">${this.value}</div> </div> `; } }",
3530
+ "description": "A read-only field component that displays a label and value."
3515
3531
  }
3516
3532
  ],
3517
3533
  "exports": [
3518
3534
  {
3519
3535
  "kind": "js",
3520
- "name": "KRTextareaField",
3536
+ "name": "KRDetailField",
3521
3537
  "declaration": {
3522
- "name": "KRTextareaField",
3523
- "module": "dist/form/textarea-field/textarea-field.js"
3538
+ "name": "KRDetailField",
3539
+ "module": "dist/form/detail-field/detail-field.js"
3540
+ }
3541
+ }
3542
+ ]
3543
+ },
3544
+ {
3545
+ "kind": "javascript-module",
3546
+ "path": "dist/form/select-field/select-field.js",
3547
+ "declarations": [
3548
+ {
3549
+ "kind": "variable",
3550
+ "name": "KRSelectField",
3551
+ "default": "class KRSelectField extends LitElement { constructor() { super(); /** * The select label text */ this.label = ''; /** * The input name for form submission */ this.name = ''; /** * The currently selected value */ this.value = ''; /** * Placeholder text when no option is selected */ this.placeholder = 'Select an option'; /** * Whether the select is disabled */ this.disabled = false; /** * Whether the field is required */ this.required = false; /** * Whether the field is readonly */ this.readonly = false; /** * Helper text shown below the select */ this.hint = ''; this._isOpen = false; this._highlightedIndex = -1; this._touched = false; this._handleInvalid = (e) => { e.preventDefault(); this._touched = true; }; this._handleOutsideClick = (e) => { if (!e.composedPath().includes(this)) { this._close(); } }; this._handleKeyDown = (e) => { if (!this._isOpen) return; const options = Array.from(this.querySelectorAll('kr-select-option')); switch (e.key) { case 'Escape': this._close(); this._triggerElement?.focus(); break; case 'ArrowDown': e.preventDefault(); if (options.some(o => !o.disabled)) { let newIndex = this._highlightedIndex + 1; while (newIndex < options.length && options[newIndex]?.disabled) newIndex++; if (newIndex < options.length) this._highlightedIndex = newIndex; } break; case 'ArrowUp': e.preventDefault(); { let newIndex = this._highlightedIndex - 1; while (newIndex >= 0 && options[newIndex]?.disabled) newIndex--; if (newIndex >= 0) this._highlightedIndex = newIndex; } break; case 'Enter': e.preventDefault(); if (this._highlightedIndex >= 0 && this._highlightedIndex < options.length) { this._selectOption(options[this._highlightedIndex]); } break; } }; this._internals = this.attachInternals(); } // Form-associated custom element callbacks get form() { return this._internals.form; } get validity() { return this._internals.validity; } get validationMessage() { return this._internals.validationMessage; } get willValidate() { return this._internals.willValidate; } checkValidity() { return this._internals.checkValidity(); } reportValidity() { return this._internals.reportValidity(); } formResetCallback() { this.value = ''; this._touched = false; this._internals.setFormValue(''); this._internals.setValidity({}); } formStateRestoreCallback(state) { this.value = state; } connectedCallback() { super.connectedCallback(); document.addEventListener('click', this._handleOutsideClick); document.addEventListener('keydown', this._handleKeyDown); this.addEventListener('invalid', this._handleInvalid); } firstUpdated() { this._updateValidity(); } updated(changedProperties) { if (changedProperties.has('required') || changedProperties.has('value')) { this._updateValidity(); } } disconnectedCallback() { super.disconnectedCallback(); document.removeEventListener('click', this._handleOutsideClick); document.removeEventListener('keydown', this._handleKeyDown); this.removeEventListener('invalid', this._handleInvalid); } _toggle() { if (this.disabled || this.readonly) return; if (this._isOpen) { this._close(); } else { this._isOpen = true; const options = Array.from(this.querySelectorAll('kr-select-option')); this._highlightedIndex = options.findIndex(o => o.value === this.value); } } _close() { this._isOpen = false; this._highlightedIndex = -1; } _selectOption(option) { if (option.disabled) return; this.value = option.value; this._internals.setFormValue(this.value); this._updateValidity(); this.dispatchEvent(new Event('change', { bubbles: true, composed: true })); this._close(); this._triggerElement?.focus(); } _handleBlur() { this._touched = true; this._updateValidity(); } _updateValidity() { if (this.required && !this.value) { this._internals.setValidity({ valueMissing: true }, 'Please select an option', this._triggerElement); } else { this._internals.setValidity({}); } } render() { const options = Array.from(this.querySelectorAll('kr-select-option')); const selectedLabel = options.find(o => o.value === this.value)?.label; return html ` <div class=\"wrapper\"> ${this.label ? html ` <label> ${this.label} ${this.required ? html `<span class=\"required\" aria-hidden=\"true\">*</span>` : ''} </label> ` : nothing} <div class=\"select-wrapper\"> <button class=${classMap({ 'select-trigger': true, 'select-trigger--open': this._isOpen, 'select-trigger--invalid': this._touched && this.required && !this.value, })} type=\"button\" ?disabled=${this.disabled} aria-haspopup=\"listbox\" aria-expanded=${this._isOpen} @click=${this._toggle} @blur=${this._handleBlur} > <span class=${classMap({ 'select-value': true, 'select-placeholder': !selectedLabel })}> ${selectedLabel || this.placeholder} </span> <svg class=${classMap({ 'chevron-icon': true, 'select-icon': true, 'select-icon--open': this._isOpen })} viewBox=\"0 0 20 20\" fill=\"currentColor\" > <path fill-rule=\"evenodd\" d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\" clip-rule=\"evenodd\" /> </svg> </button> <div class=${classMap({ 'select-dropdown': true, 'hidden': !this._isOpen })} role=\"listbox\"> <div class=\"select-options\"> ${options.length === 0 ? html `<div class=\"select-empty\">No options available</div>` : options.map((option, idx) => { const isSelected = option.value === this.value; return html ` <div class=${classMap({ 'select-option': true, 'select-option--selected': isSelected, 'select-option--disabled': option.disabled, 'select-option--highlighted': idx === this._highlightedIndex, })} role=\"option\" aria-selected=${isSelected} @click=${() => this._selectOption(option)} @mouseenter=${() => (this._highlightedIndex = idx)} > ${option.label} ${isSelected ? html `<svg class=\"chevron-icon select-check\" viewBox=\"0 0 20 20\" fill=\"currentColor\"> <path fill-rule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clip-rule=\"evenodd\" /> </svg>` : nothing} </div> `; })} </div> </div> </div> ${this._touched && this.required && !this.value ? html `<div class=\"validation-message\">Please select an option</div>` : this.hint ? html `<div class=\"hint\">${this.hint}</div>` : ''} </div> <div class=\"options-slot\"> <slot @slotchange=${() => this.requestUpdate()}></slot> </div> `; } // Public methods for programmatic control focus() { this._triggerElement?.focus(); } blur() { this._triggerElement?.blur(); } }",
3552
+ "description": "A select dropdown component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset."
3553
+ }
3554
+ ],
3555
+ "exports": [
3556
+ {
3557
+ "kind": "js",
3558
+ "name": "KRSelectField",
3559
+ "declaration": {
3560
+ "name": "KRSelectField",
3561
+ "module": "dist/form/select-field/select-field.js"
3562
+ }
3563
+ }
3564
+ ]
3565
+ },
3566
+ {
3567
+ "kind": "javascript-module",
3568
+ "path": "dist/form/select-field/select-option.js",
3569
+ "declarations": [
3570
+ {
3571
+ "kind": "variable",
3572
+ "name": "KRSelectOption",
3573
+ "default": "class KRSelectOption extends LitElement { constructor() { super(...arguments); /** * The option value */ this.value = ''; /** * Whether the option is disabled */ this.disabled = false; } /** Gets the label text from the slot */ get label() { return this.textContent?.trim() || ''; } render() { return html `<slot></slot>`; } }",
3574
+ "description": "An option for the kr-select-field component."
3575
+ }
3576
+ ],
3577
+ "exports": [
3578
+ {
3579
+ "kind": "js",
3580
+ "name": "KRSelectOption",
3581
+ "declaration": {
3582
+ "name": "KRSelectOption",
3583
+ "module": "dist/form/select-field/select-option.js"
3524
3584
  }
3525
3585
  }
3526
3586
  ]
@@ -3549,44 +3609,99 @@
3549
3609
  },
3550
3610
  {
3551
3611
  "kind": "javascript-module",
3552
- "path": "dist/form/select-field/select-field.js",
3612
+ "path": "dist/form/textarea-field/textarea-field.js",
3553
3613
  "declarations": [
3554
3614
  {
3555
3615
  "kind": "variable",
3556
- "name": "KRSelectField",
3557
- "default": "class KRSelectField extends LitElement { constructor() { super(); /** * The select label text */ this.label = ''; /** * The input name for form submission */ this.name = ''; /** * The currently selected value */ this.value = ''; /** * Placeholder text when no option is selected */ this.placeholder = 'Select an option'; /** * Whether the select is disabled */ this.disabled = false; /** * Whether the field is required */ this.required = false; /** * Whether the field is readonly */ this.readonly = false; /** * Helper text shown below the select */ this.hint = ''; this._isOpen = false; this._highlightedIndex = -1; this._touched = false; this._handleInvalid = (e) => { e.preventDefault(); this._touched = true; }; this._handleOutsideClick = (e) => { if (!e.composedPath().includes(this)) { this._close(); } }; this._handleKeyDown = (e) => { if (!this._isOpen) return; const options = Array.from(this.querySelectorAll('kr-select-option')); switch (e.key) { case 'Escape': this._close(); this._triggerElement?.focus(); break; case 'ArrowDown': e.preventDefault(); if (options.some(o => !o.disabled)) { let newIndex = this._highlightedIndex + 1; while (newIndex < options.length && options[newIndex]?.disabled) newIndex++; if (newIndex < options.length) this._highlightedIndex = newIndex; } break; case 'ArrowUp': e.preventDefault(); { let newIndex = this._highlightedIndex - 1; while (newIndex >= 0 && options[newIndex]?.disabled) newIndex--; if (newIndex >= 0) this._highlightedIndex = newIndex; } break; case 'Enter': e.preventDefault(); if (this._highlightedIndex >= 0 && this._highlightedIndex < options.length) { this._selectOption(options[this._highlightedIndex]); } break; } }; this._internals = this.attachInternals(); } // Form-associated custom element callbacks get form() { return this._internals.form; } get validity() { return this._internals.validity; } get validationMessage() { return this._internals.validationMessage; } get willValidate() { return this._internals.willValidate; } checkValidity() { return this._internals.checkValidity(); } reportValidity() { return this._internals.reportValidity(); } formResetCallback() { this.value = ''; this._touched = false; this._internals.setFormValue(''); this._internals.setValidity({}); } formStateRestoreCallback(state) { this.value = state; } connectedCallback() { super.connectedCallback(); document.addEventListener('click', this._handleOutsideClick); document.addEventListener('keydown', this._handleKeyDown); this.addEventListener('invalid', this._handleInvalid); } firstUpdated() { this._updateValidity(); } updated(changedProperties) { if (changedProperties.has('required') || changedProperties.has('value')) { this._updateValidity(); } } disconnectedCallback() { super.disconnectedCallback(); document.removeEventListener('click', this._handleOutsideClick); document.removeEventListener('keydown', this._handleKeyDown); this.removeEventListener('invalid', this._handleInvalid); } _toggle() { if (this.disabled || this.readonly) return; if (this._isOpen) { this._close(); } else { this._isOpen = true; const options = Array.from(this.querySelectorAll('kr-select-option')); this._highlightedIndex = options.findIndex(o => o.value === this.value); } } _close() { this._isOpen = false; this._highlightedIndex = -1; } _selectOption(option) { if (option.disabled) return; this.value = option.value; this._internals.setFormValue(this.value); this._updateValidity(); this.dispatchEvent(new Event('change', { bubbles: true, composed: true })); this._close(); this._triggerElement?.focus(); } _handleBlur() { this._touched = true; this._updateValidity(); } _updateValidity() { if (this.required && !this.value) { this._internals.setValidity({ valueMissing: true }, 'Please select an option', this._triggerElement); } else { this._internals.setValidity({}); } } render() { const options = Array.from(this.querySelectorAll('kr-select-option')); const selectedLabel = options.find(o => o.value === this.value)?.label; return html ` <div class=\"wrapper\"> ${this.label ? html ` <label> ${this.label} ${this.required ? html `<span class=\"required\" aria-hidden=\"true\">*</span>` : ''} </label> ` : nothing} <div class=\"select-wrapper\"> <button class=${classMap({ 'select-trigger': true, 'select-trigger--open': this._isOpen, 'select-trigger--invalid': this._touched && this.required && !this.value, })} type=\"button\" ?disabled=${this.disabled} aria-haspopup=\"listbox\" aria-expanded=${this._isOpen} @click=${this._toggle} @blur=${this._handleBlur} > <span class=${classMap({ 'select-value': true, 'select-placeholder': !selectedLabel })}> ${selectedLabel || this.placeholder} </span> <svg class=${classMap({ 'chevron-icon': true, 'select-icon': true, 'select-icon--open': this._isOpen })} viewBox=\"0 0 20 20\" fill=\"currentColor\" > <path fill-rule=\"evenodd\" d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\" clip-rule=\"evenodd\" /> </svg> </button> <div class=${classMap({ 'select-dropdown': true, 'hidden': !this._isOpen })} role=\"listbox\"> <div class=\"select-options\"> ${options.length === 0 ? html `<div class=\"select-empty\">No options available</div>` : options.map((option, idx) => { const isSelected = option.value === this.value; return html ` <div class=${classMap({ 'select-option': true, 'select-option--selected': isSelected, 'select-option--disabled': option.disabled, 'select-option--highlighted': idx === this._highlightedIndex, })} role=\"option\" aria-selected=${isSelected} @click=${() => this._selectOption(option)} @mouseenter=${() => (this._highlightedIndex = idx)} > ${option.label} ${isSelected ? html `<svg class=\"chevron-icon select-check\" viewBox=\"0 0 20 20\" fill=\"currentColor\"> <path fill-rule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clip-rule=\"evenodd\" /> </svg>` : nothing} </div> `; })} </div> </div> </div> ${this._touched && this.required && !this.value ? html `<div class=\"validation-message\">Please select an option</div>` : this.hint ? html `<div class=\"hint\">${this.hint}</div>` : ''} </div> <div class=\"options-slot\"> <slot @slotchange=${() => this.requestUpdate()}></slot> </div> `; } // Public methods for programmatic control focus() { this._triggerElement?.focus(); } blur() { this._triggerElement?.blur(); } }",
3558
- "description": "A select dropdown component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset."
3616
+ "name": "KRTextareaField",
3617
+ "default": "class KRTextareaField extends LitElement { constructor() { super(); /** * The textarea label text */ this.label = ''; /** * The textarea name for form submission */ this.name = ''; /** * The current value */ this.value = ''; /** * Placeholder text */ this.placeholder = ''; /** * Whether the field is required */ this.required = false; /** * Whether the field is disabled */ this.disabled = false; /** * Whether the field is readonly */ this.readonly = false; /** * Number of visible text lines */ this.rows = 3; /** * Autocomplete attribute value */ this.autocomplete = ''; /** * Helper text shown below the textarea */ this.hint = ''; this._touched = false; this._dirty = false; this._handleInvalid = (e) => { e.preventDefault(); this._touched = true; }; this._internals = this.attachInternals(); } // Form-associated custom element callbacks get form() { return this._internals.form; } get validity() { return this._internals.validity; } get validationMessage() { return this._internals.validationMessage; } get willValidate() { return this._internals.willValidate; } checkValidity() { return this._internals.checkValidity(); } reportValidity() { return this._internals.reportValidity(); } formResetCallback() { this.value = ''; this._touched = false; this._dirty = false; this._internals.setFormValue(''); this._internals.setValidity({}); } formStateRestoreCallback(state) { this.value = state; } connectedCallback() { super.connectedCallback(); this.addEventListener('invalid', this._handleInvalid); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('invalid', this._handleInvalid); } firstUpdated() { this._updateValidity(); } updated(changedProperties) { if (changedProperties.has('required') || changedProperties.has('value')) { this._updateValidity(); } } _updateValidity() { if (this._textarea) { this._internals.setValidity(this._textarea.validity, this._textarea.validationMessage); } } _handleInput(e) { this.value = e.target.value; this._dirty = true; this._internals.setFormValue(this.value); this._internals.setValidity(this._textarea.validity, this._textarea.validationMessage); } _handleChange(e) { this.value = e.target.value; this._internals.setFormValue(this.value); } _handleBlur() { this._touched = true; this._internals.setValidity(this._textarea.validity, this._textarea.validationMessage); } render() { let validationMessage = ''; if (this._touched && this._textarea && !this._textarea.validity.valid) { validationMessage = this._textarea.validationMessage; } return html ` <div class=\"wrapper\"> ${this.label ? html ` <label for=\"textarea\"> ${this.label} ${this.required ? html `<span class=\"required\" aria-hidden=\"true\">*</span>` : nothing} </label> ` : nothing} <textarea id=\"textarea\" class=${classMap({ 'textarea--invalid': this._touched && this._textarea && !this._textarea.validity.valid })} name=${this.name} .value=${live(this.value)} placeholder=${this.placeholder} ?required=${this.required} ?disabled=${this.disabled} ?readonly=${this.readonly} rows=${this.rows} cols=${ifDefined(this.cols)} minlength=${ifDefined(this.minlength)} maxlength=${ifDefined(this.maxlength)} autocomplete=${ifDefined(this.autocomplete || undefined)} @input=${this._handleInput} @change=${this._handleChange} @blur=${this._handleBlur} ></textarea> ${validationMessage ? html `<div class=\"validation-message\">${validationMessage}</div>` : this.hint ? html `<div class=\"hint\">${this.hint}</div>` : nothing} </div> `; } // Public methods for programmatic control focus() { this._textarea?.focus(); } blur() { this._textarea?.blur(); } select() { this._textarea?.select(); } }",
3618
+ "description": "A textarea component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset.\n\nNative input and change events bubble up from the inner textarea element."
3559
3619
  }
3560
3620
  ],
3561
3621
  "exports": [
3562
3622
  {
3563
3623
  "kind": "js",
3564
- "name": "KRSelectField",
3624
+ "name": "KRTextareaField",
3565
3625
  "declaration": {
3566
- "name": "KRSelectField",
3567
- "module": "dist/form/select-field/select-field.js"
3626
+ "name": "KRTextareaField",
3627
+ "module": "dist/form/textarea-field/textarea-field.js"
3568
3628
  }
3569
3629
  }
3570
3630
  ]
3571
3631
  },
3572
3632
  {
3573
3633
  "kind": "javascript-module",
3574
- "path": "dist/form/select-field/select-option.js",
3634
+ "path": "src/form/detail-field/detail-field.ts",
3575
3635
  "declarations": [
3576
3636
  {
3577
- "kind": "variable",
3578
- "name": "KRSelectOption",
3579
- "default": "class KRSelectOption extends LitElement { constructor() { super(...arguments); /** * The option value */ this.value = ''; /** * Whether the option is disabled */ this.disabled = false; } /** Gets the label text from the slot */ get label() { return this.textContent?.trim() || ''; } render() { return html `<slot></slot>`; } }",
3580
- "description": "An option for the kr-select-field component."
3637
+ "kind": "class",
3638
+ "description": "A read-only field component that displays a label and value.",
3639
+ "name": "KRDetailField",
3640
+ "members": [
3641
+ {
3642
+ "kind": "field",
3643
+ "name": "label",
3644
+ "type": {
3645
+ "text": "string"
3646
+ },
3647
+ "default": "''",
3648
+ "description": "The field label text",
3649
+ "attribute": "label"
3650
+ },
3651
+ {
3652
+ "kind": "field",
3653
+ "name": "value",
3654
+ "type": {
3655
+ "text": "string"
3656
+ },
3657
+ "default": "''",
3658
+ "description": "The value to display",
3659
+ "attribute": "value"
3660
+ }
3661
+ ],
3662
+ "attributes": [
3663
+ {
3664
+ "name": "label",
3665
+ "type": {
3666
+ "text": "string"
3667
+ },
3668
+ "default": "''",
3669
+ "description": "The field label text",
3670
+ "fieldName": "label"
3671
+ },
3672
+ {
3673
+ "name": "value",
3674
+ "type": {
3675
+ "text": "string"
3676
+ },
3677
+ "default": "''",
3678
+ "description": "The value to display",
3679
+ "fieldName": "value"
3680
+ }
3681
+ ],
3682
+ "superclass": {
3683
+ "name": "LitElement",
3684
+ "package": "lit"
3685
+ },
3686
+ "tagName": "kr-detail-field",
3687
+ "customElement": true
3581
3688
  }
3582
3689
  ],
3583
3690
  "exports": [
3584
3691
  {
3585
3692
  "kind": "js",
3586
- "name": "KRSelectOption",
3693
+ "name": "KRDetailField",
3587
3694
  "declaration": {
3588
- "name": "KRSelectOption",
3589
- "module": "dist/form/select-field/select-option.js"
3695
+ "name": "KRDetailField",
3696
+ "module": "src/form/detail-field/detail-field.ts"
3697
+ }
3698
+ },
3699
+ {
3700
+ "kind": "custom-element-definition",
3701
+ "name": "kr-detail-field",
3702
+ "declaration": {
3703
+ "name": "KRDetailField",
3704
+ "module": "src/form/detail-field/detail-field.ts"
3590
3705
  }
3591
3706
  }
3592
3707
  ]
@@ -0,0 +1,24 @@
1
+ import { LitElement } from 'lit';
2
+ /**
3
+ * A read-only field component that displays a label and value.
4
+ *
5
+ * @element kr-detail-field
6
+ */
7
+ export declare class KRDetailField extends LitElement {
8
+ static styles: import("lit").CSSResult;
9
+ /**
10
+ * The field label text
11
+ */
12
+ label: string;
13
+ /**
14
+ * The value to display
15
+ */
16
+ value: string;
17
+ render(): import("lit-html").TemplateResult<1>;
18
+ }
19
+ declare global {
20
+ interface HTMLElementTagNameMap {
21
+ 'kr-detail-field': KRDetailField;
22
+ }
23
+ }
24
+ //# sourceMappingURL=detail-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail-field.d.ts","sourceRoot":"","sources":["../../../src/form/detail-field/detail-field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD;;;;GAIG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,OAAgB,MAAM,0BAsBpB;IAEF;;OAEG;IAEH,KAAK,SAAM;IAEX;;OAEG;IAEH,KAAK,SAAM;IAEF,MAAM;CAUhB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iBAAiB,EAAE,aAAa,CAAC;KAClC;CACF"}