@kodaris/krubble-components 1.0.13 → 1.0.15
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.
- package/custom-elements.json +421 -375
- package/dist/button/button.d.ts +8 -0
- package/dist/button/button.d.ts.map +1 -1
- package/dist/button/button.js +17 -2
- package/dist/button/button.js.map +1 -1
- package/dist/krubble.bundled.js +51 -11
- package/dist/krubble.bundled.js.map +1 -1
- package/dist/krubble.bundled.min.js +24 -18
- package/dist/krubble.bundled.min.js.map +1 -1
- package/dist/krubble.umd.js +51 -11
- package/dist/krubble.umd.js.map +1 -1
- package/dist/krubble.umd.min.js +26 -20
- package/dist/krubble.umd.min.js.map +1 -1
- package/dist/table/table.d.ts +2 -0
- package/dist/table/table.d.ts.map +1 -1
- package/dist/table/table.js +34 -10
- package/dist/table/table.js.map +1 -1
- package/package.json +1 -1
package/custom-elements.json
CHANGED
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
{
|
|
163
163
|
"kind": "variable",
|
|
164
164
|
"name": "KRButton",
|
|
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._dropdownOpened = false; this._dropdownAlignRight = false; this._handleHostClick = (e) => { if (this.options.length) { e.stopPropagation(); this._toggleDropdown(); } }; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (this.options.length) { this._toggleDropdown(); } else { this.click(); } } if (e.key === 'Escape' && this._dropdownOpened) { this._dropdownOpened = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpened && !this.contains(e.target)) { this._dropdownOpened = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); this.addEventListener('click', this._handleHostClick); document.addEventListener('click', this._handleClickOutside); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); this.removeEventListener('click', this._handleHostClick); document.removeEventListener('click', this._handleClickOutside); } _toggleDropdown() { this._dropdownOpened = !this._dropdownOpened; if (this._dropdownOpened) { // Check if dropdown would overflow viewport after render requestAnimationFrame(() => { const dropdown = this.shadowRoot?.querySelector('.dropdown'); if (dropdown) { const rect = dropdown.getBoundingClientRect(); this._dropdownAlignRight = rect.right > window.innerWidth; } }); } } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpened = false; this.dispatchEvent(new CustomEvent('option-select', { detail: { id: option.id, label: option.label }, bubbles: true, composed: true })); } /** * Shows a loading spinner and disables the button. */ showLoading() { this._clearStateTimeout(); this._state = 'loading'; this._stateText = ''; } /** * Shows a success state with optional custom text. * @param text - Text to display (default: \"Saved\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showSuccess(text = 'Success', duration = 2000) { this._clearStateTimeout(); this._state = 'success'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Shows an error state with optional custom text. * @param text - Text to display (default: \"Error\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showError(text = 'Error', duration = 2000) { this._clearStateTimeout(); this._state = 'error'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Resets the button to its idle state. */ reset() { this._clearStateTimeout(); this._state = 'idle'; this._stateText = ''; } _clearStateTimeout() { if (this._stateTimeout) { clearTimeout(this._stateTimeout); this._stateTimeout = undefined; } } updated(changedProperties) { // Reflect state classes to host this.classList.toggle('kr-button--loading', this._state === 'loading'); this.classList.toggle('kr-button--success', this._state === 'success'); this.classList.toggle('kr-button--error', this._state === 'error'); this.classList.toggle(`kr-button--${this.variant}`, true); this.classList.toggle(`kr-button--${this.color}`, true); this.classList.toggle('kr-button--small', this.size === 'small'); this.classList.toggle('kr-button--large', this.size === 'large'); } render() {
|
|
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._dropdownOpened = false; this._dropdownAlignRight = false; this._handleHostClick = (e) => { if (this.options.length) { e.stopPropagation(); this._toggleDropdown(); } }; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (this.options.length) { this._toggleDropdown(); } else { this.click(); } } if (e.key === 'Escape' && this._dropdownOpened) { this._dropdownOpened = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpened && !this.contains(e.target)) { this._dropdownOpened = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', this.href ? 'link' : 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); this.addEventListener('click', this._handleHostClick); document.addEventListener('click', this._handleClickOutside); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); this.removeEventListener('click', this._handleHostClick); document.removeEventListener('click', this._handleClickOutside); } _toggleDropdown() { this._dropdownOpened = !this._dropdownOpened; if (this._dropdownOpened) { // Check if dropdown would overflow viewport after render requestAnimationFrame(() => { const dropdown = this.shadowRoot?.querySelector('.dropdown'); if (dropdown) { const rect = dropdown.getBoundingClientRect(); this._dropdownAlignRight = rect.right > window.innerWidth; } }); } } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpened = false; this.dispatchEvent(new CustomEvent('option-select', { detail: { id: option.id, label: option.label }, bubbles: true, composed: true })); } /** * Shows a loading spinner and disables the button. */ showLoading() { this._clearStateTimeout(); this._state = 'loading'; this._stateText = ''; } /** * Shows a success state with optional custom text. * @param text - Text to display (default: \"Saved\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showSuccess(text = 'Success', duration = 2000) { this._clearStateTimeout(); this._state = 'success'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Shows an error state with optional custom text. * @param text - Text to display (default: \"Error\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showError(text = 'Error', duration = 2000) { this._clearStateTimeout(); this._state = 'error'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Resets the button to its idle state. */ reset() { this._clearStateTimeout(); this._state = 'idle'; this._stateText = ''; } _clearStateTimeout() { if (this._stateTimeout) { clearTimeout(this._stateTimeout); this._stateTimeout = undefined; } } updated(changedProperties) { // Reflect state classes to host this.classList.toggle('kr-button--loading', this._state === 'loading'); this.classList.toggle('kr-button--success', this._state === 'success'); this.classList.toggle('kr-button--error', this._state === 'error'); this.classList.toggle(`kr-button--${this.variant}`, true); this.classList.toggle(`kr-button--${this.color}`, true); this.classList.toggle('kr-button--small', this.size === 'small'); this.classList.toggle('kr-button--large', this.size === 'large'); } render() { const content = b ` <slot></slot> ${this.options.length ? 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} ${this.options.length ? b ` <div class=\"dropdown ${this._dropdownOpened ? 'dropdown--opened' : ''} ${this._dropdownAlignRight ? 'dropdown--align-right' : ''}\"> ${this.options.map(option => b ` <button class=\"dropdown-item\" @click=${(e) => this._handleOptionClick(option, e)} >${option.label}</button> `)} </div> ` : A} `; return this.href ? b `<a class=\"link\" href=${this.href} target=${this.target || A}>${content}</a>` : content; } }",
|
|
166
166
|
"description": "A customizable button component."
|
|
167
167
|
},
|
|
168
168
|
{
|
|
@@ -256,7 +256,7 @@
|
|
|
256
256
|
{
|
|
257
257
|
"kind": "variable",
|
|
258
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 = 'overlay'; 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._resizing = null; this._resizeObserver = null; this._searchPositionLocked = false; this._def = { columns: [] }; 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; const col = this._def.columns.find(c => c.id === this._resizing.columnId); if (col) { const newWidth = this._resizing.startWidth + (e.clientX - this._resizing.startX); col.width = `${Math.min(900, Math.max(50, newWidth))}px`; 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(); } willUpdate(changedProperties) { if (changedProperties.has('def')) { // Copy user's def and normalize action columns this._def = { ...this.def, columns: this.def.columns.map(col => { if (col.type === 'actions') { return { ...col, sticky: 'right', resizable: false }; } return { ...col }; }) }; this._displayedColumns = this._def.displayedColumns || this._def.columns.map(c => c.id); this._fetch(); this._initRefresh(); } } updated(changedProperties) { 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(); } _getGridTemplateColumns() { const cols = this.getDisplayedColumns(); return cols.map((col) => { // If column has explicit width, use it if (col.width) { return col.width; } // Actions columns: fit content without minimum if (col.type === 'actions') { return 'max-content'; } // No width specified - use content-based sizing with minimum return 'minmax(80px, auto)'; }).join(' '); } /** * 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. // Actions columns are always moved to the end. getDisplayedColumns() { return this._displayedColumns .map(id => this._def.columns.find(col => col.id === id)) .sort((a, b) => { if (a.type === 'actions' && b.type !== 'actions') return 1; if (a.type !== 'actions' && b.type === 'actions') return -1; return 0; }); } // ---------------------------------------------------------------------------- // 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); this.classList.toggle('kr-table--sticky-left', this.getDisplayedColumns().some(c => c.sticky === 'left')); this.classList.toggle('kr-table--sticky-right', this.getDisplayedColumns().some(c => c.sticky === 'right')); } // ---------------------------------------------------------------------------- // Column Resizing // ---------------------------------------------------------------------------- _handleResizeStart(e, columnId) { e.preventDefault(); const headerCell = this.shadowRoot?.querySelector(`.header-cell[data-column-id=\"${columnId}\"]`); this._resizing = { columnId, startX: e.clientX, startWidth: headerCell?.offsetWidth || 200 }; 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) { const result = column.render(row); // If render returns a string, treat it as HTML return typeof result === 'string' ? o$2(result) : result; } 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--actions': column.type === 'actions', '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 = {}; if (column.sticky === 'left') { let leftOffset = 0; for (let i = 0; i < index; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'left') { leftOffset += parseInt(col.width || '0', 10); } } styles.left = `${leftOffset}px`; } if (column.sticky === 'right') { let rightOffset = 0; for (let i = index + 1; i < this.getDisplayedColumns().length; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'right') { rightOffset += parseInt(col.width || '0', 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].filter(col => col.type !== 'actions').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 === 1 ? b ` <kr-button class=\"actions\" @click=${() => this._handleAction(this._def.actions[0])} > ${this._def.actions[0].label} </kr-button> ` : 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\" style=\"grid-template-columns: ${this._getGridTemplateColumns()}\"> <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))} data-column-id=${col.id} >${col.label ?? col.id}${col.resizable !== false ? b `<div class=\"header-cell__resize\" @mousedown=${(e) => this._handleResizeStart(e, col.id)} ></div>` : A}</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))} data-column-id=${col.id} > ${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()} `; } }"
|
|
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 = 'overlay'; 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._resizing = null; this._resizeObserver = null; this._searchPositionLocked = false; this._def = { columns: [] }; 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; const col = this._def.columns.find(c => c.id === this._resizing.columnId); if (col) { const newWidth = this._resizing.startWidth + (e.clientX - this._resizing.startX); col.width = `${Math.min(900, Math.max(50, newWidth))}px`; 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(); } willUpdate(changedProperties) { if (changedProperties.has('def')) { // Copy user's def and normalize action columns this._def = { ...this.def, columns: this.def.columns.map(col => { if (col.type === 'actions') { return { ...col, label: col.label ?? '', sticky: 'right', resizable: false }; } return { ...col }; }) }; this._displayedColumns = this._def.displayedColumns || this._def.columns.map(c => c.id); this._fetch(); this._initRefresh(); } } updated(changedProperties) { this._updateScrollFlags(); this._syncSlottedContent(); } /** Syncs light DOM content for cells with custom render functions */ _syncSlottedContent() { const columns = this.getDisplayedColumns().filter(col => col.render); if (!columns.length) return; // Clear old slotted content this.querySelectorAll('[slot^=\"cell-\"]').forEach(el => el.remove()); // Create new slotted content this._data.forEach((row, rowIndex) => { columns.forEach(col => { const result = col.render(row); const content = typeof result === 'string' ? result : ''; if (content) { const el = document.createElement('span'); el.slot = `cell-${rowIndex}-${col.id}`; if (col.type === 'actions') { el.style.display = 'flex'; el.style.gap = '8px'; } el.innerHTML = content; this.appendChild(el); } }); }); } // ---------------------------------------------------------------------------- // 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(); } _getGridTemplateColumns() { const cols = this.getDisplayedColumns(); return cols.map((col) => { // If column has explicit width, use it if (col.width) { return col.width; } // Actions columns: fit content without minimum if (col.type === 'actions') { return 'max-content'; } // No width specified - use content-based sizing with minimum return 'minmax(80px, auto)'; }).join(' '); } /** * 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. // Actions columns are always moved to the end. getDisplayedColumns() { return this._displayedColumns .map(id => this._def.columns.find(col => col.id === id)) .sort((a, b) => { if (a.type === 'actions' && b.type !== 'actions') return 1; if (a.type !== 'actions' && b.type === 'actions') return -1; return 0; }); } // ---------------------------------------------------------------------------- // 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); this.classList.toggle('kr-table--sticky-left', this.getDisplayedColumns().some(c => c.sticky === 'left')); this.classList.toggle('kr-table--sticky-right', this.getDisplayedColumns().some(c => c.sticky === 'right')); } // ---------------------------------------------------------------------------- // Column Resizing // ---------------------------------------------------------------------------- _handleResizeStart(e, columnId) { e.preventDefault(); const headerCell = this.shadowRoot?.querySelector(`.header-cell[data-column-id=\"${columnId}\"]`); this._resizing = { columnId, startX: e.clientX, startWidth: headerCell?.offsetWidth || 200 }; 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, rowIndex) { const value = row[column.id]; if (column.render) { // Use slot to project content from light DOM so external styles apply return b `<slot name=\"cell-${rowIndex}-${column.id}\"></slot>`; } 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--actions': column.type === 'actions', '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 = {}; if (column.sticky === 'left') { let leftOffset = 0; for (let i = 0; i < index; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'left') { leftOffset += parseInt(col.width || '0', 10); } } styles.left = `${leftOffset}px`; } if (column.sticky === 'right') { let rightOffset = 0; for (let i = index + 1; i < this.getDisplayedColumns().length; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'right') { rightOffset += parseInt(col.width || '0', 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].filter(col => col.type !== 'actions').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 === 1 ? b ` <kr-button class=\"actions\" @click=${() => this._handleAction(this._def.actions[0])} > ${this._def.actions[0].label} </kr-button> ` : 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\" style=\"grid-template-columns: ${this._getGridTemplateColumns()}\"> <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))} data-column-id=${col.id} >${col.label ?? col.id}${col.resizable !== false ? b `<div class=\"header-cell__resize\" @mousedown=${(e) => this._handleResizeStart(e, col.id)} ></div>` : A}</div> `)} </div> ${this._data.map((row, rowIndex) => 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))} data-column-id=${col.id} > ${this._renderCellContent(col, row, rowIndex)} </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
260
|
},
|
|
261
261
|
{
|
|
262
262
|
"kind": "variable",
|
|
@@ -447,7 +447,7 @@
|
|
|
447
447
|
{
|
|
448
448
|
"kind": "variable",
|
|
449
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._dropdownOpened=!1,this._dropdownAlignRight=!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._dropdownOpened&&(this._dropdownOpened=!1)},this._handleClickOutside=e=>{this._dropdownOpened&&!this.contains(e.target)&&(this._dropdownOpened=!1)}}connectedCallback(){super.connectedCallback(),this.setAttribute(\"role\"
|
|
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._dropdownOpened=!1,this._dropdownAlignRight=!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._dropdownOpened&&(this._dropdownOpened=!1)},this._handleClickOutside=e=>{this._dropdownOpened&&!this.contains(e.target)&&(this._dropdownOpened=!1)}}connectedCallback(){super.connectedCallback(),this.setAttribute(\"role\",this.href?\"link\":\"button\"),this.setAttribute(\"tabindex\",\"0\"),this.addEventListener(\"keydown\",this._handleKeydown),this.addEventListener(\"click\",this._handleHostClick),document.addEventListener(\"click\",this._handleClickOutside)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(\"keydown\",this._handleKeydown),this.removeEventListener(\"click\",this._handleHostClick),document.removeEventListener(\"click\",this._handleClickOutside)}_toggleDropdown(){this._dropdownOpened=!this._dropdownOpened,this._dropdownOpened&&requestAnimationFrame(()=>{const e=this.shadowRoot?.querySelector(\".dropdown\");if(e){const t=e.getBoundingClientRect();this._dropdownAlignRight=t.right>window.innerWidth}})}_handleOptionClick(e,t){t.stopPropagation(),this._dropdownOpened=!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=U` <slot></slot> ${this.options.length?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} ${this.options.length?U` <div class=\"dropdown ${this._dropdownOpened?\"dropdown--opened\":\"\"} ${this._dropdownAlignRight?\"dropdown--align-right\":\"\"}\"> ${this.options.map(e=>U` <button class=\"dropdown-item\" @click=${t=>this._handleOptionClick(e,t)} >${e.label}</button> `)} </div> `:V} `;return this.href?U`<a class=\"link\" href=${this.href} target=${this.target||V}>${e}</a>`:e}}"
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
453
|
"kind": "variable",
|
|
@@ -511,7 +511,7 @@
|
|
|
511
511
|
},
|
|
512
512
|
{
|
|
513
513
|
"kind": "variable",
|
|
514
|
-
"name": "
|
|
514
|
+
"name": "Me",
|
|
515
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> `}}"
|
|
516
516
|
},
|
|
517
517
|
{
|
|
@@ -531,7 +531,7 @@
|
|
|
531
531
|
{
|
|
532
532
|
"kind": "variable",
|
|
533
533
|
"name": "Ye",
|
|
534
|
-
"default": "class extends ne{constructor(){super(...arguments),this._scrollStyle=\"overlay\",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._resizing=null,this._resizeObserver=null,this._searchPositionLocked=!1,this._def={columns:[]},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=>{if(!this._resizing)return;const t=this._def.columns.find(e=>e.id===this._resizing.columnId);if(t){const i=this._resizing.startWidth+(e.clientX-this._resizing.startX);t.width=`${Math.min(900,Math.max(50,i))}px`,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()}willUpdate(e){e.has(\"def\")&&(this._def={...this.def,columns:this.def.columns.map(e=>\"actions\"===e.type?{...e,sticky:\"right\",resizable:!1}:{...e})},this._displayedColumns=this._def.displayedColumns||this._def.columns.map(e=>e.id),this._fetch(),this._initRefresh())}updated(e){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()}_getGridTemplateColumns(){return this.getDisplayedColumns().map(e=>e.width?e.width:\"actions\"===e.type?\"max-content\":\"minmax(80px, auto)\").join(\" \")}_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)).sort((e,t)=>\"actions\"===e.type&&\"actions\"!==t.type?1:\"actions\"!==e.type&&\"actions\"===t.type?-1:0)}_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),this.classList.toggle(\"kr-table--sticky-left\",this.getDisplayedColumns().some(e=>\"left\"===e.sticky)),this.classList.toggle(\"kr-table--sticky-right\",this.getDisplayedColumns().some(e=>\"right\"===e.sticky))}_handleResizeStart(e,t){e.preventDefault();const i=this.shadowRoot?.querySelector(`.header-cell[data-column-id=\"${t}\"]`);this._resizing={columnId:t,startX:e.clientX,startWidth:i?.offsetWidth||200},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){const i=e.render(t);return\"string\"==typeof i?Oe(i):i}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--actions\":\"actions\"===e.type,\"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={};if(\"left\"===e.sticky){let e=0;for(let i=0;i<t;i++){const t=this.getDisplayedColumns()[i];\"left\"===t.sticky&&(e+=parseInt(t.width||\"0\",10))}i.left=`${e}px`}if(\"right\"===e.sticky){let e=0;for(let i=t+1;i<this.getDisplayedColumns().length;i++){const t=this.getDisplayedColumns()[i];\"right\"===t.sticky&&(e+=parseInt(t.width||\"0\",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].filter(e=>\"actions\"!==e.type).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> ${1===this._def.actions?.length?U` <kr-button class=\"actions\" @click=${()=>this._handleAction(this._def.actions[0])} > ${this._def.actions[0].label} </kr-button> `: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\" style=\"grid-template-columns: ${this._getGridTemplateColumns()}\"> <div class=\"header-row\"> ${this.getDisplayedColumns().map((e,t)=>U` <div class=${we(this._getHeaderCellClasses(e,t))} style=${Ue(this._getCellStyle(e,t))} data-column-id=${e.id} >${e.label??e.id}${!1!==e.resizable?U`<div class=\"header-cell__resize\" @mousedown=${t=>this._handleResizeStart(t,e.id)} ></div>`:V}</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))} data-column-id=${t.id} > ${this._renderCellContent(t,e)} </div> `)} </div> `)} </div> </div> </div> `}render(){return this._def.columns.length?U` ${this._renderHeader()} ${this._renderTable()} `:U`<slot></slot>`}}"
|
|
534
|
+
"default": "class extends ne{constructor(){super(...arguments),this._scrollStyle=\"overlay\",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._resizing=null,this._resizeObserver=null,this._searchPositionLocked=!1,this._def={columns:[]},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=>{if(!this._resizing)return;const t=this._def.columns.find(e=>e.id===this._resizing.columnId);if(t){const i=this._resizing.startWidth+(e.clientX-this._resizing.startX);t.width=`${Math.min(900,Math.max(50,i))}px`,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()}willUpdate(e){e.has(\"def\")&&(this._def={...this.def,columns:this.def.columns.map(e=>\"actions\"===e.type?{...e,label:e.label??\"\",sticky:\"right\",resizable:!1}:{...e})},this._displayedColumns=this._def.displayedColumns||this._def.columns.map(e=>e.id),this._fetch(),this._initRefresh())}updated(e){this._updateScrollFlags(),this._syncSlottedContent()}_syncSlottedContent(){const e=this.getDisplayedColumns().filter(e=>e.render);e.length&&(this.querySelectorAll('[slot^=\"cell-\"]').forEach(e=>e.remove()),this._data.forEach((t,i)=>{e.forEach(e=>{const o=e.render(t),s=\"string\"==typeof o?o:\"\";if(s){const t=document.createElement(\"span\");t.slot=`cell-${i}-${e.id}`,\"actions\"===e.type&&(t.style.display=\"flex\",t.style.gap=\"8px\"),t.innerHTML=s,this.appendChild(t)}})}))}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()}_getGridTemplateColumns(){return this.getDisplayedColumns().map(e=>e.width?e.width:\"actions\"===e.type?\"max-content\":\"minmax(80px, auto)\").join(\" \")}_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)).sort((e,t)=>\"actions\"===e.type&&\"actions\"!==t.type?1:\"actions\"!==e.type&&\"actions\"===t.type?-1:0)}_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),this.classList.toggle(\"kr-table--sticky-left\",this.getDisplayedColumns().some(e=>\"left\"===e.sticky)),this.classList.toggle(\"kr-table--sticky-right\",this.getDisplayedColumns().some(e=>\"right\"===e.sticky))}_handleResizeStart(e,t){e.preventDefault();const i=this.shadowRoot?.querySelector(`.header-cell[data-column-id=\"${t}\"]`);this._resizing={columnId:t,startX:e.clientX,startWidth:i?.offsetWidth||200},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,i){const o=t[e.id];if(e.render)return U`<slot name=\"cell-${i}-${e.id}\"></slot>`;if(null==o)return\"\";switch(e.type){case\"number\":return\"number\"==typeof o?o.toLocaleString():String(o);case\"currency\":return\"number\"==typeof o?o.toLocaleString(\"en-US\",{style:\"currency\",currency:\"USD\"}):String(o);case\"date\":return o instanceof Date?o.toLocaleDateString():new Date(o).toLocaleDateString();case\"boolean\":return!0===o?\"Yes\":!1===o?\"No\":\"\";default:return String(o)}}_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--actions\":\"actions\"===e.type,\"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={};if(\"left\"===e.sticky){let e=0;for(let i=0;i<t;i++){const t=this.getDisplayedColumns()[i];\"left\"===t.sticky&&(e+=parseInt(t.width||\"0\",10))}i.left=`${e}px`}if(\"right\"===e.sticky){let e=0;for(let i=t+1;i<this.getDisplayedColumns().length;i++){const t=this.getDisplayedColumns()[i];\"right\"===t.sticky&&(e+=parseInt(t.width||\"0\",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].filter(e=>\"actions\"!==e.type).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> ${1===this._def.actions?.length?U` <kr-button class=\"actions\" @click=${()=>this._handleAction(this._def.actions[0])} > ${this._def.actions[0].label} </kr-button> `: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\" style=\"grid-template-columns: ${this._getGridTemplateColumns()}\"> <div class=\"header-row\"> ${this.getDisplayedColumns().map((e,t)=>U` <div class=${we(this._getHeaderCellClasses(e,t))} style=${Ue(this._getCellStyle(e,t))} data-column-id=${e.id} >${e.label??e.id}${!1!==e.resizable?U`<div class=\"header-cell__resize\" @mousedown=${t=>this._handleResizeStart(t,e.id)} ></div>`:V}</div> `)} </div> ${this._data.map((e,t)=>U` <div class=\"row\"> ${this.getDisplayedColumns().map((i,o)=>U` <div class=${we(this._getCellClasses(i,o))} style=${Ue(this._getCellStyle(i,o))} data-column-id=${i.id} > ${this._renderCellContent(i,e,t)} </div> `)} </div> `)} </div> </div> </div> `}render(){return this._def.columns.length?U` ${this._renderHeader()} ${this._renderTable()} `:U`<slot></slot>`}}"
|
|
535
535
|
},
|
|
536
536
|
{
|
|
537
537
|
"kind": "variable",
|
|
@@ -618,7 +618,7 @@
|
|
|
618
618
|
"kind": "js",
|
|
619
619
|
"name": "KRDialog",
|
|
620
620
|
"declaration": {
|
|
621
|
-
"name": "
|
|
621
|
+
"name": "Me",
|
|
622
622
|
"module": "dist/krubble.bundled.min.js"
|
|
623
623
|
}
|
|
624
624
|
},
|
|
@@ -1015,7 +1015,7 @@
|
|
|
1015
1015
|
{
|
|
1016
1016
|
"kind": "variable",
|
|
1017
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._dropdownOpened = false; this._dropdownAlignRight = false; this._handleHostClick = (e) => { if (this.options.length) { e.stopPropagation(); this._toggleDropdown(); } }; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (this.options.length) { this._toggleDropdown(); } else { this.click(); } } if (e.key === 'Escape' && this._dropdownOpened) { this._dropdownOpened = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpened && !this.contains(e.target)) { this._dropdownOpened = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); this.addEventListener('click', this._handleHostClick); document.addEventListener('click', this._handleClickOutside); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); this.removeEventListener('click', this._handleHostClick); document.removeEventListener('click', this._handleClickOutside); } _toggleDropdown() { this._dropdownOpened = !this._dropdownOpened; if (this._dropdownOpened) { // Check if dropdown would overflow viewport after render requestAnimationFrame(() => { const dropdown = this.shadowRoot?.querySelector('.dropdown'); if (dropdown) { const rect = dropdown.getBoundingClientRect(); this._dropdownAlignRight = rect.right > window.innerWidth; } }); } } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpened = false; this.dispatchEvent(new CustomEvent('option-select', { detail: { id: option.id, label: option.label }, bubbles: true, composed: true })); } /** * Shows a loading spinner and disables the button. */ showLoading() { this._clearStateTimeout(); this._state = 'loading'; this._stateText = ''; } /** * Shows a success state with optional custom text. * @param text - Text to display (default: \"Saved\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showSuccess(text = 'Success', duration = 2000) { this._clearStateTimeout(); this._state = 'success'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Shows an error state with optional custom text. * @param text - Text to display (default: \"Error\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showError(text = 'Error', duration = 2000) { this._clearStateTimeout(); this._state = 'error'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Resets the button to its idle state. */ reset() { this._clearStateTimeout(); this._state = 'idle'; this._stateText = ''; } _clearStateTimeout() { if (this._stateTimeout) { clearTimeout(this._stateTimeout); this._stateTimeout = undefined; } } updated(changedProperties) { // Reflect state classes to host this.classList.toggle('kr-button--loading', this._state === 'loading'); this.classList.toggle('kr-button--success', this._state === 'success'); this.classList.toggle('kr-button--error', this._state === 'error'); this.classList.toggle(`kr-button--${this.variant}`, true); this.classList.toggle(`kr-button--${this.color}`, true); this.classList.toggle('kr-button--small', this.size === 'small'); this.classList.toggle('kr-button--large', this.size === 'large'); } render() {
|
|
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._dropdownOpened = false; this._dropdownAlignRight = false; this._handleHostClick = (e) => { if (this.options.length) { e.stopPropagation(); this._toggleDropdown(); } }; this._handleKeydown = (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (this.options.length) { this._toggleDropdown(); } else { this.click(); } } if (e.key === 'Escape' && this._dropdownOpened) { this._dropdownOpened = false; } }; this._handleClickOutside = (e) => { if (this._dropdownOpened && !this.contains(e.target)) { this._dropdownOpened = false; } }; } connectedCallback() { super.connectedCallback(); this.setAttribute('role', this.href ? 'link' : 'button'); this.setAttribute('tabindex', '0'); this.addEventListener('keydown', this._handleKeydown); this.addEventListener('click', this._handleHostClick); document.addEventListener('click', this._handleClickOutside); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleKeydown); this.removeEventListener('click', this._handleHostClick); document.removeEventListener('click', this._handleClickOutside); } _toggleDropdown() { this._dropdownOpened = !this._dropdownOpened; if (this._dropdownOpened) { // Check if dropdown would overflow viewport after render requestAnimationFrame(() => { const dropdown = this.shadowRoot?.querySelector('.dropdown'); if (dropdown) { const rect = dropdown.getBoundingClientRect(); this._dropdownAlignRight = rect.right > window.innerWidth; } }); } } _handleOptionClick(option, e) { e.stopPropagation(); this._dropdownOpened = false; this.dispatchEvent(new CustomEvent('option-select', { detail: { id: option.id, label: option.label }, bubbles: true, composed: true })); } /** * Shows a loading spinner and disables the button. */ showLoading() { this._clearStateTimeout(); this._state = 'loading'; this._stateText = ''; } /** * Shows a success state with optional custom text. * @param text - Text to display (default: \"Saved\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showSuccess(text = 'Success', duration = 2000) { this._clearStateTimeout(); this._state = 'success'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Shows an error state with optional custom text. * @param text - Text to display (default: \"Error\") * @param duration - Duration in ms before auto-reset (default: 2000) */ showError(text = 'Error', duration = 2000) { this._clearStateTimeout(); this._state = 'error'; this._stateText = text; this._stateTimeout = window.setTimeout(() => this.reset(), duration); } /** * Resets the button to its idle state. */ reset() { this._clearStateTimeout(); this._state = 'idle'; this._stateText = ''; } _clearStateTimeout() { if (this._stateTimeout) { clearTimeout(this._stateTimeout); this._stateTimeout = undefined; } } updated(changedProperties) { // Reflect state classes to host this.classList.toggle('kr-button--loading', this._state === 'loading'); this.classList.toggle('kr-button--success', this._state === 'success'); this.classList.toggle('kr-button--error', this._state === 'error'); this.classList.toggle(`kr-button--${this.variant}`, true); this.classList.toggle(`kr-button--${this.color}`, true); this.classList.toggle('kr-button--small', this.size === 'small'); this.classList.toggle('kr-button--large', this.size === 'large'); } render() { const content = html ` <slot></slot> ${this.options.length ? html `<svg class=\"caret\" xmlns=\"http://www.w3.org/2000/svg\" height=\"20\" width=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z\"/></svg>` : nothing} ${this._state !== 'idle' ? html `<span class=\"state-overlay\"> ${this._state === 'loading' ? html `<span class=\"spinner\"></span>` : this._stateText} </span>` : nothing} ${this.options.length ? html ` <div class=\"dropdown ${this._dropdownOpened ? 'dropdown--opened' : ''} ${this._dropdownAlignRight ? 'dropdown--align-right' : ''}\"> ${this.options.map(option => html ` <button class=\"dropdown-item\" @click=${(e) => this._handleOptionClick(option, e)} >${option.label}</button> `)} </div> ` : nothing} `; return this.href ? html `<a class=\"link\" href=${this.href} target=${this.target || nothing}>${content}</a>` : content; } }",
|
|
1019
1019
|
"description": "A customizable button component."
|
|
1020
1020
|
}
|
|
1021
1021
|
],
|
|
@@ -1032,44 +1032,44 @@
|
|
|
1032
1032
|
},
|
|
1033
1033
|
{
|
|
1034
1034
|
"kind": "javascript-module",
|
|
1035
|
-
"path": "dist/
|
|
1035
|
+
"path": "dist/context-menu/context-menu.js",
|
|
1036
1036
|
"declarations": [
|
|
1037
1037
|
{
|
|
1038
1038
|
"kind": "variable",
|
|
1039
|
-
"name": "
|
|
1040
|
-
"default": "class
|
|
1041
|
-
"description": "
|
|
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```"
|
|
1042
1042
|
}
|
|
1043
1043
|
],
|
|
1044
1044
|
"exports": [
|
|
1045
1045
|
{
|
|
1046
1046
|
"kind": "js",
|
|
1047
|
-
"name": "
|
|
1047
|
+
"name": "KRContextMenu",
|
|
1048
1048
|
"declaration": {
|
|
1049
|
-
"name": "
|
|
1050
|
-
"module": "dist/
|
|
1049
|
+
"name": "KRContextMenu",
|
|
1050
|
+
"module": "dist/context-menu/context-menu.js"
|
|
1051
1051
|
}
|
|
1052
1052
|
}
|
|
1053
1053
|
]
|
|
1054
1054
|
},
|
|
1055
1055
|
{
|
|
1056
1056
|
"kind": "javascript-module",
|
|
1057
|
-
"path": "dist/
|
|
1057
|
+
"path": "dist/code-demo/code-demo.js",
|
|
1058
1058
|
"declarations": [
|
|
1059
1059
|
{
|
|
1060
1060
|
"kind": "variable",
|
|
1061
|
-
"name": "
|
|
1062
|
-
"default": "class
|
|
1063
|
-
"description": "
|
|
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."
|
|
1064
1064
|
}
|
|
1065
1065
|
],
|
|
1066
1066
|
"exports": [
|
|
1067
1067
|
{
|
|
1068
1068
|
"kind": "js",
|
|
1069
|
-
"name": "
|
|
1069
|
+
"name": "KRCodeDemo",
|
|
1070
1070
|
"declaration": {
|
|
1071
|
-
"name": "
|
|
1072
|
-
"module": "dist/
|
|
1071
|
+
"name": "KRCodeDemo",
|
|
1072
|
+
"module": "dist/code-demo/code-demo.js"
|
|
1073
1073
|
}
|
|
1074
1074
|
}
|
|
1075
1075
|
]
|
|
@@ -1226,7 +1226,7 @@
|
|
|
1226
1226
|
{
|
|
1227
1227
|
"kind": "variable",
|
|
1228
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 = 'overlay'; 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._resizing = null; this._resizeObserver = null; this._searchPositionLocked = false; this._def = { columns: [] }; 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; const col = this._def.columns.find(c => c.id === this._resizing.columnId); if (col) { const newWidth = this._resizing.startWidth + (e.clientX - this._resizing.startX); col.width = `${Math.min(900, Math.max(50, newWidth))}px`; 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(); } willUpdate(changedProperties) { if (changedProperties.has('def')) { // Copy user's def and normalize action columns this._def = { ...this.def, columns: this.def.columns.map(col => { if (col.type === 'actions') { return { ...col, sticky: 'right', resizable: false }; } return { ...col }; }) }; this._displayedColumns = this._def.displayedColumns || this._def.columns.map(c => c.id); this._fetch(); this._initRefresh(); } } updated(changedProperties) { 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(); } _getGridTemplateColumns() { const cols = this.getDisplayedColumns(); return cols.map((col) => { // If column has explicit width, use it if (col.width) { return col.width; } // Actions columns: fit content without minimum if (col.type === 'actions') { return 'max-content'; } // No width specified - use content-based sizing with minimum return 'minmax(80px, auto)'; }).join(' '); } /** * 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. // Actions columns are always moved to the end. getDisplayedColumns() { return this._displayedColumns .map(id => this._def.columns.find(col => col.id === id)) .sort((a, b) => { if (a.type === 'actions' && b.type !== 'actions') return 1; if (a.type !== 'actions' && b.type === 'actions') return -1; return 0; }); } // ---------------------------------------------------------------------------- // 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); this.classList.toggle('kr-table--sticky-left', this.getDisplayedColumns().some(c => c.sticky === 'left')); this.classList.toggle('kr-table--sticky-right', this.getDisplayedColumns().some(c => c.sticky === 'right')); } // ---------------------------------------------------------------------------- // Column Resizing // ---------------------------------------------------------------------------- _handleResizeStart(e, columnId) { e.preventDefault(); const headerCell = this.shadowRoot?.querySelector(`.header-cell[data-column-id=\"${columnId}\"]`); this._resizing = { columnId, startX: e.clientX, startWidth: headerCell?.offsetWidth || 200 }; 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) { const result = column.render(row); // If render returns a string, treat it as HTML return typeof result === 'string' ? unsafeHTML(result) : result; } 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--actions': column.type === 'actions', '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 = {}; if (column.sticky === 'left') { let leftOffset = 0; for (let i = 0; i < index; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'left') { leftOffset += parseInt(col.width || '0', 10); } } styles.left = `${leftOffset}px`; } if (column.sticky === 'right') { let rightOffset = 0; for (let i = index + 1; i < this.getDisplayedColumns().length; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'right') { rightOffset += parseInt(col.width || '0', 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].filter(col => col.type !== 'actions').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 === 1 ? html ` <kr-button class=\"actions\" @click=${() => this._handleAction(this._def.actions[0])} > ${this._def.actions[0].label} </kr-button> ` : 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\" style=\"grid-template-columns: ${this._getGridTemplateColumns()}\"> <div class=\"header-row\"> ${this.getDisplayedColumns().map((col, i) => html ` <div class=${classMap(this._getHeaderCellClasses(col, i))} style=${styleMap(this._getCellStyle(col, i))} data-column-id=${col.id} >${col.label ?? col.id}${col.resizable !== false ? html `<div class=\"header-cell__resize\" @mousedown=${(e) => this._handleResizeStart(e, col.id)} ></div>` : nothing}</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))} data-column-id=${col.id} > ${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()} `; } }"
|
|
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 = 'overlay'; 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._resizing = null; this._resizeObserver = null; this._searchPositionLocked = false; this._def = { columns: [] }; 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; const col = this._def.columns.find(c => c.id === this._resizing.columnId); if (col) { const newWidth = this._resizing.startWidth + (e.clientX - this._resizing.startX); col.width = `${Math.min(900, Math.max(50, newWidth))}px`; 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(); } willUpdate(changedProperties) { if (changedProperties.has('def')) { // Copy user's def and normalize action columns this._def = { ...this.def, columns: this.def.columns.map(col => { if (col.type === 'actions') { return { ...col, label: col.label ?? '', sticky: 'right', resizable: false }; } return { ...col }; }) }; this._displayedColumns = this._def.displayedColumns || this._def.columns.map(c => c.id); this._fetch(); this._initRefresh(); } } updated(changedProperties) { this._updateScrollFlags(); this._syncSlottedContent(); } /** Syncs light DOM content for cells with custom render functions */ _syncSlottedContent() { const columns = this.getDisplayedColumns().filter(col => col.render); if (!columns.length) return; // Clear old slotted content this.querySelectorAll('[slot^=\"cell-\"]').forEach(el => el.remove()); // Create new slotted content this._data.forEach((row, rowIndex) => { columns.forEach(col => { const result = col.render(row); const content = typeof result === 'string' ? result : ''; if (content) { const el = document.createElement('span'); el.slot = `cell-${rowIndex}-${col.id}`; if (col.type === 'actions') { el.style.display = 'flex'; el.style.gap = '8px'; } el.innerHTML = content; this.appendChild(el); } }); }); } // ---------------------------------------------------------------------------- // 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(); } _getGridTemplateColumns() { const cols = this.getDisplayedColumns(); return cols.map((col) => { // If column has explicit width, use it if (col.width) { return col.width; } // Actions columns: fit content without minimum if (col.type === 'actions') { return 'max-content'; } // No width specified - use content-based sizing with minimum return 'minmax(80px, auto)'; }).join(' '); } /** * 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. // Actions columns are always moved to the end. getDisplayedColumns() { return this._displayedColumns .map(id => this._def.columns.find(col => col.id === id)) .sort((a, b) => { if (a.type === 'actions' && b.type !== 'actions') return 1; if (a.type !== 'actions' && b.type === 'actions') return -1; return 0; }); } // ---------------------------------------------------------------------------- // 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); this.classList.toggle('kr-table--sticky-left', this.getDisplayedColumns().some(c => c.sticky === 'left')); this.classList.toggle('kr-table--sticky-right', this.getDisplayedColumns().some(c => c.sticky === 'right')); } // ---------------------------------------------------------------------------- // Column Resizing // ---------------------------------------------------------------------------- _handleResizeStart(e, columnId) { e.preventDefault(); const headerCell = this.shadowRoot?.querySelector(`.header-cell[data-column-id=\"${columnId}\"]`); this._resizing = { columnId, startX: e.clientX, startWidth: headerCell?.offsetWidth || 200 }; 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, rowIndex) { const value = row[column.id]; if (column.render) { // Use slot to project content from light DOM so external styles apply return html `<slot name=\"cell-${rowIndex}-${column.id}\"></slot>`; } 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--actions': column.type === 'actions', '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 = {}; if (column.sticky === 'left') { let leftOffset = 0; for (let i = 0; i < index; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'left') { leftOffset += parseInt(col.width || '0', 10); } } styles.left = `${leftOffset}px`; } if (column.sticky === 'right') { let rightOffset = 0; for (let i = index + 1; i < this.getDisplayedColumns().length; i++) { const col = this.getDisplayedColumns()[i]; if (col.sticky === 'right') { rightOffset += parseInt(col.width || '0', 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].filter(col => col.type !== 'actions').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 === 1 ? html ` <kr-button class=\"actions\" @click=${() => this._handleAction(this._def.actions[0])} > ${this._def.actions[0].label} </kr-button> ` : 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\" style=\"grid-template-columns: ${this._getGridTemplateColumns()}\"> <div class=\"header-row\"> ${this.getDisplayedColumns().map((col, i) => html ` <div class=${classMap(this._getHeaderCellClasses(col, i))} style=${styleMap(this._getCellStyle(col, i))} data-column-id=${col.id} >${col.label ?? col.id}${col.resizable !== false ? html `<div class=\"header-cell__resize\" @mousedown=${(e) => this._handleResizeStart(e, col.id)} ></div>` : nothing}</div> `)} </div> ${this._data.map((row, rowIndex) => html ` <div class=\"row\"> ${this.getDisplayedColumns().map((col, i) => html ` <div class=${classMap(this._getCellClasses(col, i))} style=${styleMap(this._getCellStyle(col, i))} data-column-id=${col.id} > ${this._renderCellContent(col, row, rowIndex)} </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
1230
|
}
|
|
1231
1231
|
],
|
|
1232
1232
|
"exports": [
|
|
@@ -1566,6 +1566,24 @@
|
|
|
1566
1566
|
"attribute": "disabled",
|
|
1567
1567
|
"reflects": true
|
|
1568
1568
|
},
|
|
1569
|
+
{
|
|
1570
|
+
"kind": "field",
|
|
1571
|
+
"name": "href",
|
|
1572
|
+
"type": {
|
|
1573
|
+
"text": "string | undefined"
|
|
1574
|
+
},
|
|
1575
|
+
"description": "URL to navigate to - when set, button acts as a link",
|
|
1576
|
+
"attribute": "href"
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
"kind": "field",
|
|
1580
|
+
"name": "target",
|
|
1581
|
+
"type": {
|
|
1582
|
+
"text": "string | undefined"
|
|
1583
|
+
},
|
|
1584
|
+
"description": "Link target (e.g., '_blank' for new tab)",
|
|
1585
|
+
"attribute": "target"
|
|
1586
|
+
},
|
|
1569
1587
|
{
|
|
1570
1588
|
"kind": "field",
|
|
1571
1589
|
"name": "options",
|
|
@@ -1759,6 +1777,22 @@
|
|
|
1759
1777
|
"description": "Whether the button is disabled",
|
|
1760
1778
|
"fieldName": "disabled"
|
|
1761
1779
|
},
|
|
1780
|
+
{
|
|
1781
|
+
"name": "href",
|
|
1782
|
+
"type": {
|
|
1783
|
+
"text": "string | undefined"
|
|
1784
|
+
},
|
|
1785
|
+
"description": "URL to navigate to - when set, button acts as a link",
|
|
1786
|
+
"fieldName": "href"
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
"name": "target",
|
|
1790
|
+
"type": {
|
|
1791
|
+
"text": "string | undefined"
|
|
1792
|
+
},
|
|
1793
|
+
"description": "Link target (e.g., '_blank' for new tab)",
|
|
1794
|
+
"fieldName": "target"
|
|
1795
|
+
},
|
|
1762
1796
|
{
|
|
1763
1797
|
"name": "options",
|
|
1764
1798
|
"type": {
|
|
@@ -2667,6 +2701,12 @@
|
|
|
2667
2701
|
"default": "{ columns: [] }",
|
|
2668
2702
|
"attribute": "def"
|
|
2669
2703
|
},
|
|
2704
|
+
{
|
|
2705
|
+
"kind": "method",
|
|
2706
|
+
"name": "_syncSlottedContent",
|
|
2707
|
+
"privacy": "private",
|
|
2708
|
+
"description": "Syncs light DOM content for cells with custom render functions"
|
|
2709
|
+
},
|
|
2670
2710
|
{
|
|
2671
2711
|
"kind": "method",
|
|
2672
2712
|
"name": "refresh"
|
|
@@ -2842,6 +2882,12 @@
|
|
|
2842
2882
|
"type": {
|
|
2843
2883
|
"text": "Record<string, any>"
|
|
2844
2884
|
}
|
|
2885
|
+
},
|
|
2886
|
+
{
|
|
2887
|
+
"name": "rowIndex",
|
|
2888
|
+
"type": {
|
|
2889
|
+
"text": "number"
|
|
2890
|
+
}
|
|
2845
2891
|
}
|
|
2846
2892
|
]
|
|
2847
2893
|
},
|
|
@@ -3398,6 +3444,50 @@
|
|
|
3398
3444
|
}
|
|
3399
3445
|
]
|
|
3400
3446
|
},
|
|
3447
|
+
{
|
|
3448
|
+
"kind": "javascript-module",
|
|
3449
|
+
"path": "dist/form/select-field/select-field-option.js",
|
|
3450
|
+
"declarations": [
|
|
3451
|
+
{
|
|
3452
|
+
"kind": "variable",
|
|
3453
|
+
"name": "KRSelectFieldOption",
|
|
3454
|
+
"default": "class KRSelectFieldOption extends LitElement { constructor() { super(...arguments); /** * The option value */ this.value = ''; /** * Whether the option is disabled */ this.disabled = false; } /** Gets the label text from the slot */ get label() { return this.textContent?.trim() || ''; } render() { return html `<slot></slot>`; } }",
|
|
3455
|
+
"description": "An option for the kr-select-field component."
|
|
3456
|
+
}
|
|
3457
|
+
],
|
|
3458
|
+
"exports": [
|
|
3459
|
+
{
|
|
3460
|
+
"kind": "js",
|
|
3461
|
+
"name": "KRSelectFieldOption",
|
|
3462
|
+
"declaration": {
|
|
3463
|
+
"name": "KRSelectFieldOption",
|
|
3464
|
+
"module": "dist/form/select-field/select-field-option.js"
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
]
|
|
3468
|
+
},
|
|
3469
|
+
{
|
|
3470
|
+
"kind": "javascript-module",
|
|
3471
|
+
"path": "dist/form/select-field/select-field.js",
|
|
3472
|
+
"declarations": [
|
|
3473
|
+
{
|
|
3474
|
+
"kind": "variable",
|
|
3475
|
+
"name": "KRSelectField",
|
|
3476
|
+
"default": "class KRSelectField extends LitElement { constructor() { super(); /** * The select label text */ this.label = ''; /** * The input name for form submission */ this.name = ''; /** * The currently selected value */ this.value = ''; /** * Placeholder text when no option is selected */ this.placeholder = 'Select an option'; /** * Whether the select is disabled */ this.disabled = false; /** * Whether the field is required */ this.required = false; /** * Whether the field is readonly */ this.readonly = false; /** * Helper text shown below the select */ this.hint = ''; this._isOpen = false; this._highlightedIndex = -1; this._touched = false; this._handleInvalid = (e) => { e.preventDefault(); this._touched = true; }; this._handleOutsideClick = (e) => { if (!e.composedPath().includes(this)) { this._close(); } }; this._handleKeyDown = (e) => { if (!this._isOpen) return; const options = Array.from(this.querySelectorAll('kr-select-field-option')); switch (e.key) { case 'Escape': this._close(); this._triggerElement?.focus(); break; case 'ArrowDown': e.preventDefault(); if (options.some(o => !o.disabled)) { let newIndex = this._highlightedIndex + 1; while (newIndex < options.length && options[newIndex]?.disabled) newIndex++; if (newIndex < options.length) this._highlightedIndex = newIndex; } break; case 'ArrowUp': e.preventDefault(); { let newIndex = this._highlightedIndex - 1; while (newIndex >= 0 && options[newIndex]?.disabled) newIndex--; if (newIndex >= 0) this._highlightedIndex = newIndex; } break; case 'Enter': e.preventDefault(); if (this._highlightedIndex >= 0 && this._highlightedIndex < options.length) { this._selectOption(options[this._highlightedIndex]); } break; } }; this._internals = this.attachInternals(); } // Form-associated custom element callbacks get form() { return this._internals.form; } get validity() { return this._internals.validity; } get validationMessage() { return this._internals.validationMessage; } get willValidate() { return this._internals.willValidate; } checkValidity() { return this._internals.checkValidity(); } reportValidity() { return this._internals.reportValidity(); } formResetCallback() { this.value = ''; this._touched = false; this._internals.setFormValue(''); this._internals.setValidity({}); } formStateRestoreCallback(state) { this.value = state; } connectedCallback() { super.connectedCallback(); document.addEventListener('click', this._handleOutsideClick); document.addEventListener('keydown', this._handleKeyDown); this.addEventListener('invalid', this._handleInvalid); } firstUpdated() { this._updateValidity(); } updated(changedProperties) { if (changedProperties.has('required') || changedProperties.has('value')) { this._updateValidity(); } } disconnectedCallback() { super.disconnectedCallback(); document.removeEventListener('click', this._handleOutsideClick); document.removeEventListener('keydown', this._handleKeyDown); this.removeEventListener('invalid', this._handleInvalid); } _toggle() { if (this.disabled || this.readonly) return; if (this._isOpen) { this._close(); } else { this._isOpen = true; const options = Array.from(this.querySelectorAll('kr-select-field-option')); this._highlightedIndex = options.findIndex(o => o.value === this.value); } } _close() { this._isOpen = false; this._highlightedIndex = -1; } _selectOption(option) { if (option.disabled) return; this.value = option.value; this._internals.setFormValue(this.value); this._updateValidity(); this.dispatchEvent(new Event('change', { bubbles: true, composed: true })); this._close(); this._triggerElement?.focus(); } _handleBlur() { this._touched = true; this._updateValidity(); } _updateValidity() { if (this.required && !this.value) { this._internals.setValidity({ valueMissing: true }, 'Please select an option', this._triggerElement); } else { this._internals.setValidity({}); } } render() { const options = Array.from(this.querySelectorAll('kr-select-field-option')); const selectedLabel = options.find(o => o.value === this.value)?.label; return html ` <div class=\"wrapper\"> ${this.label ? html ` <label> ${this.label} ${this.required ? html `<span class=\"required\" aria-hidden=\"true\">*</span>` : ''} </label> ` : nothing} <div class=\"select-wrapper\"> <button class=${classMap({ 'select-trigger': true, 'select-trigger--open': this._isOpen, 'select-trigger--invalid': this._touched && this.required && !this.value, })} type=\"button\" ?disabled=${this.disabled} aria-haspopup=\"listbox\" aria-expanded=${this._isOpen} @click=${this._toggle} @blur=${this._handleBlur} > <span class=${classMap({ 'select-value': true, 'select-placeholder': !selectedLabel })}> ${selectedLabel || this.placeholder} </span> <svg class=${classMap({ 'chevron-icon': true, 'select-icon': true, 'select-icon--open': this._isOpen })} viewBox=\"0 0 20 20\" fill=\"currentColor\" > <path fill-rule=\"evenodd\" d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\" clip-rule=\"evenodd\" /> </svg> </button> <div class=${classMap({ 'select-dropdown': true, 'hidden': !this._isOpen })} role=\"listbox\"> <div class=\"select-options\"> ${options.length === 0 ? html `<div class=\"select-empty\">No options available</div>` : options.map((option, idx) => { const isSelected = option.value === this.value; return html ` <div class=${classMap({ 'select-option': true, 'select-option--selected': isSelected, 'select-option--disabled': option.disabled, 'select-option--highlighted': idx === this._highlightedIndex, })} role=\"option\" aria-selected=${isSelected} @click=${() => this._selectOption(option)} @mouseenter=${() => (this._highlightedIndex = idx)} > ${option.label} ${isSelected ? html `<svg class=\"chevron-icon select-check\" viewBox=\"0 0 20 20\" fill=\"currentColor\"> <path fill-rule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clip-rule=\"evenodd\" /> </svg>` : nothing} </div> `; })} </div> </div> </div> ${this._touched && this.required && !this.value ? html `<div class=\"validation-message\">Please select an option</div>` : this.hint ? html `<div class=\"hint\">${this.hint}</div>` : ''} </div> <div class=\"options-slot\"> <slot @slotchange=${() => this.requestUpdate()}></slot> </div> `; } // Public methods for programmatic control focus() { this._triggerElement?.focus(); } blur() { this._triggerElement?.blur(); } }",
|
|
3477
|
+
"description": "A select dropdown component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset."
|
|
3478
|
+
}
|
|
3479
|
+
],
|
|
3480
|
+
"exports": [
|
|
3481
|
+
{
|
|
3482
|
+
"kind": "js",
|
|
3483
|
+
"name": "KRSelectField",
|
|
3484
|
+
"declaration": {
|
|
3485
|
+
"name": "KRSelectField",
|
|
3486
|
+
"module": "dist/form/select-field/select-field.js"
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
]
|
|
3490
|
+
},
|
|
3401
3491
|
{
|
|
3402
3492
|
"kind": "javascript-module",
|
|
3403
3493
|
"path": "dist/form/text-field/text-field.js",
|
|
@@ -3422,12 +3512,110 @@
|
|
|
3422
3512
|
},
|
|
3423
3513
|
{
|
|
3424
3514
|
"kind": "javascript-module",
|
|
3425
|
-
"path": "src/form/
|
|
3515
|
+
"path": "src/form/select-field/select-field-option.ts",
|
|
3426
3516
|
"declarations": [
|
|
3427
3517
|
{
|
|
3428
3518
|
"kind": "class",
|
|
3429
|
-
"description": "
|
|
3430
|
-
"name": "
|
|
3519
|
+
"description": "An option for the kr-select-field component.",
|
|
3520
|
+
"name": "KRSelectFieldOption",
|
|
3521
|
+
"slots": [
|
|
3522
|
+
{
|
|
3523
|
+
"description": "The option label content",
|
|
3524
|
+
"name": ""
|
|
3525
|
+
}
|
|
3526
|
+
],
|
|
3527
|
+
"members": [
|
|
3528
|
+
{
|
|
3529
|
+
"kind": "field",
|
|
3530
|
+
"name": "value",
|
|
3531
|
+
"type": {
|
|
3532
|
+
"text": "string"
|
|
3533
|
+
},
|
|
3534
|
+
"default": "''",
|
|
3535
|
+
"description": "The option value",
|
|
3536
|
+
"attribute": "value"
|
|
3537
|
+
},
|
|
3538
|
+
{
|
|
3539
|
+
"kind": "field",
|
|
3540
|
+
"name": "disabled",
|
|
3541
|
+
"type": {
|
|
3542
|
+
"text": "boolean"
|
|
3543
|
+
},
|
|
3544
|
+
"default": "false",
|
|
3545
|
+
"description": "Whether the option is disabled",
|
|
3546
|
+
"attribute": "disabled"
|
|
3547
|
+
},
|
|
3548
|
+
{
|
|
3549
|
+
"kind": "field",
|
|
3550
|
+
"name": "label",
|
|
3551
|
+
"type": {
|
|
3552
|
+
"text": "string"
|
|
3553
|
+
},
|
|
3554
|
+
"description": "Gets the label text from the slot",
|
|
3555
|
+
"readonly": true
|
|
3556
|
+
}
|
|
3557
|
+
],
|
|
3558
|
+
"attributes": [
|
|
3559
|
+
{
|
|
3560
|
+
"name": "value",
|
|
3561
|
+
"type": {
|
|
3562
|
+
"text": "string"
|
|
3563
|
+
},
|
|
3564
|
+
"default": "''",
|
|
3565
|
+
"description": "The option value",
|
|
3566
|
+
"fieldName": "value"
|
|
3567
|
+
},
|
|
3568
|
+
{
|
|
3569
|
+
"name": "disabled",
|
|
3570
|
+
"type": {
|
|
3571
|
+
"text": "boolean"
|
|
3572
|
+
},
|
|
3573
|
+
"default": "false",
|
|
3574
|
+
"description": "Whether the option is disabled",
|
|
3575
|
+
"fieldName": "disabled"
|
|
3576
|
+
}
|
|
3577
|
+
],
|
|
3578
|
+
"superclass": {
|
|
3579
|
+
"name": "LitElement",
|
|
3580
|
+
"package": "lit"
|
|
3581
|
+
},
|
|
3582
|
+
"tagName": "kr-select-field-option",
|
|
3583
|
+
"customElement": true
|
|
3584
|
+
}
|
|
3585
|
+
],
|
|
3586
|
+
"exports": [
|
|
3587
|
+
{
|
|
3588
|
+
"kind": "js",
|
|
3589
|
+
"name": "KRSelectFieldOption",
|
|
3590
|
+
"declaration": {
|
|
3591
|
+
"name": "KRSelectFieldOption",
|
|
3592
|
+
"module": "src/form/select-field/select-field-option.ts"
|
|
3593
|
+
}
|
|
3594
|
+
},
|
|
3595
|
+
{
|
|
3596
|
+
"kind": "custom-element-definition",
|
|
3597
|
+
"name": "kr-select-field-option",
|
|
3598
|
+
"declaration": {
|
|
3599
|
+
"name": "KRSelectFieldOption",
|
|
3600
|
+
"module": "src/form/select-field/select-field-option.ts"
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
]
|
|
3604
|
+
},
|
|
3605
|
+
{
|
|
3606
|
+
"kind": "javascript-module",
|
|
3607
|
+
"path": "src/form/select-field/select-field.ts",
|
|
3608
|
+
"declarations": [
|
|
3609
|
+
{
|
|
3610
|
+
"kind": "class",
|
|
3611
|
+
"description": "A select dropdown component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset.",
|
|
3612
|
+
"name": "KRSelectField",
|
|
3613
|
+
"slots": [
|
|
3614
|
+
{
|
|
3615
|
+
"description": "The kr-select-field-option elements",
|
|
3616
|
+
"name": ""
|
|
3617
|
+
}
|
|
3618
|
+
],
|
|
3431
3619
|
"members": [
|
|
3432
3620
|
{
|
|
3433
3621
|
"kind": "field",
|
|
@@ -3453,7 +3641,7 @@
|
|
|
3453
3641
|
"text": "string"
|
|
3454
3642
|
},
|
|
3455
3643
|
"default": "''",
|
|
3456
|
-
"description": "The
|
|
3644
|
+
"description": "The select label text",
|
|
3457
3645
|
"attribute": "label"
|
|
3458
3646
|
},
|
|
3459
3647
|
{
|
|
@@ -3473,7 +3661,7 @@
|
|
|
3473
3661
|
"text": "string"
|
|
3474
3662
|
},
|
|
3475
3663
|
"default": "''",
|
|
3476
|
-
"description": "The
|
|
3664
|
+
"description": "The currently selected value",
|
|
3477
3665
|
"attribute": "value"
|
|
3478
3666
|
},
|
|
3479
3667
|
{
|
|
@@ -3482,19 +3670,19 @@
|
|
|
3482
3670
|
"type": {
|
|
3483
3671
|
"text": "string"
|
|
3484
3672
|
},
|
|
3485
|
-
"default": "''",
|
|
3486
|
-
"description": "Placeholder text",
|
|
3673
|
+
"default": "'Select an option'",
|
|
3674
|
+
"description": "Placeholder text when no option is selected",
|
|
3487
3675
|
"attribute": "placeholder"
|
|
3488
3676
|
},
|
|
3489
3677
|
{
|
|
3490
3678
|
"kind": "field",
|
|
3491
|
-
"name": "
|
|
3679
|
+
"name": "disabled",
|
|
3492
3680
|
"type": {
|
|
3493
|
-
"text": "
|
|
3681
|
+
"text": "boolean"
|
|
3494
3682
|
},
|
|
3495
|
-
"default": "
|
|
3496
|
-
"description": "
|
|
3497
|
-
"attribute": "
|
|
3683
|
+
"default": "false",
|
|
3684
|
+
"description": "Whether the select is disabled",
|
|
3685
|
+
"attribute": "disabled"
|
|
3498
3686
|
},
|
|
3499
3687
|
{
|
|
3500
3688
|
"kind": "field",
|
|
@@ -3506,16 +3694,6 @@
|
|
|
3506
3694
|
"description": "Whether the field is required",
|
|
3507
3695
|
"attribute": "required"
|
|
3508
3696
|
},
|
|
3509
|
-
{
|
|
3510
|
-
"kind": "field",
|
|
3511
|
-
"name": "disabled",
|
|
3512
|
-
"type": {
|
|
3513
|
-
"text": "boolean"
|
|
3514
|
-
},
|
|
3515
|
-
"default": "false",
|
|
3516
|
-
"description": "Whether the field is disabled",
|
|
3517
|
-
"attribute": "disabled"
|
|
3518
|
-
},
|
|
3519
3697
|
{
|
|
3520
3698
|
"kind": "field",
|
|
3521
3699
|
"name": "readonly",
|
|
@@ -3528,58 +3706,31 @@
|
|
|
3528
3706
|
},
|
|
3529
3707
|
{
|
|
3530
3708
|
"kind": "field",
|
|
3531
|
-
"name": "
|
|
3709
|
+
"name": "hint",
|
|
3532
3710
|
"type": {
|
|
3533
|
-
"text": "
|
|
3711
|
+
"text": "string"
|
|
3534
3712
|
},
|
|
3535
|
-
"
|
|
3536
|
-
"
|
|
3713
|
+
"default": "''",
|
|
3714
|
+
"description": "Helper text shown below the select",
|
|
3715
|
+
"attribute": "hint"
|
|
3537
3716
|
},
|
|
3538
3717
|
{
|
|
3539
3718
|
"kind": "field",
|
|
3540
|
-
"name": "
|
|
3719
|
+
"name": "_isOpen",
|
|
3541
3720
|
"type": {
|
|
3542
|
-
"text": "
|
|
3721
|
+
"text": "boolean"
|
|
3543
3722
|
},
|
|
3544
|
-
"
|
|
3545
|
-
"
|
|
3723
|
+
"privacy": "private",
|
|
3724
|
+
"default": "false"
|
|
3546
3725
|
},
|
|
3547
3726
|
{
|
|
3548
3727
|
"kind": "field",
|
|
3549
|
-
"name": "
|
|
3728
|
+
"name": "_highlightedIndex",
|
|
3550
3729
|
"type": {
|
|
3551
|
-
"text": "
|
|
3730
|
+
"text": "number"
|
|
3552
3731
|
},
|
|
3553
|
-
"
|
|
3554
|
-
"
|
|
3555
|
-
},
|
|
3556
|
-
{
|
|
3557
|
-
"kind": "field",
|
|
3558
|
-
"name": "autocomplete",
|
|
3559
|
-
"type": {
|
|
3560
|
-
"text": "string"
|
|
3561
|
-
},
|
|
3562
|
-
"default": "''",
|
|
3563
|
-
"description": "Autocomplete attribute value",
|
|
3564
|
-
"attribute": "autocomplete"
|
|
3565
|
-
},
|
|
3566
|
-
{
|
|
3567
|
-
"kind": "field",
|
|
3568
|
-
"name": "hint",
|
|
3569
|
-
"type": {
|
|
3570
|
-
"text": "string"
|
|
3571
|
-
},
|
|
3572
|
-
"default": "''",
|
|
3573
|
-
"description": "Helper text shown below the input",
|
|
3574
|
-
"attribute": "hint"
|
|
3575
|
-
},
|
|
3576
|
-
{
|
|
3577
|
-
"kind": "field",
|
|
3578
|
-
"name": "_input",
|
|
3579
|
-
"type": {
|
|
3580
|
-
"text": "HTMLInputElement"
|
|
3581
|
-
},
|
|
3582
|
-
"privacy": "private"
|
|
3732
|
+
"privacy": "private",
|
|
3733
|
+
"default": "-1"
|
|
3583
3734
|
},
|
|
3584
3735
|
{
|
|
3585
3736
|
"kind": "field",
|
|
@@ -3592,12 +3743,11 @@
|
|
|
3592
3743
|
},
|
|
3593
3744
|
{
|
|
3594
3745
|
"kind": "field",
|
|
3595
|
-
"name": "
|
|
3746
|
+
"name": "_triggerElement",
|
|
3596
3747
|
"type": {
|
|
3597
|
-
"text": "
|
|
3748
|
+
"text": "HTMLButtonElement"
|
|
3598
3749
|
},
|
|
3599
|
-
"privacy": "private"
|
|
3600
|
-
"default": "false"
|
|
3750
|
+
"privacy": "private"
|
|
3601
3751
|
},
|
|
3602
3752
|
{
|
|
3603
3753
|
"kind": "field",
|
|
@@ -3648,33 +3798,35 @@
|
|
|
3648
3798
|
"name": "_handleInvalid",
|
|
3649
3799
|
"privacy": "private"
|
|
3650
3800
|
},
|
|
3801
|
+
{
|
|
3802
|
+
"kind": "field",
|
|
3803
|
+
"name": "_handleOutsideClick",
|
|
3804
|
+
"privacy": "private"
|
|
3805
|
+
},
|
|
3806
|
+
{
|
|
3807
|
+
"kind": "field",
|
|
3808
|
+
"name": "_handleKeyDown",
|
|
3809
|
+
"privacy": "private"
|
|
3810
|
+
},
|
|
3651
3811
|
{
|
|
3652
3812
|
"kind": "method",
|
|
3653
|
-
"name": "
|
|
3813
|
+
"name": "_toggle",
|
|
3654
3814
|
"privacy": "private"
|
|
3655
3815
|
},
|
|
3656
3816
|
{
|
|
3657
3817
|
"kind": "method",
|
|
3658
|
-
"name": "
|
|
3659
|
-
"privacy": "private"
|
|
3660
|
-
"parameters": [
|
|
3661
|
-
{
|
|
3662
|
-
"name": "e",
|
|
3663
|
-
"type": {
|
|
3664
|
-
"text": "Event"
|
|
3665
|
-
}
|
|
3666
|
-
}
|
|
3667
|
-
]
|
|
3818
|
+
"name": "_close",
|
|
3819
|
+
"privacy": "private"
|
|
3668
3820
|
},
|
|
3669
3821
|
{
|
|
3670
3822
|
"kind": "method",
|
|
3671
|
-
"name": "
|
|
3823
|
+
"name": "_selectOption",
|
|
3672
3824
|
"privacy": "private",
|
|
3673
3825
|
"parameters": [
|
|
3674
3826
|
{
|
|
3675
|
-
"name": "
|
|
3827
|
+
"name": "option",
|
|
3676
3828
|
"type": {
|
|
3677
|
-
"text": "
|
|
3829
|
+
"text": "KRSelectFieldOption"
|
|
3678
3830
|
}
|
|
3679
3831
|
}
|
|
3680
3832
|
]
|
|
@@ -3686,15 +3838,24 @@
|
|
|
3686
3838
|
},
|
|
3687
3839
|
{
|
|
3688
3840
|
"kind": "method",
|
|
3689
|
-
"name": "
|
|
3841
|
+
"name": "_updateValidity",
|
|
3842
|
+
"privacy": "private"
|
|
3690
3843
|
},
|
|
3691
3844
|
{
|
|
3692
3845
|
"kind": "method",
|
|
3693
|
-
"name": "
|
|
3846
|
+
"name": "focus"
|
|
3694
3847
|
},
|
|
3695
3848
|
{
|
|
3696
3849
|
"kind": "method",
|
|
3697
|
-
"name": "
|
|
3850
|
+
"name": "blur"
|
|
3851
|
+
}
|
|
3852
|
+
],
|
|
3853
|
+
"events": [
|
|
3854
|
+
{
|
|
3855
|
+
"name": "change",
|
|
3856
|
+
"type": {
|
|
3857
|
+
"text": "Event"
|
|
3858
|
+
}
|
|
3698
3859
|
}
|
|
3699
3860
|
],
|
|
3700
3861
|
"attributes": [
|
|
@@ -3704,7 +3865,7 @@
|
|
|
3704
3865
|
"text": "string"
|
|
3705
3866
|
},
|
|
3706
3867
|
"default": "''",
|
|
3707
|
-
"description": "The
|
|
3868
|
+
"description": "The select label text",
|
|
3708
3869
|
"fieldName": "label"
|
|
3709
3870
|
},
|
|
3710
3871
|
{
|
|
@@ -3722,7 +3883,7 @@
|
|
|
3722
3883
|
"text": "string"
|
|
3723
3884
|
},
|
|
3724
3885
|
"default": "''",
|
|
3725
|
-
"description": "The
|
|
3886
|
+
"description": "The currently selected value",
|
|
3726
3887
|
"fieldName": "value"
|
|
3727
3888
|
},
|
|
3728
3889
|
{
|
|
@@ -3730,18 +3891,18 @@
|
|
|
3730
3891
|
"type": {
|
|
3731
3892
|
"text": "string"
|
|
3732
3893
|
},
|
|
3733
|
-
"default": "''",
|
|
3734
|
-
"description": "Placeholder text",
|
|
3894
|
+
"default": "'Select an option'",
|
|
3895
|
+
"description": "Placeholder text when no option is selected",
|
|
3735
3896
|
"fieldName": "placeholder"
|
|
3736
3897
|
},
|
|
3737
3898
|
{
|
|
3738
|
-
"name": "
|
|
3899
|
+
"name": "disabled",
|
|
3739
3900
|
"type": {
|
|
3740
|
-
"text": "
|
|
3901
|
+
"text": "boolean"
|
|
3741
3902
|
},
|
|
3742
|
-
"default": "
|
|
3743
|
-
"description": "
|
|
3744
|
-
"fieldName": "
|
|
3903
|
+
"default": "false",
|
|
3904
|
+
"description": "Whether the select is disabled",
|
|
3905
|
+
"fieldName": "disabled"
|
|
3745
3906
|
},
|
|
3746
3907
|
{
|
|
3747
3908
|
"name": "required",
|
|
@@ -3752,15 +3913,6 @@
|
|
|
3752
3913
|
"description": "Whether the field is required",
|
|
3753
3914
|
"fieldName": "required"
|
|
3754
3915
|
},
|
|
3755
|
-
{
|
|
3756
|
-
"name": "disabled",
|
|
3757
|
-
"type": {
|
|
3758
|
-
"text": "boolean"
|
|
3759
|
-
},
|
|
3760
|
-
"default": "false",
|
|
3761
|
-
"description": "Whether the field is disabled",
|
|
3762
|
-
"fieldName": "disabled"
|
|
3763
|
-
},
|
|
3764
3916
|
{
|
|
3765
3917
|
"name": "readonly",
|
|
3766
3918
|
"type": {
|
|
@@ -3770,46 +3922,13 @@
|
|
|
3770
3922
|
"description": "Whether the field is readonly",
|
|
3771
3923
|
"fieldName": "readonly"
|
|
3772
3924
|
},
|
|
3773
|
-
{
|
|
3774
|
-
"name": "minlength",
|
|
3775
|
-
"type": {
|
|
3776
|
-
"text": "number | undefined"
|
|
3777
|
-
},
|
|
3778
|
-
"description": "Minimum length for the value",
|
|
3779
|
-
"fieldName": "minlength"
|
|
3780
|
-
},
|
|
3781
|
-
{
|
|
3782
|
-
"name": "maxlength",
|
|
3783
|
-
"type": {
|
|
3784
|
-
"text": "number | undefined"
|
|
3785
|
-
},
|
|
3786
|
-
"description": "Maximum length for the value",
|
|
3787
|
-
"fieldName": "maxlength"
|
|
3788
|
-
},
|
|
3789
|
-
{
|
|
3790
|
-
"name": "pattern",
|
|
3791
|
-
"type": {
|
|
3792
|
-
"text": "string | undefined"
|
|
3793
|
-
},
|
|
3794
|
-
"description": "Pattern for validation (regex)",
|
|
3795
|
-
"fieldName": "pattern"
|
|
3796
|
-
},
|
|
3797
|
-
{
|
|
3798
|
-
"name": "autocomplete",
|
|
3799
|
-
"type": {
|
|
3800
|
-
"text": "string"
|
|
3801
|
-
},
|
|
3802
|
-
"default": "''",
|
|
3803
|
-
"description": "Autocomplete attribute value",
|
|
3804
|
-
"fieldName": "autocomplete"
|
|
3805
|
-
},
|
|
3806
3925
|
{
|
|
3807
3926
|
"name": "hint",
|
|
3808
3927
|
"type": {
|
|
3809
3928
|
"text": "string"
|
|
3810
3929
|
},
|
|
3811
3930
|
"default": "''",
|
|
3812
|
-
"description": "Helper text shown below the
|
|
3931
|
+
"description": "Helper text shown below the select",
|
|
3813
3932
|
"fieldName": "hint"
|
|
3814
3933
|
}
|
|
3815
3934
|
],
|
|
@@ -3817,179 +3936,37 @@
|
|
|
3817
3936
|
"name": "LitElement",
|
|
3818
3937
|
"package": "lit"
|
|
3819
3938
|
},
|
|
3820
|
-
"tagName": "kr-
|
|
3939
|
+
"tagName": "kr-select-field",
|
|
3821
3940
|
"customElement": true
|
|
3822
3941
|
}
|
|
3823
3942
|
],
|
|
3824
|
-
"exports": [
|
|
3825
|
-
{
|
|
3826
|
-
"kind": "js",
|
|
3827
|
-
"name": "KRTextField",
|
|
3828
|
-
"declaration": {
|
|
3829
|
-
"name": "KRTextField",
|
|
3830
|
-
"module": "src/form/text-field/text-field.ts"
|
|
3831
|
-
}
|
|
3832
|
-
},
|
|
3833
|
-
{
|
|
3834
|
-
"kind": "custom-element-definition",
|
|
3835
|
-
"name": "kr-text-field",
|
|
3836
|
-
"declaration": {
|
|
3837
|
-
"name": "KRTextField",
|
|
3838
|
-
"module": "src/form/text-field/text-field.ts"
|
|
3839
|
-
}
|
|
3840
|
-
}
|
|
3841
|
-
]
|
|
3842
|
-
},
|
|
3843
|
-
{
|
|
3844
|
-
"kind": "javascript-module",
|
|
3845
|
-
"path": "dist/form/select-field/select-field-option.js",
|
|
3846
|
-
"declarations": [
|
|
3847
|
-
{
|
|
3848
|
-
"kind": "variable",
|
|
3849
|
-
"name": "KRSelectFieldOption",
|
|
3850
|
-
"default": "class KRSelectFieldOption extends LitElement { constructor() { super(...arguments); /** * The option value */ this.value = ''; /** * Whether the option is disabled */ this.disabled = false; } /** Gets the label text from the slot */ get label() { return this.textContent?.trim() || ''; } render() { return html `<slot></slot>`; } }",
|
|
3851
|
-
"description": "An option for the kr-select-field component."
|
|
3852
|
-
}
|
|
3853
|
-
],
|
|
3854
|
-
"exports": [
|
|
3855
|
-
{
|
|
3856
|
-
"kind": "js",
|
|
3857
|
-
"name": "KRSelectFieldOption",
|
|
3858
|
-
"declaration": {
|
|
3859
|
-
"name": "KRSelectFieldOption",
|
|
3860
|
-
"module": "dist/form/select-field/select-field-option.js"
|
|
3861
|
-
}
|
|
3862
|
-
}
|
|
3863
|
-
]
|
|
3864
|
-
},
|
|
3865
|
-
{
|
|
3866
|
-
"kind": "javascript-module",
|
|
3867
|
-
"path": "dist/form/select-field/select-field.js",
|
|
3868
|
-
"declarations": [
|
|
3869
|
-
{
|
|
3870
|
-
"kind": "variable",
|
|
3871
|
-
"name": "KRSelectField",
|
|
3872
|
-
"default": "class KRSelectField extends LitElement { constructor() { super(); /** * The select label text */ this.label = ''; /** * The input name for form submission */ this.name = ''; /** * The currently selected value */ this.value = ''; /** * Placeholder text when no option is selected */ this.placeholder = 'Select an option'; /** * Whether the select is disabled */ this.disabled = false; /** * Whether the field is required */ this.required = false; /** * Whether the field is readonly */ this.readonly = false; /** * Helper text shown below the select */ this.hint = ''; this._isOpen = false; this._highlightedIndex = -1; this._touched = false; this._handleInvalid = (e) => { e.preventDefault(); this._touched = true; }; this._handleOutsideClick = (e) => { if (!e.composedPath().includes(this)) { this._close(); } }; this._handleKeyDown = (e) => { if (!this._isOpen) return; const options = Array.from(this.querySelectorAll('kr-select-field-option')); switch (e.key) { case 'Escape': this._close(); this._triggerElement?.focus(); break; case 'ArrowDown': e.preventDefault(); if (options.some(o => !o.disabled)) { let newIndex = this._highlightedIndex + 1; while (newIndex < options.length && options[newIndex]?.disabled) newIndex++; if (newIndex < options.length) this._highlightedIndex = newIndex; } break; case 'ArrowUp': e.preventDefault(); { let newIndex = this._highlightedIndex - 1; while (newIndex >= 0 && options[newIndex]?.disabled) newIndex--; if (newIndex >= 0) this._highlightedIndex = newIndex; } break; case 'Enter': e.preventDefault(); if (this._highlightedIndex >= 0 && this._highlightedIndex < options.length) { this._selectOption(options[this._highlightedIndex]); } break; } }; this._internals = this.attachInternals(); } // Form-associated custom element callbacks get form() { return this._internals.form; } get validity() { return this._internals.validity; } get validationMessage() { return this._internals.validationMessage; } get willValidate() { return this._internals.willValidate; } checkValidity() { return this._internals.checkValidity(); } reportValidity() { return this._internals.reportValidity(); } formResetCallback() { this.value = ''; this._touched = false; this._internals.setFormValue(''); this._internals.setValidity({}); } formStateRestoreCallback(state) { this.value = state; } connectedCallback() { super.connectedCallback(); document.addEventListener('click', this._handleOutsideClick); document.addEventListener('keydown', this._handleKeyDown); this.addEventListener('invalid', this._handleInvalid); } firstUpdated() { this._updateValidity(); } updated(changedProperties) { if (changedProperties.has('required') || changedProperties.has('value')) { this._updateValidity(); } } disconnectedCallback() { super.disconnectedCallback(); document.removeEventListener('click', this._handleOutsideClick); document.removeEventListener('keydown', this._handleKeyDown); this.removeEventListener('invalid', this._handleInvalid); } _toggle() { if (this.disabled || this.readonly) return; if (this._isOpen) { this._close(); } else { this._isOpen = true; const options = Array.from(this.querySelectorAll('kr-select-field-option')); this._highlightedIndex = options.findIndex(o => o.value === this.value); } } _close() { this._isOpen = false; this._highlightedIndex = -1; } _selectOption(option) { if (option.disabled) return; this.value = option.value; this._internals.setFormValue(this.value); this._updateValidity(); this.dispatchEvent(new Event('change', { bubbles: true, composed: true })); this._close(); this._triggerElement?.focus(); } _handleBlur() { this._touched = true; this._updateValidity(); } _updateValidity() { if (this.required && !this.value) { this._internals.setValidity({ valueMissing: true }, 'Please select an option', this._triggerElement); } else { this._internals.setValidity({}); } } render() { const options = Array.from(this.querySelectorAll('kr-select-field-option')); const selectedLabel = options.find(o => o.value === this.value)?.label; return html ` <div class=\"wrapper\"> ${this.label ? html ` <label> ${this.label} ${this.required ? html `<span class=\"required\" aria-hidden=\"true\">*</span>` : ''} </label> ` : nothing} <div class=\"select-wrapper\"> <button class=${classMap({ 'select-trigger': true, 'select-trigger--open': this._isOpen, 'select-trigger--invalid': this._touched && this.required && !this.value, })} type=\"button\" ?disabled=${this.disabled} aria-haspopup=\"listbox\" aria-expanded=${this._isOpen} @click=${this._toggle} @blur=${this._handleBlur} > <span class=${classMap({ 'select-value': true, 'select-placeholder': !selectedLabel })}> ${selectedLabel || this.placeholder} </span> <svg class=${classMap({ 'chevron-icon': true, 'select-icon': true, 'select-icon--open': this._isOpen })} viewBox=\"0 0 20 20\" fill=\"currentColor\" > <path fill-rule=\"evenodd\" d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\" clip-rule=\"evenodd\" /> </svg> </button> <div class=${classMap({ 'select-dropdown': true, 'hidden': !this._isOpen })} role=\"listbox\"> <div class=\"select-options\"> ${options.length === 0 ? html `<div class=\"select-empty\">No options available</div>` : options.map((option, idx) => { const isSelected = option.value === this.value; return html ` <div class=${classMap({ 'select-option': true, 'select-option--selected': isSelected, 'select-option--disabled': option.disabled, 'select-option--highlighted': idx === this._highlightedIndex, })} role=\"option\" aria-selected=${isSelected} @click=${() => this._selectOption(option)} @mouseenter=${() => (this._highlightedIndex = idx)} > ${option.label} ${isSelected ? html `<svg class=\"chevron-icon select-check\" viewBox=\"0 0 20 20\" fill=\"currentColor\"> <path fill-rule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clip-rule=\"evenodd\" /> </svg>` : nothing} </div> `; })} </div> </div> </div> ${this._touched && this.required && !this.value ? html `<div class=\"validation-message\">Please select an option</div>` : this.hint ? html `<div class=\"hint\">${this.hint}</div>` : ''} </div> <div class=\"options-slot\"> <slot @slotchange=${() => this.requestUpdate()}></slot> </div> `; } // Public methods for programmatic control focus() { this._triggerElement?.focus(); } blur() { this._triggerElement?.blur(); } }",
|
|
3873
|
-
"description": "A select dropdown component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset."
|
|
3874
|
-
}
|
|
3875
|
-
],
|
|
3876
3943
|
"exports": [
|
|
3877
3944
|
{
|
|
3878
3945
|
"kind": "js",
|
|
3879
3946
|
"name": "KRSelectField",
|
|
3880
3947
|
"declaration": {
|
|
3881
3948
|
"name": "KRSelectField",
|
|
3882
|
-
"module": "
|
|
3883
|
-
}
|
|
3884
|
-
}
|
|
3885
|
-
]
|
|
3886
|
-
},
|
|
3887
|
-
{
|
|
3888
|
-
"kind": "javascript-module",
|
|
3889
|
-
"path": "src/form/select-field/select-field-option.ts",
|
|
3890
|
-
"declarations": [
|
|
3891
|
-
{
|
|
3892
|
-
"kind": "class",
|
|
3893
|
-
"description": "An option for the kr-select-field component.",
|
|
3894
|
-
"name": "KRSelectFieldOption",
|
|
3895
|
-
"slots": [
|
|
3896
|
-
{
|
|
3897
|
-
"description": "The option label content",
|
|
3898
|
-
"name": ""
|
|
3899
|
-
}
|
|
3900
|
-
],
|
|
3901
|
-
"members": [
|
|
3902
|
-
{
|
|
3903
|
-
"kind": "field",
|
|
3904
|
-
"name": "value",
|
|
3905
|
-
"type": {
|
|
3906
|
-
"text": "string"
|
|
3907
|
-
},
|
|
3908
|
-
"default": "''",
|
|
3909
|
-
"description": "The option value",
|
|
3910
|
-
"attribute": "value"
|
|
3911
|
-
},
|
|
3912
|
-
{
|
|
3913
|
-
"kind": "field",
|
|
3914
|
-
"name": "disabled",
|
|
3915
|
-
"type": {
|
|
3916
|
-
"text": "boolean"
|
|
3917
|
-
},
|
|
3918
|
-
"default": "false",
|
|
3919
|
-
"description": "Whether the option is disabled",
|
|
3920
|
-
"attribute": "disabled"
|
|
3921
|
-
},
|
|
3922
|
-
{
|
|
3923
|
-
"kind": "field",
|
|
3924
|
-
"name": "label",
|
|
3925
|
-
"type": {
|
|
3926
|
-
"text": "string"
|
|
3927
|
-
},
|
|
3928
|
-
"description": "Gets the label text from the slot",
|
|
3929
|
-
"readonly": true
|
|
3930
|
-
}
|
|
3931
|
-
],
|
|
3932
|
-
"attributes": [
|
|
3933
|
-
{
|
|
3934
|
-
"name": "value",
|
|
3935
|
-
"type": {
|
|
3936
|
-
"text": "string"
|
|
3937
|
-
},
|
|
3938
|
-
"default": "''",
|
|
3939
|
-
"description": "The option value",
|
|
3940
|
-
"fieldName": "value"
|
|
3941
|
-
},
|
|
3942
|
-
{
|
|
3943
|
-
"name": "disabled",
|
|
3944
|
-
"type": {
|
|
3945
|
-
"text": "boolean"
|
|
3946
|
-
},
|
|
3947
|
-
"default": "false",
|
|
3948
|
-
"description": "Whether the option is disabled",
|
|
3949
|
-
"fieldName": "disabled"
|
|
3950
|
-
}
|
|
3951
|
-
],
|
|
3952
|
-
"superclass": {
|
|
3953
|
-
"name": "LitElement",
|
|
3954
|
-
"package": "lit"
|
|
3955
|
-
},
|
|
3956
|
-
"tagName": "kr-select-field-option",
|
|
3957
|
-
"customElement": true
|
|
3958
|
-
}
|
|
3959
|
-
],
|
|
3960
|
-
"exports": [
|
|
3961
|
-
{
|
|
3962
|
-
"kind": "js",
|
|
3963
|
-
"name": "KRSelectFieldOption",
|
|
3964
|
-
"declaration": {
|
|
3965
|
-
"name": "KRSelectFieldOption",
|
|
3966
|
-
"module": "src/form/select-field/select-field-option.ts"
|
|
3949
|
+
"module": "src/form/select-field/select-field.ts"
|
|
3967
3950
|
}
|
|
3968
3951
|
},
|
|
3969
3952
|
{
|
|
3970
3953
|
"kind": "custom-element-definition",
|
|
3971
|
-
"name": "kr-select-field
|
|
3954
|
+
"name": "kr-select-field",
|
|
3972
3955
|
"declaration": {
|
|
3973
|
-
"name": "
|
|
3974
|
-
"module": "src/form/select-field/select-field
|
|
3956
|
+
"name": "KRSelectField",
|
|
3957
|
+
"module": "src/form/select-field/select-field.ts"
|
|
3975
3958
|
}
|
|
3976
3959
|
}
|
|
3977
3960
|
]
|
|
3978
3961
|
},
|
|
3979
3962
|
{
|
|
3980
3963
|
"kind": "javascript-module",
|
|
3981
|
-
"path": "src/form/
|
|
3964
|
+
"path": "src/form/text-field/text-field.ts",
|
|
3982
3965
|
"declarations": [
|
|
3983
3966
|
{
|
|
3984
3967
|
"kind": "class",
|
|
3985
|
-
"description": "A
|
|
3986
|
-
"name": "
|
|
3987
|
-
"slots": [
|
|
3988
|
-
{
|
|
3989
|
-
"description": "The kr-select-field-option elements",
|
|
3990
|
-
"name": ""
|
|
3991
|
-
}
|
|
3992
|
-
],
|
|
3968
|
+
"description": "A text field component that works with native browser forms.\n\nUses ElementInternals for form association, allowing the component\nto participate in form submission, validation, and reset.\n\nNative input and change events bubble up from the inner input element.",
|
|
3969
|
+
"name": "KRTextField",
|
|
3993
3970
|
"members": [
|
|
3994
3971
|
{
|
|
3995
3972
|
"kind": "field",
|
|
@@ -4015,7 +3992,7 @@
|
|
|
4015
3992
|
"text": "string"
|
|
4016
3993
|
},
|
|
4017
3994
|
"default": "''",
|
|
4018
|
-
"description": "The
|
|
3995
|
+
"description": "The input label text",
|
|
4019
3996
|
"attribute": "label"
|
|
4020
3997
|
},
|
|
4021
3998
|
{
|
|
@@ -4035,7 +4012,7 @@
|
|
|
4035
4012
|
"text": "string"
|
|
4036
4013
|
},
|
|
4037
4014
|
"default": "''",
|
|
4038
|
-
"description": "The
|
|
4015
|
+
"description": "The current value",
|
|
4039
4016
|
"attribute": "value"
|
|
4040
4017
|
},
|
|
4041
4018
|
{
|
|
@@ -4044,19 +4021,19 @@
|
|
|
4044
4021
|
"type": {
|
|
4045
4022
|
"text": "string"
|
|
4046
4023
|
},
|
|
4047
|
-
"default": "'
|
|
4048
|
-
"description": "Placeholder text
|
|
4024
|
+
"default": "''",
|
|
4025
|
+
"description": "Placeholder text",
|
|
4049
4026
|
"attribute": "placeholder"
|
|
4050
4027
|
},
|
|
4051
4028
|
{
|
|
4052
4029
|
"kind": "field",
|
|
4053
|
-
"name": "
|
|
4030
|
+
"name": "type",
|
|
4054
4031
|
"type": {
|
|
4055
|
-
"text": "
|
|
4032
|
+
"text": "'text' | 'email' | 'password' | 'tel' | 'url' | 'search'"
|
|
4056
4033
|
},
|
|
4057
|
-
"default": "
|
|
4058
|
-
"description": "
|
|
4059
|
-
"attribute": "
|
|
4034
|
+
"default": "'text'",
|
|
4035
|
+
"description": "Input type (text, email, password, tel, url, search)",
|
|
4036
|
+
"attribute": "type"
|
|
4060
4037
|
},
|
|
4061
4038
|
{
|
|
4062
4039
|
"kind": "field",
|
|
@@ -4068,6 +4045,16 @@
|
|
|
4068
4045
|
"description": "Whether the field is required",
|
|
4069
4046
|
"attribute": "required"
|
|
4070
4047
|
},
|
|
4048
|
+
{
|
|
4049
|
+
"kind": "field",
|
|
4050
|
+
"name": "disabled",
|
|
4051
|
+
"type": {
|
|
4052
|
+
"text": "boolean"
|
|
4053
|
+
},
|
|
4054
|
+
"default": "false",
|
|
4055
|
+
"description": "Whether the field is disabled",
|
|
4056
|
+
"attribute": "disabled"
|
|
4057
|
+
},
|
|
4071
4058
|
{
|
|
4072
4059
|
"kind": "field",
|
|
4073
4060
|
"name": "readonly",
|
|
@@ -4080,31 +4067,58 @@
|
|
|
4080
4067
|
},
|
|
4081
4068
|
{
|
|
4082
4069
|
"kind": "field",
|
|
4083
|
-
"name": "
|
|
4070
|
+
"name": "minlength",
|
|
4071
|
+
"type": {
|
|
4072
|
+
"text": "number | undefined"
|
|
4073
|
+
},
|
|
4074
|
+
"description": "Minimum length for the value",
|
|
4075
|
+
"attribute": "minlength"
|
|
4076
|
+
},
|
|
4077
|
+
{
|
|
4078
|
+
"kind": "field",
|
|
4079
|
+
"name": "maxlength",
|
|
4080
|
+
"type": {
|
|
4081
|
+
"text": "number | undefined"
|
|
4082
|
+
},
|
|
4083
|
+
"description": "Maximum length for the value",
|
|
4084
|
+
"attribute": "maxlength"
|
|
4085
|
+
},
|
|
4086
|
+
{
|
|
4087
|
+
"kind": "field",
|
|
4088
|
+
"name": "pattern",
|
|
4089
|
+
"type": {
|
|
4090
|
+
"text": "string | undefined"
|
|
4091
|
+
},
|
|
4092
|
+
"description": "Pattern for validation (regex)",
|
|
4093
|
+
"attribute": "pattern"
|
|
4094
|
+
},
|
|
4095
|
+
{
|
|
4096
|
+
"kind": "field",
|
|
4097
|
+
"name": "autocomplete",
|
|
4084
4098
|
"type": {
|
|
4085
4099
|
"text": "string"
|
|
4086
4100
|
},
|
|
4087
4101
|
"default": "''",
|
|
4088
|
-
"description": "
|
|
4089
|
-
"attribute": "
|
|
4102
|
+
"description": "Autocomplete attribute value",
|
|
4103
|
+
"attribute": "autocomplete"
|
|
4090
4104
|
},
|
|
4091
4105
|
{
|
|
4092
4106
|
"kind": "field",
|
|
4093
|
-
"name": "
|
|
4107
|
+
"name": "hint",
|
|
4094
4108
|
"type": {
|
|
4095
|
-
"text": "
|
|
4109
|
+
"text": "string"
|
|
4096
4110
|
},
|
|
4097
|
-
"
|
|
4098
|
-
"
|
|
4111
|
+
"default": "''",
|
|
4112
|
+
"description": "Helper text shown below the input",
|
|
4113
|
+
"attribute": "hint"
|
|
4099
4114
|
},
|
|
4100
4115
|
{
|
|
4101
4116
|
"kind": "field",
|
|
4102
|
-
"name": "
|
|
4117
|
+
"name": "_input",
|
|
4103
4118
|
"type": {
|
|
4104
|
-
"text": "
|
|
4119
|
+
"text": "HTMLInputElement"
|
|
4105
4120
|
},
|
|
4106
|
-
"privacy": "private"
|
|
4107
|
-
"default": "-1"
|
|
4121
|
+
"privacy": "private"
|
|
4108
4122
|
},
|
|
4109
4123
|
{
|
|
4110
4124
|
"kind": "field",
|
|
@@ -4117,11 +4131,12 @@
|
|
|
4117
4131
|
},
|
|
4118
4132
|
{
|
|
4119
4133
|
"kind": "field",
|
|
4120
|
-
"name": "
|
|
4134
|
+
"name": "_dirty",
|
|
4121
4135
|
"type": {
|
|
4122
|
-
"text": "
|
|
4136
|
+
"text": "boolean"
|
|
4123
4137
|
},
|
|
4124
|
-
"privacy": "private"
|
|
4138
|
+
"privacy": "private",
|
|
4139
|
+
"default": "false"
|
|
4125
4140
|
},
|
|
4126
4141
|
{
|
|
4127
4142
|
"kind": "field",
|
|
@@ -4172,35 +4187,33 @@
|
|
|
4172
4187
|
"name": "_handleInvalid",
|
|
4173
4188
|
"privacy": "private"
|
|
4174
4189
|
},
|
|
4175
|
-
{
|
|
4176
|
-
"kind": "field",
|
|
4177
|
-
"name": "_handleOutsideClick",
|
|
4178
|
-
"privacy": "private"
|
|
4179
|
-
},
|
|
4180
|
-
{
|
|
4181
|
-
"kind": "field",
|
|
4182
|
-
"name": "_handleKeyDown",
|
|
4183
|
-
"privacy": "private"
|
|
4184
|
-
},
|
|
4185
4190
|
{
|
|
4186
4191
|
"kind": "method",
|
|
4187
|
-
"name": "
|
|
4192
|
+
"name": "_updateValidity",
|
|
4188
4193
|
"privacy": "private"
|
|
4189
4194
|
},
|
|
4190
4195
|
{
|
|
4191
4196
|
"kind": "method",
|
|
4192
|
-
"name": "
|
|
4193
|
-
"privacy": "private"
|
|
4197
|
+
"name": "_handleInput",
|
|
4198
|
+
"privacy": "private",
|
|
4199
|
+
"parameters": [
|
|
4200
|
+
{
|
|
4201
|
+
"name": "e",
|
|
4202
|
+
"type": {
|
|
4203
|
+
"text": "Event"
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
]
|
|
4194
4207
|
},
|
|
4195
4208
|
{
|
|
4196
4209
|
"kind": "method",
|
|
4197
|
-
"name": "
|
|
4210
|
+
"name": "_handleChange",
|
|
4198
4211
|
"privacy": "private",
|
|
4199
4212
|
"parameters": [
|
|
4200
4213
|
{
|
|
4201
|
-
"name": "
|
|
4214
|
+
"name": "e",
|
|
4202
4215
|
"type": {
|
|
4203
|
-
"text": "
|
|
4216
|
+
"text": "Event"
|
|
4204
4217
|
}
|
|
4205
4218
|
}
|
|
4206
4219
|
]
|
|
@@ -4210,11 +4223,6 @@
|
|
|
4210
4223
|
"name": "_handleBlur",
|
|
4211
4224
|
"privacy": "private"
|
|
4212
4225
|
},
|
|
4213
|
-
{
|
|
4214
|
-
"kind": "method",
|
|
4215
|
-
"name": "_updateValidity",
|
|
4216
|
-
"privacy": "private"
|
|
4217
|
-
},
|
|
4218
4226
|
{
|
|
4219
4227
|
"kind": "method",
|
|
4220
4228
|
"name": "focus"
|
|
@@ -4222,14 +4230,10 @@
|
|
|
4222
4230
|
{
|
|
4223
4231
|
"kind": "method",
|
|
4224
4232
|
"name": "blur"
|
|
4225
|
-
}
|
|
4226
|
-
],
|
|
4227
|
-
"events": [
|
|
4233
|
+
},
|
|
4228
4234
|
{
|
|
4229
|
-
"
|
|
4230
|
-
"
|
|
4231
|
-
"text": "Event"
|
|
4232
|
-
}
|
|
4235
|
+
"kind": "method",
|
|
4236
|
+
"name": "select"
|
|
4233
4237
|
}
|
|
4234
4238
|
],
|
|
4235
4239
|
"attributes": [
|
|
@@ -4239,7 +4243,7 @@
|
|
|
4239
4243
|
"text": "string"
|
|
4240
4244
|
},
|
|
4241
4245
|
"default": "''",
|
|
4242
|
-
"description": "The
|
|
4246
|
+
"description": "The input label text",
|
|
4243
4247
|
"fieldName": "label"
|
|
4244
4248
|
},
|
|
4245
4249
|
{
|
|
@@ -4257,7 +4261,7 @@
|
|
|
4257
4261
|
"text": "string"
|
|
4258
4262
|
},
|
|
4259
4263
|
"default": "''",
|
|
4260
|
-
"description": "The
|
|
4264
|
+
"description": "The current value",
|
|
4261
4265
|
"fieldName": "value"
|
|
4262
4266
|
},
|
|
4263
4267
|
{
|
|
@@ -4265,18 +4269,18 @@
|
|
|
4265
4269
|
"type": {
|
|
4266
4270
|
"text": "string"
|
|
4267
4271
|
},
|
|
4268
|
-
"default": "'
|
|
4269
|
-
"description": "Placeholder text
|
|
4272
|
+
"default": "''",
|
|
4273
|
+
"description": "Placeholder text",
|
|
4270
4274
|
"fieldName": "placeholder"
|
|
4271
4275
|
},
|
|
4272
4276
|
{
|
|
4273
|
-
"name": "
|
|
4277
|
+
"name": "type",
|
|
4274
4278
|
"type": {
|
|
4275
|
-
"text": "
|
|
4279
|
+
"text": "'text' | 'email' | 'password' | 'tel' | 'url' | 'search'"
|
|
4276
4280
|
},
|
|
4277
|
-
"default": "
|
|
4278
|
-
"description": "
|
|
4279
|
-
"fieldName": "
|
|
4281
|
+
"default": "'text'",
|
|
4282
|
+
"description": "Input type (text, email, password, tel, url, search)",
|
|
4283
|
+
"fieldName": "type"
|
|
4280
4284
|
},
|
|
4281
4285
|
{
|
|
4282
4286
|
"name": "required",
|
|
@@ -4287,6 +4291,15 @@
|
|
|
4287
4291
|
"description": "Whether the field is required",
|
|
4288
4292
|
"fieldName": "required"
|
|
4289
4293
|
},
|
|
4294
|
+
{
|
|
4295
|
+
"name": "disabled",
|
|
4296
|
+
"type": {
|
|
4297
|
+
"text": "boolean"
|
|
4298
|
+
},
|
|
4299
|
+
"default": "false",
|
|
4300
|
+
"description": "Whether the field is disabled",
|
|
4301
|
+
"fieldName": "disabled"
|
|
4302
|
+
},
|
|
4290
4303
|
{
|
|
4291
4304
|
"name": "readonly",
|
|
4292
4305
|
"type": {
|
|
@@ -4296,13 +4309,46 @@
|
|
|
4296
4309
|
"description": "Whether the field is readonly",
|
|
4297
4310
|
"fieldName": "readonly"
|
|
4298
4311
|
},
|
|
4312
|
+
{
|
|
4313
|
+
"name": "minlength",
|
|
4314
|
+
"type": {
|
|
4315
|
+
"text": "number | undefined"
|
|
4316
|
+
},
|
|
4317
|
+
"description": "Minimum length for the value",
|
|
4318
|
+
"fieldName": "minlength"
|
|
4319
|
+
},
|
|
4320
|
+
{
|
|
4321
|
+
"name": "maxlength",
|
|
4322
|
+
"type": {
|
|
4323
|
+
"text": "number | undefined"
|
|
4324
|
+
},
|
|
4325
|
+
"description": "Maximum length for the value",
|
|
4326
|
+
"fieldName": "maxlength"
|
|
4327
|
+
},
|
|
4328
|
+
{
|
|
4329
|
+
"name": "pattern",
|
|
4330
|
+
"type": {
|
|
4331
|
+
"text": "string | undefined"
|
|
4332
|
+
},
|
|
4333
|
+
"description": "Pattern for validation (regex)",
|
|
4334
|
+
"fieldName": "pattern"
|
|
4335
|
+
},
|
|
4336
|
+
{
|
|
4337
|
+
"name": "autocomplete",
|
|
4338
|
+
"type": {
|
|
4339
|
+
"text": "string"
|
|
4340
|
+
},
|
|
4341
|
+
"default": "''",
|
|
4342
|
+
"description": "Autocomplete attribute value",
|
|
4343
|
+
"fieldName": "autocomplete"
|
|
4344
|
+
},
|
|
4299
4345
|
{
|
|
4300
4346
|
"name": "hint",
|
|
4301
4347
|
"type": {
|
|
4302
4348
|
"text": "string"
|
|
4303
4349
|
},
|
|
4304
4350
|
"default": "''",
|
|
4305
|
-
"description": "Helper text shown below the
|
|
4351
|
+
"description": "Helper text shown below the input",
|
|
4306
4352
|
"fieldName": "hint"
|
|
4307
4353
|
}
|
|
4308
4354
|
],
|
|
@@ -4310,25 +4356,25 @@
|
|
|
4310
4356
|
"name": "LitElement",
|
|
4311
4357
|
"package": "lit"
|
|
4312
4358
|
},
|
|
4313
|
-
"tagName": "kr-
|
|
4359
|
+
"tagName": "kr-text-field",
|
|
4314
4360
|
"customElement": true
|
|
4315
4361
|
}
|
|
4316
4362
|
],
|
|
4317
4363
|
"exports": [
|
|
4318
4364
|
{
|
|
4319
4365
|
"kind": "js",
|
|
4320
|
-
"name": "
|
|
4366
|
+
"name": "KRTextField",
|
|
4321
4367
|
"declaration": {
|
|
4322
|
-
"name": "
|
|
4323
|
-
"module": "src/form/
|
|
4368
|
+
"name": "KRTextField",
|
|
4369
|
+
"module": "src/form/text-field/text-field.ts"
|
|
4324
4370
|
}
|
|
4325
4371
|
},
|
|
4326
4372
|
{
|
|
4327
4373
|
"kind": "custom-element-definition",
|
|
4328
|
-
"name": "kr-
|
|
4374
|
+
"name": "kr-text-field",
|
|
4329
4375
|
"declaration": {
|
|
4330
|
-
"name": "
|
|
4331
|
-
"module": "src/form/
|
|
4376
|
+
"name": "KRTextField",
|
|
4377
|
+
"module": "src/form/text-field/text-field.ts"
|
|
4332
4378
|
}
|
|
4333
4379
|
}
|
|
4334
4380
|
]
|