@umbraco-ui/uui 2.0.0-rc.0 → 2.0.0-rc.2
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 +38 -2
- package/dist/components/breadcrumbs/breadcrumb-item.element.d.ts +1 -0
- package/dist/components/breadcrumbs/breadcrumb-item.element.js +22 -5
- package/dist/components/breadcrumbs/breadcrumb-item.element.js.map +1 -1
- package/dist/components/input/input.element.js +2 -2
- package/dist/components/input/input.element.js.map +1 -1
- package/dist/components/modal/modal-container.js +2 -6
- package/dist/components/modal/modal-container.js.map +1 -1
- package/dist/components/modal/modal-dialog.element.js +5 -5
- package/dist/components/modal/modal-dialog.element.js.map +1 -1
- package/dist/components/modal/modal-sidebar.element.js +10 -14
- package/dist/components/modal/modal-sidebar.element.js.map +1 -1
- package/dist/components/modal/modal.element.d.ts +1 -3
- package/dist/components/modal/modal.element.js +12 -31
- package/dist/components/modal/modal.element.js.map +1 -1
- package/dist/components/pagination/pagination.element.js +1 -0
- package/dist/components/pagination/pagination.element.js.map +1 -1
- package/dist/components/popover-container/popover-container-nestedslottester.element.d.ts +26 -0
- package/dist/components/popover-container/popover-container.element.d.ts +1 -0
- package/dist/components/popover-container/popover-container.element.js +30 -6
- package/dist/components/popover-container/popover-container.element.js.map +1 -1
- package/dist/components/table/table-row.element.js +2 -2
- package/dist/components/table/table-row.element.js.map +1 -1
- package/dist/components/table/table.element.js +1 -1
- package/dist/components/table/table.element.js.map +1 -1
- package/dist/components/tabs/tab-group.element.js +17 -1
- package/dist/components/tabs/tab-group.element.js.map +1 -1
- package/dist/components/tabs/tab.element.js +3 -3
- package/dist/components/tabs/tab.element.js.map +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +10 -10
- package/vscode.html-custom-data.json +326 -533
- package/dist/components/modal/modal-with-toasts-example.element.d.ts +0 -22
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.element.js","sources":["../../../src/components/pagination/pagination.element.ts"],"sourcesContent":["import type { UUIButtonElement } from '../button/button.js';\nimport { css, html, LitElement } from 'lit';\nimport { property, query, queryAll, state } from 'lit/decorators.js';\n\nimport { UUIPaginationEvent } from './UUIPaginationEvent.js';\n\nimport '../button/button.js';\nimport '../button-group/button-group.js';\n\n//this is how wide the button gets when it has 3 digits inside.\nconst PAGE_BUTTON_MAX_WIDTH = 45;\n\nconst limit = (val: number, min: number, max: number) => {\n return Math.min(Math.max(val, min), max);\n};\n\nconst arrayOfNumbers = (start: number, stop: number) => {\n return Array.from({ length: stop - start + 1 }, (_, i) => start + i);\n};\n\n/**\n * @element uui-pagination\n * @description Jump to a certain page and navigate to the next, last, previous or first page. The amount of visible page-buttons are adjusted to the available space.\n * @fires change - When clicked on the page button fires change event\n */\nexport class UUIPaginationElement extends LitElement {\n private readonly _observer = new ResizeObserver(\n this._calculateRange.bind(this),\n );\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.hasAttribute('role')) this.setAttribute('role', 'navigation');\n this._visiblePages = this._generateVisiblePages(this.current);\n }\n\n disconnectedCallback() {\n this._observer.disconnect();\n }\n\n firstUpdated() {\n this._observer.observe(this._pagesGroup);\n\n this.updateLabel();\n this._calculateRange();\n }\n\n willUpdate(changedProperties: Map<string | number | symbol, unknown>) {\n if (changedProperties.has('current') || changedProperties.has('label')) {\n this.updateLabel();\n }\n }\n\n protected updateLabel() {\n this.ariaLabel = `${this.label || 'Pagination navigation'}. Current page: ${\n this.current\n }.`;\n }\n\n private _calculateRange() {\n const containerWidth = this.offsetWidth;\n\n // get all the buttons with .nav-button class and sum up their widths\n const navButtonsWidth = Array.from(this._navButtons).reduce(\n (totalWidth, button) => {\n return totalWidth + button.getBoundingClientRect().width;\n },\n 0,\n );\n\n // subtract width of nav-buttons from the pagination container\n const rangeBaseWidth = containerWidth - navButtonsWidth;\n\n // divide remaining width by max-width of page button (when it has 3 digits), then divide by 2 to get the range.\n // Range is number of buttons visible on either \"side\" of current pag button. So, if range === 5 we shall see 11 buttons in total - 5 before the current page and 5 after. This is why we divide by 2.\n const range = rangeBaseWidth / PAGE_BUTTON_MAX_WIDTH / 2;\n this._range = Math.max(0, Math.floor(range));\n this._visiblePages = this._generateVisiblePages(this.current);\n }\n\n private _generateVisiblePages(current: number) {\n let start: number;\n if (current < this._range) {\n start = 1;\n } else if (current < this.total - this._range) {\n start = current - this._range;\n } else {\n start = this.total - this._range * 2;\n }\n\n let stop: number;\n if (current <= this._range) {\n stop = this._range * 2 + 1;\n } else if (current < this.total - this._range) {\n stop = current + this._range;\n } else {\n stop = this.total;\n }\n\n const pages = arrayOfNumbers(\n limit(start, 1, this.total),\n limit(stop, 1, this.total),\n );\n\n return pages;\n }\n\n @queryAll('uui-button.nav')\n private readonly _navButtons!: Array<UUIButtonElement>;\n\n @query('#pages')\n private readonly _pagesGroup!: any;\n\n /**\n * This property is used to generate a proper `aria-label`. It will be announced by screen reader as: \"<<this.label>>. Current page: <<this.current>>\"\n * @type {string}\n * @attr\n */\n @property()\n label = '';\n\n /**\n * With this property you can overwrite aria-label.\n * @type {string}\n * @attr\n */\n @property({ reflect: true, attribute: 'aria-label' })\n ariaLabel = '';\n\n /**\n * This property is used to generate the name of the first button\n * @type {string}\n * @attr\n */\n @property()\n firstLabel: string = 'First';\n\n /**\n * This property is used to generate the name of the previous button\n * @type {string}\n * @attr\n */\n @property()\n previousLabel: string = 'Previous';\n\n /**\n * This property is used to generate the name of the next button\n * @type {string}\n * @attr\n */\n @property()\n nextLabel: string = 'Next';\n /**\n * This property is used to generate the name of the last button\n * @type {string}\n * @attr\n */\n @property()\n lastLabel: string = 'Last';\n\n private _total = 100;\n\n /**\n * Set the amount of pages to navigate.\n * @type {number}\n * @attr\n * @default: 1\n */\n @property({ type: Number })\n get total() {\n return this._total;\n }\n set total(newValue: number) {\n this._total = newValue;\n this._visiblePages = this._generateVisiblePages(this._current);\n this.requestUpdate('total', newValue);\n }\n\n @state()\n private _range = 0;\n\n @state()\n private _visiblePages: number[] = [];\n\n private _current = 1;\n\n /**\n * Define the current active page.\n * @type {number}\n * @attr\n */\n @property({ type: Number })\n get current() {\n return this._current;\n }\n set current(newValue: number) {\n const oldValue = this._current;\n this._current = limit(newValue, 1, this.total);\n this._visiblePages = this._generateVisiblePages(this._current);\n this.requestUpdate('current', oldValue);\n }\n\n /**\n * This method will change the page to a next one.\n * @memberof UUIPaginationElement\n */\n goToNextPage() {\n this.current++;\n this.dispatchEvent(new UUIPaginationEvent(UUIPaginationEvent.CHANGE));\n }\n\n /**\n * Change the page to a previous one.\n * @memberof UUIPaginationElement\n */\n goToPreviousPage() {\n this.current--;\n this.dispatchEvent(new UUIPaginationEvent(UUIPaginationEvent.CHANGE));\n }\n\n /**\n * Change the page to the one passed as an argument to this method.\n * @param {number} page\n * @memberof UUIPaginationElement\n */\n goToPage(page: number) {\n this.current = page;\n this.dispatchEvent(new UUIPaginationEvent(UUIPaginationEvent.CHANGE));\n }\n\n /** When having limited display of page-buttons and clicking a page-button that changes the current range, the focus stays on the position of the clicked button which is not anymore representing the number clicked, therefore we move focus to the button that represents the current page. */\n protected focusActivePage() {\n requestAnimationFrame(() => {\n // for none range changing clicks we need to ensure a rendering before querying.\n const activeButtonElement =\n this.renderRoot.querySelector<HTMLElement>('.active');\n if (activeButtonElement) {\n activeButtonElement.focus();\n }\n });\n }\n\n protected renderFirst() {\n return html`<uui-button\n compact\n class=\"nav\"\n label=${this.firstLabel}\n ?disabled=${this._current === 1}\n @click=${() => this.goToPage(1)}></uui-button>`;\n }\n\n protected renderPrevious() {\n return html`<uui-button\n compact\n class=\"nav\"\n label=${this.previousLabel}\n ?disabled=${this._current === 1}\n @click=${this.goToPreviousPage}></uui-button>`;\n }\n\n protected renderNext() {\n return html`<uui-button\n compact\n class=\"nav\"\n label=${this.nextLabel}\n ?disabled=${this._current === this.total}\n @click=${this.goToNextPage}></uui-button>`;\n }\n\n protected renderLast() {\n return html`\n <uui-button\n compact\n class=\"nav\"\n label=${this.lastLabel}\n ?disabled=${this.total === this._current}\n @click=${() => this.goToPage(this.total)}></uui-button>\n `;\n }\n\n protected renderDots() {\n return html`<uui-button\n compact\n look=\"outline\"\n role=\"listitem\"\n tabindex=\"-1\"\n class=\"dots\"\n label=\"More pages\"\n >...</uui-button\n >`;\n }\n\n protected renderPage(page: number) {\n return html`<uui-button\n compact\n look=\"outline\"\n role=\"listitem\"\n label=\"Go to page ${page}\"\n class=${'page' + (page === this._current ? ' active' : '')}\n tabindex=${page === this._current ? '-1' : ''}\n @click=${() => {\n if (page === this._current) return;\n this.goToPage(page);\n this.focusActivePage();\n }}>\n ${page}\n </uui-button>`;\n }\n\n protected renderLeftDots() {\n return html` ${this._visiblePages.includes(1) ? '' : this.renderDots()}`;\n }\n protected renderRightDots() {\n return html`${this._visiblePages.includes(this.total)\n ? ''\n : this.renderDots()}`;\n }\n\n protected renderNavigationLeft() {\n return html` ${this.renderFirst()} ${this.renderPrevious()}`;\n }\n protected renderNavigationRight() {\n return html` ${this.renderNext()} ${this.renderLast()}`;\n }\n\n render() {\n // prettier-ignore\n return html`\n ${this.renderNavigationLeft()}\n <uui-button-group role=\"list\" id=\"pages\">\n ${this.renderLeftDots()}\n ${this._visiblePages.map(\n page =>\n this.renderPage(page)\n )}\n ${this.renderRightDots()}\n </uui-button-group>\n ${this.renderNavigationRight()}\n `;\n }\n\n static override readonly styles = [\n css`\n :host {\n display: inline-flex;\n width: 100%;\n gap: 1px;\n }\n uui-button-group {\n flex-grow: 1;\n }\n\n uui-button-group uui-button {\n --uui-button-border-color: var(--uui-color-border-standalone);\n --uui-button-border-color-hover: var(--uui-color-interactive-emphasis);\n --uui-button-border-color-disabled: var(--uui-color-border-standalone);\n }\n\n uui-button-group uui-button:hover {\n z-index: 1;\n }\n\n .page {\n min-width: 36px;\n max-width: 72px;\n }\n .page.active {\n z-index: 1;\n }\n\n .nav {\n min-width: 72px;\n }\n\n .dots {\n pointer-events: none;\n min-width: 36px;\n max-width: 72px;\n }\n\n .active {\n pointer-events: none;\n --uui-button-font-weight: 700;\n --uui-button-contrast: var(--uui-color-selected);\n --uui-button-border-color: var(--uui-color-selected);\n --uui-button-border-width: 2px;\n }\n `,\n ];\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pagination.element.js","sources":["../../../src/components/pagination/pagination.element.ts"],"sourcesContent":["import type { UUIButtonElement } from '../button/button.js';\nimport { css, html, LitElement } from 'lit';\nimport { property, query, queryAll, state } from 'lit/decorators.js';\n\nimport { UUIPaginationEvent } from './UUIPaginationEvent.js';\n\nimport '../button/button.js';\nimport '../button-group/button-group.js';\n\n//this is how wide the button gets when it has 3 digits inside.\nconst PAGE_BUTTON_MAX_WIDTH = 45;\n\nconst limit = (val: number, min: number, max: number) => {\n return Math.min(Math.max(val, min), max);\n};\n\nconst arrayOfNumbers = (start: number, stop: number) => {\n return Array.from({ length: stop - start + 1 }, (_, i) => start + i);\n};\n\n/**\n * @element uui-pagination\n * @description Jump to a certain page and navigate to the next, last, previous or first page. The amount of visible page-buttons are adjusted to the available space.\n * @fires change - When clicked on the page button fires change event\n */\nexport class UUIPaginationElement extends LitElement {\n private readonly _observer = new ResizeObserver(\n this._calculateRange.bind(this),\n );\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.hasAttribute('role')) this.setAttribute('role', 'navigation');\n this._visiblePages = this._generateVisiblePages(this.current);\n }\n\n disconnectedCallback() {\n this._observer.disconnect();\n }\n\n firstUpdated() {\n this._observer.observe(this._pagesGroup);\n\n this.updateLabel();\n this._calculateRange();\n }\n\n willUpdate(changedProperties: Map<string | number | symbol, unknown>) {\n if (changedProperties.has('current') || changedProperties.has('label')) {\n this.updateLabel();\n }\n }\n\n protected updateLabel() {\n this.ariaLabel = `${this.label || 'Pagination navigation'}. Current page: ${\n this.current\n }.`;\n }\n\n private _calculateRange() {\n const containerWidth = this.offsetWidth;\n\n // get all the buttons with .nav-button class and sum up their widths\n const navButtonsWidth = Array.from(this._navButtons).reduce(\n (totalWidth, button) => {\n return totalWidth + button.getBoundingClientRect().width;\n },\n 0,\n );\n\n // subtract width of nav-buttons from the pagination container\n const rangeBaseWidth = containerWidth - navButtonsWidth;\n\n // divide remaining width by max-width of page button (when it has 3 digits), then divide by 2 to get the range.\n // Range is number of buttons visible on either \"side\" of current pag button. So, if range === 5 we shall see 11 buttons in total - 5 before the current page and 5 after. This is why we divide by 2.\n const range = rangeBaseWidth / PAGE_BUTTON_MAX_WIDTH / 2;\n this._range = Math.max(0, Math.floor(range));\n this._visiblePages = this._generateVisiblePages(this.current);\n }\n\n private _generateVisiblePages(current: number) {\n let start: number;\n if (current < this._range) {\n start = 1;\n } else if (current < this.total - this._range) {\n start = current - this._range;\n } else {\n start = this.total - this._range * 2;\n }\n\n let stop: number;\n if (current <= this._range) {\n stop = this._range * 2 + 1;\n } else if (current < this.total - this._range) {\n stop = current + this._range;\n } else {\n stop = this.total;\n }\n\n const pages = arrayOfNumbers(\n limit(start, 1, this.total),\n limit(stop, 1, this.total),\n );\n\n return pages;\n }\n\n @queryAll('uui-button.nav')\n private readonly _navButtons!: Array<UUIButtonElement>;\n\n @query('#pages')\n private readonly _pagesGroup!: any;\n\n /**\n * This property is used to generate a proper `aria-label`. It will be announced by screen reader as: \"<<this.label>>. Current page: <<this.current>>\"\n * @type {string}\n * @attr\n */\n @property()\n label = '';\n\n /**\n * With this property you can overwrite aria-label.\n * @type {string}\n * @attr\n */\n @property({ reflect: true, attribute: 'aria-label' })\n ariaLabel = '';\n\n /**\n * This property is used to generate the name of the first button\n * @type {string}\n * @attr\n */\n @property()\n firstLabel: string = 'First';\n\n /**\n * This property is used to generate the name of the previous button\n * @type {string}\n * @attr\n */\n @property()\n previousLabel: string = 'Previous';\n\n /**\n * This property is used to generate the name of the next button\n * @type {string}\n * @attr\n */\n @property()\n nextLabel: string = 'Next';\n /**\n * This property is used to generate the name of the last button\n * @type {string}\n * @attr\n */\n @property()\n lastLabel: string = 'Last';\n\n private _total = 100;\n\n /**\n * Set the amount of pages to navigate.\n * @type {number}\n * @attr\n * @default: 1\n */\n @property({ type: Number })\n get total() {\n return this._total;\n }\n set total(newValue: number) {\n this._total = newValue;\n this._visiblePages = this._generateVisiblePages(this._current);\n this.requestUpdate('total', newValue);\n }\n\n @state()\n private _range = 0;\n\n @state()\n private _visiblePages: number[] = [];\n\n private _current = 1;\n\n /**\n * Define the current active page.\n * @type {number}\n * @attr\n */\n @property({ type: Number })\n get current() {\n return this._current;\n }\n set current(newValue: number) {\n const oldValue = this._current;\n this._current = limit(newValue, 1, this.total);\n this._visiblePages = this._generateVisiblePages(this._current);\n this.requestUpdate('current', oldValue);\n }\n\n /**\n * This method will change the page to a next one.\n * @memberof UUIPaginationElement\n */\n goToNextPage() {\n this.current++;\n this.dispatchEvent(new UUIPaginationEvent(UUIPaginationEvent.CHANGE));\n }\n\n /**\n * Change the page to a previous one.\n * @memberof UUIPaginationElement\n */\n goToPreviousPage() {\n this.current--;\n this.dispatchEvent(new UUIPaginationEvent(UUIPaginationEvent.CHANGE));\n }\n\n /**\n * Change the page to the one passed as an argument to this method.\n * @param {number} page\n * @memberof UUIPaginationElement\n */\n goToPage(page: number) {\n this.current = page;\n this.dispatchEvent(new UUIPaginationEvent(UUIPaginationEvent.CHANGE));\n }\n\n /** When having limited display of page-buttons and clicking a page-button that changes the current range, the focus stays on the position of the clicked button which is not anymore representing the number clicked, therefore we move focus to the button that represents the current page. */\n protected focusActivePage() {\n requestAnimationFrame(() => {\n // for none range changing clicks we need to ensure a rendering before querying.\n const activeButtonElement =\n this.renderRoot.querySelector<HTMLElement>('.active');\n if (activeButtonElement) {\n activeButtonElement.focus();\n }\n });\n }\n\n protected renderFirst() {\n return html`<uui-button\n compact\n class=\"nav\"\n label=${this.firstLabel}\n ?disabled=${this._current === 1}\n @click=${() => this.goToPage(1)}></uui-button>`;\n }\n\n protected renderPrevious() {\n return html`<uui-button\n compact\n class=\"nav\"\n label=${this.previousLabel}\n ?disabled=${this._current === 1}\n @click=${this.goToPreviousPage}></uui-button>`;\n }\n\n protected renderNext() {\n return html`<uui-button\n compact\n class=\"nav\"\n label=${this.nextLabel}\n ?disabled=${this._current === this.total}\n @click=${this.goToNextPage}></uui-button>`;\n }\n\n protected renderLast() {\n return html`\n <uui-button\n compact\n class=\"nav\"\n label=${this.lastLabel}\n ?disabled=${this.total === this._current}\n @click=${() => this.goToPage(this.total)}></uui-button>\n `;\n }\n\n protected renderDots() {\n return html`<uui-button\n compact\n look=\"outline\"\n role=\"listitem\"\n tabindex=\"-1\"\n class=\"dots\"\n label=\"More pages\"\n >...</uui-button\n >`;\n }\n\n protected renderPage(page: number) {\n return html`<uui-button\n compact\n look=\"outline\"\n role=\"listitem\"\n label=\"Go to page ${page}\"\n class=${'page' + (page === this._current ? ' active' : '')}\n tabindex=${page === this._current ? '-1' : ''}\n @click=${() => {\n if (page === this._current) return;\n this.goToPage(page);\n this.focusActivePage();\n }}>\n ${page}\n </uui-button>`;\n }\n\n protected renderLeftDots() {\n return html` ${this._visiblePages.includes(1) ? '' : this.renderDots()}`;\n }\n protected renderRightDots() {\n return html`${this._visiblePages.includes(this.total)\n ? ''\n : this.renderDots()}`;\n }\n\n protected renderNavigationLeft() {\n return html` ${this.renderFirst()} ${this.renderPrevious()}`;\n }\n protected renderNavigationRight() {\n return html` ${this.renderNext()} ${this.renderLast()}`;\n }\n\n render() {\n // prettier-ignore\n return html`\n ${this.renderNavigationLeft()}\n <uui-button-group role=\"list\" id=\"pages\">\n ${this.renderLeftDots()}\n ${this._visiblePages.map(\n page =>\n this.renderPage(page)\n )}\n ${this.renderRightDots()}\n </uui-button-group>\n ${this.renderNavigationRight()}\n `;\n }\n\n static override readonly styles = [\n css`\n :host {\n display: inline-flex;\n width: 100%;\n gap: 1px;\n }\n uui-button-group {\n flex-grow: 1;\n justify-content: center;\n }\n\n uui-button-group uui-button {\n --uui-button-border-color: var(--uui-color-border-standalone);\n --uui-button-border-color-hover: var(--uui-color-interactive-emphasis);\n --uui-button-border-color-disabled: var(--uui-color-border-standalone);\n }\n\n uui-button-group uui-button:hover {\n z-index: 1;\n }\n\n .page {\n min-width: 36px;\n max-width: 72px;\n }\n .page.active {\n z-index: 1;\n }\n\n .nav {\n min-width: 72px;\n }\n\n .dots {\n pointer-events: none;\n min-width: 36px;\n max-width: 72px;\n }\n\n .active {\n pointer-events: none;\n --uui-button-font-weight: 700;\n --uui-button-contrast: var(--uui-color-selected);\n --uui-button-border-color: var(--uui-color-selected);\n --uui-button-border-width: 2px;\n }\n `,\n ];\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
declare class UUIPopoverContainerScrollLevelElement extends LitElement {
|
|
3
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Outermost scroll level — blue outline.
|
|
7
|
+
* @element uui-popover-container-scroll-level-1
|
|
8
|
+
*/
|
|
9
|
+
export declare class UUIPopoverContainerScrollLevel1Element extends UUIPopoverContainerScrollLevelElement {
|
|
10
|
+
static readonly styles: import("lit").CSSResult;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Middle scroll level — orange outline.
|
|
14
|
+
* @element uui-popover-container-scroll-level-2
|
|
15
|
+
*/
|
|
16
|
+
export declare class UUIPopoverContainerScrollLevel2Element extends UUIPopoverContainerScrollLevelElement {
|
|
17
|
+
static readonly styles: import("lit").CSSResult;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Innermost scroll level — green outline.
|
|
21
|
+
* @element uui-popover-container-scroll-level-3
|
|
22
|
+
*/
|
|
23
|
+
export declare class UUIPopoverContainerScrollLevel3Element extends UUIPopoverContainerScrollLevelElement {
|
|
24
|
+
static readonly styles: import("lit").CSSResult;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
|
+
import '../scroll-container/scroll-container.js';
|
|
2
3
|
export type PopoverContainerPlacement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'right' | 'right-start' | 'right-end' | 'left' | 'left-start' | 'left-end';
|
|
3
4
|
/**
|
|
4
5
|
* @element uui-popover-container
|
|
@@ -9,6 +9,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
9
9
|
var _targetElement, _scrollParents, _sizeObserver, _size, _onBeforeToggle, _initUpdate, _updatePosition, _UUIPopoverContainerElement_instances, calculateAlignedPosition_fn, clampAndFlip_fn, _updatePadding, flipPlacement_fn, startScrollListener_fn, stopScrollListener_fn, calculateScrollParents_fn, getAncestorElement_fn;
|
|
10
10
|
import { LitElement, html, css } from "lit";
|
|
11
11
|
import { property, state } from "lit/decorators.js";
|
|
12
|
+
import "../scroll-container/scroll-container.js";
|
|
12
13
|
import { findAncestorByAttributeValue } from "../../internal/utils/findAncestorByAttributeValue.js";
|
|
13
14
|
var __defProp = Object.defineProperty;
|
|
14
15
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -62,6 +63,7 @@ const _UUIPopoverContainerElement = class _UUIPopoverContainerElement extends Li
|
|
|
62
63
|
});
|
|
63
64
|
} else {
|
|
64
65
|
__privateMethod(this, _UUIPopoverContainerElement_instances, stopScrollListener_fn).call(this);
|
|
66
|
+
this.style.removeProperty("--_available-height");
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
69
|
__privateSet(this, _initUpdate, () => {
|
|
@@ -86,7 +88,22 @@ const _UUIPopoverContainerElement = class _UUIPopoverContainerElement extends Li
|
|
|
86
88
|
const isCompletelyOutsideScreen = top + popoverRect.height < 0 || top > screenHeight || left + popoverRect.width < 0 || left > screenWidth;
|
|
87
89
|
if (isCompletelyOutsideScreen) {
|
|
88
90
|
this.hidePopover();
|
|
91
|
+
return;
|
|
89
92
|
}
|
|
93
|
+
const isTopPlacement = this._actualPlacement.startsWith("top");
|
|
94
|
+
const isBottomPlacement = this._actualPlacement.startsWith("bottom");
|
|
95
|
+
let availableHeight;
|
|
96
|
+
if (isTopPlacement) {
|
|
97
|
+
availableHeight = targetRect.top - 2 * this.margin;
|
|
98
|
+
} else if (isBottomPlacement) {
|
|
99
|
+
availableHeight = screenHeight - (targetRect.top + targetRect.height) - 2 * this.margin;
|
|
100
|
+
} else {
|
|
101
|
+
availableHeight = screenHeight - targetRect.top - this.margin;
|
|
102
|
+
}
|
|
103
|
+
this.style.setProperty(
|
|
104
|
+
"--_available-height",
|
|
105
|
+
`${Math.max(availableHeight, 0)}px`
|
|
106
|
+
);
|
|
90
107
|
this.style.transform = `translate(${left}px, ${top}px)`;
|
|
91
108
|
this.style.opacity = "1";
|
|
92
109
|
});
|
|
@@ -100,9 +117,9 @@ const _UUIPopoverContainerElement = class _UUIPopoverContainerElement extends Li
|
|
|
100
117
|
let side = this._actualPlacement.split("-")[0];
|
|
101
118
|
side = oppositeSides[side] || side;
|
|
102
119
|
side = side.charAt(0).toUpperCase() + side.slice(1);
|
|
103
|
-
const
|
|
104
|
-
this.style.
|
|
105
|
-
this.style[
|
|
120
|
+
const marginSide = `margin${side}`;
|
|
121
|
+
this.style.margin = "0";
|
|
122
|
+
this.style[marginSide] = `${this.margin}px`;
|
|
106
123
|
});
|
|
107
124
|
this.addEventListener("beforetoggle", __privateGet(this, _onBeforeToggle), {
|
|
108
125
|
passive: true
|
|
@@ -151,7 +168,7 @@ const _UUIPopoverContainerElement = class _UUIPopoverContainerElement extends Li
|
|
|
151
168
|
return __privateGet(this, _scrollParents);
|
|
152
169
|
}
|
|
153
170
|
render() {
|
|
154
|
-
return html`<slot></slot>`;
|
|
171
|
+
return html`<uui-scroll-container><slot></slot></uui-scroll-container>`;
|
|
155
172
|
}
|
|
156
173
|
};
|
|
157
174
|
_targetElement = new WeakMap();
|
|
@@ -283,6 +300,9 @@ calculateScrollParents_fn = function() {
|
|
|
283
300
|
__privateGet(this, _scrollParents).push(document.body);
|
|
284
301
|
};
|
|
285
302
|
getAncestorElement_fn = function(el) {
|
|
303
|
+
if (el?.assignedSlot) {
|
|
304
|
+
return el.assignedSlot;
|
|
305
|
+
}
|
|
286
306
|
if (el?.parentElement) {
|
|
287
307
|
return el.parentElement;
|
|
288
308
|
}
|
|
@@ -295,12 +315,16 @@ _UUIPopoverContainerElement.styles = [
|
|
|
295
315
|
width: fit-content;
|
|
296
316
|
height: fit-content;
|
|
297
317
|
border: none;
|
|
298
|
-
border-radius: 0;
|
|
299
318
|
padding: 0;
|
|
300
319
|
background-color: none;
|
|
301
320
|
background: none;
|
|
302
|
-
overflow: visible;
|
|
303
321
|
color: var(--uui-color-text);
|
|
322
|
+
box-shadow: var(--uui-shadow-depth-4);
|
|
323
|
+
border-radius: var(--uui-border-radius);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
uui-scroll-container {
|
|
327
|
+
max-height: var(--_available-height, none);
|
|
304
328
|
}
|
|
305
329
|
`
|
|
306
330
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover-container.element.js","sources":["../../../src/components/popover-container/popover-container.element.ts"],"sourcesContent":["import { findAncestorByAttributeValue } from '../../internal/utils/index.js';\nimport { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nexport type PopoverContainerPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n | 'left'\n | 'left-start'\n | 'left-end';\n\n/**\n * @element uui-popover-container\n * @attr popover - Indicates that the element is a popover container\n */\nexport class UUIPopoverContainerElement extends LitElement {\n /**\n * Set the distance between popover container element and target element.\n * @type {number}\n * @attr margin\n * @default 0\n */\n @property({ type: Number })\n margin = 0;\n\n /**\n * Read-only attribute to check if the popover is open\n * @type {boolean}\n * @readonly\n * @attr open\n * @default false\n */\n @property({ type: Boolean })\n get open() {\n return this._open;\n }\n\n /**\n * Define the placement of the popover container.\n * @attr placement\n * @default 'bottom-start'\n */\n @property({ type: String, reflect: true })\n get placement(): PopoverContainerPlacement {\n return this._placement;\n }\n set placement(newValue: PopoverContainerPlacement) {\n this._placement = newValue;\n this._actualPlacement = newValue;\n this.#initUpdate();\n }\n\n @state()\n _placement: PopoverContainerPlacement = 'bottom-start';\n\n @state()\n _open: boolean = false;\n\n @state()\n _actualPlacement: PopoverContainerPlacement = this._placement;\n\n #targetElement: HTMLElement | null = null;\n #scrollParents: Element[] = [];\n #sizeObserver: ResizeObserver | null = null;\n #size: { width: number; height: number } = { width: 0, height: 0 };\n\n constructor() {\n super();\n\n this.addEventListener('beforetoggle', this.#onBeforeToggle, {\n passive: true,\n });\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute('popover')) {\n this.setAttribute('popover', '');\n }\n\n if (!this.#sizeObserver) {\n this.#sizeObserver = new ResizeObserver(entries => {\n const element = entries[0]; // should be only one\n const width = element.contentRect.width;\n const height = element.contentRect.height;\n\n if (width === this.#size.width && height === this.#size.height) {\n return; // no change\n }\n\n this.#size = { width, height };\n this.#initUpdate();\n });\n\n // start listening for size changes\n this.#sizeObserver.observe(this);\n }\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#stopScrollListener();\n this.#sizeObserver?.disconnect();\n this.#sizeObserver = null;\n }\n\n readonly #onBeforeToggle = (event: any) => {\n this._open = event.newState === 'open';\n\n this.#targetElement = findAncestorByAttributeValue(\n this,\n 'popovertarget',\n this.id,\n );\n\n // Dispatch a custom event that can be listened to by the popover target.\n // Mostly used for UUIButton.\n this.#targetElement?.dispatchEvent(\n new CustomEvent('uui-popover-before-toggle', {\n bubbles: false,\n composed: false,\n detail: { oldState: event.oldState, newState: event.newState },\n }),\n );\n\n if (this._open) {\n this.#calculateScrollParents();\n\n this.#startScrollListener();\n\n requestAnimationFrame(() => {\n this.#initUpdate();\n });\n } else {\n this.#stopScrollListener();\n }\n };\n\n readonly #initUpdate = () => {\n if (!this._open) return;\n\n this._actualPlacement = this._placement;\n this.style.opacity = '0';\n\n // 3 iterations makes the popover flip back to the initial position if theres no space for it on either side.\n this.#updatePosition(3);\n };\n\n readonly #updatePosition = (iteration: number) => {\n this.#updatePadding();\n\n // Iterations makes sure that we don't overflow the stack.\n // That could happen if the is no space for the popover on either side, which without iterations, would make it flip back and forth until the stack overflows\n iteration--;\n if (this.#targetElement === null) return;\n\n const targetRect = this.#targetElement.getBoundingClientRect();\n const popoverRect = this.getBoundingClientRect();\n\n let { top, left } = this.#calculateAlignedPosition(targetRect, popoverRect);\n const result = this.#clampAndFlip(\n top,\n left,\n targetRect,\n popoverRect,\n iteration,\n );\n\n if (result === null) return; // flipped and recursed\n\n top = result.top;\n left = result.left;\n\n // Detect if the popover is completely outside the screen on any side\n const screenWidth = window.innerWidth;\n const screenHeight = window.innerHeight;\n const isCompletelyOutsideScreen =\n top + popoverRect.height < 0 ||\n top > screenHeight ||\n left + popoverRect.width < 0 ||\n left > screenWidth;\n\n if (isCompletelyOutsideScreen) {\n this.hidePopover();\n }\n\n // Set the popover's position\n this.style.transform = `translate(${left}px, ${top}px)`;\n this.style.opacity = '1';\n };\n\n #calculateAlignedPosition(targetRect: DOMRect, popoverRect: DOMRect) {\n const isStart = this._actualPlacement.includes('-start');\n const isEnd = this._actualPlacement.includes('-end');\n\n // Alignment along the cross-axis\n const align = (pos: number, targetSize: number, popoverSize: number) => {\n if (isStart) return pos;\n if (isEnd) return pos + targetSize - popoverSize;\n return pos + targetSize / 2 - popoverSize / 2;\n };\n\n const side = this._actualPlacement.split('-')[0];\n\n // For top/bottom: top is determined by placement, left by alignment\n // For left/right: left is determined by placement, top by alignment\n const positions: Record<string, { top: number; left: number }> = {\n bottom: {\n top: targetRect.top + targetRect.height,\n left: align(targetRect.left, targetRect.width, popoverRect.width),\n },\n top: {\n top: targetRect.top - popoverRect.height,\n left: align(targetRect.left, targetRect.width, popoverRect.width),\n },\n left: {\n top: align(targetRect.top, targetRect.height, popoverRect.height),\n left: targetRect.left - popoverRect.width,\n },\n right: {\n top: align(targetRect.top, targetRect.height, popoverRect.height),\n left: targetRect.left + targetRect.width,\n },\n };\n\n return positions[side] ?? { top: 0, left: 0 };\n }\n\n #clampAndFlip(\n top: number,\n left: number,\n targetRect: DOMRect,\n popoverRect: DOMRect,\n iteration: number,\n ): { top: number; left: number } | null {\n const screenWidth = window.innerWidth;\n const screenHeight = window.innerHeight;\n const side = this._actualPlacement.split('-')[0];\n const isVertical = side === 'top' || side === 'bottom';\n\n // Vertical clamping\n const topTargetVsScreenTop = Math.min(\n 0,\n targetRect.top + targetRect.height,\n );\n const topTargetVsScreenBottom = Math.max(\n Math.min(top, screenHeight - popoverRect.height),\n targetRect.top - popoverRect.height,\n );\n const topClamped = Math.max(topTargetVsScreenTop, topTargetVsScreenBottom);\n\n if (topClamped !== top && isVertical && iteration > 0) {\n this.#flipPlacement();\n this.#updatePosition(iteration);\n return null;\n }\n\n // Horizontal clamping\n const leftTargetVsScreenLeft = Math.min(\n 0,\n targetRect.left + targetRect.width,\n );\n const leftTargetVsScreenRight = Math.max(\n Math.min(left, screenWidth - popoverRect.width),\n targetRect.left - popoverRect.width,\n );\n const leftClamped = Math.max(\n leftTargetVsScreenLeft,\n leftTargetVsScreenRight,\n );\n\n if (leftClamped !== left && !isVertical && iteration > 0) {\n this.#flipPlacement();\n this.#updatePosition(iteration);\n return null;\n }\n\n return { top: topClamped, left: leftClamped };\n }\n\n readonly #updatePadding = () => {\n const oppositeSides: Record<string, string> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n };\n\n // find the side excluding start/end\n let side = this._actualPlacement.split('-')[0];\n // find the opposite side\n side = oppositeSides[side] || side;\n // capitalize the side\n side = side.charAt(0).toUpperCase() + side.slice(1);\n\n const paddingSide = `padding${side}`;\n this.style.padding = '0';\n (this.style as any)[paddingSide] = `${this.margin}px`;\n };\n\n #flipPlacement() {\n const [direction, position] = this._actualPlacement.split('-');\n const opposites: Record<string, string> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n };\n const oppositeDirection = opposites[direction] ?? direction;\n this._actualPlacement =\n `${oppositeDirection}-${position}` as PopoverContainerPlacement;\n }\n\n #startScrollListener() {\n this.#scrollParents.forEach(el => {\n el.addEventListener('scroll', this.#initUpdate, { passive: true });\n });\n document.addEventListener('scroll', this.#initUpdate, { passive: true });\n }\n #stopScrollListener() {\n this.#scrollParents.forEach(el => {\n el.removeEventListener('scroll', this.#initUpdate);\n });\n document.removeEventListener('scroll', this.#initUpdate);\n }\n\n /**\n * @internal\n */\n _getScrollParents() {\n return this.#scrollParents;\n }\n\n #calculateScrollParents(): void {\n // Clear previous scroll parents to avoid duplicates\n this.#scrollParents = [];\n\n if (!this.#targetElement) return;\n\n let style = getComputedStyle(this.#targetElement);\n if (style.position === 'fixed') {\n return;\n }\n\n const includeHidden = false;\n let excludeStaticParent = style.position === 'absolute';\n const overflowRegex = includeHidden\n ? /(auto|scroll|hidden)/\n : /(auto|scroll)/;\n\n let el: HTMLElement | undefined | null = this.#targetElement;\n while (el) {\n style = getComputedStyle(el);\n\n if (excludeStaticParent && style.position === 'static') {\n el = this.#getAncestorElement(el);\n continue;\n }\n\n if (style.position !== 'static') {\n excludeStaticParent = style.position === 'absolute';\n }\n\n if (\n overflowRegex.test(style.overflow + style.overflowY + style.overflowX)\n ) {\n this.#scrollParents.push(el);\n }\n if (style.position === 'fixed') {\n return;\n }\n\n el = this.#getAncestorElement(el);\n }\n this.#scrollParents.push(document.body);\n }\n\n #getAncestorElement(el: HTMLElement | null): HTMLElement | null {\n if (el?.parentElement) {\n return el.parentElement;\n }\n\n // If we had no parentElement, then check for shadow roots:\n return (el?.getRootNode() as any)?.host;\n }\n\n render() {\n return html`<slot></slot>`;\n }\n\n static override readonly styles = [\n css`\n :host {\n margin: 0;\n width: fit-content;\n height: fit-content;\n border: none;\n border-radius: 0;\n padding: 0;\n background-color: none;\n background: none;\n overflow: visible;\n color: var(--uui-color-text);\n }\n `,\n ];\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"popover-container.element.js","sources":["../../../src/components/popover-container/popover-container.element.ts"],"sourcesContent":["import { findAncestorByAttributeValue } from '../../internal/utils/index.js';\nimport { css, html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport '../scroll-container/scroll-container.js';\n\nexport type PopoverContainerPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n | 'left'\n | 'left-start'\n | 'left-end';\n\n/**\n * @element uui-popover-container\n * @attr popover - Indicates that the element is a popover container\n */\nexport class UUIPopoverContainerElement extends LitElement {\n /**\n * Set the distance between popover container element and target element.\n * @type {number}\n * @attr margin\n * @default 0\n */\n @property({ type: Number })\n margin = 0;\n\n /**\n * Read-only attribute to check if the popover is open\n * @type {boolean}\n * @readonly\n * @attr open\n * @default false\n */\n @property({ type: Boolean })\n get open() {\n return this._open;\n }\n\n /**\n * Define the placement of the popover container.\n * @attr placement\n * @default 'bottom-start'\n */\n @property({ type: String, reflect: true })\n get placement(): PopoverContainerPlacement {\n return this._placement;\n }\n set placement(newValue: PopoverContainerPlacement) {\n this._placement = newValue;\n this._actualPlacement = newValue;\n this.#initUpdate();\n }\n\n @state()\n _placement: PopoverContainerPlacement = 'bottom-start';\n\n @state()\n _open: boolean = false;\n\n @state()\n _actualPlacement: PopoverContainerPlacement = this._placement;\n\n #targetElement: HTMLElement | null = null;\n #scrollParents: Element[] = [];\n #sizeObserver: ResizeObserver | null = null;\n #size: { width: number; height: number } = { width: 0, height: 0 };\n\n constructor() {\n super();\n\n this.addEventListener('beforetoggle', this.#onBeforeToggle, {\n passive: true,\n });\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n if (!this.hasAttribute('popover')) {\n this.setAttribute('popover', '');\n }\n\n if (!this.#sizeObserver) {\n this.#sizeObserver = new ResizeObserver(entries => {\n const element = entries[0]; // should be only one\n const width = element.contentRect.width;\n const height = element.contentRect.height;\n\n if (width === this.#size.width && height === this.#size.height) {\n return; // no change\n }\n\n this.#size = { width, height };\n this.#initUpdate();\n });\n\n // start listening for size changes\n this.#sizeObserver.observe(this);\n }\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#stopScrollListener();\n this.#sizeObserver?.disconnect();\n this.#sizeObserver = null;\n }\n\n readonly #onBeforeToggle = (event: any) => {\n this._open = event.newState === 'open';\n\n this.#targetElement = findAncestorByAttributeValue(\n this,\n 'popovertarget',\n this.id,\n );\n\n // Dispatch a custom event that can be listened to by the popover target.\n // Mostly used for UUIButton.\n this.#targetElement?.dispatchEvent(\n new CustomEvent('uui-popover-before-toggle', {\n bubbles: false,\n composed: false,\n detail: { oldState: event.oldState, newState: event.newState },\n }),\n );\n\n if (this._open) {\n this.#calculateScrollParents();\n\n this.#startScrollListener();\n\n requestAnimationFrame(() => {\n this.#initUpdate();\n });\n } else {\n this.#stopScrollListener();\n this.style.removeProperty('--_available-height');\n }\n };\n\n readonly #initUpdate = () => {\n if (!this._open) return;\n\n this._actualPlacement = this._placement;\n this.style.opacity = '0';\n\n // 3 iterations makes the popover flip back to the initial position if theres no space for it on either side.\n this.#updatePosition(3);\n };\n\n readonly #updatePosition = (iteration: number) => {\n this.#updatePadding();\n\n // Iterations makes sure that we don't overflow the stack.\n // That could happen if the is no space for the popover on either side, which without iterations, would make it flip back and forth until the stack overflows\n iteration--;\n if (this.#targetElement === null) return;\n\n const targetRect = this.#targetElement.getBoundingClientRect();\n const popoverRect = this.getBoundingClientRect();\n\n let { top, left } = this.#calculateAlignedPosition(targetRect, popoverRect);\n const result = this.#clampAndFlip(\n top,\n left,\n targetRect,\n popoverRect,\n iteration,\n );\n\n if (result === null) return; // flipped and recursed\n\n top = result.top;\n left = result.left;\n\n // Detect if the popover is completely outside the screen on any side\n const screenWidth = window.innerWidth;\n const screenHeight = window.innerHeight;\n const isCompletelyOutsideScreen =\n top + popoverRect.height < 0 ||\n top > screenHeight ||\n left + popoverRect.width < 0 ||\n left > screenWidth;\n\n if (isCompletelyOutsideScreen) {\n this.hidePopover();\n return;\n }\n\n // Constrain the slot height to the available space in the popover's growth direction.\n const isTopPlacement = this._actualPlacement.startsWith('top');\n const isBottomPlacement = this._actualPlacement.startsWith('bottom');\n let availableHeight: number;\n if (isTopPlacement) {\n availableHeight = targetRect.top - 2 * this.margin;\n } else if (isBottomPlacement) {\n // margin once for the popover's own padding, once for a safe edge buffer\n availableHeight =\n screenHeight - (targetRect.top + targetRect.height) - 2 * this.margin;\n } else {\n availableHeight = screenHeight - targetRect.top - this.margin;\n }\n this.style.setProperty(\n '--_available-height',\n `${Math.max(availableHeight, 0)}px`,\n );\n\n // Set the popover's position\n this.style.transform = `translate(${left}px, ${top}px)`;\n this.style.opacity = '1';\n };\n\n #calculateAlignedPosition(targetRect: DOMRect, popoverRect: DOMRect) {\n const isStart = this._actualPlacement.includes('-start');\n const isEnd = this._actualPlacement.includes('-end');\n\n // Alignment along the cross-axis\n const align = (pos: number, targetSize: number, popoverSize: number) => {\n if (isStart) return pos;\n if (isEnd) return pos + targetSize - popoverSize;\n return pos + targetSize / 2 - popoverSize / 2;\n };\n\n const side = this._actualPlacement.split('-')[0];\n\n // For top/bottom: top is determined by placement, left by alignment\n // For left/right: left is determined by placement, top by alignment\n const positions: Record<string, { top: number; left: number }> = {\n bottom: {\n top: targetRect.top + targetRect.height,\n left: align(targetRect.left, targetRect.width, popoverRect.width),\n },\n top: {\n top: targetRect.top - popoverRect.height,\n left: align(targetRect.left, targetRect.width, popoverRect.width),\n },\n left: {\n top: align(targetRect.top, targetRect.height, popoverRect.height),\n left: targetRect.left - popoverRect.width,\n },\n right: {\n top: align(targetRect.top, targetRect.height, popoverRect.height),\n left: targetRect.left + targetRect.width,\n },\n };\n\n return positions[side] ?? { top: 0, left: 0 };\n }\n\n #clampAndFlip(\n top: number,\n left: number,\n targetRect: DOMRect,\n popoverRect: DOMRect,\n iteration: number,\n ): { top: number; left: number } | null {\n const screenWidth = window.innerWidth;\n const screenHeight = window.innerHeight;\n const side = this._actualPlacement.split('-')[0];\n const isVertical = side === 'top' || side === 'bottom';\n\n // Vertical clamping\n const topTargetVsScreenTop = Math.min(\n 0,\n targetRect.top + targetRect.height,\n );\n const topTargetVsScreenBottom = Math.max(\n Math.min(top, screenHeight - popoverRect.height),\n targetRect.top - popoverRect.height,\n );\n const topClamped = Math.max(topTargetVsScreenTop, topTargetVsScreenBottom);\n\n if (topClamped !== top && isVertical && iteration > 0) {\n this.#flipPlacement();\n this.#updatePosition(iteration);\n return null;\n }\n\n // Horizontal clamping\n const leftTargetVsScreenLeft = Math.min(\n 0,\n targetRect.left + targetRect.width,\n );\n const leftTargetVsScreenRight = Math.max(\n Math.min(left, screenWidth - popoverRect.width),\n targetRect.left - popoverRect.width,\n );\n const leftClamped = Math.max(\n leftTargetVsScreenLeft,\n leftTargetVsScreenRight,\n );\n\n if (leftClamped !== left && !isVertical && iteration > 0) {\n this.#flipPlacement();\n this.#updatePosition(iteration);\n return null;\n }\n\n return { top: topClamped, left: leftClamped };\n }\n\n readonly #updatePadding = () => {\n const oppositeSides: Record<string, string> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n };\n\n // find the side excluding start/end\n let side = this._actualPlacement.split('-')[0];\n // find the opposite side\n side = oppositeSides[side] || side;\n // capitalize the side\n side = side.charAt(0).toUpperCase() + side.slice(1);\n\n const marginSide = `margin${side}`;\n this.style.margin = '0';\n (this.style as any)[marginSide] = `${this.margin}px`;\n };\n\n #flipPlacement() {\n const [direction, position] = this._actualPlacement.split('-');\n const opposites: Record<string, string> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n };\n const oppositeDirection = opposites[direction] ?? direction;\n this._actualPlacement =\n `${oppositeDirection}-${position}` as PopoverContainerPlacement;\n }\n\n #startScrollListener() {\n this.#scrollParents.forEach(el => {\n el.addEventListener('scroll', this.#initUpdate, { passive: true });\n });\n document.addEventListener('scroll', this.#initUpdate, { passive: true });\n }\n #stopScrollListener() {\n this.#scrollParents.forEach(el => {\n el.removeEventListener('scroll', this.#initUpdate);\n });\n document.removeEventListener('scroll', this.#initUpdate);\n }\n\n /**\n * @internal\n */\n _getScrollParents() {\n return this.#scrollParents;\n }\n\n #calculateScrollParents(): void {\n // Clear previous scroll parents to avoid duplicates\n this.#scrollParents = [];\n\n if (!this.#targetElement) return;\n\n let style = getComputedStyle(this.#targetElement);\n if (style.position === 'fixed') {\n return;\n }\n\n const includeHidden = false;\n let excludeStaticParent = style.position === 'absolute';\n const overflowRegex = includeHidden\n ? /(auto|scroll|hidden)/\n : /(auto|scroll)/;\n\n let el: HTMLElement | undefined | null = this.#targetElement;\n while (el) {\n style = getComputedStyle(el);\n\n if (excludeStaticParent && style.position === 'static') {\n el = this.#getAncestorElement(el);\n continue;\n }\n\n if (style.position !== 'static') {\n excludeStaticParent = style.position === 'absolute';\n }\n\n if (\n overflowRegex.test(style.overflow + style.overflowY + style.overflowX)\n ) {\n this.#scrollParents.push(el);\n }\n if (style.position === 'fixed') {\n return;\n }\n\n el = this.#getAncestorElement(el);\n }\n this.#scrollParents.push(document.body);\n }\n\n #getAncestorElement(el: HTMLElement | null): HTMLElement | null {\n if (el?.assignedSlot) {\n return el.assignedSlot;\n }\n if (el?.parentElement) {\n return el.parentElement;\n }\n\n // If we had no parentElement, then check for shadow roots:\n return (el?.getRootNode() as any)?.host;\n }\n\n render() {\n return html`<uui-scroll-container><slot></slot></uui-scroll-container>`;\n }\n\n static override readonly styles = [\n css`\n :host {\n margin: 0;\n width: fit-content;\n height: fit-content;\n border: none;\n padding: 0;\n background-color: none;\n background: none;\n color: var(--uui-color-text);\n box-shadow: var(--uui-shadow-depth-4);\n border-radius: var(--uui-border-radius);\n }\n\n uui-scroll-container {\n max-height: var(--_available-height, none);\n }\n `,\n ];\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -41,8 +41,8 @@ _UUITableRowElement.styles = [
|
|
|
41
41
|
:host {
|
|
42
42
|
display: table-row;
|
|
43
43
|
position: relative;
|
|
44
|
-
outline-offset: -
|
|
45
|
-
border-radius: var(--uui-border-radius);
|
|
44
|
+
outline-offset: -4px;
|
|
45
|
+
border-radius: var(--uui-border-radius-3);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
:host([selectable]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-row.element.js","sources":["../../../src/components/table/table-row.element.ts"],"sourcesContent":["import {\n SelectableMixin,\n SelectOnlyMixin,\n} from '../../internal/mixins/index.js';\nimport { css, html, LitElement } from 'lit';\nimport { queryAssignedElements } from 'lit/decorators.js';\n\n/**\n * Table row element with option to set is as selectable. Parent for uui-table-cell. Must be a child of uui-table.\n * @element uui-table-row\n * @slot for <uui-table-cell> elements that should be in the row.\n * @cssprop --uui-table-row-color-selected - overwrite the color of the selected row\n */\nexport class UUITableRowElement extends SelectOnlyMixin(\n SelectableMixin(LitElement),\n) {\n connectedCallback() {\n super.connectedCallback();\n this.setAttribute('role', 'row');\n }\n\n @queryAssignedElements({\n flatten: true,\n selector: 'uui-table-cell, [uui-table-cell], [role=cell]',\n })\n private readonly slotCellNodes?: unknown[];\n\n protected updated(changedProperties: Map<string | number | symbol, unknown>) {\n if (changedProperties.has('selectOnly')) {\n this.updateChildSelectOnly();\n }\n }\n\n private updateChildSelectOnly() {\n if (this.slotCellNodes) {\n this.slotCellNodes.forEach((el: any) => {\n if (el.disableChildInteraction !== undefined) {\n el.disableChildInteraction = this.selectOnly;\n }\n });\n }\n }\n\n render() {\n return html` <slot @slotchanged=${this.updateChildSelectOnly}></slot> `;\n }\n\n static override readonly styles = [\n css`\n :host {\n display: table-row;\n position: relative;\n outline-offset: -
|
|
1
|
+
{"version":3,"file":"table-row.element.js","sources":["../../../src/components/table/table-row.element.ts"],"sourcesContent":["import {\n SelectableMixin,\n SelectOnlyMixin,\n} from '../../internal/mixins/index.js';\nimport { css, html, LitElement } from 'lit';\nimport { queryAssignedElements } from 'lit/decorators.js';\n\n/**\n * Table row element with option to set is as selectable. Parent for uui-table-cell. Must be a child of uui-table.\n * @element uui-table-row\n * @slot for <uui-table-cell> elements that should be in the row.\n * @cssprop --uui-table-row-color-selected - overwrite the color of the selected row\n */\nexport class UUITableRowElement extends SelectOnlyMixin(\n SelectableMixin(LitElement),\n) {\n connectedCallback() {\n super.connectedCallback();\n this.setAttribute('role', 'row');\n }\n\n @queryAssignedElements({\n flatten: true,\n selector: 'uui-table-cell, [uui-table-cell], [role=cell]',\n })\n private readonly slotCellNodes?: unknown[];\n\n protected updated(changedProperties: Map<string | number | symbol, unknown>) {\n if (changedProperties.has('selectOnly')) {\n this.updateChildSelectOnly();\n }\n }\n\n private updateChildSelectOnly() {\n if (this.slotCellNodes) {\n this.slotCellNodes.forEach((el: any) => {\n if (el.disableChildInteraction !== undefined) {\n el.disableChildInteraction = this.selectOnly;\n }\n });\n }\n }\n\n render() {\n return html` <slot @slotchanged=${this.updateChildSelectOnly}></slot> `;\n }\n\n static override readonly styles = [\n css`\n :host {\n display: table-row;\n position: relative;\n outline-offset: -4px;\n border-radius: var(--uui-border-radius-3);\n }\n\n :host([selectable]) {\n cursor: pointer;\n }\n\n :host(:focus-visible) {\n outline: 2px solid var(--uui-color-focus);\n }\n :host([selected]) {\n outline: 2px solid\n var(--uui-table-row-color-selected, var(--uui-color-selected));\n }\n :host([selected]:focus) {\n outline-color: var(--uui-color-focus);\n }\n `,\n ];\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAaO,MAAM,sBAAN,MAAM,4BAA2B;AAAA,EACtC,gBAAgB,UAAU;AAC5B,EAAE;AAAA,EACA,oBAAoB;AAClB,UAAM,kBAAA;AACN,SAAK,aAAa,QAAQ,KAAK;AAAA,EACjC;AAAA,EAQU,QAAQ,mBAA2D;AAC3E,QAAI,kBAAkB,IAAI,YAAY,GAAG;AACvC,WAAK,sBAAA;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,wBAAwB;AAC9B,QAAI,KAAK,eAAe;AACtB,WAAK,cAAc,QAAQ,CAAC,OAAY;AACtC,YAAI,GAAG,4BAA4B,QAAW;AAC5C,aAAG,0BAA0B,KAAK;AAAA,QACpC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,SAAS;AACP,WAAO,2BAA2B,KAAK,qBAAqB;AAAA,EAC9D;AA2BF;AAzBE,oBAAyB,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAnCG,IAAM,qBAAN;AAYY,gBAAA;AAAA,EAJhB,sBAAsB;AAAA,IACrB,SAAS;AAAA,IACT,UAAU;AAAA,EAAA,CACX;AAAA,GAXU,mBAYM,WAAA,eAAA;"}
|
|
@@ -15,8 +15,8 @@ _UUITableElement.styles = [
|
|
|
15
15
|
display: table;
|
|
16
16
|
width: 100%;
|
|
17
17
|
background-color: var(--uui-color-surface);
|
|
18
|
-
cursor: default;
|
|
19
18
|
|
|
19
|
+
overflow: clip;
|
|
20
20
|
border-radius: var(--uui-border-radius-3);
|
|
21
21
|
border-width: var(--uui-box-border-width, 1px);
|
|
22
22
|
border-style: solid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.element.js","sources":["../../../src/components/table/table.element.ts"],"sourcesContent":["import { css, html, LitElement } from 'lit';\n\n/**\n * Recreation of native table and it's child elements. `<uui-table>` is a parent element to `<uui-table-head>` `<and uui-table-row>`. To make it fully accessible remember to add aria-label and aria-describedby.\n * @element uui-table\n * @slot - slot for `<uui-table-head>` and `<uui-table-row>` elements. Make a table out of them.\n */\nexport class UUITableElement extends LitElement {\n /* consider select-only attribute on this level? */\n\n connectedCallback() {\n super.connectedCallback();\n this.setAttribute('role', 'table');\n }\n\n render() {\n return html`<slot></slot>`;\n }\n\n static override readonly styles = [\n css`\n :host {\n display: table;\n width: 100%;\n background-color: var(--uui-color-surface);\n
|
|
1
|
+
{"version":3,"file":"table.element.js","sources":["../../../src/components/table/table.element.ts"],"sourcesContent":["import { css, html, LitElement } from 'lit';\n\n/**\n * Recreation of native table and it's child elements. `<uui-table>` is a parent element to `<uui-table-head>` `<and uui-table-row>`. To make it fully accessible remember to add aria-label and aria-describedby.\n * @element uui-table\n * @slot - slot for `<uui-table-head>` and `<uui-table-row>` elements. Make a table out of them.\n */\nexport class UUITableElement extends LitElement {\n /* consider select-only attribute on this level? */\n\n connectedCallback() {\n super.connectedCallback();\n this.setAttribute('role', 'table');\n }\n\n render() {\n return html`<slot></slot>`;\n }\n\n static override readonly styles = [\n css`\n :host {\n display: table;\n width: 100%;\n background-color: var(--uui-color-surface);\n\n overflow: clip;\n border-radius: var(--uui-border-radius-3);\n border-width: var(--uui-box-border-width, 1px);\n border-style: solid;\n border-color: var(--uui-color-divider-standalone);\n }\n `,\n ];\n}\n"],"names":[],"mappings":";AAOO,MAAM,mBAAN,MAAM,yBAAwB,WAAW;AAAA;AAAA,EAG9C,oBAAoB;AAClB,UAAM,kBAAA;AACN,SAAK,aAAa,QAAQ,OAAO;AAAA,EACnC;AAAA,EAEA,SAAS;AACP,WAAO;AAAA,EACT;AAiBF;AAfE,iBAAyB,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAbG,IAAM,kBAAN;"}
|
|
@@ -6,7 +6,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
|
|
|
6
6
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
7
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
8
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
-
var _currentGap, _tabElements, _hiddenTabElements, _hiddenTabElementsMap, _visibilityBreakpoints, _resizeObserver, _tabResizeObservers, _breakPointCalculationInProgress, _UUITabGroupElement_instances, initialize_fn, onResize_fn, cleanupTabs_fn, onSlotChange_fn, _onTabClicked, calculateBreakPoints_fn, setTabArray_fn, updateCollapsibleTabs_fn, isElementTabLike_fn;
|
|
9
|
+
var _currentGap, _tabElements, _hiddenTabElements, _hiddenTabElementsMap, _visibilityBreakpoints, _resizeObserver, _tabResizeObservers, _breakPointCalculationInProgress, _UUITabGroupElement_instances, initialize_fn, onResize_fn, cleanupTabs_fn, onSlotChange_fn, _onTabClicked, calculateBreakPoints_fn, setTabArray_fn, updateCollapsibleTabs_fn, isElementTabLike_fn, isElementHrefLike_fn;
|
|
10
10
|
import { LitElement, html, css } from "lit";
|
|
11
11
|
import { query, queryAssignedElements, property } from "lit/decorators.js";
|
|
12
12
|
import { repeat } from "lit/directives/repeat.js";
|
|
@@ -46,6 +46,19 @@ const _UUITabGroupElement = class _UUITabGroupElement extends LitElement {
|
|
|
46
46
|
__privateSet(this, _breakPointCalculationInProgress, false);
|
|
47
47
|
__privateSet(this, _onTabClicked, (e) => {
|
|
48
48
|
const selectedElement = e.currentTarget;
|
|
49
|
+
const isCtrlClick = e.ctrlKey || e.metaKey;
|
|
50
|
+
if (__privateMethod(this, _UUITabGroupElement_instances, isElementHrefLike_fn).call(this, selectedElement) && isCtrlClick) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (selectedElement.classList.contains("hidden-tab") && __privateMethod(this, _UUITabGroupElement_instances, isElementTabLike_fn).call(this, selectedElement)) {
|
|
54
|
+
const original = __privateGet(this, _hiddenTabElementsMap).get(selectedElement);
|
|
55
|
+
if (original) {
|
|
56
|
+
original.click();
|
|
57
|
+
this._moreButtonElement.classList.add("active-inside");
|
|
58
|
+
this._popoverContainerElement.hidePopover();
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
49
62
|
if (__privateMethod(this, _UUITabGroupElement_instances, isElementTabLike_fn).call(this, selectedElement)) {
|
|
50
63
|
selectedElement.active = true;
|
|
51
64
|
const linkedElement = __privateGet(this, _hiddenTabElementsMap).get(selectedElement);
|
|
@@ -225,6 +238,9 @@ updateCollapsibleTabs_fn = function(containerWidth) {
|
|
|
225
238
|
isElementTabLike_fn = function(el) {
|
|
226
239
|
return typeof el === "object" && "active" in el && typeof el.active === "boolean";
|
|
227
240
|
};
|
|
241
|
+
isElementHrefLike_fn = function(el) {
|
|
242
|
+
return typeof el === "object" && "href" in el && typeof el.href === "string" && el.href;
|
|
243
|
+
};
|
|
228
244
|
_UUITabGroupElement.styles = [
|
|
229
245
|
css`
|
|
230
246
|
:host {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-group.element.js","sources":["../../../src/components/tabs/tab-group.element.ts"],"sourcesContent":["import type { UUIButtonElement } from '../button/button.js';\nimport type { UUIPopoverContainerElement } from '../popover-container/popover-container.js';\nimport { css, html, LitElement } from 'lit';\nimport { property, query, queryAssignedElements } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\n\nimport type { UUITabElement } from './tab.element.js';\n\nimport '../button/button.js';\nimport '../symbol-more/symbol-more.js';\nimport '../popover-container/popover-container.js';\n\n/**\n * @element uui-tab-group\n * @slot - Default slot for the tab group\n * @cssprop --uui-tab-group-dropdown-tab-text - Define the tab text color in the dropdown\n * @cssprop --uui-tab-group-dropdown-tab-text-hover - Define the tab text hover color in the dropdown\n * @cssprop --uui-tab-group-dropdown-tab-text-active - Define the tab text active color in the dropdown\n * @cssprop --uui-tab-group-dropdown-background - Define the background color of the dropdown\n * @cssprop --uui-tab-group-gap - Define the gap between elements dropdown. Only pixel values are valid\n */\nexport class UUITabGroupElement extends LitElement {\n @query('#more-button')\n private readonly _moreButtonElement!: UUIButtonElement;\n\n @query('#popover-container')\n private readonly _popoverContainerElement!: UUIPopoverContainerElement;\n\n @query('#main') private readonly _mainElement!: HTMLElement;\n\n @queryAssignedElements({\n flatten: true,\n selector: 'uui-tab, [uui-tab], [role=tab]',\n })\n private readonly _slottedNodes?: HTMLElement[];\n\n /** Stores the current gap used in the breakpoints */\n #currentGap = 0;\n\n /**\n * Set the flex direction of the content of the dropdown.\n * @type {string}\n * @attr\n * @default vertical\n */\n @property({\n type: String,\n reflect: true,\n attribute: 'dropdown-content-direction',\n })\n dropdownContentDirection: 'vertical' | 'horizontal' = 'vertical';\n\n #tabElements: HTMLElement[] = [];\n\n #hiddenTabElements: UUITabElement[] = [];\n readonly #hiddenTabElementsMap: Map<UUITabElement, UUITabElement> = new Map();\n\n #visibilityBreakpoints: number[] = [];\n\n readonly #resizeObserver = new ResizeObserver(this.#onResize.bind(this));\n readonly #tabResizeObservers: ResizeObserver[] = [];\n\n #breakPointCalculationInProgress = false;\n\n connectedCallback() {\n super.connectedCallback();\n this.#initialize();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.#resizeObserver.unobserve(this);\n this.#cleanupTabs();\n }\n\n async #initialize() {\n await this.updateComplete;\n this.#resizeObserver.observe(this._mainElement);\n }\n\n #onResize(entries: ResizeObserverEntry[]) {\n // Check if the gap css custom property has changed.\n const gapCSSVar = Number.parseFloat(\n this.style.getPropertyValue('--uui-tab-group-gap'),\n );\n const newGap = Number.isNaN(gapCSSVar) ? 0 : gapCSSVar;\n if (newGap === this.#currentGap) {\n this.#updateCollapsibleTabs(entries[0].contentBoxSize[0].inlineSize);\n } else {\n this.#calculateBreakPoints();\n }\n }\n\n #cleanupTabs() {\n this.#tabElements.forEach(el => {\n el.removeEventListener('click', this.#onTabClicked);\n this.#tabResizeObservers.forEach(observer => observer.disconnect());\n });\n this.#tabResizeObservers.length = 0;\n this.#visibilityBreakpoints.length = 0;\n }\n\n #onSlotChange() {\n this.#cleanupTabs();\n\n this.#setTabArray();\n\n this.#tabElements.forEach(el => {\n el.addEventListener('click', this.#onTabClicked);\n const observer = new ResizeObserver(\n this.#calculateBreakPoints.bind(this),\n );\n observer.observe(el);\n this.#tabResizeObservers.push(observer);\n });\n }\n\n readonly #onTabClicked = (e: MouseEvent) => {\n const selectedElement = e.currentTarget as HTMLElement;\n if (this.#isElementTabLike(selectedElement)) {\n selectedElement.active = true;\n const linkedElement = this.#hiddenTabElementsMap.get(selectedElement);\n\n if (linkedElement) {\n linkedElement.active = true;\n }\n\n // Reset all other tabs\n const filtered = [\n ...this.#tabElements,\n ...this.#hiddenTabElements,\n ].filter(el => el !== selectedElement && el !== linkedElement);\n\n filtered.forEach(el => {\n if (this.#isElementTabLike(el)) {\n el.active = false;\n }\n });\n\n // Check if there are any active tabs in the dropdown\n const hasActiveHidden = this.#hiddenTabElements.some(\n el => el.active && el !== linkedElement,\n );\n\n if (hasActiveHidden) {\n this._moreButtonElement.classList.add('active-inside');\n } else {\n this._moreButtonElement.classList.remove('active-inside');\n }\n }\n };\n\n async #calculateBreakPoints() {\n if (this.#breakPointCalculationInProgress) return;\n\n // Prevent multiple calculations from happening in the same frame\n this.#breakPointCalculationInProgress = true;\n requestAnimationFrame(() => {\n this.#breakPointCalculationInProgress = false;\n });\n\n // Whenever a tab is added or removed, we need to recalculate the breakpoints\n\n await this.updateComplete; // Wait for the tabs to be rendered\n\n const gapCSSVar = Number.parseFloat(\n this.style.getPropertyValue('--uui-tab-group-gap'),\n );\n const gap = Number.isNaN(gapCSSVar) ? 0 : gapCSSVar;\n this.#currentGap = gap;\n let childrenWidth = 0;\n\n for (let i = 0; i < this.#tabElements.length; i++) {\n this.#tabElements[i].style.display = '';\n childrenWidth += this.#tabElements[i].offsetWidth;\n this.#visibilityBreakpoints[i] = childrenWidth;\n // Add the gap, which will then be included in the next breakpoint:\n childrenWidth += gap;\n }\n\n const tolerance = 2;\n this._mainElement.style.width = childrenWidth - gap + tolerance + 'px';\n\n this.#updateCollapsibleTabs(this._mainElement.offsetWidth);\n }\n\n #setTabArray() {\n this.#tabElements = this._slottedNodes ?? [];\n this.#calculateBreakPoints();\n }\n\n #updateCollapsibleTabs(containerWidth: number) {\n const moreButtonWidth = this._moreButtonElement.offsetWidth;\n\n const containerWithoutButtonWidth = containerWidth - (moreButtonWidth || 0);\n\n // Do the update\n // Reset the hidden tabs\n this.#hiddenTabElements.forEach(el => {\n el.removeEventListener('click', this.#onTabClicked);\n });\n this.#hiddenTabElements = [];\n this.#hiddenTabElementsMap.clear();\n\n let hasActiveTabInDropdown = false;\n\n const len = this.#visibilityBreakpoints.length;\n for (let i = 0; i < len; i++) {\n const breakpoint = this.#visibilityBreakpoints[i];\n const tab = this.#tabElements[i] as UUITabElement;\n\n // If breakpoint is smaller than the container width, then show the tab.\n // If last breakpoint, then we will use the containerWidth, as we do not want to include the more-button in that calculation.\n if (\n breakpoint <=\n (i === len - 1 ? containerWidth : containerWithoutButtonWidth)\n ) {\n // Show this tab:\n tab.style.display = '';\n } else {\n // Make a proxy tab to put in the hidden tabs container and link it to the original tab\n const proxyTab = tab.cloneNode(true) as UUITabElement;\n proxyTab.addEventListener('click', this.#onTabClicked);\n proxyTab.classList.add('hidden-tab');\n proxyTab.style.display = '';\n proxyTab.orientation = this.dropdownContentDirection;\n\n // Link the proxy tab to the original tab\n this.#hiddenTabElementsMap.set(proxyTab, tab);\n this.#hiddenTabElementsMap.set(tab, proxyTab);\n\n this.#hiddenTabElements.push(proxyTab);\n\n tab.style.display = 'none';\n if (tab.active) {\n hasActiveTabInDropdown = true;\n }\n }\n }\n\n if (this.#hiddenTabElements.length === 0) {\n // Hide more button:\n this._moreButtonElement.style.display = 'none';\n // close the popover\n this._popoverContainerElement.hidePopover();\n } else {\n // Show more button:\n this._moreButtonElement.style.display = '';\n }\n\n if (hasActiveTabInDropdown) {\n this._moreButtonElement.classList.add('active-inside');\n } else {\n this._moreButtonElement.classList.remove('active-inside');\n }\n\n this.requestUpdate();\n }\n\n #isElementTabLike(el: any): el is UUITabElement {\n return (\n typeof el === 'object' && 'active' in el && typeof el.active === 'boolean'\n );\n }\n\n render() {\n return html`\n <div id=\"main\">\n <div id=\"grid\" role=\"tablist\">\n <slot @slotchange=${this.#onSlotChange}></slot>\n </div>\n <uui-button\n popovertarget=\"popover-container\"\n style=\"display: none\"\n id=\"more-button\"\n label=\"More\"\n compact>\n <uui-symbol-more></uui-symbol-more>\n </uui-button>\n </div>\n <uui-popover-container\n id=\"popover-container\"\n popover\n placement=\"bottom-end\">\n <div id=\"hidden-tabs-container\" role=\"tablist\">\n ${repeat(this.#hiddenTabElements, el => html`${el}`)}\n </div>\n </uui-popover-container>\n `;\n }\n\n static override readonly styles = [\n css`\n :host {\n min-width: 0;\n display: flex;\n height: 100%;\n }\n\n #main {\n display: flex;\n justify-content: space-between;\n overflow: hidden;\n }\n\n #grid {\n width: 1fr;\n display: flex;\n height: 100%;\n min-height: 48px;\n overflow: hidden;\n text-wrap: nowrap;\n color: var(--uui-tab-text);\n gap: var(--uui-tab-group-gap, 0);\n }\n\n #popover-container {\n --uui-tab-text: var(--uui-tab-group-dropdown-tab-text, unset);\n --uui-tab-text-hover: var(\n --uui-tab-group-dropdown-tab-text-hover,\n unset\n );\n --uui-tab-text-active: var(\n --uui-tab-group-dropdown-tab-text-active,\n unset\n );\n }\n\n ::slotted(*:not(:last-of-type)) {\n border-right: 1px solid var(--uui-tab-divider, none);\n }\n\n .hidden-tab {\n width: 100%;\n }\n\n #hidden-tabs-container {\n width: fit-content;\n display: flex;\n flex-direction: column;\n background-color: var(\n --uui-tab-group-dropdown-background,\n var(--uui-color-surface)\n );\n border-radius: var(--uui-border-radius);\n box-shadow: var(--uui-shadow-depth-3);\n overflow: hidden;\n }\n :host([dropdown-direction='horizontal']) #hidden-tabs-container {\n flex-direction: row;\n }\n\n #more-button {\n position: relative;\n\n --uui-button-contrast: var(--uui-tab-text);\n --uui-button-contrast-hover: var(--uui-tab-text-hover);\n --uui-button-background-color: transparent;\n --uui-button-background-color-hover: transparent;\n }\n #more-button::before {\n content: '';\n position: absolute;\n bottom: 0;\n width: 100%;\n background-color: var(--uui-color-current);\n height: 0px;\n border-radius: var(--uui-border-radius) var(--uui-border-radius) 0 0;\n opacity: 0;\n transition:\n opacity ease-in 120ms,\n height ease-in 120ms;\n }\n #more-button.active-inside::before {\n opacity: 1;\n height: 4px;\n transition:\n opacity 120ms,\n height ease-out 120ms;\n }\n `,\n ];\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tab-group.element.js","sources":["../../../src/components/tabs/tab-group.element.ts"],"sourcesContent":["import type { UUIButtonElement } from '../button/button.js';\nimport type { UUIPopoverContainerElement } from '../popover-container/popover-container.js';\nimport { css, html, LitElement } from 'lit';\nimport { property, query, queryAssignedElements } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\n\nimport type { UUITabElement } from './tab.element.js';\n\nimport '../button/button.js';\nimport '../symbol-more/symbol-more.js';\nimport '../popover-container/popover-container.js';\n\n/**\n * @element uui-tab-group\n * @slot - Default slot for the tab group\n * @cssprop --uui-tab-group-dropdown-tab-text - Define the tab text color in the dropdown\n * @cssprop --uui-tab-group-dropdown-tab-text-hover - Define the tab text hover color in the dropdown\n * @cssprop --uui-tab-group-dropdown-tab-text-active - Define the tab text active color in the dropdown\n * @cssprop --uui-tab-group-dropdown-background - Define the background color of the dropdown\n * @cssprop --uui-tab-group-gap - Define the gap between elements dropdown. Only pixel values are valid\n */\nexport class UUITabGroupElement extends LitElement {\n @query('#more-button')\n private readonly _moreButtonElement!: UUIButtonElement;\n\n @query('#popover-container')\n private readonly _popoverContainerElement!: UUIPopoverContainerElement;\n\n @query('#main') private readonly _mainElement!: HTMLElement;\n\n @queryAssignedElements({\n flatten: true,\n selector: 'uui-tab, [uui-tab], [role=tab]',\n })\n private readonly _slottedNodes?: HTMLElement[];\n\n /** Stores the current gap used in the breakpoints */\n #currentGap = 0;\n\n /**\n * Set the flex direction of the content of the dropdown.\n * @type {string}\n * @attr\n * @default vertical\n */\n @property({\n type: String,\n reflect: true,\n attribute: 'dropdown-content-direction',\n })\n dropdownContentDirection: 'vertical' | 'horizontal' = 'vertical';\n\n #tabElements: HTMLElement[] = [];\n\n #hiddenTabElements: UUITabElement[] = [];\n readonly #hiddenTabElementsMap: Map<UUITabElement, UUITabElement> = new Map();\n\n #visibilityBreakpoints: number[] = [];\n\n readonly #resizeObserver = new ResizeObserver(this.#onResize.bind(this));\n readonly #tabResizeObservers: ResizeObserver[] = [];\n\n #breakPointCalculationInProgress = false;\n\n connectedCallback() {\n super.connectedCallback();\n this.#initialize();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.#resizeObserver.unobserve(this);\n this.#cleanupTabs();\n }\n\n async #initialize() {\n await this.updateComplete;\n this.#resizeObserver.observe(this._mainElement);\n }\n\n #onResize(entries: ResizeObserverEntry[]) {\n // Check if the gap css custom property has changed.\n const gapCSSVar = Number.parseFloat(\n this.style.getPropertyValue('--uui-tab-group-gap'),\n );\n const newGap = Number.isNaN(gapCSSVar) ? 0 : gapCSSVar;\n if (newGap === this.#currentGap) {\n this.#updateCollapsibleTabs(entries[0].contentBoxSize[0].inlineSize);\n } else {\n this.#calculateBreakPoints();\n }\n }\n\n #cleanupTabs() {\n this.#tabElements.forEach(el => {\n el.removeEventListener('click', this.#onTabClicked);\n this.#tabResizeObservers.forEach(observer => observer.disconnect());\n });\n this.#tabResizeObservers.length = 0;\n this.#visibilityBreakpoints.length = 0;\n }\n\n #onSlotChange() {\n this.#cleanupTabs();\n\n this.#setTabArray();\n\n this.#tabElements.forEach(el => {\n el.addEventListener('click', this.#onTabClicked);\n const observer = new ResizeObserver(\n this.#calculateBreakPoints.bind(this),\n );\n observer.observe(el);\n this.#tabResizeObservers.push(observer);\n });\n }\n\n readonly #onTabClicked = (e: MouseEvent) => {\n const selectedElement = e.currentTarget as HTMLElement;\n\n // Don't switch active tabs when a href is being opened in a new browser tab\n const isCtrlClick = e.ctrlKey || e.metaKey;\n if (this.#isElementHrefLike(selectedElement) && isCtrlClick) {\n return;\n }\n\n // If hidden, forward to the original — external listeners fire there\n if (\n selectedElement.classList.contains('hidden-tab') &&\n this.#isElementTabLike(selectedElement)\n ) {\n const original = this.#hiddenTabElementsMap.get(selectedElement);\n if (original) {\n original.click();\n this._moreButtonElement.classList.add('active-inside');\n this._popoverContainerElement.hidePopover();\n }\n return;\n }\n\n if (this.#isElementTabLike(selectedElement)) {\n selectedElement.active = true;\n const linkedElement = this.#hiddenTabElementsMap.get(selectedElement);\n\n if (linkedElement) {\n linkedElement.active = true;\n }\n\n // Reset all other tabs\n const filtered = [\n ...this.#tabElements,\n ...this.#hiddenTabElements,\n ].filter(el => el !== selectedElement && el !== linkedElement);\n\n filtered.forEach(el => {\n if (this.#isElementTabLike(el)) {\n el.active = false;\n }\n });\n\n // Check if there are any active tabs in the dropdown\n const hasActiveHidden = this.#hiddenTabElements.some(\n el => el.active && el !== linkedElement,\n );\n\n if (hasActiveHidden) {\n this._moreButtonElement.classList.add('active-inside');\n } else {\n this._moreButtonElement.classList.remove('active-inside');\n }\n }\n };\n\n async #calculateBreakPoints() {\n if (this.#breakPointCalculationInProgress) return;\n\n // Prevent multiple calculations from happening in the same frame\n this.#breakPointCalculationInProgress = true;\n requestAnimationFrame(() => {\n this.#breakPointCalculationInProgress = false;\n });\n\n // Whenever a tab is added or removed, we need to recalculate the breakpoints\n\n await this.updateComplete; // Wait for the tabs to be rendered\n\n const gapCSSVar = Number.parseFloat(\n this.style.getPropertyValue('--uui-tab-group-gap'),\n );\n const gap = Number.isNaN(gapCSSVar) ? 0 : gapCSSVar;\n this.#currentGap = gap;\n let childrenWidth = 0;\n\n for (let i = 0; i < this.#tabElements.length; i++) {\n this.#tabElements[i].style.display = '';\n childrenWidth += this.#tabElements[i].offsetWidth;\n this.#visibilityBreakpoints[i] = childrenWidth;\n // Add the gap, which will then be included in the next breakpoint:\n childrenWidth += gap;\n }\n\n const tolerance = 2;\n this._mainElement.style.width = childrenWidth - gap + tolerance + 'px';\n\n this.#updateCollapsibleTabs(this._mainElement.offsetWidth);\n }\n\n #setTabArray() {\n this.#tabElements = this._slottedNodes ?? [];\n this.#calculateBreakPoints();\n }\n\n #updateCollapsibleTabs(containerWidth: number) {\n const moreButtonWidth = this._moreButtonElement.offsetWidth;\n\n const containerWithoutButtonWidth = containerWidth - (moreButtonWidth || 0);\n\n // Do the update\n // Reset the hidden tabs\n this.#hiddenTabElements.forEach(el => {\n el.removeEventListener('click', this.#onTabClicked);\n });\n this.#hiddenTabElements = [];\n this.#hiddenTabElementsMap.clear();\n\n let hasActiveTabInDropdown = false;\n\n const len = this.#visibilityBreakpoints.length;\n for (let i = 0; i < len; i++) {\n const breakpoint = this.#visibilityBreakpoints[i];\n const tab = this.#tabElements[i] as UUITabElement;\n\n // If breakpoint is smaller than the container width, then show the tab.\n // If last breakpoint, then we will use the containerWidth, as we do not want to include the more-button in that calculation.\n if (\n breakpoint <=\n (i === len - 1 ? containerWidth : containerWithoutButtonWidth)\n ) {\n // Show this tab:\n tab.style.display = '';\n } else {\n // Make a proxy tab to put in the hidden tabs container and link it to the original tab\n const proxyTab = tab.cloneNode(true) as UUITabElement;\n proxyTab.addEventListener('click', this.#onTabClicked);\n proxyTab.classList.add('hidden-tab');\n proxyTab.style.display = '';\n proxyTab.orientation = this.dropdownContentDirection;\n\n // Link the proxy tab to the original tab\n this.#hiddenTabElementsMap.set(proxyTab, tab);\n this.#hiddenTabElementsMap.set(tab, proxyTab);\n\n this.#hiddenTabElements.push(proxyTab);\n\n tab.style.display = 'none';\n if (tab.active) {\n hasActiveTabInDropdown = true;\n }\n }\n }\n\n if (this.#hiddenTabElements.length === 0) {\n // Hide more button:\n this._moreButtonElement.style.display = 'none';\n // close the popover\n this._popoverContainerElement.hidePopover();\n } else {\n // Show more button:\n this._moreButtonElement.style.display = '';\n }\n\n if (hasActiveTabInDropdown) {\n this._moreButtonElement.classList.add('active-inside');\n } else {\n this._moreButtonElement.classList.remove('active-inside');\n }\n\n this.requestUpdate();\n }\n\n #isElementTabLike(el: any): el is UUITabElement {\n return (\n typeof el === 'object' && 'active' in el && typeof el.active === 'boolean'\n );\n }\n\n #isElementHrefLike(el: any): el is UUITabElement {\n return (\n typeof el === 'object' &&\n 'href' in el &&\n typeof el.href === 'string' &&\n el.href\n );\n }\n\n render() {\n return html`\n <div id=\"main\">\n <div id=\"grid\" role=\"tablist\">\n <slot @slotchange=${this.#onSlotChange}></slot>\n </div>\n <uui-button\n popovertarget=\"popover-container\"\n style=\"display: none\"\n id=\"more-button\"\n label=\"More\"\n compact>\n <uui-symbol-more></uui-symbol-more>\n </uui-button>\n </div>\n <uui-popover-container\n id=\"popover-container\"\n popover\n placement=\"bottom-end\">\n <div id=\"hidden-tabs-container\" role=\"tablist\">\n ${repeat(this.#hiddenTabElements, el => html`${el}`)}\n </div>\n </uui-popover-container>\n `;\n }\n\n static override readonly styles = [\n css`\n :host {\n min-width: 0;\n display: flex;\n height: 100%;\n }\n\n #main {\n display: flex;\n justify-content: space-between;\n overflow: hidden;\n }\n\n #grid {\n width: 1fr;\n display: flex;\n height: 100%;\n min-height: 48px;\n overflow: hidden;\n text-wrap: nowrap;\n color: var(--uui-tab-text);\n gap: var(--uui-tab-group-gap, 0);\n }\n\n #popover-container {\n --uui-tab-text: var(--uui-tab-group-dropdown-tab-text, unset);\n --uui-tab-text-hover: var(\n --uui-tab-group-dropdown-tab-text-hover,\n unset\n );\n --uui-tab-text-active: var(\n --uui-tab-group-dropdown-tab-text-active,\n unset\n );\n }\n\n ::slotted(*:not(:last-of-type)) {\n border-right: 1px solid var(--uui-tab-divider, none);\n }\n\n .hidden-tab {\n width: 100%;\n }\n\n #hidden-tabs-container {\n width: fit-content;\n display: flex;\n flex-direction: column;\n background-color: var(\n --uui-tab-group-dropdown-background,\n var(--uui-color-surface)\n );\n border-radius: var(--uui-border-radius);\n box-shadow: var(--uui-shadow-depth-3);\n overflow: hidden;\n }\n :host([dropdown-direction='horizontal']) #hidden-tabs-container {\n flex-direction: row;\n }\n\n #more-button {\n position: relative;\n\n --uui-button-contrast: var(--uui-tab-text);\n --uui-button-contrast-hover: var(--uui-tab-text-hover);\n --uui-button-background-color: transparent;\n --uui-button-background-color-hover: transparent;\n }\n #more-button::before {\n content: '';\n position: absolute;\n bottom: 0;\n width: 100%;\n background-color: var(--uui-color-current);\n height: 0px;\n border-radius: var(--uui-border-radius) var(--uui-border-radius) 0 0;\n opacity: 0;\n transition:\n opacity ease-in 120ms,\n height ease-in 120ms;\n }\n #more-button.active-inside::before {\n opacity: 1;\n height: 4px;\n transition:\n opacity 120ms,\n height ease-out 120ms;\n }\n `,\n ];\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAqBO,MAAM,sBAAN,MAAM,4BAA2B,WAAW;AAAA,EAA5C,cAAA;AAAA,UAAA,GAAA,SAAA;AAAA;AAgBL;AAeA;AAEA;AACS;AAET;AAES;AACA;AAET;AAuDS;AAhFT,uBAAA,aAAc;AAad,SAAA,2BAAsD;AAEtD,uBAAA,cAA8B,CAAA;AAE9B,uBAAA,oBAAsC,CAAA;AACtC,uBAAS,2CAA+D,IAAA;AAExE,uBAAA,wBAAmC,CAAA;AAEnC,uBAAS,iBAAkB,IAAI,eAAe,sBAAK,4CAAU,KAAK,IAAI,CAAC;AACvE,uBAAS,qBAAwC,CAAA;AAEjD,uBAAA,kCAAmC;AAuDnC,uBAAS,eAAgB,CAAC,MAAkB;AAC1C,YAAM,kBAAkB,EAAE;AAG1B,YAAM,cAAc,EAAE,WAAW,EAAE;AACnC,UAAI,sBAAK,qDAAL,WAAwB,oBAAoB,aAAa;AAC3D;AAAA,MACF;AAGA,UACE,gBAAgB,UAAU,SAAS,YAAY,KAC/C,sBAAK,oDAAL,WAAuB,kBACvB;AACA,cAAM,WAAW,mBAAK,uBAAsB,IAAI,eAAe;AAC/D,YAAI,UAAU;AACZ,mBAAS,MAAA;AACT,eAAK,mBAAmB,UAAU,IAAI,eAAe;AACrD,eAAK,yBAAyB,YAAA;AAAA,QAChC;AACA;AAAA,MACF;AAEA,UAAI,sBAAK,oDAAL,WAAuB,kBAAkB;AAC3C,wBAAgB,SAAS;AACzB,cAAM,gBAAgB,mBAAK,uBAAsB,IAAI,eAAe;AAEpE,YAAI,eAAe;AACjB,wBAAc,SAAS;AAAA,QACzB;AAGA,cAAM,WAAW;AAAA,UACf,GAAG,mBAAK;AAAA,UACR,GAAG,mBAAK;AAAA,QAAA,EACR,OAAO,CAAA,OAAM,OAAO,mBAAmB,OAAO,aAAa;AAE7D,iBAAS,QAAQ,CAAA,OAAM;AACrB,cAAI,sBAAK,oDAAL,WAAuB,KAAK;AAC9B,eAAG,SAAS;AAAA,UACd;AAAA,QACF,CAAC;AAGD,cAAM,kBAAkB,mBAAK,oBAAmB;AAAA,UAC9C,CAAA,OAAM,GAAG,UAAU,OAAO;AAAA,QAAA;AAG5B,YAAI,iBAAiB;AACnB,eAAK,mBAAmB,UAAU,IAAI,eAAe;AAAA,QACvD,OAAO;AACL,eAAK,mBAAmB,UAAU,OAAO,eAAe;AAAA,QAC1D;AAAA,MACF;AAAA,IACF;AAAA,EAAA;AAAA,EA3GA,oBAAoB;AAClB,UAAM,kBAAA;AACN,0BAAK,8CAAL;AAAA,EACF;AAAA,EAEA,uBAAuB;AACrB,UAAM,qBAAA;AACN,uBAAK,iBAAgB,UAAU,IAAI;AACnC,0BAAK,+CAAL;AAAA,EACF;AAAA,EA8NA,SAAS;AACP,WAAO;AAAA;AAAA;AAAA,8BAGmB,sBAAK,+CAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAgBpC,OAAO,mBAAK,qBAAoB,QAAM,OAAO,EAAE,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA,EAI5D;AA6FF;AAvXE;AAeA;AAEA;AACS;AAET;AAES;AACA;AAET;AAzCK;AAsDC,gBAAA,iBAAc;AAClB,QAAM,KAAK;AACX,qBAAK,iBAAgB,QAAQ,KAAK,YAAY;AAChD;AAEA,uBAAU,SAAgC;AAExC,QAAM,YAAY,OAAO;AAAA,IACvB,KAAK,MAAM,iBAAiB,qBAAqB;AAAA,EAAA;AAEnD,QAAM,SAAS,OAAO,MAAM,SAAS,IAAI,IAAI;AAC7C,MAAI,WAAW,mBAAK,cAAa;AAC/B,0BAAK,yDAAL,WAA4B,QAAQ,CAAC,EAAE,eAAe,CAAC,EAAE;AAAA,EAC3D,OAAO;AACL,0BAAK,wDAAL;AAAA,EACF;AACF;AAEA,iBAAA,WAAe;AACb,qBAAK,cAAa,QAAQ,CAAA,OAAM;AAC9B,OAAG,oBAAoB,SAAS,mBAAK,cAAa;AAClD,uBAAK,qBAAoB,QAAQ,CAAA,aAAY,SAAS,YAAY;AAAA,EACpE,CAAC;AACD,qBAAK,qBAAoB,SAAS;AAClC,qBAAK,wBAAuB,SAAS;AACvC;AAEA,kBAAA,WAAgB;AACd,wBAAK,+CAAL;AAEA,wBAAK,+CAAL;AAEA,qBAAK,cAAa,QAAQ,CAAA,OAAM;AAC9B,OAAG,iBAAiB,SAAS,mBAAK,cAAa;AAC/C,UAAM,WAAW,IAAI;AAAA,MACnB,sBAAK,wDAAsB,KAAK,IAAI;AAAA,IAAA;AAEtC,aAAS,QAAQ,EAAE;AACnB,uBAAK,qBAAoB,KAAK,QAAQ;AAAA,EACxC,CAAC;AACH;AAES;AAwDH,0BAAA,iBAAwB;AAC5B,MAAI,mBAAK,kCAAkC;AAG3C,qBAAK,kCAAmC;AACxC,wBAAsB,MAAM;AAC1B,uBAAK,kCAAmC;AAAA,EAC1C,CAAC;AAID,QAAM,KAAK;AAEX,QAAM,YAAY,OAAO;AAAA,IACvB,KAAK,MAAM,iBAAiB,qBAAqB;AAAA,EAAA;AAEnD,QAAM,MAAM,OAAO,MAAM,SAAS,IAAI,IAAI;AAC1C,qBAAK,aAAc;AACnB,MAAI,gBAAgB;AAEpB,WAAS,IAAI,GAAG,IAAI,mBAAK,cAAa,QAAQ,KAAK;AACjD,uBAAK,cAAa,CAAC,EAAE,MAAM,UAAU;AACrC,qBAAiB,mBAAK,cAAa,CAAC,EAAE;AACtC,uBAAK,wBAAuB,CAAC,IAAI;AAEjC,qBAAiB;AAAA,EACnB;AAEA,QAAM,YAAY;AAClB,OAAK,aAAa,MAAM,QAAQ,gBAAgB,MAAM,YAAY;AAElE,wBAAK,yDAAL,WAA4B,KAAK,aAAa;AAChD;AAEA,iBAAA,WAAe;AACb,qBAAK,cAAe,KAAK,iBAAiB,CAAA;AAC1C,wBAAK,wDAAL;AACF;AAEA,oCAAuB,gBAAwB;AAC7C,QAAM,kBAAkB,KAAK,mBAAmB;AAEhD,QAAM,8BAA8B,kBAAkB,mBAAmB;AAIzE,qBAAK,oBAAmB,QAAQ,CAAA,OAAM;AACpC,OAAG,oBAAoB,SAAS,mBAAK,cAAa;AAAA,EACpD,CAAC;AACD,qBAAK,oBAAqB,CAAA;AAC1B,qBAAK,uBAAsB,MAAA;AAE3B,MAAI,yBAAyB;AAE7B,QAAM,MAAM,mBAAK,wBAAuB;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,UAAM,aAAa,mBAAK,wBAAuB,CAAC;AAChD,UAAM,MAAM,mBAAK,cAAa,CAAC;AAI/B,QACE,eACC,MAAM,MAAM,IAAI,iBAAiB,8BAClC;AAEA,UAAI,MAAM,UAAU;AAAA,IACtB,OAAO;AAEL,YAAM,WAAW,IAAI,UAAU,IAAI;AACnC,eAAS,iBAAiB,SAAS,mBAAK,cAAa;AACrD,eAAS,UAAU,IAAI,YAAY;AACnC,eAAS,MAAM,UAAU;AACzB,eAAS,cAAc,KAAK;AAG5B,yBAAK,uBAAsB,IAAI,UAAU,GAAG;AAC5C,yBAAK,uBAAsB,IAAI,KAAK,QAAQ;AAE5C,yBAAK,oBAAmB,KAAK,QAAQ;AAErC,UAAI,MAAM,UAAU;AACpB,UAAI,IAAI,QAAQ;AACd,iCAAyB;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,MAAI,mBAAK,oBAAmB,WAAW,GAAG;AAExC,SAAK,mBAAmB,MAAM,UAAU;AAExC,SAAK,yBAAyB,YAAA;AAAA,EAChC,OAAO;AAEL,SAAK,mBAAmB,MAAM,UAAU;AAAA,EAC1C;AAEA,MAAI,wBAAwB;AAC1B,SAAK,mBAAmB,UAAU,IAAI,eAAe;AAAA,EACvD,OAAO;AACL,SAAK,mBAAmB,UAAU,OAAO,eAAe;AAAA,EAC1D;AAEA,OAAK,cAAA;AACP;AAEA,+BAAkB,IAA8B;AAC9C,SACE,OAAO,OAAO,YAAY,YAAY,MAAM,OAAO,GAAG,WAAW;AAErE;AAEA,gCAAmB,IAA8B;AAC/C,SACE,OAAO,OAAO,YACd,UAAU,MACV,OAAO,GAAG,SAAS,YACnB,GAAG;AAEP;AA4BA,oBAAyB,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA7SG,IAAM,qBAAN;AAEY,gBAAA;AAAA,EADhB,MAAM,cAAc;AAAA,GADV,mBAEM,WAAA,oBAAA;AAGA,gBAAA;AAAA,EADhB,MAAM,oBAAoB;AAAA,GAJhB,mBAKM,WAAA,0BAAA;AAEgB,gBAAA;AAAA,EAAhC,MAAM,OAAO;AAAA,GAPH,mBAOsB,WAAA,cAAA;AAMhB,gBAAA;AAAA,EAJhB,sBAAsB;AAAA,IACrB,SAAS;AAAA,IACT,UAAU;AAAA,EAAA,CACX;AAAA,GAZU,mBAaM,WAAA,eAAA;AAgBjB,gBAAA;AAAA,EALC,SAAS;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,EAAA,CACZ;AAAA,GA5BU,mBA6BX,WAAA,0BAAA;"}
|
|
@@ -179,13 +179,13 @@ __decorateClass([
|
|
|
179
179
|
property({ type: Boolean, reflect: true })
|
|
180
180
|
], UUITabElement.prototype, "disabled");
|
|
181
181
|
__decorateClass([
|
|
182
|
-
property({ type: String })
|
|
182
|
+
property({ type: String, reflect: true })
|
|
183
183
|
], UUITabElement.prototype, "href");
|
|
184
184
|
__decorateClass([
|
|
185
|
-
property({ type: String })
|
|
185
|
+
property({ type: String, reflect: true })
|
|
186
186
|
], UUITabElement.prototype, "target");
|
|
187
187
|
__decorateClass([
|
|
188
|
-
property({ type: String })
|
|
188
|
+
property({ type: String, reflect: true })
|
|
189
189
|
], UUITabElement.prototype, "rel");
|
|
190
190
|
__decorateClass([
|
|
191
191
|
property({ type: String, reflect: true })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab.element.js","sources":["../../../src/components/tabs/tab.element.ts"],"sourcesContent":["import { ActiveMixin, LabelMixin } from '../../internal/mixins/index.js';\nimport { css, html, LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\n\n/**\n * A single tab. Should be put into `<uui-tab-group>`,\n * @element uui-tabs\n * @slot - for label\n * @slot icon - for icon\n * @slot extra - for extra\n * @description - All-round tab-button\n * @cssprop --uui-tab-text - Define the tab text color\n * @cssprop --uui-tab-text-hover - Define the tab text hover color\n * @cssprop --uui-tab-text-active - Define the tab text active color\n * @cssprop --uui-tab-divider - Define the tab dividers color\n * @cssprop --uui-tab-padding-horizontal - Define the tab horizontal padding\n */\nexport class UUITabElement extends ActiveMixin(LabelMixin('', LitElement)) {\n /**\n * Reflects the disabled state of the element. True if tab is disabled. Change this to switch the state programmatically.\n * @type {boolean}\n * @attr\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public disabled = false;\n\n /**\n * Set an href, this will turns the inner button into a anchor tag.\n * @type {string}\n * @attr\n * @default undefined\n */\n @property({ type: String })\n public href?: string;\n\n /**\n * Set an anchor tag target, only used when using href.\n * @type {string}\n * @attr\n * @default undefined\n */\n @property({ type: String })\n public target?: '_blank' | '_parent' | '_self' | '_top';\n\n /**\n * Set the rel attribute for an anchor tag, only used when using href.\n * @type {string}\n * @attr\n * @default undefined\n */\n @property({ type: String })\n public rel?: string;\n\n /**\n * Set the visual orientation of this tab, this changes the look and placement of the active indication.\n * @type {string}\n * @attr\n * @default horizontal\n */\n @property({ type: String, reflect: true })\n public orientation?: 'horizontal' | 'vertical' = 'horizontal';\n\n constructor() {\n super();\n this.addEventListener('click', this.onHostClick);\n }\n\n private onHostClick(e: MouseEvent) {\n if (this.disabled) {\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n render() {\n if (this.href) {\n const rel =\n this.rel ||\n (this.target === '_blank' ? 'noopener noreferrer' : undefined);\n return html`\n <a\n id=\"button\"\n href=${ifDefined(this.disabled ? undefined : this.href)}\n target=${ifDefined(this.target || undefined)}\n rel=${ifDefined(rel)}\n role=\"tab\">\n <slot name=\"icon\"></slot>\n ${this.renderLabel()}\n <slot name=\"extra\"></slot>\n </a>\n `;\n }\n return html`\n <button type=\"button\" id=\"button\" ?disabled=${this.disabled} role=\"tab\">\n <slot name=\"icon\"></slot>\n ${this.renderLabel()}\n <slot name=\"extra\"></slot>\n </button>\n `;\n }\n\n static override readonly styles = [\n css`\n :host {\n color: var(--uui-tab-text, var(--uui-color-interactive));\n font-family: inherit;\n width: fit-content;\n }\n\n #button {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n min-height: var(--uui-size-12);\n min-width: 70px;\n padding: var(--uui-size-3)\n var(--uui-tab-padding-horizontal, var(--uui-size-5));\n border: none;\n font-size: inherit;\n background: none;\n color: inherit;\n cursor: pointer;\n font-family: inherit;\n\n /* for anchor tag: */\n text-decoration: none;\n line-height: normal;\n }\n\n :host([orientation='vertical']) #button {\n min-height: var(--uui-size-14);\n padding: var(--uui-size-2)\n var(--uui-tab-padding-horizontal, var(--uui-size-5));\n }\n\n :host(:not([disabled])) #button:hover {\n color: var(--uui-tab-text-hover, var(--uui-color-default-emphasis));\n }\n\n :host(:not([disabled])) #button:active {\n box-shadow:\n inset 0 2px 4px rgba(0, 0, 0, 0.15),\n 0 1px 2px rgba(0, 0, 0, 0.05);\n }\n\n :host([active]) {\n color: var(--uui-tab-text-active, unset);\n }\n\n :host([disabled]) #button {\n color: var(--uui-color-disabled-contrast);\n cursor: default;\n }\n\n #button::before {\n content: '';\n position: absolute;\n background-color: var(--uui-color-current);\n opacity: 0;\n }\n :host([active]) #button::before {\n opacity: 1;\n }\n\n /* HORIZONTAL */\n :host([orientation='horizontal']) #button::before {\n left: auto;\n right: auto;\n border-radius: var(--uui-border-radius) var(--uui-border-radius) 0 0;\n height: 0px;\n width: calc(100% - 14px);\n bottom: 0;\n transition:\n opacity linear 120ms,\n height ease-in-out 120ms;\n }\n :host([active][orientation='horizontal']) #button::before {\n height: 4px;\n }\n\n /* VERTICAL */\n :host([orientation='vertical']) #button::before {\n top: auto;\n bottom: auto;\n border-radius: 0 var(--uui-border-radius) var(--uui-border-radius) 0;\n height: calc(100% - 12px);\n width: 0px;\n left: 0;\n transition:\n opacity linear 120ms,\n width ease-in-out 120ms;\n }\n :host([active][orientation='vertical']) #button::before {\n width: 4px;\n }\n\n #button:hover::before {\n background-color: var(--uui-color-current-emphasis);\n }\n :host([disabled]) #button::before {\n background-color: var(--uui-color-disabled-standalone);\n }\n\n slot[name='icon']::slotted(*) {\n font-size: 20px;\n margin-bottom: var(--uui-size-2);\n }\n\n slot.label {\n text-align: center;\n display: flex;\n width: 100%;\n flex-direction: column;\n }\n\n :host([orientation='vertical']) slot.label {\n text-align: left;\n }\n `,\n ];\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAkBO,MAAM,iBAAN,MAAM,uBAAsB,YAAY,WAAW,IAAI,UAAU,CAAC,EAAE;AAAA,EA8CzE,cAAc;AACZ,UAAA;AAvCF,SAAO,WAAW;AAoClB,SAAO,cAA0C;AAI/C,SAAK,iBAAiB,SAAS,KAAK,WAAW;AAAA,EACjD;AAAA,EAEQ,YAAY,GAAe;AACjC,QAAI,KAAK,UAAU;AACjB,QAAE,eAAA;AACF,QAAE,yBAAA;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,SAAS;AACP,QAAI,KAAK,MAAM;AACb,YAAM,MACJ,KAAK,QACJ,KAAK,WAAW,WAAW,wBAAwB;AACtD,aAAO;AAAA;AAAA;AAAA,iBAGI,UAAU,KAAK,WAAW,SAAY,KAAK,IAAI,CAAC;AAAA,mBAC9C,UAAU,KAAK,UAAU,MAAS,CAAC;AAAA,gBACtC,UAAU,GAAG,CAAC;AAAA;AAAA;AAAA,YAGlB,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA,IAI1B;AACA,WAAO;AAAA,oDACyC,KAAK,QAAQ;AAAA;AAAA,UAEvD,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA,EAI1B;AA8HF;AA5HE,eAAyB,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAtFG,IAAM,gBAAN;AAQE,gBAAA;AAAA,EADN,SAAS,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,GAP/B,cAQJ,WAAA,UAAA;AASA,gBAAA;AAAA,EADN,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"tab.element.js","sources":["../../../src/components/tabs/tab.element.ts"],"sourcesContent":["import { ActiveMixin, LabelMixin } from '../../internal/mixins/index.js';\nimport { css, html, LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\n\n/**\n * A single tab. Should be put into `<uui-tab-group>`,\n * @element uui-tabs\n * @slot - for label\n * @slot icon - for icon\n * @slot extra - for extra\n * @description - All-round tab-button\n * @cssprop --uui-tab-text - Define the tab text color\n * @cssprop --uui-tab-text-hover - Define the tab text hover color\n * @cssprop --uui-tab-text-active - Define the tab text active color\n * @cssprop --uui-tab-divider - Define the tab dividers color\n * @cssprop --uui-tab-padding-horizontal - Define the tab horizontal padding\n */\nexport class UUITabElement extends ActiveMixin(LabelMixin('', LitElement)) {\n /**\n * Reflects the disabled state of the element. True if tab is disabled. Change this to switch the state programmatically.\n * @type {boolean}\n * @attr\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n public disabled = false;\n\n /**\n * Set an href, this will turns the inner button into a anchor tag.\n * @type {string}\n * @attr\n * @default undefined\n */\n @property({ type: String, reflect: true })\n public href?: string;\n\n /**\n * Set an anchor tag target, only used when using href.\n * @type {string}\n * @attr\n * @default undefined\n */\n @property({ type: String, reflect: true })\n public target?: '_blank' | '_parent' | '_self' | '_top';\n\n /**\n * Set the rel attribute for an anchor tag, only used when using href.\n * @type {string}\n * @attr\n * @default undefined\n */\n @property({ type: String, reflect: true })\n public rel?: string;\n\n /**\n * Set the visual orientation of this tab, this changes the look and placement of the active indication.\n * @type {string}\n * @attr\n * @default horizontal\n */\n @property({ type: String, reflect: true })\n public orientation?: 'horizontal' | 'vertical' = 'horizontal';\n\n constructor() {\n super();\n this.addEventListener('click', this.onHostClick);\n }\n\n private onHostClick(e: MouseEvent) {\n if (this.disabled) {\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n }\n\n render() {\n if (this.href) {\n const rel =\n this.rel ||\n (this.target === '_blank' ? 'noopener noreferrer' : undefined);\n return html`\n <a\n id=\"button\"\n href=${ifDefined(this.disabled ? undefined : this.href)}\n target=${ifDefined(this.target || undefined)}\n rel=${ifDefined(rel)}\n role=\"tab\">\n <slot name=\"icon\"></slot>\n ${this.renderLabel()}\n <slot name=\"extra\"></slot>\n </a>\n `;\n }\n return html`\n <button type=\"button\" id=\"button\" ?disabled=${this.disabled} role=\"tab\">\n <slot name=\"icon\"></slot>\n ${this.renderLabel()}\n <slot name=\"extra\"></slot>\n </button>\n `;\n }\n\n static override readonly styles = [\n css`\n :host {\n color: var(--uui-tab-text, var(--uui-color-interactive));\n font-family: inherit;\n width: fit-content;\n }\n\n #button {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n min-height: var(--uui-size-12);\n min-width: 70px;\n padding: var(--uui-size-3)\n var(--uui-tab-padding-horizontal, var(--uui-size-5));\n border: none;\n font-size: inherit;\n background: none;\n color: inherit;\n cursor: pointer;\n font-family: inherit;\n\n /* for anchor tag: */\n text-decoration: none;\n line-height: normal;\n }\n\n :host([orientation='vertical']) #button {\n min-height: var(--uui-size-14);\n padding: var(--uui-size-2)\n var(--uui-tab-padding-horizontal, var(--uui-size-5));\n }\n\n :host(:not([disabled])) #button:hover {\n color: var(--uui-tab-text-hover, var(--uui-color-default-emphasis));\n }\n\n :host(:not([disabled])) #button:active {\n box-shadow:\n inset 0 2px 4px rgba(0, 0, 0, 0.15),\n 0 1px 2px rgba(0, 0, 0, 0.05);\n }\n\n :host([active]) {\n color: var(--uui-tab-text-active, unset);\n }\n\n :host([disabled]) #button {\n color: var(--uui-color-disabled-contrast);\n cursor: default;\n }\n\n #button::before {\n content: '';\n position: absolute;\n background-color: var(--uui-color-current);\n opacity: 0;\n }\n :host([active]) #button::before {\n opacity: 1;\n }\n\n /* HORIZONTAL */\n :host([orientation='horizontal']) #button::before {\n left: auto;\n right: auto;\n border-radius: var(--uui-border-radius) var(--uui-border-radius) 0 0;\n height: 0px;\n width: calc(100% - 14px);\n bottom: 0;\n transition:\n opacity linear 120ms,\n height ease-in-out 120ms;\n }\n :host([active][orientation='horizontal']) #button::before {\n height: 4px;\n }\n\n /* VERTICAL */\n :host([orientation='vertical']) #button::before {\n top: auto;\n bottom: auto;\n border-radius: 0 var(--uui-border-radius) var(--uui-border-radius) 0;\n height: calc(100% - 12px);\n width: 0px;\n left: 0;\n transition:\n opacity linear 120ms,\n width ease-in-out 120ms;\n }\n :host([active][orientation='vertical']) #button::before {\n width: 4px;\n }\n\n #button:hover::before {\n background-color: var(--uui-color-current-emphasis);\n }\n :host([disabled]) #button::before {\n background-color: var(--uui-color-disabled-standalone);\n }\n\n slot[name='icon']::slotted(*) {\n font-size: 20px;\n margin-bottom: var(--uui-size-2);\n }\n\n slot.label {\n text-align: center;\n display: flex;\n width: 100%;\n flex-direction: column;\n }\n\n :host([orientation='vertical']) slot.label {\n text-align: left;\n }\n `,\n ];\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAkBO,MAAM,iBAAN,MAAM,uBAAsB,YAAY,WAAW,IAAI,UAAU,CAAC,EAAE;AAAA,EA8CzE,cAAc;AACZ,UAAA;AAvCF,SAAO,WAAW;AAoClB,SAAO,cAA0C;AAI/C,SAAK,iBAAiB,SAAS,KAAK,WAAW;AAAA,EACjD;AAAA,EAEQ,YAAY,GAAe;AACjC,QAAI,KAAK,UAAU;AACjB,QAAE,eAAA;AACF,QAAE,yBAAA;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,SAAS;AACP,QAAI,KAAK,MAAM;AACb,YAAM,MACJ,KAAK,QACJ,KAAK,WAAW,WAAW,wBAAwB;AACtD,aAAO;AAAA;AAAA;AAAA,iBAGI,UAAU,KAAK,WAAW,SAAY,KAAK,IAAI,CAAC;AAAA,mBAC9C,UAAU,KAAK,UAAU,MAAS,CAAC;AAAA,gBACtC,UAAU,GAAG,CAAC;AAAA;AAAA;AAAA,YAGlB,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA,IAI1B;AACA,WAAO;AAAA,oDACyC,KAAK,QAAQ;AAAA;AAAA,UAEvD,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA,EAI1B;AA8HF;AA5HE,eAAyB,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAtFG,IAAM,gBAAN;AAQE,gBAAA;AAAA,EADN,SAAS,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,GAP/B,cAQJ,WAAA,UAAA;AASA,gBAAA;AAAA,EADN,SAAS,EAAE,MAAM,QAAQ,SAAS,MAAM;AAAA,GAhB9B,cAiBJ,WAAA,MAAA;AASA,gBAAA;AAAA,EADN,SAAS,EAAE,MAAM,QAAQ,SAAS,MAAM;AAAA,GAzB9B,cA0BJ,WAAA,QAAA;AASA,gBAAA;AAAA,EADN,SAAS,EAAE,MAAM,QAAQ,SAAS,MAAM;AAAA,GAlC9B,cAmCJ,WAAA,KAAA;AASA,gBAAA;AAAA,EADN,SAAS,EAAE,MAAM,QAAQ,SAAS,MAAM;AAAA,GA3C9B,cA4CJ,WAAA,aAAA;"}
|
package/dist/package.json.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umbraco-ui/uui",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.2",
|
|
4
4
|
"description": "The Umbraco UI Library - web components for the Umbraco backoffice.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -92,10 +92,10 @@
|
|
|
92
92
|
"@lerna-lite/cli": "5.2.1",
|
|
93
93
|
"@lerna-lite/publish": "5.2.1",
|
|
94
94
|
"@lerna-lite/version": "5.2.1",
|
|
95
|
-
"@storybook/addon-a11y": "^10.
|
|
96
|
-
"@storybook/addon-docs": "^10.
|
|
97
|
-
"@storybook/addon-links": "^10.
|
|
98
|
-
"@storybook/web-components-vite": "^10.
|
|
95
|
+
"@storybook/addon-a11y": "^10.4.0",
|
|
96
|
+
"@storybook/addon-docs": "^10.4.0",
|
|
97
|
+
"@storybook/addon-links": "^10.4.0",
|
|
98
|
+
"@storybook/web-components-vite": "^10.4.0",
|
|
99
99
|
"@testing-library/dom": "10.4.1",
|
|
100
100
|
"@types/culori": "^4.0.1",
|
|
101
101
|
"@types/react": "18.3.1",
|
|
@@ -103,14 +103,14 @@
|
|
|
103
103
|
"@vitest/browser": "^4.1.6",
|
|
104
104
|
"@vitest/browser-playwright": "^4.1.6",
|
|
105
105
|
"axe-core": "^4.11.4",
|
|
106
|
-
"chromatic": "^
|
|
106
|
+
"chromatic": "^17.0.0",
|
|
107
107
|
"eslint": "^10.3.0",
|
|
108
108
|
"eslint-config-prettier": "10.1.8",
|
|
109
109
|
"eslint-plugin-import": "^2.32.0",
|
|
110
110
|
"eslint-plugin-lit": "^2.3.1",
|
|
111
111
|
"eslint-plugin-local-rules": "3.0.2",
|
|
112
112
|
"eslint-plugin-prettier": "5.5.5",
|
|
113
|
-
"eslint-plugin-storybook": "10.
|
|
113
|
+
"eslint-plugin-storybook": "10.4.0",
|
|
114
114
|
"eslint-plugin-wc": "3.1.0",
|
|
115
115
|
"github-markdown-css": "5.9.0",
|
|
116
116
|
"globals": "^17.6.0",
|
|
@@ -126,9 +126,9 @@
|
|
|
126
126
|
"react-syntax-highlighter": "16.1.1",
|
|
127
127
|
"remark-gfm": "4.0.1",
|
|
128
128
|
"rimraf": "6.1.3",
|
|
129
|
-
"storybook": "^10.
|
|
129
|
+
"storybook": "^10.4.0",
|
|
130
130
|
"tsc-files": "1.1.4",
|
|
131
|
-
"typescript": "
|
|
131
|
+
"typescript": "6.0.3",
|
|
132
132
|
"typescript-eslint": "8.56.1",
|
|
133
133
|
"vite": "7.3.2",
|
|
134
134
|
"vitest": "^4.1.6",
|
|
@@ -147,5 +147,5 @@
|
|
|
147
147
|
"url": "https://uui.umbraco.com"
|
|
148
148
|
},
|
|
149
149
|
"packageManager": "npm@11.1.0",
|
|
150
|
-
"gitHead": "
|
|
150
|
+
"gitHead": "e7a55ef69ef9ffd7a99d7e2e0e20b23a2d516da8"
|
|
151
151
|
}
|