@kodaris/krubble-components 1.0.8 → 1.0.10

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.
@@ -95,6 +95,14 @@
95
95
  "module": "./tabs/tab.js"
96
96
  }
97
97
  },
98
+ {
99
+ "kind": "js",
100
+ "name": "KRTable",
101
+ "declaration": {
102
+ "name": "KRTable",
103
+ "module": "./table/table.js"
104
+ }
105
+ },
98
106
  {
99
107
  "kind": "js",
100
108
  "name": "KRTextField",
@@ -154,7 +162,7 @@
154
162
  {
155
163
  "kind": "variable",
156
164
  "name": "KRButton",
157
- "default": "class KRButton extends i$2 { 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; this._state = 'idle'; this._stateText = ''; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.click(); } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); } /** * 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() { return b ` <slot></slot> ${this._state !== 'idle' ? b `<span class=\"state-overlay\"> ${this._state === 'loading' ? b `<span class=\"spinner\"></span>` : this._stateText} </span>` : ''} `; } }",
165
+ "default": "class KRButton extends i$2 { 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._dropdownOpen = 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._dropdownOpen) { this._dropdownOpen = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpen && !this.contains(e.target)) { this._dropdownOpen = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', '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._dropdownOpen = !this._dropdownOpen; } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpen = 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 hasOptions = this.options.length > 0; return b ` <slot></slot> ${hasOptions ? b `<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>` : A} ${this._state !== 'idle' ? b `<span class=\"state-overlay\"> ${this._state === 'loading' ? b `<span class=\"spinner\"></span>` : this._stateText} </span>` : A} ${hasOptions ? b ` <div class=\"dropdown ${this._dropdownOpen ? 'open' : ''}\"> ${this.options.map(option => b ` <button class=\"dropdown-item\" @click=${(e) => this._handleOptionClick(option, e)} >${option.label}</button> `)} </div> ` : A} `; } }",
158
166
  "description": "A customizable button component."
159
167
  },
160
168
  {
@@ -245,6 +253,11 @@
245
253
  "default": "class KRTab extends i$2 { constructor() { super(...arguments); /** * Unique identifier for the tab */ this.id = ''; /** * Display title for the tab */ this.title = ''; /** * Badge text displayed next to title (e.g. notification count) */ this.badge = ''; /** * Badge background color */ this.badgeBackground = ''; /** * Badge text color */ this.badgeColor = ''; /** * Whether the tab is disabled */ this.disabled = false; /** * Whether the tab can be dismissed/closed */ this.dismissible = false; /** * Whether this tab is currently active (set by parent) */ this.active = false; } /** * Gets the icon element from the icon slot (if any) */ getIconElement() { return this.querySelector('[slot=\"icon\"]'); } render() { return b `<slot></slot>`; } }",
246
254
  "description": "A tab for the kr-tab-group component."
247
255
  },
256
+ {
257
+ "kind": "variable",
258
+ "name": "KRTable",
259
+ "default": "class KRTable extends i$2 { constructor() { super(...arguments); /** * Internal flag to switch between scroll edge modes: * - 'overlay': Fixed padding with overlay elements that hide content at edges (scrollbar at viewport edge) * - 'edge': Padding scrolls with content, allowing table to reach edges when scrolling */ this._scrollStyle = 'edge'; this._data = []; this._dataState = 'idle'; this._page = 1; this._pageSize = 50; this._totalItems = 0; this._totalPages = 0; this._searchQuery = ''; this._canScrollLeft = false; this._canScrollRight = false; this._canScrollHorizontal = false; this._columnPickerOpen = false; this._displayedColumns = []; this._columnWidths = new Map(); this._resizing = null; this._resizeObserver = null; this._searchPositionLocked = false; this.def = { columns: [] }; this._handleClickOutsideColumnPicker = (e) => { if (!this._columnPickerOpen) return; const path = e.composedPath(); const picker = this.shadowRoot?.querySelector('.column-picker-wrapper'); if (picker && !path.includes(picker)) { this._columnPickerOpen = false; } }; this._handleResizeMove = (e) => { if (!this._resizing) return; this._columnWidths.set(this._resizing.columnId, Math.max(50, this._resizing.startWidth + (e.clientX - this._resizing.startX))); this.requestUpdate(); }; this._handleResizeEnd = () => { this._resizing = null; document.removeEventListener('mousemove', this._handleResizeMove); document.removeEventListener('mouseup', this._handleResizeEnd); }; } connectedCallback() { super.connectedCallback(); this.classList.toggle('kr-table--scroll-overlay', this._scrollStyle === 'overlay'); this.classList.toggle('kr-table--scroll-edge', this._scrollStyle === 'edge'); this._fetch(); this._initRefresh(); document.addEventListener('click', this._handleClickOutsideColumnPicker); this._resizeObserver = new ResizeObserver(() => { // Unlock and recalculate on resize since layout changes this._searchPositionLocked = false; this._updateSearchPosition(); }); this._resizeObserver.observe(this); } disconnectedCallback() { super.disconnectedCallback(); clearInterval(this._refreshTimer); document.removeEventListener('click', this._handleClickOutsideColumnPicker); this._resizeObserver?.disconnect(); } updated(changedProperties) { if (changedProperties.has('def')) { this._displayedColumns = this.def.displayedColumns || this.def.columns.map(c => c.id); this._fetch(); this._initRefresh(); } this._updateScrollFlags(); } // ---------------------------------------------------------------------------- // Public Interface // ---------------------------------------------------------------------------- refresh() { this._fetch(); } goToPrevPage() { if (this._page > 1) { this._page--; this._fetch(); } } goToNextPage() { if (this._page < this._totalPages) { this._page++; this._fetch(); } } goToPage(page) { if (page >= 1 && page <= this._totalPages) { this._page = page; this._fetch(); } } // ---------------------------------------------------------------------------- // Data Fetching // ---------------------------------------------------------------------------- /** * Fetches data from the API and updates the table. * Shows a loading spinner while fetching, then displays rows on success * or an error snackbar on failure. * Request/response format depends on dataSource.mode (solr, opensearch, db). */ _fetch() { if (!this.def.dataSource) return; this._dataState = 'loading'; // Build request based on mode let request; switch (this.def.dataSource.mode) { case 'opensearch': throw Error('Opensearch not supported yet'); case 'db': throw Error('DB not supported yet'); default: // solr request = { page: this._page - 1, size: this._pageSize, sorts: [], filterFields: [], queryFields: [], facetFields: [] }; if (this._searchQuery?.trim().length) { request.queryFields.push({ name: '_text_', operation: 'IS', value: escapeSolrQuery(this._searchQuery) }); } } this.def.dataSource.fetch(request) .then(response => { // Parse response based on mode switch (this.def.dataSource?.mode) { case 'opensearch': { throw Error('Opensearch not supported yet'); } case 'db': { throw Error('DB not supported yet'); } default: { // solr const res = response; this._data = res.data.content; this._totalItems = res.data.totalElements; this._totalPages = res.data.totalPages; this._pageSize = res.data.size; } } this._dataState = 'success'; this._updateSearchPosition(); }) .catch(err => { this._dataState = 'error'; KRSnackbar.show({ message: err instanceof Error ? err.message : 'Failed to load data', type: 'error' }); }); } /** * Sets up auto-refresh so the table automatically fetches fresh data * at a regular interval (useful for dashboards, monitoring views). * Configured via def.refreshInterval in milliseconds. */ _initRefresh() { clearInterval(this._refreshTimer); if (this.def.refreshInterval && this.def.refreshInterval > 0) { this._refreshTimer = window.setInterval(() => { this._fetch(); }, this.def.refreshInterval); } } _handleSearch(e) { const input = e.target; this._searchQuery = input.value; this._page = 1; this._fetch(); } /** * Updates search position to be centered with equal gaps from title and tools. * On first call: resets to flex centering, measures position, then locks with fixed margin. * Subsequent calls are ignored unless _searchPositionLocked is reset (e.g., on resize). */ _updateSearchPosition() { // Skip if already locked (prevents shifts on pagination changes) if (this._searchPositionLocked) return; const search = this.shadowRoot?.querySelector('.search'); const searchField = search?.querySelector('.search-field'); if (!search || !searchField) return; // Reset to flex centering search.style.justifyContent = 'center'; searchField.style.marginLeft = ''; requestAnimationFrame(() => { const searchRect = search.getBoundingClientRect(); const fieldRect = searchField.getBoundingClientRect(); // Calculate how far from the left of search container the field currently is const currentOffset = fieldRect.left - searchRect.left; // Lock position: switch to flex-start and use fixed margin search.style.justifyContent = 'flex-start'; searchField.style.marginLeft = `${currentOffset}px`; // Mark as locked so pagination changes don't shift the search this._searchPositionLocked = true; }); } // ---------------------------------------------------------------------------- // Columns // ---------------------------------------------------------------------------- _toggleColumnPicker() { this._columnPickerOpen = !this._columnPickerOpen; } _toggleColumn(columnId) { if (this._displayedColumns.includes(columnId)) { this._displayedColumns = this._displayedColumns.filter(id => id !== columnId); } else { this._displayedColumns = [...this._displayedColumns, columnId]; } } // When a user toggles a column on via the column picker, it gets appended // to _displayedColumns. By mapping over _displayedColumns (not def.columns), // the new column appears at the right edge of the table instead of jumping // back to its original position in the column definition. getDisplayedColumns() { return this._displayedColumns.map(id => this.def.columns.find(col => col.id === id)); } // ---------------------------------------------------------------------------- // Scrolling // ---------------------------------------------------------------------------- /** * Scroll event handler that updates scroll flags in real-time as user scrolls. * Updates shadow indicators to show if more content exists left/right. */ _handleScroll(e) { const container = e.target; this._canScrollLeft = container.scrollLeft > 0; this._canScrollRight = container.scrollLeft < container.scrollWidth - container.clientWidth - 1; } /** * Updates scroll state flags for the table content container. * - _canScrollLeft: true if scrolled right (can scroll back left) * - _canScrollRight: true if more content exists to the right * - _canScrollHorizontal: true if content is wider than container * These flags control scroll shadow indicators and CSS classes. */ _updateScrollFlags() { const container = this.shadowRoot?.querySelector('.content'); if (container) { this._canScrollLeft = container.scrollLeft > 0; this._canScrollRight = container.scrollWidth > container.clientWidth && container.scrollLeft < container.scrollWidth - container.clientWidth - 1; this._canScrollHorizontal = container.scrollWidth > container.clientWidth; } this.classList.toggle('kr-table--scroll-left-available', this._canScrollLeft); this.classList.toggle('kr-table--scroll-right-available', this._canScrollRight); this.classList.toggle('kr-table--scroll-horizontal-available', this._canScrollHorizontal); } // ---------------------------------------------------------------------------- // Column Resizing // ---------------------------------------------------------------------------- _handleResizeStart(e, columnId) { e.preventDefault(); const column = this.def.columns.find(c => c.id === columnId); this._resizing = { columnId, startX: e.clientX, startWidth: this._columnWidths.get(columnId) || parseInt(column?.width || '150', 10) }; document.addEventListener('mousemove', this._handleResizeMove); document.addEventListener('mouseup', this._handleResizeEnd); } // ---------------------------------------------------------------------------- // Header // ---------------------------------------------------------------------------- _handleAction(action) { this.dispatchEvent(new CustomEvent('action', { detail: { action: action.id }, bubbles: true, composed: true })); } // ---------------------------------------------------------------------------- // Rendering // ---------------------------------------------------------------------------- _renderCellContent(column, row) { const value = row[column.id]; if (column.render) { return column.render(row); } if (value === null || value === undefined) { return ''; } switch (column.type) { case 'number': return typeof value === 'number' ? value.toLocaleString() : String(value); case 'currency': return typeof value === 'number' ? value.toLocaleString('en-US', { style: 'currency', currency: 'USD' }) : String(value); case 'date': return value instanceof Date ? value.toLocaleDateString() : new Date(value).toLocaleDateString(); case 'boolean': if (value === true) return 'Yes'; if (value === false) return 'No'; return ''; default: return String(value); } } /** * Returns CSS classes for a header cell based on column config. */ _getHeaderCellClasses(column, index) { return { 'header-cell': true, 'header-cell--align-center': column.align === 'center', 'header-cell--align-right': column.align === 'right', 'header-cell--sticky-left': column.sticky === 'left', 'header-cell--sticky-left-last': column.sticky === 'left' && !this.getDisplayedColumns().slice(index + 1).some(c => c.sticky === 'left'), 'header-cell--sticky-right': column.sticky === 'right', 'header-cell--sticky-right-first': column.sticky === 'right' && !this.getDisplayedColumns().slice(0, index).some(c => c.sticky === 'right') }; } /** * Returns CSS classes for a table cell based on column config: * - Alignment (center, right) * - Sticky positioning (left, right) * - Border classes for the last left-sticky or first right-sticky column */ _getCellClasses(column, index) { return { 'cell': true, 'cell--align-center': column.align === 'center', 'cell--align-right': column.align === 'right', 'cell--sticky-left': column.sticky === 'left', 'cell--sticky-left-last': column.sticky === 'left' && !this.getDisplayedColumns().slice(index + 1).some(c => c.sticky === 'left'), 'cell--sticky-right': column.sticky === 'right', 'cell--sticky-right-first': column.sticky === 'right' && !this.getDisplayedColumns().slice(0, index).some(c => c.sticky === 'right') }; } /** * Returns inline styles for a table cell: * - Width (from column config or default 150px) * - Min-width (if specified) * - Left/right offset for sticky columns (calculated from widths of preceding sticky columns) */ _getCellStyle(column, index) { const styles = {}; const isLastColumn = index === this.getDisplayedColumns().length - 1; const customWidth = this._columnWidths.get(column.id); const width = customWidth ? `${customWidth}px` : (column.width || '150px'); if (isLastColumn && !customWidth) { styles.flex = '1'; styles.minWidth = column.width || '150px'; styles.marginRight = '24px'; } else { styles.width = width; } if (column.minWidth) styles.minWidth = column.minWidth; if (column.sticky === 'left') { let leftOffset = 0; for (let i = 0; i < index; i++) { if (this.getDisplayedColumns()[i].sticky === 'left') { leftOffset += parseInt(this.getDisplayedColumns()[i].width || '150', 10); } } styles.left = `${leftOffset}px`; } if (column.sticky === 'right') { let rightOffset = 0; for (let i = index + 1; i < this.getDisplayedColumns().length; i++) { if (this.getDisplayedColumns()[i].sticky === 'right') { rightOffset += parseInt(this.getDisplayedColumns()[i].width || '150', 10); } } styles.right = `${rightOffset}px`; } return styles; } /** * Renders the pagination controls: * - Previous page arrow (disabled on first page) * - Range text showing \"1-50 of 150\" format * - Next page arrow (disabled on last page) * * Hidden when there's no data or all data fits on one page. */ _renderPagination() { const start = (this._page - 1) * this._pageSize + 1; const end = Math.min(this._page * this._pageSize, this._totalItems); return b ` <div class=\"pagination\"> <span class=\"pagination-icon ${this._page === 1 ? 'pagination-icon--disabled' : ''}\" @click=${this.goToPrevPage} > <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"/></svg> </span> <span class=\"pagination-info\">${start}-${end} of ${this._totalItems}</span> <span class=\"pagination-icon ${this._page === this._totalPages ? 'pagination-icon--disabled' : ''}\" @click=${this.goToNextPage} > <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"/></svg> </span> </div> `; } /** * Renders the header toolbar containing: * - Title (left) * - Search bar with view selector dropdown (center) * - Tools (right): page navigation, refresh button, column visibility picker, actions dropdown * * Hidden when there's no title, no actions, and data fits on one page. */ _renderHeader() { if (!this.def.title && !this.def.actions?.length && this._totalPages <= 1) { return A; } return b ` <div class=\"header\"> <div class=\"title\">${this.def.title ?? ''}</div> <div class=\"search\"> <!-- TODO: Saved views dropdown <div class=\"views\"> <span>Default View</span> <svg 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> </div> --> <div class=\"search-field\"> <svg class=\"search-icon\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\"/></svg> <input type=\"text\" class=\"search-input\" placeholder=\"Search...\" .value=${this._searchQuery} @input=${this._handleSearch} /> </div> </div> <div class=\"tools\"> ${this._renderPagination()} <span class=\"refresh\" title=\"Refresh\" @click=${() => this.refresh()}> <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M480-160q-134 0-227-93t-93-227q0-134 93-227t227-93q69 0 132 28.5T720-690v-110h80v280H520v-80h168q-32-56-87.5-88T480-720q-100 0-170 70t-70 170q0 100 70 170t170 70q77 0 139-44t87-116h84q-28 106-114 173t-196 67Z\"/></svg> </span> <div class=\"column-picker-wrapper\"> <span class=\"header-icon\" title=\"Columns\" @click=${this._toggleColumnPicker}> <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M121-280v-400q0-33 23.5-56.5T201-760h559q33 0 56.5 23.5T840-680v400q0 33-23.5 56.5T760-200H201q-33 0-56.5-23.5T121-280Zm79 0h133v-400H200v400Zm213 0h133v-400H413v400Zm213 0h133v-400H626v400Z\"/></svg> </span> <div class=\"column-picker ${this._columnPickerOpen ? 'open' : ''}\"> ${[...this.def.columns].sort((a, b) => (a.label ?? a.id).localeCompare(b.label ?? b.id)).map(col => b ` <div class=\"column-picker-item\" @click=${() => this._toggleColumn(col.id)}> <div class=\"column-picker-checkbox ${this._displayedColumns.includes(col.id) ? 'checked' : ''}\"> <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg> </div> <span class=\"column-picker-label\">${col.label ?? col.id}</span> </div> `)} </div> </div> ${this.def.actions?.length ? b ` <kr-button class=\"actions\" .options=${this.def.actions.map(a => ({ id: a.id, label: a.label }))} @option-select=${(e) => this._handleAction({ id: e.detail.id, label: e.detail.label })} > Actions </kr-button> ` : A} </div> </div> `; } /** Renders status message (loading, error, empty) */ _renderStatus() { if (this._dataState === 'loading' && this._data.length === 0) { return b `<div class=\"status\">Loading...</div>`; } if (this._dataState === 'error' && this._data.length === 0) { return b `<div class=\"status status--error\">Error loading data</div>`; } if (this._data.length === 0) { return b `<div class=\"status\">No data available</div>`; } return A; } /** Renders the scrollable data grid with column headers and rows. */ _renderTable() { return b ` <div class=\"wrapper\"> <div class=\"overlay-left\"></div> <div class=\"overlay-right\"></div> ${this._renderStatus()} <div class=\"content\" @scroll=${this._handleScroll}> <div class=\"table\"> <div class=\"header-row\"> ${this.getDisplayedColumns().map((col, i) => b ` <div class=${e$1(this._getHeaderCellClasses(col, i))} style=${o$1(this._getCellStyle(col, i))} > <span class=\"header-cell__label\">${col.label ?? col.id}</span> <div class=\"header-cell__resize\" @mousedown=${(e) => this._handleResizeStart(e, col.id)} ></div> </div> `)} </div> ${this._data.map(row => b ` <div class=\"row\"> ${this.getDisplayedColumns().map((col, i) => b ` <div class=${e$1(this._getCellClasses(col, i))} style=${o$1(this._getCellStyle(col, i))} > ${this._renderCellContent(col, row)} </div> `)} </div> `)} </div> </div> </div> `; } /** * Renders a data table with: * - Header bar with title, search input with view selector, and tools (pagination, refresh, column visibility, actions dropdown) * - Scrollable grid with sticky header row and optional sticky left/right columns * - Loading, error message, or empty state when no data */ render() { if (!this.def.columns.length) { return b `<slot></slot>`; } return b ` ${this._renderHeader()} ${this._renderTable()} `; } }"
260
+ },
248
261
  {
249
262
  "kind": "variable",
250
263
  "name": "KRTextField",
@@ -377,6 +390,14 @@
377
390
  "module": "dist/krubble.bundled.js"
378
391
  }
379
392
  },
393
+ {
394
+ "kind": "js",
395
+ "name": "KRTable",
396
+ "declaration": {
397
+ "name": "KRTable",
398
+ "module": "dist/krubble.bundled.js"
399
+ }
400
+ },
380
401
  {
381
402
  "kind": "js",
382
403
  "name": "KRTextField",
@@ -410,38 +431,38 @@
410
431
  },
411
432
  {
412
433
  "kind": "variable",
413
- "name": "le",
434
+ "name": "ae",
414
435
  "default": "r` :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; } `"
415
436
  },
416
437
  {
417
438
  "kind": "variable",
418
- "name": "ge",
419
- "default": "class extends ne{constructor(){super(...arguments),this.header=\"\",this.expanded=!1}toggle(){this.expanded=!this.expanded}render(){return V` <div class=\"header\" @click=${this.toggle}> <span class=\"header__title\">${this.header}</span> <svg class=\"header__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"> <path d=\"M6 9l6 6 6-6\"/> </svg> </div> <div class=\"content\"> <div class=\"content__inner\"> <div class=\"content__body\"> <slot></slot> </div> </div> </div> `}}"
439
+ "name": "be",
440
+ "default": "class extends ne{constructor(){super(...arguments),this.header=\"\",this.expanded=!1}toggle(){this.expanded=!this.expanded}render(){return U` <div class=\"header\" @click=${this.toggle}> <span class=\"header__title\">${this.header}</span> <svg class=\"header__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"> <path d=\"M6 9l6 6 6-6\"/> </svg> </div> <div class=\"content\"> <div class=\"content__inner\"> <div class=\"content__body\"> <slot></slot> </div> </div> </div> `}}"
420
441
  },
421
442
  {
422
443
  "kind": "variable",
423
- "name": "Ae",
424
- "default": "class extends ne{constructor(){super(...arguments),this.type=\"info\",this.dismissible=!1,this.visible=!0}_handleDismiss(){this.visible=!1,this.dispatchEvent(new CustomEvent(\"dismiss\",{bubbles:!0,composed:!0}))}render(){const e={info:V`<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:V`<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:V`<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:V`<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 V` <div class=${$e({alert:!0,[\"alert--\"+this.type]:!0,\"alert--hidden\":!this.visible})} role=\"alert\" > ${e[this.type]} <div class=\"content\"> ${this.header?V`<h4 class=\"header\">${this.header}</h4>`:q} <div class=\"message\"> <slot></slot> </div> </div> ${this.dismissible?V` <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> `:q} </div> `}}"
444
+ "name": "Ce",
445
+ "default": "class extends ne{constructor(){super(...arguments),this.type=\"info\",this.dismissible=!1,this.visible=!0}_handleDismiss(){this.visible=!1,this.dispatchEvent(new CustomEvent(\"dismiss\",{bubbles:!0,composed:!0}))}render(){const e={info:U`<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:U`<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:U`<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:U`<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 U` <div class=${we({alert:!0,[\"alert--\"+this.type]:!0,\"alert--hidden\":!this.visible})} role=\"alert\" > ${e[this.type]} <div class=\"content\"> ${this.header?U`<h4 class=\"header\">${this.header}</h4>`:V} <div class=\"message\"> <slot></slot> </div> </div> ${this.dismissible?U` <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> `:V} </div> `}}"
425
446
  },
426
447
  {
427
448
  "kind": "variable",
428
- "name": "Se",
429
- "default": "class extends ne{constructor(){super(...arguments),this.variant=\"flat\",this.color=\"primary\",this.size=\"medium\",this.disabled=!1,this._state=\"idle\",this._stateText=\"\",this._handleKeydown=e=>{\"Enter\"!==e.key&&\" \"!==e.key||(e.preventDefault(),this.click())}}connectedCallback(){super.connectedCallback(),this.setAttribute(\"role\",\"button\"),this.setAttribute(\"tabindex\",\"0\"),this.addEventListener(\"keydown\",this._handleKeydown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(\"keydown\",this._handleKeydown)}showLoading(){this._clearStateTimeout(),this._state=\"loading\",this._stateText=\"\"}showSuccess(e=\"Success\",t=2e3){this._clearStateTimeout(),this._state=\"success\",this._stateText=e,this._stateTimeout=window.setTimeout(()=>this.reset(),t)}showError(e=\"Error\",t=2e3){this._clearStateTimeout(),this._state=\"error\",this._stateText=e,this._stateTimeout=window.setTimeout(()=>this.reset(),t)}reset(){this._clearStateTimeout(),this._state=\"idle\",this._stateText=\"\"}_clearStateTimeout(){this._stateTimeout&&(clearTimeout(this._stateTimeout),this._stateTimeout=void 0)}updated(e){this.classList.toggle(\"kr-button--loading\",\"loading\"===this._state),this.classList.toggle(\"kr-button--success\",\"success\"===this._state),this.classList.toggle(\"kr-button--error\",\"error\"===this._state),this.classList.toggle(`kr-button--${this.variant}`,!0),this.classList.toggle(`kr-button--${this.color}`,!0),this.classList.toggle(\"kr-button--small\",\"small\"===this.size),this.classList.toggle(\"kr-button--large\",\"large\"===this.size)}render(){return V` <slot></slot> ${\"idle\"!==this._state?V`<span class=\"state-overlay\"> ${\"loading\"===this._state?V`<span class=\"spinner\"></span>`:this._stateText} </span>`:\"\"} `}}"
449
+ "name": "Ee",
450
+ "default": "class extends ne{constructor(){super(...arguments),this.variant=\"flat\",this.color=\"primary\",this.size=\"medium\",this.disabled=!1,this.options=[],this._state=\"idle\",this._stateText=\"\",this._dropdownOpen=!1,this._handleHostClick=e=>{this.options.length&&(e.stopPropagation(),this._toggleDropdown())},this._handleKeydown=e=>{\"Enter\"!==e.key&&\" \"!==e.key||(e.preventDefault(),this.options.length?this._toggleDropdown():this.click()),\"Escape\"===e.key&&this._dropdownOpen&&(this._dropdownOpen=!1)},this._handleClickOutside=e=>{this._dropdownOpen&&!this.contains(e.target)&&(this._dropdownOpen=!1)}}connectedCallback(){super.connectedCallback(),this.setAttribute(\"role\",\"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._dropdownOpen=!this._dropdownOpen}_handleOptionClick(e,t){t.stopPropagation(),this._dropdownOpen=!1,this.dispatchEvent(new CustomEvent(\"option-select\",{detail:{id:e.id,label:e.label},bubbles:!0,composed:!0}))}showLoading(){this._clearStateTimeout(),this._state=\"loading\",this._stateText=\"\"}showSuccess(e=\"Success\",t=2e3){this._clearStateTimeout(),this._state=\"success\",this._stateText=e,this._stateTimeout=window.setTimeout(()=>this.reset(),t)}showError(e=\"Error\",t=2e3){this._clearStateTimeout(),this._state=\"error\",this._stateText=e,this._stateTimeout=window.setTimeout(()=>this.reset(),t)}reset(){this._clearStateTimeout(),this._state=\"idle\",this._stateText=\"\"}_clearStateTimeout(){this._stateTimeout&&(clearTimeout(this._stateTimeout),this._stateTimeout=void 0)}updated(e){this.classList.toggle(\"kr-button--loading\",\"loading\"===this._state),this.classList.toggle(\"kr-button--success\",\"success\"===this._state),this.classList.toggle(\"kr-button--error\",\"error\"===this._state),this.classList.toggle(`kr-button--${this.variant}`,!0),this.classList.toggle(`kr-button--${this.color}`,!0),this.classList.toggle(\"kr-button--small\",\"small\"===this.size),this.classList.toggle(\"kr-button--large\",\"large\"===this.size)}render(){const e=this.options.length>0;return U` <slot></slot> ${e?U`<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>`:V} ${\"idle\"!==this._state?U`<span class=\"state-overlay\"> ${\"loading\"===this._state?U`<span class=\"spinner\"></span>`:this._stateText} </span>`:V} ${e?U` <div class=\"dropdown ${this._dropdownOpen?\"open\":\"\"}\"> ${this.options.map(e=>U` <button class=\"dropdown-item\" @click=${t=>this._handleOptionClick(e,t)} >${e.label}</button> `)} </div> `:V} `}}"
430
451
  },
431
452
  {
432
453
  "kind": "variable",
433
- "name": "Pe",
434
- "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 V` <div class=\"tabs\"> <button class=${$e({tab:!0,\"tab--active\":\"preview\"===this.activeTab})} @click=${()=>this.setTab(\"preview\")} > Preview </button> <button class=${$e({tab:!0,\"tab--active\":\"code\"===this.activeTab})} @click=${()=>this.setTab(\"code\")} > Code </button> </div> <div class=${$e({panel:!0,\"panel--active\":\"preview\"===this.activeTab,preview:!0})}> <slot name=\"preview\"></slot> </div> <div class=${$e({panel:!0,\"panel--active\":\"code\"===this.activeTab,\"code-container\":!0})}> <button class=${$e({\"copy-btn\":!0,\"copy-btn--copied\":this.copied})} @click=${this.copyCode} > ${this.copied?\"Copied!\":\"Copy\"} </button> <pre class=\"code\"><code>${Te(this.getHighlightedCode())}</code></pre> </div> `}}"
454
+ "name": "ze",
455
+ "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>${Pe(this.getHighlightedCode())}</code></pre> </div> `}}"
435
456
  },
436
457
  {
437
458
  "kind": "variable",
438
- "name": "Re",
439
- "default": "class extends ne{constructor(){super(...arguments),this.items=[],this.resolvePromise=null,this.boundHandleOutsideClick=this.handleOutsideClick.bind(this),this.boundHandleKeyDown=this.handleKeyDown.bind(this)}static async open(e){const t=document.querySelector(\"kr-context-menu\");t&&t.remove();const i=document.createElement(\"kr-context-menu\");return document.body.appendChild(i),i.show(e)}async show(e){this.items=e.items,this.style.left=`${e.x}px`,this.style.top=`${e.y}px`,await this.updateComplete;const t=this.getBoundingClientRect();return t.right>window.innerWidth&&(this.style.left=e.x-t.width+\"px\"),t.bottom>window.innerHeight&&(this.style.top=e.y-t.height+\"px\"),requestAnimationFrame(()=>{document.addEventListener(\"click\",this.boundHandleOutsideClick),document.addEventListener(\"contextmenu\",this.boundHandleOutsideClick),document.addEventListener(\"keydown\",this.boundHandleKeyDown)}),new Promise(e=>{this.resolvePromise=e})}handleOutsideClick(e){this.contains(e.target)||this.close(null)}handleKeyDown(e){\"Escape\"===e.key&&this.close(null)}handleItemClick(e){e.disabled||e.divider||this.close(e)}close(e){document.removeEventListener(\"click\",this.boundHandleOutsideClick),document.removeEventListener(\"contextmenu\",this.boundHandleOutsideClick),document.removeEventListener(\"keydown\",this.boundHandleKeyDown),this.resolvePromise&&(this.resolvePromise(e),this.resolvePromise=null),this.remove()}render(){return V` <div class=\"menu\"> ${this.items.map(e=>e.divider?V`<div class=\"menu__divider\"></div>`:V` <button class=\"menu__item\" ?disabled=${e.disabled} @click=${()=>this.handleItemClick(e)} > ${e.icon?V`<span class=\"menu__item-icon\">${e.icon}</span>`:null} ${e.label} </button> `)} </div> `}}"
459
+ "name": "Le",
460
+ "default": "class extends ne{constructor(){super(...arguments),this.items=[],this.resolvePromise=null,this.boundHandleOutsideClick=this.handleOutsideClick.bind(this),this.boundHandleKeyDown=this.handleKeyDown.bind(this)}static async open(e){const t=document.querySelector(\"kr-context-menu\");t&&t.remove();const i=document.createElement(\"kr-context-menu\");return document.body.appendChild(i),i.show(e)}async show(e){this.items=e.items,this.style.left=`${e.x}px`,this.style.top=`${e.y}px`,await this.updateComplete;const t=this.getBoundingClientRect();return t.right>window.innerWidth&&(this.style.left=e.x-t.width+\"px\"),t.bottom>window.innerHeight&&(this.style.top=e.y-t.height+\"px\"),requestAnimationFrame(()=>{document.addEventListener(\"click\",this.boundHandleOutsideClick),document.addEventListener(\"contextmenu\",this.boundHandleOutsideClick),document.addEventListener(\"keydown\",this.boundHandleKeyDown)}),new Promise(e=>{this.resolvePromise=e})}handleOutsideClick(e){this.contains(e.target)||this.close(null)}handleKeyDown(e){\"Escape\"===e.key&&this.close(null)}handleItemClick(e){e.disabled||e.divider||this.close(e)}close(e){document.removeEventListener(\"click\",this.boundHandleOutsideClick),document.removeEventListener(\"contextmenu\",this.boundHandleOutsideClick),document.removeEventListener(\"keydown\",this.boundHandleKeyDown),this.resolvePromise&&(this.resolvePromise(e),this.resolvePromise=null),this.remove()}render(){return U` <div class=\"menu\"> ${this.items.map(e=>e.divider?U`<div class=\"menu__divider\"></div>`:U` <button class=\"menu__item\" ?disabled=${e.disabled} @click=${()=>this.handleItemClick(e)} > ${e.icon?U`<span class=\"menu__item-icon\">${e.icon}</span>`:null} ${e.label} </button> `)} </div> `}}"
440
461
  },
441
462
  {
442
463
  "kind": "class",
443
464
  "description": "",
444
- "name": "Le",
465
+ "name": "De",
445
466
  "members": [
446
467
  {
447
468
  "kind": "method",
@@ -491,36 +512,41 @@
491
512
  {
492
513
  "kind": "variable",
493
514
  "name": "Me",
494
- "default": "class extends ne{constructor(){super(...arguments),this.contentElement=null,this.dialogRef=null,this.boundHandleKeyDown=this.handleKeyDown.bind(this)}static open(e,t){const i=document.querySelector(\"kr-dialog\");i&&i.remove();const o=new Le,s=document.createElement(\"kr-dialog\");o.setDialogElement(s),s.dialogRef=o;const r=new e;return r.dialogRef=o,t?.data&&(r.data=t.data),s.contentElement=r,document.body.appendChild(s),document.addEventListener(\"keydown\",s.boundHandleKeyDown),o}handleKeyDown(e){\"Escape\"===e.key&&this.dialogRef?.close(void 0)}handleBackdropClick(e){e.target.classList.contains(\"backdrop\")&&this.dialogRef?.close(void 0)}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener(\"keydown\",this.boundHandleKeyDown)}render(){return V` <div class=\"backdrop\" @click=${this.handleBackdropClick}></div> <div class=\"dialog\"> ${this.contentElement} </div> `}}"
515
+ "default": "class extends ne{constructor(){super(...arguments),this.contentElement=null,this.dialogRef=null,this.boundHandleKeyDown=this.handleKeyDown.bind(this)}static open(e,t){const i=document.querySelector(\"kr-dialog\");i&&i.remove();const o=new De,s=document.createElement(\"kr-dialog\");o.setDialogElement(s),s.dialogRef=o;const r=new e;return r.dialogRef=o,t?.data&&(r.data=t.data),s.contentElement=r,document.body.appendChild(s),document.addEventListener(\"keydown\",s.boundHandleKeyDown),o}handleKeyDown(e){\"Escape\"===e.key&&this.dialogRef?.close(void 0)}handleBackdropClick(e){e.target.classList.contains(\"backdrop\")&&this.dialogRef?.close(void 0)}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener(\"keydown\",this.boundHandleKeyDown)}render(){return U` <div class=\"backdrop\" @click=${this.handleBackdropClick}></div> <div class=\"dialog\"> ${this.contentElement} </div> `}}"
495
516
  },
496
517
  {
497
518
  "kind": "variable",
498
- "name": "He"
519
+ "name": "Ie"
499
520
  },
500
521
  {
501
522
  "kind": "variable",
502
- "name": "qe",
503
- "default": "class extends ne{constructor(){super(...arguments),this.justified=!1,this.size=\"medium\"}updated(e){e.has(\"activeTabId\")&&this._updateActiveTab()}firstUpdated(){this._updateActiveTab()}_getTabs(){return Array.from(this.querySelectorAll(\"kr-tab\"))}_updateActiveTab(){const e=this._getTabs();0!==e.length&&(this.activeTabId||(this.activeTabId=e[0]?.id),e.forEach(e=>{e.active=e.id===this.activeTabId}),this.requestUpdate())}_handleTabClick(e){e.disabled||(this.activeTabId=e.id,this.dispatchEvent(new CustomEvent(\"tab-change\",{detail:{activeTabId:e.id},bubbles:!0,composed:!0})))}_handleTabDismiss(e,t){t.stopPropagation(),this.dispatchEvent(new CustomEvent(\"tab-dismiss\",{detail:{tabId:e.id},bubbles:!0,composed:!0}))}_handleKeyDown(e){const t=this._getTabs().filter(e=>!e.disabled),i=t.findIndex(e=>e.id===this.activeTabId);let o=-1;switch(e.key){case\"ArrowLeft\":o=i>0?i-1:t.length-1;break;case\"ArrowRight\":o=i<t.length-1?i+1:0;break;case\"Home\":o=0;break;case\"End\":o=t.length-1}if(o>=0){e.preventDefault();const i=t[o];this.activeTabId=i.id,this.dispatchEvent(new CustomEvent(\"tab-change\",{detail:{activeTabId:i.id},bubbles:!0,composed:!0}));const s=this.shadowRoot?.querySelectorAll(\".label\"),r=Array.from(s||[]).find(e=>e.getAttribute(\"data-tab-id\")===i.id);r?.focus()}}_renderTabIcon(e){const t=e.getIconElement();if(!t)return q;const i=t.cloneNode(!0);return i.removeAttribute(\"slot\"),V`<span class=\"label-icon\">${i}</span>`}render(){return V` <div class=\"header\" role=\"tablist\" @keydown=${this._handleKeyDown}> ${this._getTabs().map(e=>V` <button class=${$e({label:!0,\"label--active\":e.id===this.activeTabId,\"label--justified\":this.justified})} role=\"tab\" data-tab-id=${e.id} aria-selected=${e.id===this.activeTabId} aria-controls=${`panel-${e.id}`} tabindex=${e.id===this.activeTabId?0:-1} ?disabled=${e.disabled} @click=${()=>this._handleTabClick(e)} > ${this._renderTabIcon(e)} <span>${e.title}</span> ${e.badge?V`<span class=\"label-badge\" style=${Ve({backgroundColor:e.badgeBackground,color:e.badgeColor})}>${e.badge}</span>`:q} ${e.dismissible?V` <button class=\"label-dismiss\" type=\"button\" aria-label=\"Close tab\" @click=${t=>this._handleTabDismiss(e,t)} > <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" width=\"12\" height=\"12\"><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> `:q} </button> `)} </div> <div class=\"content\" role=\"tabpanel\" aria-labelledby=${this.activeTabId||\"\"}> <slot @slotchange=${this._updateActiveTab}></slot> </div> `}}"
523
+ "name": "Ve",
524
+ "default": "class extends ne{constructor(){super(...arguments),this.justified=!1,this.size=\"medium\"}updated(e){e.has(\"activeTabId\")&&this._updateActiveTab()}firstUpdated(){this._updateActiveTab()}_getTabs(){return Array.from(this.querySelectorAll(\"kr-tab\"))}_updateActiveTab(){const e=this._getTabs();0!==e.length&&(this.activeTabId||(this.activeTabId=e[0]?.id),e.forEach(e=>{e.active=e.id===this.activeTabId}),this.requestUpdate())}_handleTabClick(e){e.disabled||(this.activeTabId=e.id,this.dispatchEvent(new CustomEvent(\"tab-change\",{detail:{activeTabId:e.id},bubbles:!0,composed:!0})))}_handleTabDismiss(e,t){t.stopPropagation(),this.dispatchEvent(new CustomEvent(\"tab-dismiss\",{detail:{tabId:e.id},bubbles:!0,composed:!0}))}_handleKeyDown(e){const t=this._getTabs().filter(e=>!e.disabled),i=t.findIndex(e=>e.id===this.activeTabId);let o=-1;switch(e.key){case\"ArrowLeft\":o=i>0?i-1:t.length-1;break;case\"ArrowRight\":o=i<t.length-1?i+1:0;break;case\"Home\":o=0;break;case\"End\":o=t.length-1}if(o>=0){e.preventDefault();const i=t[o];this.activeTabId=i.id,this.dispatchEvent(new CustomEvent(\"tab-change\",{detail:{activeTabId:i.id},bubbles:!0,composed:!0}));const s=this.shadowRoot?.querySelectorAll(\".label\"),r=Array.from(s||[]).find(e=>e.getAttribute(\"data-tab-id\")===i.id);r?.focus()}}_renderTabIcon(e){const t=e.getIconElement();if(!t)return V;const i=t.cloneNode(!0);return i.removeAttribute(\"slot\"),U`<span class=\"label-icon\">${i}</span>`}render(){return U` <div class=\"header\" role=\"tablist\" @keydown=${this._handleKeyDown}> ${this._getTabs().map(e=>U` <button class=${we({label:!0,\"label--active\":e.id===this.activeTabId,\"label--justified\":this.justified})} role=\"tab\" data-tab-id=${e.id} aria-selected=${e.id===this.activeTabId} aria-controls=${`panel-${e.id}`} tabindex=${e.id===this.activeTabId?0:-1} ?disabled=${e.disabled} @click=${()=>this._handleTabClick(e)} > ${this._renderTabIcon(e)} <span>${e.title}</span> ${e.badge?U`<span class=\"label-badge\" style=${Ue({backgroundColor:e.badgeBackground,color:e.badgeColor})}>${e.badge}</span>`:V} ${e.dismissible?U` <button class=\"label-dismiss\" type=\"button\" aria-label=\"Close tab\" @click=${t=>this._handleTabDismiss(e,t)} > <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" width=\"12\" height=\"12\"><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> `:V} </button> `)} </div> <div class=\"content\" role=\"tabpanel\" aria-labelledby=${this.activeTabId||\"\"}> <slot @slotchange=${this._updateActiveTab}></slot> </div> `}}"
504
525
  },
505
526
  {
506
527
  "kind": "variable",
507
528
  "name": "Fe",
508
- "default": "class extends ne{constructor(){super(...arguments),this.id=\"\",this.title=\"\",this.badge=\"\",this.badgeBackground=\"\",this.badgeColor=\"\",this.disabled=!1,this.dismissible=!1,this.active=!1}getIconElement(){return this.querySelector('[slot=\"icon\"]')}render(){return V`<slot></slot>`}}"
529
+ "default": "class extends ne{constructor(){super(...arguments),this.id=\"\",this.title=\"\",this.badge=\"\",this.badgeBackground=\"\",this.badgeColor=\"\",this.disabled=!1,this.dismissible=!1,this.active=!1}getIconElement(){return this.querySelector('[slot=\"icon\"]')}render(){return U`<slot></slot>`}}"
509
530
  },
510
531
  {
511
532
  "kind": "variable",
512
- "name": "Ge",
513
- "default": "class extends ne{constructor(){super(),this.label=\"\",this.name=\"\",this.value=\"\",this.placeholder=\"\",this.type=\"text\",this.required=!1,this.disabled=!1,this.readonly=!1,this.autocomplete=\"\",this.hint=\"\",this._touched=!1,this._dirty=!1,this._handleInvalid=e=>{e.preventDefault(),this._touched=!0},this._internals=this.attachInternals()}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=!1,this._dirty=!1,this._internals.setFormValue(\"\"),this._internals.setValidity({})}formStateRestoreCallback(e){this.value=e}connectedCallback(){super.connectedCallback(),this.addEventListener(\"invalid\",this._handleInvalid)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(\"invalid\",this._handleInvalid)}firstUpdated(){this._updateValidity()}updated(e){(e.has(\"required\")||e.has(\"value\"))&&this._updateValidity()}_updateValidity(){this._input&&this._internals.setValidity(this._input.validity,this._input.validationMessage)}_handleInput(e){this.value=e.target.value,this._dirty=!0,this._internals.setFormValue(this.value),this._internals.setValidity(this._input.validity,this._input.validationMessage)}_handleChange(e){this.value=e.target.value,this._internals.setFormValue(this.value)}_handleBlur(){this._touched=!0,this._internals.setValidity(this._input.validity,this._input.validationMessage)}render(){let e=\"\";return this._touched&&this._input&&!this._input.validity.valid&&(e=this._input.validationMessage),V` <div class=\"wrapper\"> ${this.label?V` <label for=\"input\"> ${this.label} ${this.required?V`<span class=\"required\" aria-hidden=\"true\">*</span>`:\"\"} </label> `:\"\"} <input id=\"input\" class=${$e({\"input--invalid\":this._touched&&this._input&&!this._input.validity.valid})} type=${this.type} name=${this.name} .value=${Ze(this.value)} placeholder=${this.placeholder} ?required=${this.required} ?disabled=${this.disabled} ?readonly=${this.readonly} minlength=${We(this.minlength)} maxlength=${We(this.maxlength)} pattern=${We(this.pattern)} autocomplete=${We(this.autocomplete||void 0)} @input=${this._handleInput} @change=${this._handleChange} @blur=${this._handleBlur} /> ${e?V`<div class=\"validation-message\">${e}</div>`:this.hint?V`<div class=\"hint\">${this.hint}</div>`:\"\"} </div> `}focus(){this._input?.focus()}blur(){this._input?.blur()}select(){this._input?.select()}}"
533
+ "name": "Ye",
534
+ "default": "class extends ne{constructor(){super(...arguments),this._scrollStyle=\"edge\",this._data=[],this._dataState=\"idle\",this._page=1,this._pageSize=50,this._totalItems=0,this._totalPages=0,this._searchQuery=\"\",this._canScrollLeft=!1,this._canScrollRight=!1,this._canScrollHorizontal=!1,this._columnPickerOpen=!1,this._displayedColumns=[],this._columnWidths=new Map,this._resizing=null,this._resizeObserver=null,this._searchPositionLocked=!1,this.def={columns:[]},this._handleClickOutsideColumnPicker=e=>{if(!this._columnPickerOpen)return;const t=e.composedPath(),i=this.shadowRoot?.querySelector(\".column-picker-wrapper\");i&&!t.includes(i)&&(this._columnPickerOpen=!1)},this._handleResizeMove=e=>{this._resizing&&(this._columnWidths.set(this._resizing.columnId,Math.max(50,this._resizing.startWidth+(e.clientX-this._resizing.startX))),this.requestUpdate())},this._handleResizeEnd=()=>{this._resizing=null,document.removeEventListener(\"mousemove\",this._handleResizeMove),document.removeEventListener(\"mouseup\",this._handleResizeEnd)}}connectedCallback(){super.connectedCallback(),this.classList.toggle(\"kr-table--scroll-overlay\",\"overlay\"===this._scrollStyle),this.classList.toggle(\"kr-table--scroll-edge\",\"edge\"===this._scrollStyle),this._fetch(),this._initRefresh(),document.addEventListener(\"click\",this._handleClickOutsideColumnPicker),this._resizeObserver=new ResizeObserver(()=>{this._searchPositionLocked=!1,this._updateSearchPosition()}),this._resizeObserver.observe(this)}disconnectedCallback(){super.disconnectedCallback(),clearInterval(this._refreshTimer),document.removeEventListener(\"click\",this._handleClickOutsideColumnPicker),this._resizeObserver?.disconnect()}updated(e){e.has(\"def\")&&(this._displayedColumns=this.def.displayedColumns||this.def.columns.map(e=>e.id),this._fetch(),this._initRefresh()),this._updateScrollFlags()}refresh(){this._fetch()}goToPrevPage(){this._page>1&&(this._page--,this._fetch())}goToNextPage(){this._page<this._totalPages&&(this._page++,this._fetch())}goToPage(e){e>=1&&e<=this._totalPages&&(this._page=e,this._fetch())}_fetch(){if(!this.def.dataSource)return;let e;switch(this._dataState=\"loading\",this.def.dataSource.mode){case\"opensearch\":throw Error(\"Opensearch not supported yet\");case\"db\":throw Error(\"DB not supported yet\");default:e={page:this._page-1,size:this._pageSize,sorts:[],filterFields:[],queryFields:[],facetFields:[]},this._searchQuery?.trim().length&&e.queryFields.push({name:\"_text_\",operation:\"IS\",value:Qe(this._searchQuery)})}this.def.dataSource.fetch(e).then(e=>{switch(this.def.dataSource?.mode){case\"opensearch\":throw Error(\"Opensearch not supported yet\");case\"db\":throw Error(\"DB not supported yet\");default:{const t=e;this._data=t.data.content,this._totalItems=t.data.totalElements,this._totalPages=t.data.totalPages,this._pageSize=t.data.size}}this._dataState=\"success\",this._updateSearchPosition()}).catch(e=>{this._dataState=\"error\",Ie.show({message:e instanceof Error?e.message:\"Failed to load data\",type:\"error\"})})}_initRefresh(){clearInterval(this._refreshTimer),this.def.refreshInterval&&this.def.refreshInterval>0&&(this._refreshTimer=window.setInterval(()=>{this._fetch()},this.def.refreshInterval))}_handleSearch(e){const t=e.target;this._searchQuery=t.value,this._page=1,this._fetch()}_updateSearchPosition(){if(this._searchPositionLocked)return;const e=this.shadowRoot?.querySelector(\".search\"),t=e?.querySelector(\".search-field\");e&&t&&(e.style.justifyContent=\"center\",t.style.marginLeft=\"\",requestAnimationFrame(()=>{const i=e.getBoundingClientRect(),o=t.getBoundingClientRect().left-i.left;e.style.justifyContent=\"flex-start\",t.style.marginLeft=`${o}px`,this._searchPositionLocked=!0}))}_toggleColumnPicker(){this._columnPickerOpen=!this._columnPickerOpen}_toggleColumn(e){this._displayedColumns.includes(e)?this._displayedColumns=this._displayedColumns.filter(t=>t!==e):this._displayedColumns=[...this._displayedColumns,e]}getDisplayedColumns(){return this._displayedColumns.map(e=>this.def.columns.find(t=>t.id===e))}_handleScroll(e){const t=e.target;this._canScrollLeft=t.scrollLeft>0,this._canScrollRight=t.scrollLeft<t.scrollWidth-t.clientWidth-1}_updateScrollFlags(){const e=this.shadowRoot?.querySelector(\".content\");e&&(this._canScrollLeft=e.scrollLeft>0,this._canScrollRight=e.scrollWidth>e.clientWidth&&e.scrollLeft<e.scrollWidth-e.clientWidth-1,this._canScrollHorizontal=e.scrollWidth>e.clientWidth),this.classList.toggle(\"kr-table--scroll-left-available\",this._canScrollLeft),this.classList.toggle(\"kr-table--scroll-right-available\",this._canScrollRight),this.classList.toggle(\"kr-table--scroll-horizontal-available\",this._canScrollHorizontal)}_handleResizeStart(e,t){e.preventDefault();const i=this.def.columns.find(e=>e.id===t);this._resizing={columnId:t,startX:e.clientX,startWidth:this._columnWidths.get(t)||parseInt(i?.width||\"150\",10)},document.addEventListener(\"mousemove\",this._handleResizeMove),document.addEventListener(\"mouseup\",this._handleResizeEnd)}_handleAction(e){this.dispatchEvent(new CustomEvent(\"action\",{detail:{action:e.id},bubbles:!0,composed:!0}))}_renderCellContent(e,t){const i=t[e.id];if(e.render)return e.render(t);if(null==i)return\"\";switch(e.type){case\"number\":return\"number\"==typeof i?i.toLocaleString():String(i);case\"currency\":return\"number\"==typeof i?i.toLocaleString(\"en-US\",{style:\"currency\",currency:\"USD\"}):String(i);case\"date\":return i instanceof Date?i.toLocaleDateString():new Date(i).toLocaleDateString();case\"boolean\":return!0===i?\"Yes\":!1===i?\"No\":\"\";default:return String(i)}}_getHeaderCellClasses(e,t){return{\"header-cell\":!0,\"header-cell--align-center\":\"center\"===e.align,\"header-cell--align-right\":\"right\"===e.align,\"header-cell--sticky-left\":\"left\"===e.sticky,\"header-cell--sticky-left-last\":\"left\"===e.sticky&&!this.getDisplayedColumns().slice(t+1).some(e=>\"left\"===e.sticky),\"header-cell--sticky-right\":\"right\"===e.sticky,\"header-cell--sticky-right-first\":\"right\"===e.sticky&&!this.getDisplayedColumns().slice(0,t).some(e=>\"right\"===e.sticky)}}_getCellClasses(e,t){return{cell:!0,\"cell--align-center\":\"center\"===e.align,\"cell--align-right\":\"right\"===e.align,\"cell--sticky-left\":\"left\"===e.sticky,\"cell--sticky-left-last\":\"left\"===e.sticky&&!this.getDisplayedColumns().slice(t+1).some(e=>\"left\"===e.sticky),\"cell--sticky-right\":\"right\"===e.sticky,\"cell--sticky-right-first\":\"right\"===e.sticky&&!this.getDisplayedColumns().slice(0,t).some(e=>\"right\"===e.sticky)}}_getCellStyle(e,t){const i={},o=t===this.getDisplayedColumns().length-1,s=this._columnWidths.get(e.id),r=s?`${s}px`:e.width||\"150px\";if(o&&!s?(i.flex=\"1\",i.minWidth=e.width||\"150px\",i.marginRight=\"24px\"):i.width=r,e.minWidth&&(i.minWidth=e.minWidth),\"left\"===e.sticky){let e=0;for(let i=0;i<t;i++)\"left\"===this.getDisplayedColumns()[i].sticky&&(e+=parseInt(this.getDisplayedColumns()[i].width||\"150\",10));i.left=`${e}px`}if(\"right\"===e.sticky){let e=0;for(let i=t+1;i<this.getDisplayedColumns().length;i++)\"right\"===this.getDisplayedColumns()[i].sticky&&(e+=parseInt(this.getDisplayedColumns()[i].width||\"150\",10));i.right=`${e}px`}return i}_renderPagination(){const e=(this._page-1)*this._pageSize+1,t=Math.min(this._page*this._pageSize,this._totalItems);return U` <div class=\"pagination\"> <span class=\"pagination-icon ${1===this._page?\"pagination-icon--disabled\":\"\"}\" @click=${this.goToPrevPage} > <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"/></svg> </span> <span class=\"pagination-info\">${e}-${t} of ${this._totalItems}</span> <span class=\"pagination-icon ${this._page===this._totalPages?\"pagination-icon--disabled\":\"\"}\" @click=${this.goToNextPage} > <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"/></svg> </span> </div> `}_renderHeader(){return!this.def.title&&!this.def.actions?.length&&this._totalPages<=1?V:U` <div class=\"header\"> <div class=\"title\">${this.def.title??\"\"}</div> <div class=\"search\"> <!-- TODO: Saved views dropdown <div class=\"views\"> <span>Default View</span> <svg 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> </div> --> <div class=\"search-field\"> <svg class=\"search-icon\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\"/></svg> <input type=\"text\" class=\"search-input\" placeholder=\"Search...\" .value=${this._searchQuery} @input=${this._handleSearch} /> </div> </div> <div class=\"tools\"> ${this._renderPagination()} <span class=\"refresh\" title=\"Refresh\" @click=${()=>this.refresh()}> <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M480-160q-134 0-227-93t-93-227q0-134 93-227t227-93q69 0 132 28.5T720-690v-110h80v280H520v-80h168q-32-56-87.5-88T480-720q-100 0-170 70t-70 170q0 100 70 170t170 70q77 0 139-44t87-116h84q-28 106-114 173t-196 67Z\"/></svg> </span> <div class=\"column-picker-wrapper\"> <span class=\"header-icon\" title=\"Columns\" @click=${this._toggleColumnPicker}> <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M121-280v-400q0-33 23.5-56.5T201-760h559q33 0 56.5 23.5T840-680v400q0 33-23.5 56.5T760-200H201q-33 0-56.5-23.5T121-280Zm79 0h133v-400H200v400Zm213 0h133v-400H413v400Zm213 0h133v-400H626v400Z\"/></svg> </span> <div class=\"column-picker ${this._columnPickerOpen?\"open\":\"\"}\"> ${[...this.def.columns].sort((e,t)=>(e.label??e.id).localeCompare(t.label??t.id)).map(e=>U` <div class=\"column-picker-item\" @click=${()=>this._toggleColumn(e.id)}> <div class=\"column-picker-checkbox ${this._displayedColumns.includes(e.id)?\"checked\":\"\"}\"> <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg> </div> <span class=\"column-picker-label\">${e.label??e.id}</span> </div> `)} </div> </div> ${this.def.actions?.length?U` <kr-button class=\"actions\" .options=${this.def.actions.map(e=>({id:e.id,label:e.label}))} @option-select=${e=>this._handleAction({id:e.detail.id,label:e.detail.label})} > Actions </kr-button> `:V} </div> </div> `}_renderStatus(){return\"loading\"===this._dataState&&0===this._data.length?U`<div class=\"status\">Loading...</div>`:\"error\"===this._dataState&&0===this._data.length?U`<div class=\"status status--error\">Error loading data</div>`:0===this._data.length?U`<div class=\"status\">No data available</div>`:V}_renderTable(){return U` <div class=\"wrapper\"> <div class=\"overlay-left\"></div> <div class=\"overlay-right\"></div> ${this._renderStatus()} <div class=\"content\" @scroll=${this._handleScroll}> <div class=\"table\"> <div class=\"header-row\"> ${this.getDisplayedColumns().map((e,t)=>U` <div class=${we(this._getHeaderCellClasses(e,t))} style=${Ue(this._getCellStyle(e,t))} > <span class=\"header-cell__label\">${e.label??e.id}</span> <div class=\"header-cell__resize\" @mousedown=${t=>this._handleResizeStart(t,e.id)} ></div> </div> `)} </div> ${this._data.map(e=>U` <div class=\"row\"> ${this.getDisplayedColumns().map((t,i)=>U` <div class=${we(this._getCellClasses(t,i))} style=${Ue(this._getCellStyle(t,i))} > ${this._renderCellContent(t,e)} </div> `)} </div> `)} </div> </div> </div> `}render(){return this.def.columns.length?U` ${this._renderHeader()} ${this._renderTable()} `:U`<slot></slot>`}}"
514
535
  },
515
536
  {
516
537
  "kind": "variable",
517
- "name": "Ye",
518
- "default": "class extends ne{constructor(){super(),this.label=\"\",this.name=\"\",this.value=\"\",this.placeholder=\"Select an option\",this.disabled=!1,this.required=!1,this.readonly=!1,this.hint=\"\",this._isOpen=!1,this._highlightedIndex=-1,this._touched=!1,this._handleInvalid=e=>{e.preventDefault(),this._touched=!0},this._handleOutsideClick=e=>{this.contains(e.target)||this._close()},this._handleKeyDown=e=>{if(!this._isOpen)return;const t=Array.from(this.querySelectorAll(\"kr-select-option\"));switch(e.key){case\"Escape\":this._close(),this._triggerElement?.focus();break;case\"ArrowDown\":if(e.preventDefault(),t.some(e=>!e.disabled)){let e=this._highlightedIndex+1;for(;e<t.length&&t[e]?.disabled;)e++;e<t.length&&(this._highlightedIndex=e)}break;case\"ArrowUp\":e.preventDefault();{let e=this._highlightedIndex-1;for(;e>=0&&t[e]?.disabled;)e--;e>=0&&(this._highlightedIndex=e)}break;case\"Enter\":e.preventDefault(),this._highlightedIndex>=0&&this._highlightedIndex<t.length&&this._selectOption(t[this._highlightedIndex])}},this._internals=this.attachInternals()}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=!1,this._internals.setFormValue(\"\"),this._internals.setValidity({})}formStateRestoreCallback(e){this.value=e}connectedCallback(){super.connectedCallback(),document.addEventListener(\"click\",this._handleOutsideClick),document.addEventListener(\"keydown\",this._handleKeyDown),this.addEventListener(\"invalid\",this._handleInvalid)}firstUpdated(){this._updateValidity()}updated(e){(e.has(\"required\")||e.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)if(this._isOpen)this._close();else{this._isOpen=!0;const e=Array.from(this.querySelectorAll(\"kr-select-option\"));this._highlightedIndex=e.findIndex(e=>e.value===this.value)}}_close(){this._isOpen=!1,this._highlightedIndex=-1}_selectOption(e){e.disabled||(this.value=e.value,this._internals.setFormValue(this.value),this._updateValidity(),this.dispatchEvent(new Event(\"change\",{bubbles:!0,composed:!0})),this._close(),this._triggerElement?.focus())}_handleBlur(){this._touched=!0,this._updateValidity()}_updateValidity(){this.required&&!this.value?this._internals.setValidity({valueMissing:!0},\"Please select an option\",this._triggerElement):this._internals.setValidity({})}render(){const e=Array.from(this.querySelectorAll(\"kr-select-option\")),t=e.find(e=>e.value===this.value)?.label;return V` <div class=\"wrapper\"> ${this.label?V` <label> ${this.label} ${this.required?V`<span class=\"required\" aria-hidden=\"true\">*</span>`:\"\"} </label> `:q} <div class=\"select-wrapper\"> <button class=${$e({\"select-trigger\":!0,\"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=${$e({\"select-value\":!0,\"select-placeholder\":!t})}> ${t||this.placeholder} </span> <svg class=${$e({\"chevron-icon\":!0,\"select-icon\":!0,\"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=${$e({\"select-dropdown\":!0,hidden:!this._isOpen})} role=\"listbox\"> <div class=\"select-options\"> ${0===e.length?V`<div class=\"select-empty\">No options available</div>`:e.map((e,t)=>{const i=e.value===this.value;return V` <div class=${$e({\"select-option\":!0,\"select-option--selected\":i,\"select-option--disabled\":e.disabled,\"select-option--highlighted\":t===this._highlightedIndex})} role=\"option\" aria-selected=${i} @click=${()=>this._selectOption(e)} @mouseenter=${()=>this._highlightedIndex=t} > ${e.label} ${i?V`<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>`:q} </div> `})} </div> </div> </div> ${this._touched&&this.required&&!this.value?V`<div class=\"validation-message\">Please select an option</div>`:this.hint?V`<div class=\"hint\">${this.hint}</div>`:\"\"} </div> <div class=\"options-slot\"> <slot @slotchange=${()=>this.requestUpdate()}></slot> </div> `}focus(){this._triggerElement?.focus()}blur(){this._triggerElement?.blur()}}"
538
+ "name": "it",
539
+ "default": "class extends ne{constructor(){super(),this.label=\"\",this.name=\"\",this.value=\"\",this.placeholder=\"\",this.type=\"text\",this.required=!1,this.disabled=!1,this.readonly=!1,this.autocomplete=\"\",this.hint=\"\",this._touched=!1,this._dirty=!1,this._handleInvalid=e=>{e.preventDefault(),this._touched=!0},this._internals=this.attachInternals()}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=!1,this._dirty=!1,this._internals.setFormValue(\"\"),this._internals.setValidity({})}formStateRestoreCallback(e){this.value=e}connectedCallback(){super.connectedCallback(),this.addEventListener(\"invalid\",this._handleInvalid)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(\"invalid\",this._handleInvalid)}firstUpdated(){this._updateValidity()}updated(e){(e.has(\"required\")||e.has(\"value\"))&&this._updateValidity()}_updateValidity(){this._input&&this._internals.setValidity(this._input.validity,this._input.validationMessage)}_handleInput(e){this.value=e.target.value,this._dirty=!0,this._internals.setFormValue(this.value),this._internals.setValidity(this._input.validity,this._input.validationMessage)}_handleChange(e){this.value=e.target.value,this._internals.setFormValue(this.value)}_handleBlur(){this._touched=!0,this._internals.setValidity(this._input.validity,this._input.validationMessage)}render(){let e=\"\";return this._touched&&this._input&&!this._input.validity.valid&&(e=this._input.validationMessage),U` <div class=\"wrapper\"> ${this.label?U` <label for=\"input\"> ${this.label} ${this.required?U`<span class=\"required\" aria-hidden=\"true\">*</span>`:\"\"} </label> `:\"\"} <input id=\"input\" class=${we({\"input--invalid\":this._touched&&this._input&&!this._input.validity.valid})} type=${this.type} name=${this.name} .value=${et(this.value)} placeholder=${this.placeholder} ?required=${this.required} ?disabled=${this.disabled} ?readonly=${this.readonly} minlength=${Je(this.minlength)} maxlength=${Je(this.maxlength)} pattern=${Je(this.pattern)} autocomplete=${Je(this.autocomplete||void 0)} @input=${this._handleInput} @change=${this._handleChange} @blur=${this._handleBlur} /> ${e?U`<div class=\"validation-message\">${e}</div>`:this.hint?U`<div class=\"hint\">${this.hint}</div>`:\"\"} </div> `}focus(){this._input?.focus()}blur(){this._input?.blur()}select(){this._input?.select()}}"
540
+ },
541
+ {
542
+ "kind": "variable",
543
+ "name": "st",
544
+ "default": "class extends ne{constructor(){super(),this.label=\"\",this.name=\"\",this.value=\"\",this.placeholder=\"Select an option\",this.disabled=!1,this.required=!1,this.readonly=!1,this.hint=\"\",this._isOpen=!1,this._highlightedIndex=-1,this._touched=!1,this._handleInvalid=e=>{e.preventDefault(),this._touched=!0},this._handleOutsideClick=e=>{this.contains(e.target)||this._close()},this._handleKeyDown=e=>{if(!this._isOpen)return;const t=Array.from(this.querySelectorAll(\"kr-select-option\"));switch(e.key){case\"Escape\":this._close(),this._triggerElement?.focus();break;case\"ArrowDown\":if(e.preventDefault(),t.some(e=>!e.disabled)){let e=this._highlightedIndex+1;for(;e<t.length&&t[e]?.disabled;)e++;e<t.length&&(this._highlightedIndex=e)}break;case\"ArrowUp\":e.preventDefault();{let e=this._highlightedIndex-1;for(;e>=0&&t[e]?.disabled;)e--;e>=0&&(this._highlightedIndex=e)}break;case\"Enter\":e.preventDefault(),this._highlightedIndex>=0&&this._highlightedIndex<t.length&&this._selectOption(t[this._highlightedIndex])}},this._internals=this.attachInternals()}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=!1,this._internals.setFormValue(\"\"),this._internals.setValidity({})}formStateRestoreCallback(e){this.value=e}connectedCallback(){super.connectedCallback(),document.addEventListener(\"click\",this._handleOutsideClick),document.addEventListener(\"keydown\",this._handleKeyDown),this.addEventListener(\"invalid\",this._handleInvalid)}firstUpdated(){this._updateValidity()}updated(e){(e.has(\"required\")||e.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)if(this._isOpen)this._close();else{this._isOpen=!0;const e=Array.from(this.querySelectorAll(\"kr-select-option\"));this._highlightedIndex=e.findIndex(e=>e.value===this.value)}}_close(){this._isOpen=!1,this._highlightedIndex=-1}_selectOption(e){e.disabled||(this.value=e.value,this._internals.setFormValue(this.value),this._updateValidity(),this.dispatchEvent(new Event(\"change\",{bubbles:!0,composed:!0})),this._close(),this._triggerElement?.focus())}_handleBlur(){this._touched=!0,this._updateValidity()}_updateValidity(){this.required&&!this.value?this._internals.setValidity({valueMissing:!0},\"Please select an option\",this._triggerElement):this._internals.setValidity({})}render(){const e=Array.from(this.querySelectorAll(\"kr-select-option\")),t=e.find(e=>e.value===this.value)?.label;return U` <div class=\"wrapper\"> ${this.label?U` <label> ${this.label} ${this.required?U`<span class=\"required\" aria-hidden=\"true\">*</span>`:\"\"} </label> `:V} <div class=\"select-wrapper\"> <button class=${we({\"select-trigger\":!0,\"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=${we({\"select-value\":!0,\"select-placeholder\":!t})}> ${t||this.placeholder} </span> <svg class=${we({\"chevron-icon\":!0,\"select-icon\":!0,\"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=${we({\"select-dropdown\":!0,hidden:!this._isOpen})} role=\"listbox\"> <div class=\"select-options\"> ${0===e.length?U`<div class=\"select-empty\">No options available</div>`:e.map((e,t)=>{const i=e.value===this.value;return U` <div class=${we({\"select-option\":!0,\"select-option--selected\":i,\"select-option--disabled\":e.disabled,\"select-option--highlighted\":t===this._highlightedIndex})} role=\"option\" aria-selected=${i} @click=${()=>this._selectOption(e)} @mouseenter=${()=>this._highlightedIndex=t} > ${e.label} ${i?U`<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>`:V} </div> `})} </div> </div> </div> ${this._touched&&this.required&&!this.value?U`<div class=\"validation-message\">Please select an option</div>`:this.hint?U`<div class=\"hint\">${this.hint}</div>`:\"\"} </div> <div class=\"options-slot\"> <slot @slotchange=${()=>this.requestUpdate()}></slot> </div> `}focus(){this._triggerElement?.focus()}blur(){this._triggerElement?.blur()}}"
519
545
  },
520
546
  {
521
547
  "kind": "variable",
522
- "name": "tt",
523
- "default": "class extends ne{constructor(){super(...arguments),this.value=\"\",this.disabled=!1}get label(){return this.textContent?.trim()||\"\"}render(){return V`<slot></slot>`}}"
548
+ "name": "nt",
549
+ "default": "class extends ne{constructor(){super(...arguments),this.value=\"\",this.disabled=!1}get label(){return this.textContent?.trim()||\"\"}render(){return U`<slot></slot>`}}"
524
550
  }
525
551
  ],
526
552
  "exports": [
@@ -544,7 +570,7 @@
544
570
  "kind": "js",
545
571
  "name": "DialogRef",
546
572
  "declaration": {
547
- "name": "Le",
573
+ "name": "De",
548
574
  "module": "dist/krubble.bundled.min.js"
549
575
  }
550
576
  },
@@ -552,7 +578,7 @@
552
578
  "kind": "js",
553
579
  "name": "KRAccordion",
554
580
  "declaration": {
555
- "name": "ge",
581
+ "name": "be",
556
582
  "module": "dist/krubble.bundled.min.js"
557
583
  }
558
584
  },
@@ -560,7 +586,7 @@
560
586
  "kind": "js",
561
587
  "name": "KRAlert",
562
588
  "declaration": {
563
- "name": "Ae",
589
+ "name": "Ce",
564
590
  "module": "dist/krubble.bundled.min.js"
565
591
  }
566
592
  },
@@ -568,7 +594,7 @@
568
594
  "kind": "js",
569
595
  "name": "KRButton",
570
596
  "declaration": {
571
- "name": "Se",
597
+ "name": "Ee",
572
598
  "module": "dist/krubble.bundled.min.js"
573
599
  }
574
600
  },
@@ -576,7 +602,7 @@
576
602
  "kind": "js",
577
603
  "name": "KRCodeDemo",
578
604
  "declaration": {
579
- "name": "Pe",
605
+ "name": "ze",
580
606
  "module": "dist/krubble.bundled.min.js"
581
607
  }
582
608
  },
@@ -584,7 +610,7 @@
584
610
  "kind": "js",
585
611
  "name": "KRContextMenu",
586
612
  "declaration": {
587
- "name": "Re",
613
+ "name": "Le",
588
614
  "module": "dist/krubble.bundled.min.js"
589
615
  }
590
616
  },
@@ -600,7 +626,7 @@
600
626
  "kind": "js",
601
627
  "name": "KRSelect",
602
628
  "declaration": {
603
- "name": "Ye",
629
+ "name": "st",
604
630
  "module": "dist/krubble.bundled.min.js"
605
631
  }
606
632
  },
@@ -608,7 +634,7 @@
608
634
  "kind": "js",
609
635
  "name": "KRSelectOption",
610
636
  "declaration": {
611
- "name": "tt",
637
+ "name": "nt",
612
638
  "module": "dist/krubble.bundled.min.js"
613
639
  }
614
640
  },
@@ -616,7 +642,7 @@
616
642
  "kind": "js",
617
643
  "name": "KRSnackbar",
618
644
  "declaration": {
619
- "name": "He",
645
+ "name": "Ie",
620
646
  "module": "dist/krubble.bundled.min.js"
621
647
  }
622
648
  },
@@ -632,7 +658,15 @@
632
658
  "kind": "js",
633
659
  "name": "KRTabGroup",
634
660
  "declaration": {
635
- "name": "qe",
661
+ "name": "Ve",
662
+ "module": "dist/krubble.bundled.min.js"
663
+ }
664
+ },
665
+ {
666
+ "kind": "js",
667
+ "name": "KRTable",
668
+ "declaration": {
669
+ "name": "Ye",
636
670
  "module": "dist/krubble.bundled.min.js"
637
671
  }
638
672
  },
@@ -640,7 +674,7 @@
640
674
  "kind": "js",
641
675
  "name": "KRTextField",
642
676
  "declaration": {
643
- "name": "Ge",
677
+ "name": "it",
644
678
  "module": "dist/krubble.bundled.min.js"
645
679
  }
646
680
  },
@@ -648,7 +682,7 @@
648
682
  "kind": "js",
649
683
  "name": "krBaseCSS",
650
684
  "declaration": {
651
- "name": "le",
685
+ "name": "ae",
652
686
  "module": "dist/krubble.bundled.min.js"
653
687
  }
654
688
  }
@@ -768,6 +802,14 @@
768
802
  "module": "./button/button.js"
769
803
  }
770
804
  },
805
+ {
806
+ "kind": "js",
807
+ "name": "KRButtonOption",
808
+ "declaration": {
809
+ "name": "KRButtonOption",
810
+ "module": "./button/button.js"
811
+ }
812
+ },
771
813
  {
772
814
  "kind": "js",
773
815
  "name": "KRCodeDemo",
@@ -824,6 +866,46 @@
824
866
  "module": "./tabs/tab.js"
825
867
  }
826
868
  },
869
+ {
870
+ "kind": "js",
871
+ "name": "KRTable",
872
+ "declaration": {
873
+ "name": "KRTable",
874
+ "module": "./table/table.js"
875
+ }
876
+ },
877
+ {
878
+ "kind": "js",
879
+ "name": "KRTableDef",
880
+ "declaration": {
881
+ "name": "KRTableDef",
882
+ "module": "./table/table.js"
883
+ }
884
+ },
885
+ {
886
+ "kind": "js",
887
+ "name": "KRTableColumn",
888
+ "declaration": {
889
+ "name": "KRTableColumn",
890
+ "module": "./table/table.js"
891
+ }
892
+ },
893
+ {
894
+ "kind": "js",
895
+ "name": "KRTableAction",
896
+ "declaration": {
897
+ "name": "KRTableAction",
898
+ "module": "./table/table.js"
899
+ }
900
+ },
901
+ {
902
+ "kind": "js",
903
+ "name": "KRTableDataSource",
904
+ "declaration": {
905
+ "name": "KRTableDataSource",
906
+ "module": "./table/table.js"
907
+ }
908
+ },
827
909
  {
828
910
  "kind": "js",
829
911
  "name": "KRTextField",
@@ -928,66 +1010,66 @@
928
1010
  },
929
1011
  {
930
1012
  "kind": "javascript-module",
931
- "path": "dist/code-demo/code-demo.js",
1013
+ "path": "dist/button/button.js",
932
1014
  "declarations": [
933
1015
  {
934
1016
  "kind": "variable",
935
- "name": "KRCodeDemo",
936
- "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> `; } }",
937
- "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."
1017
+ "name": "KRButton",
1018
+ "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._dropdownOpen = 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._dropdownOpen) { this._dropdownOpen = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpen && !this.contains(e.target)) { this._dropdownOpen = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', '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._dropdownOpen = !this._dropdownOpen; } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpen = 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 hasOptions = this.options.length > 0; return html ` <slot></slot> ${hasOptions ? 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} ${hasOptions ? html ` <div class=\"dropdown ${this._dropdownOpen ? 'open' : ''}\"> ${this.options.map(option => html ` <button class=\"dropdown-item\" @click=${(e) => this._handleOptionClick(option, e)} >${option.label}</button> `)} </div> ` : nothing} `; } }",
1019
+ "description": "A customizable button component."
938
1020
  }
939
1021
  ],
940
1022
  "exports": [
941
1023
  {
942
1024
  "kind": "js",
943
- "name": "KRCodeDemo",
1025
+ "name": "KRButton",
944
1026
  "declaration": {
945
- "name": "KRCodeDemo",
946
- "module": "dist/code-demo/code-demo.js"
1027
+ "name": "KRButton",
1028
+ "module": "dist/button/button.js"
947
1029
  }
948
1030
  }
949
1031
  ]
950
1032
  },
951
1033
  {
952
1034
  "kind": "javascript-module",
953
- "path": "dist/button/button.js",
1035
+ "path": "dist/context-menu/context-menu.js",
954
1036
  "declarations": [
955
1037
  {
956
1038
  "kind": "variable",
957
- "name": "KRButton",
958
- "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; this._state = 'idle'; this._stateText = ''; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.click(); } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); } /** * 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() { return html ` <slot></slot> ${this._state !== 'idle' ? html `<span class=\"state-overlay\"> ${this._state === 'loading' ? html `<span class=\"spinner\"></span>` : this._stateText} </span>` : ''} `; } }",
959
- "description": "A customizable button component."
1039
+ "name": "KRContextMenu",
1040
+ "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> `; } }",
1041
+ "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```"
960
1042
  }
961
1043
  ],
962
1044
  "exports": [
963
1045
  {
964
1046
  "kind": "js",
965
- "name": "KRButton",
1047
+ "name": "KRContextMenu",
966
1048
  "declaration": {
967
- "name": "KRButton",
968
- "module": "dist/button/button.js"
1049
+ "name": "KRContextMenu",
1050
+ "module": "dist/context-menu/context-menu.js"
969
1051
  }
970
1052
  }
971
1053
  ]
972
1054
  },
973
1055
  {
974
1056
  "kind": "javascript-module",
975
- "path": "dist/context-menu/context-menu.js",
1057
+ "path": "dist/code-demo/code-demo.js",
976
1058
  "declarations": [
977
1059
  {
978
1060
  "kind": "variable",
979
- "name": "KRContextMenu",
980
- "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> `; } }",
981
- "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```"
1061
+ "name": "KRCodeDemo",
1062
+ "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> `; } }",
1063
+ "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."
982
1064
  }
983
1065
  ],
984
1066
  "exports": [
985
1067
  {
986
1068
  "kind": "js",
987
- "name": "KRContextMenu",
1069
+ "name": "KRCodeDemo",
988
1070
  "declaration": {
989
- "name": "KRContextMenu",
990
- "module": "dist/context-menu/context-menu.js"
1071
+ "name": "KRCodeDemo",
1072
+ "module": "dist/code-demo/code-demo.js"
991
1073
  }
992
1074
  }
993
1075
  ]
@@ -1137,6 +1219,27 @@
1137
1219
  }
1138
1220
  ]
1139
1221
  },
1222
+ {
1223
+ "kind": "javascript-module",
1224
+ "path": "dist/table/table.js",
1225
+ "declarations": [
1226
+ {
1227
+ "kind": "variable",
1228
+ "name": "KRTable",
1229
+ "default": "class KRTable extends LitElement { constructor() { super(...arguments); /** * Internal flag to switch between scroll edge modes: * - 'overlay': Fixed padding with overlay elements that hide content at edges (scrollbar at viewport edge) * - 'edge': Padding scrolls with content, allowing table to reach edges when scrolling */ this._scrollStyle = 'edge'; this._data = []; this._dataState = 'idle'; this._page = 1; this._pageSize = 50; this._totalItems = 0; this._totalPages = 0; this._searchQuery = ''; this._canScrollLeft = false; this._canScrollRight = false; this._canScrollHorizontal = false; this._columnPickerOpen = false; this._displayedColumns = []; this._columnWidths = new Map(); this._resizing = null; this._resizeObserver = null; this._searchPositionLocked = false; this.def = { columns: [] }; this._handleClickOutsideColumnPicker = (e) => { if (!this._columnPickerOpen) return; const path = e.composedPath(); const picker = this.shadowRoot?.querySelector('.column-picker-wrapper'); if (picker && !path.includes(picker)) { this._columnPickerOpen = false; } }; this._handleResizeMove = (e) => { if (!this._resizing) return; this._columnWidths.set(this._resizing.columnId, Math.max(50, this._resizing.startWidth + (e.clientX - this._resizing.startX))); this.requestUpdate(); }; this._handleResizeEnd = () => { this._resizing = null; document.removeEventListener('mousemove', this._handleResizeMove); document.removeEventListener('mouseup', this._handleResizeEnd); }; } connectedCallback() { super.connectedCallback(); this.classList.toggle('kr-table--scroll-overlay', this._scrollStyle === 'overlay'); this.classList.toggle('kr-table--scroll-edge', this._scrollStyle === 'edge'); this._fetch(); this._initRefresh(); document.addEventListener('click', this._handleClickOutsideColumnPicker); this._resizeObserver = new ResizeObserver(() => { // Unlock and recalculate on resize since layout changes this._searchPositionLocked = false; this._updateSearchPosition(); }); this._resizeObserver.observe(this); } disconnectedCallback() { super.disconnectedCallback(); clearInterval(this._refreshTimer); document.removeEventListener('click', this._handleClickOutsideColumnPicker); this._resizeObserver?.disconnect(); } updated(changedProperties) { if (changedProperties.has('def')) { this._displayedColumns = this.def.displayedColumns || this.def.columns.map(c => c.id); this._fetch(); this._initRefresh(); } this._updateScrollFlags(); } // ---------------------------------------------------------------------------- // Public Interface // ---------------------------------------------------------------------------- refresh() { this._fetch(); } goToPrevPage() { if (this._page > 1) { this._page--; this._fetch(); } } goToNextPage() { if (this._page < this._totalPages) { this._page++; this._fetch(); } } goToPage(page) { if (page >= 1 && page <= this._totalPages) { this._page = page; this._fetch(); } } // ---------------------------------------------------------------------------- // Data Fetching // ---------------------------------------------------------------------------- /** * Fetches data from the API and updates the table. * Shows a loading spinner while fetching, then displays rows on success * or an error snackbar on failure. * Request/response format depends on dataSource.mode (solr, opensearch, db). */ _fetch() { if (!this.def.dataSource) return; this._dataState = 'loading'; // Build request based on mode let request; switch (this.def.dataSource.mode) { case 'opensearch': throw Error('Opensearch not supported yet'); case 'db': throw Error('DB not supported yet'); default: // solr request = { page: this._page - 1, size: this._pageSize, sorts: [], filterFields: [], queryFields: [], facetFields: [] }; if (this._searchQuery?.trim().length) { request.queryFields.push({ name: '_text_', operation: 'IS', value: escapeSolrQuery(this._searchQuery) }); } } this.def.dataSource.fetch(request) .then(response => { // Parse response based on mode switch (this.def.dataSource?.mode) { case 'opensearch': { throw Error('Opensearch not supported yet'); break; } case 'db': { throw Error('DB not supported yet'); break; } default: { // solr const res = response; this._data = res.data.content; this._totalItems = res.data.totalElements; this._totalPages = res.data.totalPages; this._pageSize = res.data.size; } } this._dataState = 'success'; this._updateSearchPosition(); }) .catch(err => { this._dataState = 'error'; KRSnackbar.show({ message: err instanceof Error ? err.message : 'Failed to load data', type: 'error' }); }); } /** * Sets up auto-refresh so the table automatically fetches fresh data * at a regular interval (useful for dashboards, monitoring views). * Configured via def.refreshInterval in milliseconds. */ _initRefresh() { clearInterval(this._refreshTimer); if (this.def.refreshInterval && this.def.refreshInterval > 0) { this._refreshTimer = window.setInterval(() => { this._fetch(); }, this.def.refreshInterval); } } _handleSearch(e) { const input = e.target; this._searchQuery = input.value; this._page = 1; this._fetch(); } /** * Updates search position to be centered with equal gaps from title and tools. * On first call: resets to flex centering, measures position, then locks with fixed margin. * Subsequent calls are ignored unless _searchPositionLocked is reset (e.g., on resize). */ _updateSearchPosition() { // Skip if already locked (prevents shifts on pagination changes) if (this._searchPositionLocked) return; const search = this.shadowRoot?.querySelector('.search'); const searchField = search?.querySelector('.search-field'); if (!search || !searchField) return; // Reset to flex centering search.style.justifyContent = 'center'; searchField.style.marginLeft = ''; requestAnimationFrame(() => { const searchRect = search.getBoundingClientRect(); const fieldRect = searchField.getBoundingClientRect(); // Calculate how far from the left of search container the field currently is const currentOffset = fieldRect.left - searchRect.left; // Lock position: switch to flex-start and use fixed margin search.style.justifyContent = 'flex-start'; searchField.style.marginLeft = `${currentOffset}px`; // Mark as locked so pagination changes don't shift the search this._searchPositionLocked = true; }); } // ---------------------------------------------------------------------------- // Columns // ---------------------------------------------------------------------------- _toggleColumnPicker() { this._columnPickerOpen = !this._columnPickerOpen; } _toggleColumn(columnId) { if (this._displayedColumns.includes(columnId)) { this._displayedColumns = this._displayedColumns.filter(id => id !== columnId); } else { this._displayedColumns = [...this._displayedColumns, columnId]; } } // When a user toggles a column on via the column picker, it gets appended // to _displayedColumns. By mapping over _displayedColumns (not def.columns), // the new column appears at the right edge of the table instead of jumping // back to its original position in the column definition. getDisplayedColumns() { return this._displayedColumns.map(id => this.def.columns.find(col => col.id === id)); } // ---------------------------------------------------------------------------- // Scrolling // ---------------------------------------------------------------------------- /** * Scroll event handler that updates scroll flags in real-time as user scrolls. * Updates shadow indicators to show if more content exists left/right. */ _handleScroll(e) { const container = e.target; this._canScrollLeft = container.scrollLeft > 0; this._canScrollRight = container.scrollLeft < container.scrollWidth - container.clientWidth - 1; } /** * Updates scroll state flags for the table content container. * - _canScrollLeft: true if scrolled right (can scroll back left) * - _canScrollRight: true if more content exists to the right * - _canScrollHorizontal: true if content is wider than container * These flags control scroll shadow indicators and CSS classes. */ _updateScrollFlags() { const container = this.shadowRoot?.querySelector('.content'); if (container) { this._canScrollLeft = container.scrollLeft > 0; this._canScrollRight = container.scrollWidth > container.clientWidth && container.scrollLeft < container.scrollWidth - container.clientWidth - 1; this._canScrollHorizontal = container.scrollWidth > container.clientWidth; } this.classList.toggle('kr-table--scroll-left-available', this._canScrollLeft); this.classList.toggle('kr-table--scroll-right-available', this._canScrollRight); this.classList.toggle('kr-table--scroll-horizontal-available', this._canScrollHorizontal); } // ---------------------------------------------------------------------------- // Column Resizing // ---------------------------------------------------------------------------- _handleResizeStart(e, columnId) { e.preventDefault(); const column = this.def.columns.find(c => c.id === columnId); this._resizing = { columnId, startX: e.clientX, startWidth: this._columnWidths.get(columnId) || parseInt(column?.width || '150', 10) }; document.addEventListener('mousemove', this._handleResizeMove); document.addEventListener('mouseup', this._handleResizeEnd); } // ---------------------------------------------------------------------------- // Header // ---------------------------------------------------------------------------- _handleAction(action) { this.dispatchEvent(new CustomEvent('action', { detail: { action: action.id }, bubbles: true, composed: true })); } // ---------------------------------------------------------------------------- // Rendering // ---------------------------------------------------------------------------- _renderCellContent(column, row) { const value = row[column.id]; if (column.render) { return column.render(row); } if (value === null || value === undefined) { return ''; } switch (column.type) { case 'number': return typeof value === 'number' ? value.toLocaleString() : String(value); case 'currency': return typeof value === 'number' ? value.toLocaleString('en-US', { style: 'currency', currency: 'USD' }) : String(value); case 'date': return value instanceof Date ? value.toLocaleDateString() : new Date(value).toLocaleDateString(); case 'boolean': if (value === true) return 'Yes'; if (value === false) return 'No'; return ''; default: return String(value); } } /** * Returns CSS classes for a header cell based on column config. */ _getHeaderCellClasses(column, index) { return { 'header-cell': true, 'header-cell--align-center': column.align === 'center', 'header-cell--align-right': column.align === 'right', 'header-cell--sticky-left': column.sticky === 'left', 'header-cell--sticky-left-last': column.sticky === 'left' && !this.getDisplayedColumns().slice(index + 1).some(c => c.sticky === 'left'), 'header-cell--sticky-right': column.sticky === 'right', 'header-cell--sticky-right-first': column.sticky === 'right' && !this.getDisplayedColumns().slice(0, index).some(c => c.sticky === 'right') }; } /** * Returns CSS classes for a table cell based on column config: * - Alignment (center, right) * - Sticky positioning (left, right) * - Border classes for the last left-sticky or first right-sticky column */ _getCellClasses(column, index) { return { 'cell': true, 'cell--align-center': column.align === 'center', 'cell--align-right': column.align === 'right', 'cell--sticky-left': column.sticky === 'left', 'cell--sticky-left-last': column.sticky === 'left' && !this.getDisplayedColumns().slice(index + 1).some(c => c.sticky === 'left'), 'cell--sticky-right': column.sticky === 'right', 'cell--sticky-right-first': column.sticky === 'right' && !this.getDisplayedColumns().slice(0, index).some(c => c.sticky === 'right') }; } /** * Returns inline styles for a table cell: * - Width (from column config or default 150px) * - Min-width (if specified) * - Left/right offset for sticky columns (calculated from widths of preceding sticky columns) */ _getCellStyle(column, index) { const styles = {}; const isLastColumn = index === this.getDisplayedColumns().length - 1; const customWidth = this._columnWidths.get(column.id); const width = customWidth ? `${customWidth}px` : (column.width || '150px'); if (isLastColumn && !customWidth) { styles.flex = '1'; styles.minWidth = column.width || '150px'; styles.marginRight = '24px'; } else { styles.width = width; } if (column.minWidth) styles.minWidth = column.minWidth; if (column.sticky === 'left') { let leftOffset = 0; for (let i = 0; i < index; i++) { if (this.getDisplayedColumns()[i].sticky === 'left') { leftOffset += parseInt(this.getDisplayedColumns()[i].width || '150', 10); } } styles.left = `${leftOffset}px`; } if (column.sticky === 'right') { let rightOffset = 0; for (let i = index + 1; i < this.getDisplayedColumns().length; i++) { if (this.getDisplayedColumns()[i].sticky === 'right') { rightOffset += parseInt(this.getDisplayedColumns()[i].width || '150', 10); } } styles.right = `${rightOffset}px`; } return styles; } /** * Renders the pagination controls: * - Previous page arrow (disabled on first page) * - Range text showing \"1-50 of 150\" format * - Next page arrow (disabled on last page) * * Hidden when there's no data or all data fits on one page. */ _renderPagination() { const start = (this._page - 1) * this._pageSize + 1; const end = Math.min(this._page * this._pageSize, this._totalItems); return html ` <div class=\"pagination\"> <span class=\"pagination-icon ${this._page === 1 ? 'pagination-icon--disabled' : ''}\" @click=${this.goToPrevPage} > <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"/></svg> </span> <span class=\"pagination-info\">${start}-${end} of ${this._totalItems}</span> <span class=\"pagination-icon ${this._page === this._totalPages ? 'pagination-icon--disabled' : ''}\" @click=${this.goToNextPage} > <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"/></svg> </span> </div> `; } /** * Renders the header toolbar containing: * - Title (left) * - Search bar with view selector dropdown (center) * - Tools (right): page navigation, refresh button, column visibility picker, actions dropdown * * Hidden when there's no title, no actions, and data fits on one page. */ _renderHeader() { if (!this.def.title && !this.def.actions?.length && this._totalPages <= 1) { return nothing; } return html ` <div class=\"header\"> <div class=\"title\">${this.def.title ?? ''}</div> <div class=\"search\"> <!-- TODO: Saved views dropdown <div class=\"views\"> <span>Default View</span> <svg 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> </div> --> <div class=\"search-field\"> <svg class=\"search-icon\" viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\"/></svg> <input type=\"text\" class=\"search-input\" placeholder=\"Search...\" .value=${this._searchQuery} @input=${this._handleSearch} /> </div> </div> <div class=\"tools\"> ${this._renderPagination()} <span class=\"refresh\" title=\"Refresh\" @click=${() => this.refresh()}> <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M480-160q-134 0-227-93t-93-227q0-134 93-227t227-93q69 0 132 28.5T720-690v-110h80v280H520v-80h168q-32-56-87.5-88T480-720q-100 0-170 70t-70 170q0 100 70 170t170 70q77 0 139-44t87-116h84q-28 106-114 173t-196 67Z\"/></svg> </span> <div class=\"column-picker-wrapper\"> <span class=\"header-icon\" title=\"Columns\" @click=${this._toggleColumnPicker}> <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\"><path d=\"M121-280v-400q0-33 23.5-56.5T201-760h559q33 0 56.5 23.5T840-680v400q0 33-23.5 56.5T760-200H201q-33 0-56.5-23.5T121-280Zm79 0h133v-400H200v400Zm213 0h133v-400H413v400Zm213 0h133v-400H626v400Z\"/></svg> </span> <div class=\"column-picker ${this._columnPickerOpen ? 'open' : ''}\"> ${[...this.def.columns].sort((a, b) => (a.label ?? a.id).localeCompare(b.label ?? b.id)).map(col => html ` <div class=\"column-picker-item\" @click=${() => this._toggleColumn(col.id)}> <div class=\"column-picker-checkbox ${this._displayedColumns.includes(col.id) ? 'checked' : ''}\"> <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg> </div> <span class=\"column-picker-label\">${col.label ?? col.id}</span> </div> `)} </div> </div> ${this.def.actions?.length ? html ` <kr-button class=\"actions\" .options=${this.def.actions.map(a => ({ id: a.id, label: a.label }))} @option-select=${(e) => this._handleAction({ id: e.detail.id, label: e.detail.label })} > Actions </kr-button> ` : nothing} </div> </div> `; } /** Renders status message (loading, error, empty) */ _renderStatus() { if (this._dataState === 'loading' && this._data.length === 0) { return html `<div class=\"status\">Loading...</div>`; } if (this._dataState === 'error' && this._data.length === 0) { return html `<div class=\"status status--error\">Error loading data</div>`; } if (this._data.length === 0) { return html `<div class=\"status\">No data available</div>`; } return nothing; } /** Renders the scrollable data grid with column headers and rows. */ _renderTable() { return html ` <div class=\"wrapper\"> <div class=\"overlay-left\"></div> <div class=\"overlay-right\"></div> ${this._renderStatus()} <div class=\"content\" @scroll=${this._handleScroll}> <div class=\"table\"> <div class=\"header-row\"> ${this.getDisplayedColumns().map((col, i) => html ` <div class=${classMap(this._getHeaderCellClasses(col, i))} style=${styleMap(this._getCellStyle(col, i))} > <span class=\"header-cell__label\">${col.label ?? col.id}</span> <div class=\"header-cell__resize\" @mousedown=${(e) => this._handleResizeStart(e, col.id)} ></div> </div> `)} </div> ${this._data.map(row => html ` <div class=\"row\"> ${this.getDisplayedColumns().map((col, i) => html ` <div class=${classMap(this._getCellClasses(col, i))} style=${styleMap(this._getCellStyle(col, i))} > ${this._renderCellContent(col, row)} </div> `)} </div> `)} </div> </div> </div> `; } /** * Renders a data table with: * - Header bar with title, search input with view selector, and tools (pagination, refresh, column visibility, actions dropdown) * - Scrollable grid with sticky header row and optional sticky left/right columns * - Loading, error message, or empty state when no data */ render() { if (!this.def.columns.length) { return html `<slot></slot>`; } return html ` ${this._renderHeader()} ${this._renderTable()} `; } }"
1230
+ }
1231
+ ],
1232
+ "exports": [
1233
+ {
1234
+ "kind": "js",
1235
+ "name": "KRTable",
1236
+ "declaration": {
1237
+ "name": "KRTable",
1238
+ "module": "dist/table/table.js"
1239
+ }
1240
+ }
1241
+ ]
1242
+ },
1140
1243
  {
1141
1244
  "kind": "javascript-module",
1142
1245
  "path": "dist/tabs/tab.js",
@@ -1463,6 +1566,16 @@
1463
1566
  "attribute": "disabled",
1464
1567
  "reflects": true
1465
1568
  },
1569
+ {
1570
+ "kind": "field",
1571
+ "name": "options",
1572
+ "type": {
1573
+ "text": "KRButtonOption[]"
1574
+ },
1575
+ "default": "[]",
1576
+ "description": "Dropdown options - when provided, button becomes a dropdown",
1577
+ "attribute": "options"
1578
+ },
1466
1579
  {
1467
1580
  "kind": "field",
1468
1581
  "name": "_state",
@@ -1481,6 +1594,15 @@
1481
1594
  "privacy": "private",
1482
1595
  "default": "''"
1483
1596
  },
1597
+ {
1598
+ "kind": "field",
1599
+ "name": "_dropdownOpen",
1600
+ "type": {
1601
+ "text": "boolean"
1602
+ },
1603
+ "privacy": "private",
1604
+ "default": "false"
1605
+ },
1484
1606
  {
1485
1607
  "kind": "field",
1486
1608
  "name": "_stateTimeout",
@@ -1489,11 +1611,45 @@
1489
1611
  },
1490
1612
  "privacy": "private"
1491
1613
  },
1614
+ {
1615
+ "kind": "field",
1616
+ "name": "_handleHostClick",
1617
+ "privacy": "private"
1618
+ },
1492
1619
  {
1493
1620
  "kind": "field",
1494
1621
  "name": "_handleKeydown",
1495
1622
  "privacy": "private"
1496
1623
  },
1624
+ {
1625
+ "kind": "field",
1626
+ "name": "_handleClickOutside",
1627
+ "privacy": "private"
1628
+ },
1629
+ {
1630
+ "kind": "method",
1631
+ "name": "_toggleDropdown",
1632
+ "privacy": "private"
1633
+ },
1634
+ {
1635
+ "kind": "method",
1636
+ "name": "_handleOptionClick",
1637
+ "privacy": "private",
1638
+ "parameters": [
1639
+ {
1640
+ "name": "option",
1641
+ "type": {
1642
+ "text": "KRButtonOption"
1643
+ }
1644
+ },
1645
+ {
1646
+ "name": "e",
1647
+ "type": {
1648
+ "text": "MouseEvent"
1649
+ }
1650
+ }
1651
+ ]
1652
+ },
1497
1653
  {
1498
1654
  "kind": "method",
1499
1655
  "name": "showLoading",
@@ -1545,6 +1701,13 @@
1545
1701
  }
1546
1702
  ],
1547
1703
  "events": [
1704
+ {
1705
+ "name": "option-select",
1706
+ "type": {
1707
+ "text": "CustomEvent"
1708
+ },
1709
+ "description": "Fired when a dropdown option is selected"
1710
+ },
1548
1711
  {
1549
1712
  "description": "Fired when the button is clicked",
1550
1713
  "name": "click"
@@ -1586,6 +1749,15 @@
1586
1749
  "default": "false",
1587
1750
  "description": "Whether the button is disabled",
1588
1751
  "fieldName": "disabled"
1752
+ },
1753
+ {
1754
+ "name": "options",
1755
+ "type": {
1756
+ "text": "KRButtonOption[]"
1757
+ },
1758
+ "default": "[]",
1759
+ "description": "Dropdown options - when provided, button becomes a dropdown",
1760
+ "fieldName": "options"
1589
1761
  }
1590
1762
  ],
1591
1763
  "superclass": {
@@ -2306,6 +2478,504 @@
2306
2478
  }
2307
2479
  ]
2308
2480
  },
2481
+ {
2482
+ "kind": "javascript-module",
2483
+ "path": "src/table/table.ts",
2484
+ "declarations": [
2485
+ {
2486
+ "kind": "class",
2487
+ "description": "",
2488
+ "name": "KRTable",
2489
+ "members": [
2490
+ {
2491
+ "kind": "field",
2492
+ "name": "_scrollStyle",
2493
+ "type": {
2494
+ "text": "'overlay' | 'edge'"
2495
+ },
2496
+ "privacy": "private",
2497
+ "default": "'edge'",
2498
+ "description": "Internal flag to switch between scroll edge modes:\n- 'overlay': Fixed padding with overlay elements that hide content at edges (scrollbar at viewport edge)\n- 'edge': Padding scrolls with content, allowing table to reach edges when scrolling"
2499
+ },
2500
+ {
2501
+ "kind": "field",
2502
+ "name": "_refreshTimer",
2503
+ "type": {
2504
+ "text": "number | undefined"
2505
+ },
2506
+ "privacy": "private"
2507
+ },
2508
+ {
2509
+ "kind": "field",
2510
+ "name": "_data",
2511
+ "type": {
2512
+ "text": "Record<string, any>[]"
2513
+ },
2514
+ "privacy": "private",
2515
+ "default": "[]"
2516
+ },
2517
+ {
2518
+ "kind": "field",
2519
+ "name": "_dataState",
2520
+ "type": {
2521
+ "text": "'idle' | 'loading' | 'success' | 'error'"
2522
+ },
2523
+ "privacy": "private",
2524
+ "default": "'idle'"
2525
+ },
2526
+ {
2527
+ "kind": "field",
2528
+ "name": "_page",
2529
+ "type": {
2530
+ "text": "number"
2531
+ },
2532
+ "privacy": "private",
2533
+ "default": "1"
2534
+ },
2535
+ {
2536
+ "kind": "field",
2537
+ "name": "_pageSize",
2538
+ "type": {
2539
+ "text": "number"
2540
+ },
2541
+ "privacy": "private",
2542
+ "default": "50"
2543
+ },
2544
+ {
2545
+ "kind": "field",
2546
+ "name": "_totalItems",
2547
+ "type": {
2548
+ "text": "number"
2549
+ },
2550
+ "privacy": "private",
2551
+ "default": "0"
2552
+ },
2553
+ {
2554
+ "kind": "field",
2555
+ "name": "_totalPages",
2556
+ "type": {
2557
+ "text": "number"
2558
+ },
2559
+ "privacy": "private",
2560
+ "default": "0"
2561
+ },
2562
+ {
2563
+ "kind": "field",
2564
+ "name": "_searchQuery",
2565
+ "type": {
2566
+ "text": "string"
2567
+ },
2568
+ "privacy": "private",
2569
+ "default": "''"
2570
+ },
2571
+ {
2572
+ "kind": "field",
2573
+ "name": "_canScrollLeft",
2574
+ "type": {
2575
+ "text": "boolean"
2576
+ },
2577
+ "privacy": "private",
2578
+ "default": "false"
2579
+ },
2580
+ {
2581
+ "kind": "field",
2582
+ "name": "_canScrollRight",
2583
+ "type": {
2584
+ "text": "boolean"
2585
+ },
2586
+ "privacy": "private",
2587
+ "default": "false"
2588
+ },
2589
+ {
2590
+ "kind": "field",
2591
+ "name": "_canScrollHorizontal",
2592
+ "type": {
2593
+ "text": "boolean"
2594
+ },
2595
+ "privacy": "private",
2596
+ "default": "false"
2597
+ },
2598
+ {
2599
+ "kind": "field",
2600
+ "name": "_columnPickerOpen",
2601
+ "type": {
2602
+ "text": "boolean"
2603
+ },
2604
+ "privacy": "private",
2605
+ "default": "false"
2606
+ },
2607
+ {
2608
+ "kind": "field",
2609
+ "name": "_displayedColumns",
2610
+ "type": {
2611
+ "text": "string[]"
2612
+ },
2613
+ "privacy": "private",
2614
+ "default": "[]"
2615
+ },
2616
+ {
2617
+ "kind": "field",
2618
+ "name": "_columnWidths",
2619
+ "type": {
2620
+ "text": "Map<string, number>"
2621
+ },
2622
+ "privacy": "private",
2623
+ "default": "new Map()"
2624
+ },
2625
+ {
2626
+ "kind": "field",
2627
+ "name": "_resizing",
2628
+ "type": {
2629
+ "text": "{ columnId: string; startX: number; startWidth: number } | null"
2630
+ },
2631
+ "privacy": "private",
2632
+ "default": "null"
2633
+ },
2634
+ {
2635
+ "kind": "field",
2636
+ "name": "_resizeObserver",
2637
+ "type": {
2638
+ "text": "ResizeObserver | null"
2639
+ },
2640
+ "privacy": "private",
2641
+ "default": "null"
2642
+ },
2643
+ {
2644
+ "kind": "field",
2645
+ "name": "_searchPositionLocked",
2646
+ "type": {
2647
+ "text": "boolean"
2648
+ },
2649
+ "privacy": "private",
2650
+ "default": "false"
2651
+ },
2652
+ {
2653
+ "kind": "field",
2654
+ "name": "def",
2655
+ "type": {
2656
+ "text": "KRTableDef"
2657
+ },
2658
+ "default": "{ columns: [] }",
2659
+ "attribute": "def"
2660
+ },
2661
+ {
2662
+ "kind": "method",
2663
+ "name": "refresh"
2664
+ },
2665
+ {
2666
+ "kind": "method",
2667
+ "name": "goToPrevPage"
2668
+ },
2669
+ {
2670
+ "kind": "method",
2671
+ "name": "goToNextPage"
2672
+ },
2673
+ {
2674
+ "kind": "method",
2675
+ "name": "goToPage",
2676
+ "parameters": [
2677
+ {
2678
+ "name": "page",
2679
+ "type": {
2680
+ "text": "number"
2681
+ }
2682
+ }
2683
+ ]
2684
+ },
2685
+ {
2686
+ "kind": "method",
2687
+ "name": "_fetch",
2688
+ "privacy": "private",
2689
+ "description": "Fetches data from the API and updates the table.\nShows a loading spinner while fetching, then displays rows on success\nor an error snackbar on failure.\nRequest/response format depends on dataSource.mode (solr, opensearch, db)."
2690
+ },
2691
+ {
2692
+ "kind": "method",
2693
+ "name": "_initRefresh",
2694
+ "privacy": "private",
2695
+ "description": "Sets up auto-refresh so the table automatically fetches fresh data\nat a regular interval (useful for dashboards, monitoring views).\nConfigured via def.refreshInterval in milliseconds."
2696
+ },
2697
+ {
2698
+ "kind": "method",
2699
+ "name": "_handleSearch",
2700
+ "privacy": "private",
2701
+ "parameters": [
2702
+ {
2703
+ "name": "e",
2704
+ "type": {
2705
+ "text": "Event"
2706
+ }
2707
+ }
2708
+ ]
2709
+ },
2710
+ {
2711
+ "kind": "method",
2712
+ "name": "_updateSearchPosition",
2713
+ "privacy": "private",
2714
+ "description": "Updates search position to be centered with equal gaps from title and tools.\nOn first call: resets to flex centering, measures position, then locks with fixed margin.\nSubsequent calls are ignored unless _searchPositionLocked is reset (e.g., on resize)."
2715
+ },
2716
+ {
2717
+ "kind": "method",
2718
+ "name": "_toggleColumnPicker",
2719
+ "privacy": "private"
2720
+ },
2721
+ {
2722
+ "kind": "method",
2723
+ "name": "_toggleColumn",
2724
+ "privacy": "private",
2725
+ "parameters": [
2726
+ {
2727
+ "name": "columnId",
2728
+ "type": {
2729
+ "text": "string"
2730
+ }
2731
+ }
2732
+ ]
2733
+ },
2734
+ {
2735
+ "kind": "field",
2736
+ "name": "_handleClickOutsideColumnPicker",
2737
+ "privacy": "private"
2738
+ },
2739
+ {
2740
+ "kind": "method",
2741
+ "name": "getDisplayedColumns",
2742
+ "return": {
2743
+ "type": {
2744
+ "text": "KRTableColumn[]"
2745
+ }
2746
+ }
2747
+ },
2748
+ {
2749
+ "kind": "method",
2750
+ "name": "_handleScroll",
2751
+ "privacy": "private",
2752
+ "parameters": [
2753
+ {
2754
+ "name": "e",
2755
+ "type": {
2756
+ "text": "Event"
2757
+ }
2758
+ }
2759
+ ],
2760
+ "description": "Scroll event handler that updates scroll flags in real-time as user scrolls.\nUpdates shadow indicators to show if more content exists left/right."
2761
+ },
2762
+ {
2763
+ "kind": "method",
2764
+ "name": "_updateScrollFlags",
2765
+ "privacy": "private",
2766
+ "description": "Updates scroll state flags for the table content container.\n- _canScrollLeft: true if scrolled right (can scroll back left)\n- _canScrollRight: true if more content exists to the right\n- _canScrollHorizontal: true if content is wider than container\nThese flags control scroll shadow indicators and CSS classes."
2767
+ },
2768
+ {
2769
+ "kind": "method",
2770
+ "name": "_handleResizeStart",
2771
+ "privacy": "private",
2772
+ "parameters": [
2773
+ {
2774
+ "name": "e",
2775
+ "type": {
2776
+ "text": "MouseEvent"
2777
+ }
2778
+ },
2779
+ {
2780
+ "name": "columnId",
2781
+ "type": {
2782
+ "text": "string"
2783
+ }
2784
+ }
2785
+ ]
2786
+ },
2787
+ {
2788
+ "kind": "field",
2789
+ "name": "_handleResizeMove",
2790
+ "privacy": "private"
2791
+ },
2792
+ {
2793
+ "kind": "field",
2794
+ "name": "_handleResizeEnd",
2795
+ "privacy": "private"
2796
+ },
2797
+ {
2798
+ "kind": "method",
2799
+ "name": "_handleAction",
2800
+ "privacy": "private",
2801
+ "parameters": [
2802
+ {
2803
+ "name": "action",
2804
+ "type": {
2805
+ "text": "KRTableAction"
2806
+ }
2807
+ }
2808
+ ]
2809
+ },
2810
+ {
2811
+ "kind": "method",
2812
+ "name": "_renderCellContent",
2813
+ "privacy": "private",
2814
+ "return": {
2815
+ "type": {
2816
+ "text": "TemplateResult | string"
2817
+ }
2818
+ },
2819
+ "parameters": [
2820
+ {
2821
+ "name": "column",
2822
+ "type": {
2823
+ "text": "KRTableColumn"
2824
+ }
2825
+ },
2826
+ {
2827
+ "name": "row",
2828
+ "type": {
2829
+ "text": "Record<string, any>"
2830
+ }
2831
+ }
2832
+ ]
2833
+ },
2834
+ {
2835
+ "kind": "method",
2836
+ "name": "_getHeaderCellClasses",
2837
+ "privacy": "private",
2838
+ "return": {
2839
+ "type": {
2840
+ "text": "Record<string, boolean>"
2841
+ }
2842
+ },
2843
+ "parameters": [
2844
+ {
2845
+ "name": "column",
2846
+ "type": {
2847
+ "text": "KRTableColumn"
2848
+ }
2849
+ },
2850
+ {
2851
+ "name": "index",
2852
+ "type": {
2853
+ "text": "number"
2854
+ }
2855
+ }
2856
+ ],
2857
+ "description": "Returns CSS classes for a header cell based on column config."
2858
+ },
2859
+ {
2860
+ "kind": "method",
2861
+ "name": "_getCellClasses",
2862
+ "privacy": "private",
2863
+ "return": {
2864
+ "type": {
2865
+ "text": "Record<string, boolean>"
2866
+ }
2867
+ },
2868
+ "parameters": [
2869
+ {
2870
+ "name": "column",
2871
+ "type": {
2872
+ "text": "KRTableColumn"
2873
+ }
2874
+ },
2875
+ {
2876
+ "name": "index",
2877
+ "type": {
2878
+ "text": "number"
2879
+ }
2880
+ }
2881
+ ],
2882
+ "description": "Returns CSS classes for a table cell based on column config:\n- Alignment (center, right)\n- Sticky positioning (left, right)\n- Border classes for the last left-sticky or first right-sticky column"
2883
+ },
2884
+ {
2885
+ "kind": "method",
2886
+ "name": "_getCellStyle",
2887
+ "privacy": "private",
2888
+ "return": {
2889
+ "type": {
2890
+ "text": "Record<string, string>"
2891
+ }
2892
+ },
2893
+ "parameters": [
2894
+ {
2895
+ "name": "column",
2896
+ "type": {
2897
+ "text": "KRTableColumn"
2898
+ }
2899
+ },
2900
+ {
2901
+ "name": "index",
2902
+ "type": {
2903
+ "text": "number"
2904
+ }
2905
+ }
2906
+ ],
2907
+ "description": "Returns inline styles for a table cell:\n- Width (from column config or default 150px)\n- Min-width (if specified)\n- Left/right offset for sticky columns (calculated from widths of preceding sticky columns)"
2908
+ },
2909
+ {
2910
+ "kind": "method",
2911
+ "name": "_renderPagination",
2912
+ "privacy": "private",
2913
+ "description": "Renders the pagination controls:\n- Previous page arrow (disabled on first page)\n- Range text showing \"1-50 of 150\" format\n- Next page arrow (disabled on last page)\n\nHidden when there's no data or all data fits on one page."
2914
+ },
2915
+ {
2916
+ "kind": "method",
2917
+ "name": "_renderHeader",
2918
+ "privacy": "private",
2919
+ "description": "Renders the header toolbar containing:\n- Title (left)\n- Search bar with view selector dropdown (center)\n- Tools (right): page navigation, refresh button, column visibility picker, actions dropdown\n\nHidden when there's no title, no actions, and data fits on one page."
2920
+ },
2921
+ {
2922
+ "kind": "method",
2923
+ "name": "_renderStatus",
2924
+ "privacy": "private",
2925
+ "description": "Renders status message (loading, error, empty)"
2926
+ },
2927
+ {
2928
+ "kind": "method",
2929
+ "name": "_renderTable",
2930
+ "privacy": "private",
2931
+ "description": "Renders the scrollable data grid with column headers and rows."
2932
+ }
2933
+ ],
2934
+ "events": [
2935
+ {
2936
+ "name": "action",
2937
+ "type": {
2938
+ "text": "CustomEvent"
2939
+ }
2940
+ }
2941
+ ],
2942
+ "attributes": [
2943
+ {
2944
+ "name": "def",
2945
+ "type": {
2946
+ "text": "KRTableDef"
2947
+ },
2948
+ "default": "{ columns: [] }",
2949
+ "fieldName": "def"
2950
+ }
2951
+ ],
2952
+ "superclass": {
2953
+ "name": "LitElement",
2954
+ "package": "lit"
2955
+ },
2956
+ "tagName": "kr-table",
2957
+ "customElement": true
2958
+ }
2959
+ ],
2960
+ "exports": [
2961
+ {
2962
+ "kind": "js",
2963
+ "name": "KRTable",
2964
+ "declaration": {
2965
+ "name": "KRTable",
2966
+ "module": "src/table/table.ts"
2967
+ }
2968
+ },
2969
+ {
2970
+ "kind": "custom-element-definition",
2971
+ "name": "kr-table",
2972
+ "declaration": {
2973
+ "name": "KRTable",
2974
+ "module": "src/table/table.ts"
2975
+ }
2976
+ }
2977
+ ]
2978
+ },
2309
2979
  {
2310
2980
  "kind": "javascript-module",
2311
2981
  "path": "src/tabs/tab.ts",