From de5d555c7680bf9f8675ca5097c9b0f29e6bd356 Mon Sep 17 00:00:00 2001 From: Aaron Drabeck Date: Thu, 25 Jun 2026 10:46:03 -0600 Subject: [PATCH 01/14] fix(titanium): reflect host styling attributes to match :host selectors Remove dead has-search CSS and add reflect: true to variant properties so host styling applies when set via JS, not just HTML attributes. --- .../web/titanium/chip-multi-select/chip-multi-select.ts | 2 +- packages/web/titanium/chip/chip.ts | 2 +- packages/web/titanium/data-table/data-table-action-bar.ts | 4 ---- packages/web/titanium/data-table/data-table-item.ts | 2 +- packages/web/titanium/data-table/data-table.ts | 2 +- packages/web/titanium/date-input/date-input.ts | 2 +- .../titanium/profile-picture-stack/profile-picture-stack.ts | 2 +- .../web/titanium/single-select-base/single-select-base.ts | 6 +++--- 8 files changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/web/titanium/chip-multi-select/chip-multi-select.ts b/packages/web/titanium/chip-multi-select/chip-multi-select.ts index 4369c808d..a9580d0e4 100644 --- a/packages/web/titanium/chip-multi-select/chip-multi-select.ts +++ b/packages/web/titanium/chip-multi-select/chip-multi-select.ts @@ -20,7 +20,7 @@ export class TitaniumChipMultiSelect extends LitElement { /** * Swaps outlined validator for filled validator */ - @property({ type: Boolean, attribute: 'filled' }) accessor filled: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'filled' }) accessor filled: boolean = false; /** * Label of input to display to users */ diff --git a/packages/web/titanium/chip/chip.ts b/packages/web/titanium/chip/chip.ts index c55229bb4..b3e2bbd60 100644 --- a/packages/web/titanium/chip/chip.ts +++ b/packages/web/titanium/chip/chip.ts @@ -58,7 +58,7 @@ export class TitaniumChip extends LitElement { */ @property({ type: Boolean, reflect: true }) accessor disabled: boolean = false; - @property({ type: Boolean }) accessor filled: boolean = false; + @property({ type: Boolean, reflect: true }) accessor filled: boolean = false; @property({ type: Boolean, reflect: true, attribute: 'has-leading-items' }) private accessor hasLeadingItems = false; @property({ type: Boolean, reflect: true, attribute: 'has-trailing-items' }) private accessor hasTrailingItems = false; diff --git a/packages/web/titanium/data-table/data-table-action-bar.ts b/packages/web/titanium/data-table/data-table-action-bar.ts index c5e724292..592a0a521 100644 --- a/packages/web/titanium/data-table/data-table-action-bar.ts +++ b/packages/web/titanium/data-table/data-table-action-bar.ts @@ -49,10 +49,6 @@ export class TitaniumDataTableActionBar extends LitElement { } } - :host([has-search][has-add-button]) div[add-button] { - margin: 0 0 0 12px; - } - selected-action-veil { display: none; grid: 'text buttons' / minmax(min-content, 1fr) auto; diff --git a/packages/web/titanium/data-table/data-table-item.ts b/packages/web/titanium/data-table/data-table-item.ts index a787ddbcd..c8dbfde2c 100644 --- a/packages/web/titanium/data-table/data-table-item.ts +++ b/packages/web/titanium/data-table/data-table-item.ts @@ -44,7 +44,7 @@ export class TitaniumDataTableItem extends LitElement { /** * Disables ability to select this row. */ - @property({ type: Boolean, attribute: 'disable-select' }) accessor disableSelect: boolean; + @property({ type: Boolean, reflect: true, attribute: 'disable-select' }) accessor disableSelect: boolean; /** * Sets if view port is small diff --git a/packages/web/titanium/data-table/data-table.ts b/packages/web/titanium/data-table/data-table.ts index 335b27f58..d0227c468 100644 --- a/packages/web/titanium/data-table/data-table.ts +++ b/packages/web/titanium/data-table/data-table.ts @@ -81,7 +81,7 @@ export class TitaniumDataTable extends LitElement { /** * Disables all item selection on the data-table. */ - @property({ type: Boolean, attribute: 'disable-select' }) accessor disableSelect: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'disable-select' }) accessor disableSelect: boolean = false; /** * Disables paging. diff --git a/packages/web/titanium/date-input/date-input.ts b/packages/web/titanium/date-input/date-input.ts index dc58f0806..6dfad6a3f 100644 --- a/packages/web/titanium/date-input/date-input.ts +++ b/packages/web/titanium/date-input/date-input.ts @@ -91,7 +91,7 @@ export class TitaniumDateInput extends LitElement { /** * Swaps out outlined text field for a filled text field. */ - @property({ type: Boolean, attribute: 'filled' }) accessor filled: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'filled' }) accessor filled: boolean = false; @property({ type: Boolean, reflect: true }) accessor required = false; diff --git a/packages/web/titanium/profile-picture-stack/profile-picture-stack.ts b/packages/web/titanium/profile-picture-stack/profile-picture-stack.ts index 017f2b57e..e47b28a88 100644 --- a/packages/web/titanium/profile-picture-stack/profile-picture-stack.ts +++ b/packages/web/titanium/profile-picture-stack/profile-picture-stack.ts @@ -31,7 +31,7 @@ export class TitaniumProfilePictureStack extends LitElement { * This will open a new tab to the directory profile of the person. * This will only work if the person has an Id. */ - @property({ type: Boolean, attribute: 'enable-directory-href' }) accessor enableDirectoryHref: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'enable-directory-href' }) accessor enableDirectoryHref: boolean = false; /** * Toggle to show the full name of the person if there is one result in the stack. diff --git a/packages/web/titanium/single-select-base/single-select-base.ts b/packages/web/titanium/single-select-base/single-select-base.ts index 14d80d99e..f7b8d975e 100644 --- a/packages/web/titanium/single-select-base/single-select-base.ts +++ b/packages/web/titanium/single-select-base/single-select-base.ts @@ -41,7 +41,7 @@ export class TitaniumSingleSelectBase extends ThemePrefere /** * Swaps out outlined text field for a filled text field. */ - @property({ type: Boolean, attribute: 'filled' }) accessor filled: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'filled' }) accessor filled: boolean = false; /** * Sets placeholder text value. @@ -147,8 +147,8 @@ export class TitaniumSingleSelectBase extends ThemePrefere @state() protected accessor count: number; @property({ type: Boolean, attribute: 'menu-open', reflect: true }) private accessor menuOpen: boolean = false; - @property({ type: Boolean, attribute: 'large' }) accessor large: boolean = false; - @property({ type: Boolean, attribute: 'shaped' }) accessor shaped: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'large' }) accessor large: boolean = false; + @property({ type: Boolean, reflect: true, attribute: 'shaped' }) accessor shaped: boolean = false; getTextField() { return this.filled ? this.shadowRoot?.querySelector('md-filled-text-field') : this.shadowRoot?.querySelector('md-outlined-text-field'); From a4204765a62f6965c7c50b3e8a3074ec2e3036bb Mon Sep 17 00:00:00 2001 From: Aaron Drabeck Date: Thu, 25 Jun 2026 11:33:08 -0600 Subject: [PATCH 02/14] refactor!: remove deprecated components and LoadWhile mixin BREAKING CHANGE: Removes legacy titanium/leavitt components and migrates async loading from the LoadWhile mixin to @promiseTracking. Removed components include titanium-card, titanium-header, titanium-error-page, titanium-confirm-dialog, titanium-access-denied-page, legacy data-table (data-table, data-table-item, data-table-header), leavitt-user-feedback, legacy email-history-viewer, and unqualified/outlined duration, validator, search, and youtube inputs (filled variants only). LoadWhile and loadWhile() are removed; use @promiseTracking with trackLoadingPromise() and isLoading instead. TitaniumDataTableCore.loadWhile is now trackLoadingPromise. Leavittbook demos, routes, and shell updated; file-explorer uses titanium-confirmation-dialog. --- .../leavitt-email-history-viewer-demo.ts | 22 +- .../src/demos/leavitt-error-page-demo.ts | 3 +- .../src/demos/leavitt-user-feedback-demo.ts | 57 -- .../demos/titanium-access-denied-page-demo.ts | 38 -- .../src/demos/titanium-card-demo.ts | 113 ---- .../demos/titanium-chip-multi-select-demo.ts | 38 +- .../src/demos/titanium-confirm-dialog-demo.ts | 72 -- .../demos/titanium-data-table-core-demo.ts | 2 +- .../src/demos/titanium-data-table-demo.ts | 530 --------------- .../demos/titanium-data-table-header-demo.ts | 63 -- .../demos/titanium-data-table-item-demo.ts | 77 --- .../src/demos/titanium-duration-input-demo.ts | 67 +- .../src/demos/titanium-error-page-demo.ts | 40 -- ...tanium-full-page-loading-indicator-demo.ts | 61 -- .../src/demos/titanium-header-demo.ts | 83 --- .../demos/titanium-input-validator-demo.ts | 76 +-- .../src/demos/titanium-search-input-demo.ts | 40 +- .../src/demos/titanium-youtube-input-demo.ts | 60 +- packages/leavittbook/src/getting-started.ts | 4 +- packages/leavittbook/src/my-app.ts | 105 +-- .../leavittbook/src/styles/story-styles.ts | 4 - .../leavitt/company-select/company-select.ts | 2 +- .../email-history-view-list-filter-dialog.ts | 221 ------- ...ail-history-viewer-filled-filter-dialog.ts | 11 +- .../email-history-viewer-filled.ts | 13 +- .../email-history-viewer.ts | 367 ---------- .../view-email-template-info-dialog.ts | 11 +- .../view-sent-email-dialog.ts | 11 +- .../leavitt/file-explorer/add-folder-modal.ts | 11 +- .../leavitt/file-explorer/file-explorer.ts | 38 +- .../web/leavitt/file-explorer/file-modal.ts | 11 +- .../web/leavitt/file-explorer/folder-modal.ts | 11 +- .../person-company-select.ts | 2 +- .../person-group-select.ts | 2 +- .../leavitt/person-select/person-select.ts | 2 +- .../user-feedback/provide-feedback-dialog.ts | 13 +- .../user-feedback/report-a-problem-dialog.ts | 13 +- .../leavitt/user-feedback/user-feedback.ts | 210 ------ .../access-denied-page/access-denied-page.ts | 352 ---------- .../address-input/google-address-input.ts | 2 +- packages/web/titanium/card/card.ts | 163 ----- .../chip-multi-select/chip-multi-select.ts | 12 +- .../confirm-dialog-open-event.ts | 18 - .../titanium/confirm-dialog/confirm-dialog.ts | 95 --- .../data-table-core-reorder-dialog.ts | 11 +- .../titanium/data-table/data-table-core.ts | 9 +- .../titanium/data-table/data-table-header.ts | 246 ------- .../titanium/data-table/data-table-item.ts | 540 --------------- .../web/titanium/data-table/data-table.ts | 626 ------------------ .../titanium/duration-input/duration-input.ts | 109 --- .../duration-input/outlined-duration-input.ts | 92 --- .../web/titanium/error-page/error-page.ts | 89 --- .../full-page-loading-indicator.ts | 121 ---- packages/web/titanium/header/header.ts | 145 ---- packages/web/titanium/helpers/helpers.ts | 1 - packages/web/titanium/helpers/load-while.ts | 28 - .../input-validator/input-validator.ts | 38 -- .../outlined-input-validator.ts | 38 -- .../web/titanium/search-input/search-input.ts | 103 --- .../service-worker-notifier.ts | 50 -- .../single-select-base/single-select-base.ts | 9 +- .../crop-and-save-image-dialog.ts | 38 +- .../titanium/youtube-input/youtube-input.ts | 67 -- 63 files changed, 186 insertions(+), 5320 deletions(-) delete mode 100644 packages/leavittbook/src/demos/leavitt-user-feedback-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-access-denied-page-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-card-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-confirm-dialog-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-data-table-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-data-table-header-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-data-table-item-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-error-page-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-full-page-loading-indicator-demo.ts delete mode 100644 packages/leavittbook/src/demos/titanium-header-demo.ts delete mode 100644 packages/web/leavitt/email-history-viewer/email-history-view-list-filter-dialog.ts delete mode 100644 packages/web/leavitt/email-history-viewer/email-history-viewer.ts delete mode 100644 packages/web/leavitt/user-feedback/user-feedback.ts delete mode 100644 packages/web/titanium/access-denied-page/access-denied-page.ts delete mode 100644 packages/web/titanium/card/card.ts delete mode 100644 packages/web/titanium/confirm-dialog/confirm-dialog-open-event.ts delete mode 100644 packages/web/titanium/confirm-dialog/confirm-dialog.ts delete mode 100644 packages/web/titanium/data-table/data-table-header.ts delete mode 100644 packages/web/titanium/data-table/data-table-item.ts delete mode 100644 packages/web/titanium/data-table/data-table.ts delete mode 100644 packages/web/titanium/duration-input/duration-input.ts delete mode 100644 packages/web/titanium/duration-input/outlined-duration-input.ts delete mode 100644 packages/web/titanium/error-page/error-page.ts delete mode 100644 packages/web/titanium/full-page-loading-indicator/full-page-loading-indicator.ts delete mode 100644 packages/web/titanium/header/header.ts delete mode 100644 packages/web/titanium/helpers/load-while.ts delete mode 100644 packages/web/titanium/input-validator/input-validator.ts delete mode 100644 packages/web/titanium/input-validator/outlined-input-validator.ts delete mode 100644 packages/web/titanium/search-input/search-input.ts delete mode 100644 packages/web/titanium/service-worker-notifier/service-worker-notifier.ts delete mode 100644 packages/web/titanium/youtube-input/youtube-input.ts diff --git a/packages/leavittbook/src/demos/leavitt-email-history-viewer-demo.ts b/packages/leavittbook/src/demos/leavitt-email-history-viewer-demo.ts index 9ad40b64c..26be9483e 100644 --- a/packages/leavittbook/src/demos/leavitt-email-history-viewer-demo.ts +++ b/packages/leavittbook/src/demos/leavitt-email-history-viewer-demo.ts @@ -4,25 +4,18 @@ import '@leavittsoftware/web/leavitt/app/app-main-content-container'; import '@leavittsoftware/web/leavitt/app/app-navigation-header'; import '@leavittsoftware/web/leavitt/app/app-width-limiter'; import '@api-viewer/docs'; -import '@material/web/divider/divider'; -import '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer'; import '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer-filled'; import { css, html, LitElement } from 'lit'; -import { customElement, query } from 'lit/decorators.js'; +import { customElement } from 'lit/decorators.js'; import { ThemePreference } from '@leavittsoftware/web/leavitt/theme/theme-preference'; -import LeavittEmailHistoryViewer from '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer'; -import LeavittEmailHistoryViewerFilled from '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer-filled'; import StoryStyles from '../styles/story-styles'; import { siteSearchTextFieldContext } from '../contexts/site-search-text-field-context'; import api3UserService from '../services/api3-user-service'; @customElement('leavitt-email-history-viewer-demo') export class LeavittEmailHistoryViewerDemo extends ThemePreference(LitElement) { - @query('leavitt-email-history-viewer') protected accessor demo1!: LeavittEmailHistoryViewer; - @query('leavitt-email-history-viewer-filled') protected accessor demo2!: LeavittEmailHistoryViewerFilled; - connectedCallback() { super.connectedCallback(); api3UserService.addHeader('X-LGAppName', 'EducationAdminV2'); @@ -55,7 +48,6 @@ export class LeavittEmailHistoryViewerDemo extends ThemePreference(LitElement) {
-

Filled

- - - - -
-

Outlined

-
- -
-
- - diff --git a/packages/leavittbook/src/demos/leavitt-error-page-demo.ts b/packages/leavittbook/src/demos/leavitt-error-page-demo.ts index 18a24c34b..e4fa867f9 100644 --- a/packages/leavittbook/src/demos/leavitt-error-page-demo.ts +++ b/packages/leavittbook/src/demos/leavitt-error-page-demo.ts @@ -42,7 +42,8 @@ export class LeavittErrorPageDemo extends LitElement { Return home or contact support.`} + .message=${html`The page you requested could not be found. Return home or + contact support.`} >
diff --git a/packages/leavittbook/src/demos/leavitt-user-feedback-demo.ts b/packages/leavittbook/src/demos/leavitt-user-feedback-demo.ts deleted file mode 100644 index 079cb54f6..000000000 --- a/packages/leavittbook/src/demos/leavitt-user-feedback-demo.ts +++ /dev/null @@ -1,57 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@api-viewer/docs'; -import '@material/web/button/filled-tonal-button'; -import '@leavittsoftware/web/titanium/snackbar/snackbar-stack'; -import '@leavittsoftware/web/leavitt/user-feedback/user-feedback'; -import '@leavittsoftware/web/leavitt/user-feedback/report-a-problem-dialog'; -import '@leavittsoftware/web/leavitt/user-feedback/provide-feedback-dialog'; - -import { html, LitElement } from 'lit'; -import { customElement, query } from 'lit/decorators.js'; -import { ReportAProblemDialog } from '@leavittsoftware/web/leavitt/user-feedback/report-a-problem-dialog'; -import { ProvideFeedbackDialog } from '@leavittsoftware/web/leavitt/user-feedback/provide-feedback-dialog'; - -import UserManager from '../services/user-manager-service'; -import StoryStyles from '../styles/story-styles'; - -@customElement('leavitt-user-feedback-demo') -export class LeavittUserFeedbackDemo extends LitElement { - @query('report-a-problem-dialog') protected accessor reportAProblemDialog!: ReportAProblemDialog; - @query('provide-feedback-dialog') protected accessor provideFeedbackDialog!: ProvideFeedbackDialog; - - static styles = [StoryStyles]; - - render() { - return html` - -
- - - - - -
-

Default

-

User feedback components with report problem and provide feedback dialogs

- - this.reportAProblemDialog.show()}>Report a problem - this.provideFeedbackDialog.show()}>Provide feedback - - -
- - -
-
-
- - - - - `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-access-denied-page-demo.ts b/packages/leavittbook/src/demos/titanium-access-denied-page-demo.ts deleted file mode 100644 index 9e3c5c357..000000000 --- a/packages/leavittbook/src/demos/titanium-access-denied-page-demo.ts +++ /dev/null @@ -1,38 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@api-viewer/docs'; - -import '@leavittsoftware/web/titanium/access-denied-page/access-denied-page'; - -import { html, LitElement } from 'lit'; -import { customElement } from 'lit/decorators.js'; - -import StoryStyles from '../styles/story-styles'; - -@customElement('titanium-access-denied-page-demo') -export class TitaniumAccessDeniedPageDemo extends LitElement { - static styles = [StoryStyles]; - - render() { - return html` - -
- - - - -
-

Default

-

Access denied page sample

- -
- -
-
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-card-demo.ts b/packages/leavittbook/src/demos/titanium-card-demo.ts deleted file mode 100644 index 46fc9cfb2..000000000 --- a/packages/leavittbook/src/demos/titanium-card-demo.ts +++ /dev/null @@ -1,113 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@api-viewer/docs'; - -import '@material/web/button/filled-tonal-button'; -import '@material/web/iconbutton/icon-button'; -import '@material/web/icon/icon'; -import '@material/web/list/list-item'; - -import '@leavittsoftware/web/titanium/card/card'; - -import { css, html, LitElement } from 'lit'; -import { customElement } from 'lit/decorators.js'; -import { h1 } from '@leavittsoftware/web/titanium/styles/h1'; - -import StoryStyles from '../styles/story-styles'; - -@customElement('titanium-card-demo') -export class TitaniumCardDemo extends LitElement { - static styles = [ - StoryStyles, - css` - titanium-card { - ${h1}; - } - - img[card-image] { - width: 110px; - } - `, - ]; - - render() { - return html` - -
- - - -

Default

-

Basic card with default styling

- -

Lorem ipsum dolor sit amet

-
- -

Filled

-

Card with filled background

- -

Lorem ipsum dolor sit amet

-
- -

Elevated

-

Card with elevation shadow

- -

Lorem ipsum dolor sit amet

-
- -

With Image

-

Card containing an image

- - password -

Lorem ipsum dolor sit amet

-

-
- -

With Footer

-

Card with footer containing action buttons

- -

Lorem ipsum dolor sit amet

-
- - Save - -
- -

With Menu

-

Card with menu button in the header

- -

Lorem ipsum dolor sit amet

- more_vert -
-
- -

With List Items

-

Card containing interactive list items

- -

Lorem ipsum dolor sit amet

- more_vert -
- - -
Go Home
-
This will link you out in a new tab
- navigate_next -
- - -
Details
-
This will link you out in a new tab
- navigate_next -
-
-
- -
-
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts b/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts index a6184585e..0ff60c6a8 100644 --- a/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts +++ b/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts @@ -25,7 +25,6 @@ export class TitaniumChipMultiSelectDemo extends LitElement { @state() protected accessor disabled: boolean = false; @state() protected accessor supportingText: string | null = 'Service animals are welcome.'; @query('titanium-chip-multi-select') private accessor titaniumChipMultiSelect: TitaniumChipMultiSelect; - @query('titanium-chip-multi-select[filled]') private accessor titaniumChipMultiSelectFilled: TitaniumChipMultiSelect; static styles = [ StoryStyles, @@ -53,46 +52,13 @@ export class TitaniumChipMultiSelectDemo extends LitElement {
-

Default

- - { - this.demoItems.push(chipLabels[this.demoItems.length % chipLabels.length]); - this.requestUpdate('demoItems'); - }} - >Add Animal add - ${repeat( - this.demoItems, - (o) => o, - (o, index) => - html` { - e.preventDefault(); - this.demoItems = this.demoItems.filter((_, i) => i !== index); - }} - >` - )} - -

Filled

{ this.titaniumChipMultiSelect.reportValidity(); - this.titaniumChipMultiSelectFilled.reportValidity(); }} >Report validity @@ -146,7 +111,6 @@ export class TitaniumChipMultiSelectDemo extends LitElement { { this.titaniumChipMultiSelect.reset(); - this.titaniumChipMultiSelectFilled.reset(); }} >Reset diff --git a/packages/leavittbook/src/demos/titanium-confirm-dialog-demo.ts b/packages/leavittbook/src/demos/titanium-confirm-dialog-demo.ts deleted file mode 100644 index d525b29b0..000000000 --- a/packages/leavittbook/src/demos/titanium-confirm-dialog-demo.ts +++ /dev/null @@ -1,72 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@api-viewer/docs'; -import '@material/web/icon/icon'; - -import '@material/web/button/filled-tonal-button'; - -import { css, html, LitElement } from 'lit'; -import { customElement, query, state } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/confirm-dialog/confirm-dialog'; -import { ConfirmDialogOpenEvent } from '@leavittsoftware/web/titanium/confirm-dialog/confirm-dialog-open-event'; - -import StoryStyles from '../styles/story-styles'; -import TitaniumConfirmDialog from '@leavittsoftware/web/titanium/confirm-dialog/confirm-dialog'; - -@customElement('titanium-confirm-dialog-demo') -export class TitaniumConfirmDialogDemo extends LitElement { - @state() private accessor confirmed = false; - @query('titanium-confirm-dialog') private accessor confirmDialog!: TitaniumConfirmDialog; - - async #open() { - const confirmationDialogEvent = new ConfirmDialogOpenEvent( - 'Confirm delete?', - 'Are you sure you would like to delete the universe? Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Turpis massa tincidunt dui ut ornare. Ut tortor pretium viverra suspendisse potenti nullam. Dolor morbi non arcu risus. Porttitor rhoncus dolor purus non. Vitae justo eget magna fermentum iaculis eu non diam. Pretium quam vulputate dignissim suspendisse in est ante in. Semper quis lectus nulla at volutpat. Id volutpat lacus laoreet non curabitur gravida arcu ac tortor. Orci dapibus ultrices in iaculis.' - ); - this.dispatchEvent(confirmationDialogEvent); - this.confirmed = await confirmationDialogEvent.dialogResult; - } - - firstUpdated() { - this.addEventListener(ConfirmDialogOpenEvent.eventType, async (e: ConfirmDialogOpenEvent) => { - await import('@leavittsoftware/web/titanium/confirm-dialog/confirm-dialog'); - this.confirmDialog.handleEvent(e); - }); - } - - static styles = [ - StoryStyles, - css` - h3 { - margin-bottom: 12px; - } - `, - ]; - - render() { - return html` - -
- - - - - warning -

titanium-confirm-dialog is deprecated. Use titanium-confirmation-dialog instead.

-
-
-

Default

-

Confirmed: ${this.confirmed}

- Open -
- -
-
-
- - `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-data-table-core-demo.ts b/packages/leavittbook/src/demos/titanium-data-table-core-demo.ts index 682f91037..5f349eb85 100644 --- a/packages/leavittbook/src/demos/titanium-data-table-core-demo.ts +++ b/packages/leavittbook/src/demos/titanium-data-table-core-demo.ts @@ -394,7 +394,7 @@ export class TitaniumDataTableCoreDemo extends LitElement { @sort-changed=${async (e: DOMEvent>) => { this.sort = e.target.sort; const _delay = delay(300); - this.tableCore.loadWhile(_delay); + this.tableCore.trackLoadingPromise(_delay); await _delay; this.items = this.sortItems(this.items, this.sort); diff --git a/packages/leavittbook/src/demos/titanium-data-table-demo.ts b/packages/leavittbook/src/demos/titanium-data-table-demo.ts deleted file mode 100644 index 7c0e9e7e8..000000000 --- a/packages/leavittbook/src/demos/titanium-data-table-demo.ts +++ /dev/null @@ -1,530 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@material/web/divider/divider'; -import '@api-viewer/docs'; -import '@leavittsoftware/web/titanium/data-table/data-table-item'; -import '@leavittsoftware/web/titanium/data-table/data-table-header'; -import '@leavittsoftware/web/titanium/search-input/search-input'; -import '@material/web/dialog/dialog'; -import '@material/web/button/outlined-button'; -import '@material/web/button/filled-tonal-button'; -import '@material/web/icon/icon'; -import '@material/web/iconbutton/icon-button'; -import '@material/web/select/outlined-select.js'; -import '@material/web/select/select-option.js'; -import '@material/web/menu/menu'; -import '@material/web/menu/menu-item'; -import '@material/web/switch/switch'; -import '@material/web/chips/input-chip'; -import '@material/web/button/text-button'; - -import { css, html, LitElement } from 'lit'; -import { customElement, query, state } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/data-table/data-table'; -import { DOMEvent } from '@leavittsoftware/web/titanium/types/dom-event'; -import { Debouncer, getSearchTokens } from '@leavittsoftware/web/titanium/helpers/helpers'; -import { FilterController } from '@leavittsoftware/web/titanium/data-table/filter-controller'; -import { TitaniumDataTable } from '@leavittsoftware/web/titanium/data-table/data-table'; -import { TitaniumSearchInput } from '@leavittsoftware/web/titanium/search-input/search-input'; -import { repeat } from 'lit/directives/repeat.js'; -import { CloseMenuEvent, MdMenu, MenuItem } from '@material/web/menu/menu'; -import { MdIconButton } from '@material/web/iconbutton/icon-button'; -import { MdDialog } from '@material/web/dialog/dialog'; - -import StoryStyles from '../styles/story-styles'; - -type FilterKeys = 'Appearance'; -type Car = { Name: string; Appearance: 'Plaid' | 'Ugly' | 'Slick' }; - -const allTeslas: Array = [ - { Name: 'Model 3', Appearance: 'Slick' }, - { Name: 'Model X', Appearance: 'Slick' }, - { Name: 'Model Y', Appearance: 'Slick' }, - { Name: 'Model S', Appearance: 'Slick' }, - { Name: 'Cybertruck', Appearance: 'Ugly' }, - { Name: 'Tesla Semi', Appearance: 'Ugly' }, - { Name: 'Model X Plaid', Appearance: 'Plaid' }, - { Name: 'Model S Plaid', Appearance: 'Plaid' }, - { Name: 'Model S Plaid+', Appearance: 'Plaid' }, - { Name: 'Gen. 2 Roadster', Appearance: 'Slick' }, -]; - -@customElement('titanium-data-table-demo') -export class TitaniumDataTableDemo extends LitElement { - @state() protected accessor allItems: Array> = []; - @state() protected accessor items: Array> = []; - @state() protected accessor selected: Array> = []; - @state() protected accessor searchTerm: string = ''; - @state() protected accessor resultTotal: number = 0; - @state() protected accessor sortDirection: '' | 'asc' | 'desc' = 'asc'; - @state() protected accessor sortBy: string = 'Name'; - @state() protected accessor filterController: FilterController; - - @state() protected accessor singleSelect: boolean = false; - @state() protected accessor disableSelect: boolean = false; - @state() protected accessor disablePaging: boolean = false; - @state() protected accessor draggableItems: Array> = []; - - @query('titanium-data-table') protected accessor dataTable!: TitaniumDataTable; - @query('data-table-demo-filter-modal') protected accessor filterModal!: DataTableDemoFilterModal; - - constructor() { - super(); - this.filterController = new FilterController('/titanium-data-table'); - this.filterController.setFilter('Appearance', (val) => `BasketId eq ${val}`); - - this.filterController.subscribeToFilterChange(async () => { - if (this.dataTable) { - this.dataTable?.resetPage(); - this.#reload(); - } - }); - this.filterController.loadFromQueryString(); - } - - firstUpdated() { - this.#reset(); - this.items = this.allItems.slice(0); - this.draggableItems = [ - { Name: 'Model 3', Appearance: 'Slick' }, - { Name: 'Model X', Appearance: 'Slick' }, - { Name: 'Model Y', Appearance: 'Slick' }, - { Name: 'Model S', Appearance: 'Slick' }, - { Name: 'Cybertruck', Appearance: 'Ugly' }, - { Name: 'Tesla Semi', Appearance: 'Ugly' }, - { Name: 'Model X Plaid', Appearance: 'Plaid' }, - { Name: 'Model S Plaid', Appearance: 'Plaid' }, - { Name: 'Model S Plaid+', Appearance: 'Plaid' }, - { Name: 'Gen. 2 Roadster', Appearance: 'Slick' }, - ]; - } - - #reload() { - this.getItemsAsync(this.searchTerm); - } - - #reset() { - this.allItems = allTeslas.map((o) => ({ Name: o.Name, Appearance: o.Appearance })); - this.#reload(); - } - - #onSortDirectionChange(e: CustomEvent<'' | 'asc' | 'desc'>) { - this.sortDirection = e.detail; - this.dataTable.resetPage(); - this.#reload(); - } - - #onSortByChange(e: CustomEvent) { - this.sortBy = e.detail; - this.dataTable.resetPage(); - this.#reload(); - } - - #doSearchDebouncer = new Debouncer((searchTerm: string) => this.getItemsAsync(searchTerm)); - - async getItemsAsync(searchTerm: string) { - const searchTokens = getSearchTokens(searchTerm); - - const take = await this.dataTable.getTake(); - const page = await this.dataTable.getPage(); - const sortDirection = this.sortDirection === 'asc' ? 1 : -1; - - let filterItems = this.allItems.filter((o) => searchTokens.every((st) => o.Name?.trim().toLowerCase()?.includes(st.trim().toLowerCase()))); - - const appearanceValue = this.filterController.getValue('Appearance'); - if (appearanceValue) { - filterItems = filterItems.filter((o) => o.Appearance === appearanceValue); - } - - this.items = filterItems - .slice(0) - .sort((a, b) => (a[this.sortBy] === b[this.sortBy] ? 0 : (a[this.sortBy] ?? '') < (b[this.sortBy] ?? '') ? sortDirection : -sortDirection)) - .slice(take * page, take * page + take); - - this.resultTotal = filterItems.length; - } - - static styles = [ - StoryStyles, - css` - titanium-data-table { - margin: 24px 0 36px 0; - --titanium-page-control-select-width: 108px; - } - - knob-container { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 24px; - margin-top: 12px; - } - `, - ]; - - render() { - return html` - -
- - - - - warning -

titanium-data-table is deprecated. Use titanium-data-table-core instead (shown in separate demo).

-
- - -
-

Full working example

-

Table with items and method controls

- - >>) => { - this.selected = [...e.detail]; - }} - @paging-changed=${() => { - this.#reload(); - }} - narrow-max-width="800" - .count=${this.resultTotal} - .items=${this.items} - .searchTerm=${this.searchTerm} - ?single-select=${this.singleSelect} - ?disable-select=${this.disableSelect} - ?disable-paging=${this.disablePaging} - > - ) => { - this.searchTerm = e.target.value; - this.dataTable.resetPage(); - this.#doSearchDebouncer.debounce(this.searchTerm); - }} - > - - - - { - (e.detail.itemPath?.[0] as MenuItem & { action?: () => void })?.action?.(); - }} - > - this.#reset()}> - refresh - Refresh - - - - { - const car = allTeslas[this.allItems.length % allTeslas.length]; - const newItem: Partial = { Name: car.Name, Appearance: car.Appearance }; - this.allItems.push(newItem); - this.#reload(); - }} - > - add - Add item - - - - - { - this.filterModal.open(); - }} - > - filter_list - - - 1} - @click=${() => { - this.allItems = this.allItems.filter((f) => !this.selected.includes(f)); - this.resultTotal = this.resultTotal - this.selected.length; - this.#reload(); - }} - > - delete - - - - - - - - - ${repeat( - this.items ?? [], - (item) => item.Name, - (item) => html` - { - this.dataTable.clearSelection(); - }} - .item=${item} - slot="items" - > - ${item.Name ?? '-'} - ${item.Appearance ?? '-'} - ${item.Appearance ?? '-'} - - ` - )} - - -

Knobs

- - { - this.dataTable.clearSelection(); - this.singleSelect = !this.singleSelect; - }} - > - - - { - this.dataTable.clearSelection(); - this.disableSelect = !this.disableSelect; - }} - > - - - { - this.disablePaging = !this.disablePaging; - }} - > - - - this.dataTable.resetPage()}>Reset page - this.dataTable.clearSelection()}>Clear selection - -
- -
-

Draggable

-

Table with draggable items

- { - this.draggableItems = structuredClone(this.draggableItems); - await this.requestUpdate('draggableItems'); - }} - > - - - - - ${repeat( - this.draggableItems ?? [], - (item) => item.Name, - (item) => html` - - ${item.Name ?? '-'} - ${item.Appearance ?? '-'} - Learn More - - ` - )} - -

Results

-

${this.draggableItems.map((o) => o.Name).join(',')}

-
- - -
-
-
- `; - } -} - -@customElement('data-table-demo-filter-modal') -export class DataTableDemoFilterModal extends LitElement { - @state() protected accessor filterController: FilterController; - @state() protected accessor appearance: string; - - @query('md-dialog') private accessor dialog!: MdDialog; - - async firstUpdated() { - this.filterController.subscribeToFilterChange(async () => { - this.requestUpdate('filterController'); - }); - } - - public async open() { - this.dialog.show(); - } - - #preventDialogOverflow() { - const dialog = this.dialog.shadowRoot?.querySelector('dialog'); - const container = dialog?.querySelector('.container'); - const scroller = container?.querySelector('.scroller'); - if (scroller) { - scroller.style.overflow = 'initial'; - } - if (container) { - container.style.overflow = 'initial'; - } - } - - #restoreDialogOverflow() { - const dialog = this.dialog.shadowRoot?.querySelector('dialog'); - const container = dialog?.querySelector('.container'); - const scroller = container?.querySelector('.scroller'); - if (scroller) { - scroller.style.overflow = ''; - } - if (container) { - container.style.overflow = ''; - } - } - - static styles = [ - css` - :host { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; - } - - md-dialog { - width: 100%; - } - - md-dialog form { - container-type: inline-size; - } - - md-outlined-select { - width: 100%; - } - - [hidden] { - display: none !important; - } - `, - ]; - - render() { - return html` - { - e.preventDefault(); - this.filterController.setValue('Appearance', null); - this.requestUpdate('filterController'); - }} - > - conditions - - - -
Filter items by
-
- this.#preventDialogOverflow()} - @closing=${() => this.#restoreDialogOverflow()} - label="Appearance" - hasLeadingIcon - .value=${this.filterController.getValue('Appearance') ?? ''} - @request-selection=${(e) => { - this.appearance = e.target.value; - }} - > - conditions - - -
Ugly
- conditions -
- -
Plaid
- conditions -
- -
Slick
- conditions -
-
-
-
- this.dialog.close('cancel')}> Close - { - this.filterController.setValue('Appearance', this.appearance || null); - this.requestUpdate('filterController'); - this.dialog.close(); - }} - >Apply -
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-data-table-header-demo.ts b/packages/leavittbook/src/demos/titanium-data-table-header-demo.ts deleted file mode 100644 index 4951eaf62..000000000 --- a/packages/leavittbook/src/demos/titanium-data-table-header-demo.ts +++ /dev/null @@ -1,63 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@material/web/divider/divider'; -import '@material/web/icon/icon'; -import '@api-viewer/docs'; - -import { css, html, LitElement } from 'lit'; -import { customElement } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/data-table/data-table-header'; - -import StoryStyles from '../styles/story-styles'; - -@customElement('titanium-data-table-header-demo') -export class TitaniumDataTableHeaderDemo extends LitElement { - static styles = [ - StoryStyles, - css` - header-container { - display: flex; - flex-direction: row; - gap: 24px; - } - `, - ]; - - render() { - return html` - -
- - - - - - warning -

titanium-data-table-header is deprecated. Use titanium-data-table-core instead (shown in separate demo).

-
- - -
-

Table headers

-

Data table header components with different alignment and sizing options

- - - - - - - - - -
- - -
-
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-data-table-item-demo.ts b/packages/leavittbook/src/demos/titanium-data-table-item-demo.ts deleted file mode 100644 index 2bd5bcecf..000000000 --- a/packages/leavittbook/src/demos/titanium-data-table-item-demo.ts +++ /dev/null @@ -1,77 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@material/web/divider/divider'; -import '@material/web/icon/icon'; -import '@api-viewer/docs'; -import '@material/web/button/filled-tonal-button'; - -import { css, html, LitElement } from 'lit'; -import { customElement, query } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/data-table/data-table-item'; -import { TitaniumDataTableItem } from '@leavittsoftware/web/titanium/data-table/data-table-item'; - -import StoryStyles from '../styles/story-styles'; - -@customElement('titanium-data-table-item-demo') -export class TitaniumDataTableItemDemo extends LitElement { - @query('titanium-data-table-item[select-demo]') protected accessor selectItem!: TitaniumDataTableItem; - - static styles = [ - StoryStyles, - css` - section { - display: flex; - margin-top: 12px; - gap: 12px; - } - `, - ]; - - render() { - return html` - -
- - - - - - warning -

titanium-data-table-item is deprecated. Use titanium-data-table-core instead (shown in separate demo).

-
- - -
-

Default

-

Examples using disabled, closeable, and readonly attribute

-
- Default - Selected - Select disabled - -
-
- -
-

Methods

-

Select, Deselect, Toggle

-
- Item -
- this.selectItem.select()}>select() - this.selectItem.deselect()}>deselect() - this.selectItem.toggleSelected()}>toggleSelected() -
-
-
- - -
-
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-duration-input-demo.ts b/packages/leavittbook/src/demos/titanium-duration-input-demo.ts index 86c0ea746..e4e6ea698 100644 --- a/packages/leavittbook/src/demos/titanium-duration-input-demo.ts +++ b/packages/leavittbook/src/demos/titanium-duration-input-demo.ts @@ -3,19 +3,14 @@ import '../shared/story-header'; import '@leavittsoftware/web/leavitt/app/app-main-content-container'; import '@leavittsoftware/web/leavitt/app/app-navigation-header'; import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@material/web/divider/divider'; -import '@material/web/icon/icon'; import '@api-viewer/docs'; import '@material/web/button/filled-tonal-button'; import { css, html, LitElement } from 'lit'; import { customElement, query, state } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/duration-input/duration-input'; import '@leavittsoftware/web/titanium/duration-input/filled-duration-input'; -import '@leavittsoftware/web/titanium/duration-input/outlined-duration-input'; import { TitaniumFilledDurationInput } from '@leavittsoftware/web/titanium/duration-input/filled-duration-input'; -import { TitaniumOutlinedDurationInput } from '@leavittsoftware/web/titanium/duration-input/outlined-duration-input'; import { DOMEvent } from '@leavittsoftware/web/titanium/types/dom-event'; import dayjs from 'dayjs/esm'; import duration from 'dayjs/esm/plugin/duration'; @@ -27,16 +22,13 @@ dayjs.extend(duration); @customElement('titanium-duration-input-demo') export class TitaniumDurationInputDemo extends LitElement { @state() private accessor filledDuration: duration.Duration | null = dayjs.duration(14400); - @state() private accessor outlinedDuration: duration.Duration | null = dayjs.duration(14400); - @query('titanium-filled-duration-input') private accessor filledInput: TitaniumFilledDurationInput; - @query('titanium-outlined-duration-input') private accessor outlinedInput: TitaniumOutlinedDurationInput; + @query('titanium-filled-duration-input[required]') private accessor filledInput: TitaniumFilledDurationInput; static styles = [ StoryStyles, css` - titanium-filled-duration-input, - titanium-outlined-duration-input { + titanium-filled-duration-input { width: 100%; margin-bottom: 24px; } @@ -50,13 +42,6 @@ export class TitaniumDurationInputDemo extends LitElement { - - warning -

- titanium-duration-input is deprecated. Use titanium-filled-duration-input or - titanium-outlined-duration-input instead (shown below). -

-
@@ -94,54 +79,14 @@ export class TitaniumDurationInputDemo extends LitElement {
- - - - - - -
-

Outlined duration input

- - ) => { - this.outlinedDuration = e.target.duration; - }} - > - -
- { - this.outlinedInput.reset(); - }} - >Reset - { - this.outlinedInput.focus(); - }} - >Focus - { - this.outlinedInput.reportValidity(); - }} - >Report validity -
-
-

With different durations

- - - + + +
- +
diff --git a/packages/leavittbook/src/demos/titanium-error-page-demo.ts b/packages/leavittbook/src/demos/titanium-error-page-demo.ts deleted file mode 100644 index b16c5852f..000000000 --- a/packages/leavittbook/src/demos/titanium-error-page-demo.ts +++ /dev/null @@ -1,40 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@api-viewer/docs'; - -import '@leavittsoftware/web/titanium/error-page/error-page'; - -import { html, LitElement } from 'lit'; -import { customElement } from 'lit/decorators.js'; - -import StoryStyles from '../styles/story-styles'; - -@customElement('titanium-error-page-demo') -export class TitaniumErrorPageDemo extends LitElement { - static styles = [StoryStyles]; - - render() { - return html` - -
- - - -
-

Default

- -

TemplateResult message

- Go back home or contact support.`} - > -
- -
-
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-full-page-loading-indicator-demo.ts b/packages/leavittbook/src/demos/titanium-full-page-loading-indicator-demo.ts deleted file mode 100644 index 88ad6d7d3..000000000 --- a/packages/leavittbook/src/demos/titanium-full-page-loading-indicator-demo.ts +++ /dev/null @@ -1,61 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@api-viewer/docs'; - -import '@material/web/button/filled-tonal-button'; - -import { html, LitElement } from 'lit'; -import { customElement } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/full-page-loading-indicator/full-page-loading-indicator'; - -import StoryStyles from '../styles/story-styles'; - -@customElement('titanium-full-page-loading-indicator-demo') -export class TitaniumFullPageLoadingIndicatorDemo extends LitElement { - static styles = [StoryStyles]; - - render() { - return html` - -
- - - - -
-

Demo

-

A promise driven pending-state-events loading scrim

- - { - e.preventDefault(); - const work = new Promise((r) => setTimeout(r, 50)); - const work2 = new Promise((r) => setTimeout(r, 3000)); - window.dispatchEvent( - new CustomEvent('pending-state', { - composed: true, - bubbles: true, - detail: { promise: work.then(() => console.log('Done 1')) }, - }) - ); - window.dispatchEvent( - new CustomEvent('pending-state', { - composed: true, - bubbles: true, - detail: { promise: work2.then(() => console.log('Done 2')) }, - }) - ); - }} - >Open loading veil for 2 seconds -
- -
-
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-header-demo.ts b/packages/leavittbook/src/demos/titanium-header-demo.ts deleted file mode 100644 index c75f76520..000000000 --- a/packages/leavittbook/src/demos/titanium-header-demo.ts +++ /dev/null @@ -1,83 +0,0 @@ -import '../shared/story-header'; - -import '@leavittsoftware/web/leavitt/app/app-main-content-container'; -import '@leavittsoftware/web/leavitt/app/app-navigation-header'; -import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@api-viewer/docs'; -import '@material/web/icon/icon'; - -import '@leavittsoftware/web/titanium/header/header'; - -import { css, html, LitElement } from 'lit'; -import { customElement } from 'lit/decorators.js'; - -import StoryStyles from '../styles/story-styles'; - -@customElement('titanium-header-demo') -export class TitaniumHeaderDemo extends LitElement { - static styles = [ - StoryStyles, - css` - h1 { - margin-bottom: 24px; - } - `, - ]; - - render() { - return html` - -
- - - - - warning -

titanium-header is deprecated. Use leavitt-app-navigation-header instead.

-
-
-

No nav

- -
- -
-

Back Arrow (default window.history.back)

- -
- -
-

Back Arrow (overloaded action)

- { - alert('back clicked'); - }} - > -
- -
-

With custom icon

- { - alert('back clicked'); - }} - > -
- -
-

Custom icon with no navigation controls

- -
- - -
-
-
- `; - } -} diff --git a/packages/leavittbook/src/demos/titanium-input-validator-demo.ts b/packages/leavittbook/src/demos/titanium-input-validator-demo.ts index e86fc4751..168538f2c 100644 --- a/packages/leavittbook/src/demos/titanium-input-validator-demo.ts +++ b/packages/leavittbook/src/demos/titanium-input-validator-demo.ts @@ -3,20 +3,16 @@ import '../shared/story-header'; import '@leavittsoftware/web/leavitt/app/app-main-content-container'; import '@leavittsoftware/web/leavitt/app/app-navigation-header'; import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@leavittsoftware/web/leavitt/profile-picture/profile-picture'; import '@material/web/iconbutton/outlined-icon-button'; import '@material/web/icon/icon'; import '@material/web/button/filled-tonal-button'; -import '@material/web/divider/divider'; import '@api-viewer/docs'; import '@leavittsoftware/web/titanium/input-validator/filled-input-validator'; -import '@leavittsoftware/web/titanium/input-validator/outlined-input-validator'; import { css, html, LitElement } from 'lit'; import { customElement, queryAll, state } from 'lit/decorators.js'; import { ShowSnackbarEvent } from '@leavittsoftware/web/titanium/snackbar/show-snackbar-event'; -import { TitaniumOutlinedInputValidator } from '@leavittsoftware/web/titanium/input-validator/outlined-input-validator'; import { TitaniumFilledInputValidator } from '@leavittsoftware/web/titanium/input-validator/filled-input-validator'; import StoryStyles from '../styles/story-styles'; @@ -24,29 +20,19 @@ import StoryStyles from '../styles/story-styles'; @customElement('titanium-input-validator-demo') export class TitaniumInputValidatorDemo extends LitElement { @state() private accessor filledIconSelected = ''; - @state() private accessor outlinedIconSelected = ''; @queryAll('titanium-filled-input-validator') private accessor filledValidators: NodeListOf; - @queryAll('titanium-outlined-input-validator') private accessor outlinedValidators: NodeListOf; static styles = [ StoryStyles, css` - md-divider { - margin-bottom: 48px; - } - button-container { padding: 12px; display: flex; gap: 12px; } - titanium-filled-input-validator, - titanium-outlined-input-validator { - width: 299px; - } - titanium-filled-input-validator { + width: 299px; --md-filled-field-container-shape: 16px; --md-filled-field-active-indicator-height: 0; @@ -70,13 +56,6 @@ export class TitaniumInputValidatorDemo extends LitElement { - - warning -

- titanium-input-validator is deprecated. Use titanium-outlined-input-validator or - titanium-filled-input-validator instead. -

-

Filled

- - - -
-

Outlined

- this.outlinedIconSelected === 'cruelty_free'} - .errorText=${'Bunny is not selected'} - > - - (this.outlinedIconSelected = 'cruelty_free')} - toggle - ?selected=${this.outlinedIconSelected === 'cruelty_free'} - > - cruelty_free - - (this.outlinedIconSelected = 'pets')} - toggle - ?selected=${this.outlinedIconSelected === 'pets'} - > - pets - - (this.outlinedIconSelected = 'person')} - toggle - ?selected=${this.outlinedIconSelected === 'person'} - > - person - - - -
- Array.from(this.outlinedValidators).forEach((v) => v.reportValidity())} - >Report Validity - - this.dispatchEvent(new ShowSnackbarEvent(`Check Validity is ${Array.from(this.outlinedValidators).map((v) => v.checkValidity())}`))} - > - Check Validity - Array.from(this.outlinedValidators).forEach((v) => v.reset())}> Reset -
-
- - diff --git a/packages/leavittbook/src/demos/titanium-search-input-demo.ts b/packages/leavittbook/src/demos/titanium-search-input-demo.ts index 9e9059582..8d8cd3325 100644 --- a/packages/leavittbook/src/demos/titanium-search-input-demo.ts +++ b/packages/leavittbook/src/demos/titanium-search-input-demo.ts @@ -3,7 +3,6 @@ import '../shared/story-header'; import '@leavittsoftware/web/leavitt/app/app-main-content-container'; import '@leavittsoftware/web/leavitt/app/app-navigation-header'; import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@material/web/divider/divider'; import '@api-viewer/docs'; import '@material/web/button/filled-tonal-button'; @@ -11,15 +10,16 @@ import '@material/web/icon/icon'; import { html, LitElement } from 'lit'; import { customElement, query } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/search-input/search-input'; -import { TitaniumSearchInput } from '@leavittsoftware/web/titanium/search-input/search-input'; +import '@leavittsoftware/web/titanium/search-input/filled-search-input'; +import TitaniumFilledSearchInput from '@leavittsoftware/web/titanium/search-input/filled-search-input'; import { DOMEvent } from '@leavittsoftware/web/titanium/types/dom-event'; +import { MdFilledTextField } from '@material/web/textfield/filled-text-field'; import StoryStyles from '../styles/story-styles'; @customElement('titanium-search-input-demo') export class TitaniumSearchInputDemo extends LitElement { - @query('titanium-search-input[method-focused]') protected accessor methodFocus!: TitaniumSearchInput; + @query('titanium-filled-search-input[method-focused]') protected accessor methodFocus!: TitaniumFilledSearchInput; static styles = [StoryStyles]; @@ -30,48 +30,46 @@ export class TitaniumSearchInputDemo extends LitElement { - - warning -

titanium-search-input is deprecated and no longer in use.

-
- +

Default

-

Basic search input with expand/collapse functionality

- ) => console.log(e.target.value)}> +

Basic filled search input with clear button

+ ) => console.log(e.target.value)}>

Disabled

Disabled search input

- +

Placeholder text

-

Search input with placeholder and hidden clear button

- +

Search input with custom placeholder

+
-

Collapse Prevented

-

Search input that stays expanded

- +

With value

+

Search input with an initial value

+

Methods

-

Demonstrates public methods

- +

Demonstrates focusing the underlying text field

+
- this.methodFocus.focus()}>Focus + this.methodFocus.shadowRoot?.querySelector('md-filled-text-field')?.focus()} + >Focus
- +
diff --git a/packages/leavittbook/src/demos/titanium-youtube-input-demo.ts b/packages/leavittbook/src/demos/titanium-youtube-input-demo.ts index 0da495d96..860a2d842 100644 --- a/packages/leavittbook/src/demos/titanium-youtube-input-demo.ts +++ b/packages/leavittbook/src/demos/titanium-youtube-input-demo.ts @@ -3,16 +3,13 @@ import '../shared/story-header'; import '@leavittsoftware/web/leavitt/app/app-main-content-container'; import '@leavittsoftware/web/leavitt/app/app-navigation-header'; import '@leavittsoftware/web/leavitt/app/app-width-limiter'; -import '@material/web/divider/divider'; import '@material/web/icon/icon'; import '@api-viewer/docs'; import '@material/web/button/filled-tonal-button'; import { css, html, LitElement } from 'lit'; import { customElement, query } from 'lit/decorators.js'; -import '@leavittsoftware/web/titanium/youtube-input/youtube-input'; import '@leavittsoftware/web/titanium/youtube-input/filled-youtube-input'; -import { TitaniumYouTubeInput } from '@leavittsoftware/web/titanium/youtube-input/youtube-input'; import { TitaniumFilledYouTubeInput } from '@leavittsoftware/web/titanium/youtube-input/filled-youtube-input'; import { DOMEvent } from '@leavittsoftware/web/titanium/types/dom-event'; @@ -20,14 +17,12 @@ import StoryStyles from '../styles/story-styles'; @customElement('titanium-youtube-input-demo') export class TitaniumYoutubeInputDemo extends LitElement { - @query('titanium-filled-youtube-input') private accessor filledInput: TitaniumFilledYouTubeInput; - @query('titanium-youtube-input[required]') private accessor outlinedInput: TitaniumYouTubeInput; + @query('titanium-filled-youtube-input[required]') private accessor filledInput: TitaniumFilledYouTubeInput; static styles = [ StoryStyles, css` - titanium-filled-youtube-input, - titanium-youtube-input { + titanium-filled-youtube-input { width: 100%; margin-bottom: 24px; } @@ -41,10 +36,6 @@ export class TitaniumYoutubeInputDemo extends LitElement { - - warning -

titanium-youtube-input is deprecated. Use titanium-filled-youtube-input instead (shown below).

-
@@ -81,56 +72,13 @@ export class TitaniumYoutubeInputDemo extends LitElement {
- - - - - - -
-

Default

-

YouTube video input with URL validation

- -
-

Disabled

Disabled YouTube input

- -
- -
-

Methods

-

Demonstrates public methods like reset and reportValidity

- ) => console.log(e.target.value)} - > -
-
- { - this.outlinedInput.reset(); - }} - >Reset - { - this.outlinedInput.focus(); - }} - >Focus - { - this.outlinedInput.reportValidity(); - }} - >Report validity -
+
- +
diff --git a/packages/leavittbook/src/getting-started.ts b/packages/leavittbook/src/getting-started.ts index f4a0fb911..cad43c5a7 100644 --- a/packages/leavittbook/src/getting-started.ts +++ b/packages/leavittbook/src/getting-started.ts @@ -73,9 +73,9 @@ export default class GettingStarted extends LitElement {

NPM install:

npm i @leavittsoftware/web

Include the element on your page.

- import '@leavittsoftware/web/titanium/card/card'; + import '@leavittsoftware/web/titanium/chip/chip';

Use the element:

- ${''} + ${''}
diff --git a/packages/leavittbook/src/my-app.ts b/packages/leavittbook/src/my-app.ts index 09b16ae67..eb7a8a422 100644 --- a/packages/leavittbook/src/my-app.ts +++ b/packages/leavittbook/src/my-app.ts @@ -3,7 +3,6 @@ import '@leavittsoftware/web/leavitt/app/app-logo'; import '@leavittsoftware/web/titanium/search-input/filled-search-input'; import '@leavittsoftware/web/titanium/toolbar/toolbar'; import '@leavittsoftware/web/titanium/snackbar/snackbar-stack'; -import '@leavittsoftware/web/titanium/error-page/error-page'; import '@leavittsoftware/web/titanium/drawer/drawer'; import '@leavittsoftware/web/leavitt/profile-picture/profile-picture-menu'; import '@leavittsoftware/web/leavitt/user-feedback/report-a-problem-dialog'; @@ -137,16 +136,12 @@ export class MyApp extends PendingStateCatcher(LitElement) { page.show('/getting-started'); }); page('/getting-started', () => this.#changePage('getting-started', () => import('./getting-started.js'))); - page('/titanium-full-page-loading-indicator', () => - this.#changePage('titanium-full-page-loading-indicator', () => import('./demos/titanium-full-page-loading-indicator-demo.js')) - ); page('/available-cdn-icons', () => this.#changePage('available-cdn-icons', () => import('./demos/available-cdn-icons-demo.js'))); page('/leavitt-company-select', () => this.#changePage('leavitt-company-select', () => import('./demos/leavitt-company-select-demo.js'))); page('/leavitt-file-explorer', () => this.#changePage('leavitt-file-explorer', () => import('./demos/leavitt-file-explorer-demo.js'))); page('/titanium-date-range-selector', () => this.#changePage('titanium-date-range-selector', () => import('./demos/titanium-date-range-selector-demo.js'))); - page('/titanium-data-table-item', () => this.#changePage('titanium-data-table-item', () => import('./demos/titanium-data-table-item-demo.js'))); page('/leavitt-person-select', () => this.#changePage('leavitt-person-select', () => import('./demos/leavitt-person-select-demo.js'))); page('/leavitt-person-company-select', () => @@ -156,32 +151,18 @@ export class MyApp extends PendingStateCatcher(LitElement) { page('/leavitt-email-history-viewer', () => this.#changePage('leavitt-email-history-viewer', () => import('./demos/leavitt-email-history-viewer-demo.js'))); - page('/leavitt-user-feedback', () => this.#changePage('leavitt-user-feedback', () => import('./demos/leavitt-user-feedback-demo.js'))); page('/leavitt-error-page', () => this.#changePage('leavitt-error-page', () => import('./demos/leavitt-error-page-demo.js'))); page('/profile-picture', () => this.#changePage('profile-picture', () => import('./demos/profile-picture-demo.js'))); page('/profile-picture-menu', () => this.#changePage('profile-picture-menu', () => import('./demos/profile-picture-menu-demo.js'))); - page('/titanium-access-denied-page', () => this.#changePage('titanium-access-denied-page', () => import('./demos/titanium-access-denied-page-demo.js'))); - page('/titanium-data-table', () => this.#changePage('titanium-data-table', () => import('./demos/titanium-data-table-demo.js'))); - page('/titanium-data-table-item', () => this.#changePage('titanium-data-table-item', () => import('./demos/titanium-data-table-item-demo.js'))); + page('/titanium-data-table-core', () => this.#changePage('titanium-data-table-core', () => import('./demos/titanium-data-table-core-demo.js'))); page('/titanium-drawer', () => this.#changePage('titanium-drawer', () => import('./demos/titanium-drawer-demo.js'))); - page('/titanium-error-page', () => this.#changePage('titanium-error-page', () => import('./demos/titanium-error-page-demo.js'))); page('/titanium-address-input', () => this.#changePage('titanium-address-input', () => import('./demos/titanium-address-input-demo.js'))); - page('/titanium-header', () => this.#changePage('titanium-header', () => import('./demos/titanium-header-demo.js'))); page('/titanium-icon-picker', () => this.#changePage('titanium-icon-picker', () => import('./demos/titanium-icon-picker-demo.js'))); - page('/titanium-header', () => this.#changePage('titanium-header', () => import('./demos/titanium-header-demo.js'))); page('/titanium-chip-multi-select', () => this.#changePage('titanium-chip-multi-select', () => import('./demos/titanium-chip-multi-select-demo.js'))); page('/titanium-input-validator', () => this.#changePage('titanium-input-validator', () => import('./demos/titanium-input-validator-demo.js'))); - page('/titanium-data-table-header', () => this.#changePage('titanium-data-table-header', () => import('./demos/titanium-data-table-header-demo.js'))); - page('/titanium-data-table-core', () => this.#changePage('titanium-data-table-core', () => import('./demos/titanium-data-table-core-demo.js'))); - page('/titanium-promise-tracking', () => - this.#changePage('titanium-promise-tracking', () => import('./demos/titanium-promise-tracking-demo.js')) - ); - - page('/titanium-full-page-loading-indicator', () => - this.#changePage('titanium-full-page-loading-indicator', () => import('./demos/titanium-full-page-loading-indicator-demo.js')) - ); + page('/titanium-promise-tracking', () => this.#changePage('titanium-promise-tracking', () => import('./demos/titanium-promise-tracking-demo.js'))); page('/titanium-page-control', () => this.#changePage('titanium-page-control', () => import('./demos/titanium-page-control-demo.js'))); @@ -196,7 +177,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { page('/titanium-styles', () => this.#changePage('titanium-styles', () => import('./demos/titanium-styles-demo.js'))); page('/titanium-snackbar', () => this.#changePage('titanium-snackbar', () => import('./demos/titanium-snackbar-demo.js'))); - page('/titanium-card', () => this.#changePage('titanium-card', () => import('./demos/titanium-card-demo.js'))); page('/titanium-chip', () => this.#changePage('titanium-chip', () => import('./demos/titanium-chip-demo.js'))); page('/titanium-youtube-input', () => this.#changePage('titanium-youtube-input', () => import('./demos/titanium-youtube-input-demo.js'))); page('/titanium-show-hide', () => this.#changePage('titanium-show-hide', () => import('./demos/titanium-show-hide-demo.js'))); @@ -205,7 +185,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { this.#changePage('titanium-profile-picture-stack', () => import('./demos/titanium-profile-picture-stack-demo.js')) ); - page('/titanium-confirm-dialog', () => this.#changePage('titanium-confirm-dialog', () => import('./demos/titanium-confirm-dialog-demo.js'))); page('/titanium-confirmation-dialog', () => this.#changePage('titanium-confirmation-dialog', () => import('./demos/titanium-confirmation-dialog-demo.js'))); page('*', () => { @@ -296,8 +275,7 @@ export class MyApp extends PendingStateCatcher(LitElement) { ]; render() { - return html` - + return html`

Titanium

- - block Access denied page - - location_on Address input - - dashboard Card - - label Chip @@ -379,30 +349,14 @@ export class MyApp extends PendingStateCatcher(LitElement) { checklist Chip multi select - - help_outline Confirm dialog - - check_circle Confirmation dialog - - table_chart Data table - - table_rows Data table core - - view_column Data table header - - - - format_list_numbered Data table item - - calendar_today Date input @@ -419,18 +373,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { timer Duration input - - error Error page - - - - hourglass_top Full page loading indicator - - - - title Header - - emoji_symbols Icon picker @@ -517,10 +459,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { account_box Profile picture menu - - - feedback User feedback -
` : nothing} - ${this.page === 'leavitt-user-feedback' - ? html` ` - : nothing} ${this.page === 'leavitt-error-page' ? html` ` : nothing} @@ -585,33 +520,15 @@ export class MyApp extends PendingStateCatcher(LitElement) { ${this.page === 'titanium-input-validator' ? html` ` : nothing} - ${this.page === 'titanium-data-table' - ? html` ` - : nothing} ${this.page === 'titanium-data-table-core' ? html` ` : nothing} ${this.page === 'titanium-promise-tracking' - ? html` - - ` - : nothing} - ${this.page === 'titanium-data-table-header' - ? html` ` - : nothing} - ${this.page === 'titanium-data-table-item' - ? html` ` - : nothing} - ${this.page === 'titanium-access-denied-page' - ? html` ` + ? html` ` : nothing} ${this.page === 'titanium-address-input' ? html` ` : nothing} - ${this.page === 'titanium-error-page' - ? html` ` - : nothing} - ${this.page === 'titanium-header' ? html` ` : nothing} ${this.page === 'titanium-icon' ? html` ` : nothing} ${this.page === 'titanium-icon-picker' ? html` ` @@ -628,14 +545,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { ${this.page === 'titanium-toolbar' ? html` ` : nothing} - ${this.page === 'titanium-full-page-loading-indicator' - ? html` - - ` - : nothing} ${this.page === 'titanium-loading-indicator' ? html` ` : nothing} @@ -651,7 +560,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { ` : nothing} - ${this.page === 'titanium-card' ? html` ` : nothing} ${this.page === 'titanium-chip' ? html` ` : nothing} ${this.page === 'titanium-youtube-input' ? html` ` @@ -662,9 +570,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { ${this.page === 'titanium-duration-input' ? html` ` : nothing} - ${this.page === 'titanium-confirm-dialog' - ? html` ` - : nothing} ${this.page === 'titanium-confirmation-dialog' ? html` ` : nothing} @@ -673,7 +578,6 @@ export class MyApp extends PendingStateCatcher(LitElement) { ` : nothing} - - `; } } diff --git a/packages/leavittbook/src/styles/story-styles.ts b/packages/leavittbook/src/styles/story-styles.ts index d20e8bc10..5cb0d8e9c 100644 --- a/packages/leavittbook/src/styles/story-styles.ts +++ b/packages/leavittbook/src/styles/story-styles.ts @@ -11,10 +11,6 @@ const StoryStyles = [ margin-bottom: 48px; } - titanium-card { - margin-bottom: 36px; - } - h1 { margin-bottom: 0; } diff --git a/packages/web/leavitt/company-select/company-select.ts b/packages/web/leavitt/company-select/company-select.ts index 786def96c..6040a6d60 100644 --- a/packages/web/leavitt/company-select/company-select.ts +++ b/packages/web/leavitt/company-select/company-select.ts @@ -98,7 +98,7 @@ export class LeavittCompanySelect extends TitaniumSingleSelectBase>(`${this.apiControllerName}?${this.odataParts.join('&')}`); - this.loadWhile(get); + this.trackLoadingPromise(get); const result = await get; return result?.toList() ?? []; } catch (error) { diff --git a/packages/web/leavitt/email-history-viewer/email-history-view-list-filter-dialog.ts b/packages/web/leavitt/email-history-viewer/email-history-view-list-filter-dialog.ts deleted file mode 100644 index 45619a04f..000000000 --- a/packages/web/leavitt/email-history-viewer/email-history-view-list-filter-dialog.ts +++ /dev/null @@ -1,221 +0,0 @@ -import '../../titanium/date-range-selector/date-range-selector'; - -import '@material/web/dialog/dialog'; -import '@material/web/button/text-button'; -import '@material/web/icon/icon'; -import '@material/web/chips/input-chip'; - -import { dialogZIndexHack } from '../../titanium/hacks/dialog-zindex-hack'; -import { LitElement, PropertyValues, css, html } from 'lit'; -import { customElement, property, query, state } from 'lit/decorators.js'; -import { MdDialog } from '@material/web/dialog/dialog'; -import { LoadWhile } from '../../titanium/helpers/load-while'; -import { FilterController } from '../../titanium/data-table/filter-controller'; -import { rangeLabel } from '../../titanium/date-range-selector/types/range-label'; -import { TitaniumDateRangeSelector } from '../../titanium/date-range-selector/date-range-selector'; -import { DateRangeKey } from '../../titanium/date-range-selector/types/date-range-key'; -import { DateRanges } from '../../titanium/date-range-selector/types/date-ranges'; -import { DOMEvent } from '../../titanium/types/dom-event'; -import { dialogCloseNavigationHack, dialogOpenNavigationHack } from '../../titanium/hacks/dialog-navigation-hack'; -import ApiService from '../api-service/api-service'; -import { EmailTemplate } from '@leavittsoftware/lg-core-typescript'; -import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; -import { MdOutlinedSelect } from '@material/web/select/outlined-select'; - -export type FilterKeys = 'template' | 'startDate' | 'endDate' | 'dateRange'; - -@customElement('leavitt-email-history-view-list-filter-dialog') -export class LeavittEmailHistoryViewListFilterDialog extends LoadWhile(LitElement) { - @property({ type: Boolean }) accessor isActive: boolean; - @property({ type: Object }) accessor apiService: ApiService | null; - - @state() private accessor filterController: FilterController; - @state() private accessor template: Partial[] = []; - @state() private accessor templateId: string; - - #templatesAreDirty = true; - - //Date range props - @state() private accessor startDate: string; - @state() private accessor endDate: string; - - @query('md-dialog') private accessor dialog!: MdDialog; - @query('titanium-date-range-selector') private accessor dateRangeSelect!: TitaniumDateRangeSelector; - - async firstUpdated() { - this.filterController.subscribeToFilterChange(async () => { - this.#preloadChipData(); - this.requestUpdate('filterController'); - }); - } - - async updated(changedProps: PropertyValues) { - if (this.isActive && changedProps.has('isActive')) { - this.#preloadChipData(); - } - } - - async #preloadChipData() { - //Preload for chips - if (this.filterController.getValue('template') && this.#templatesAreDirty) { - this.template = await this.#getTemplatesAsync(); - } - } - - async #getTemplatesAsync() { - if (!this.apiService) { - console.warn('No api service provided'); - return []; - } - - const odataParts = ['select=Id,Name,IsExpired', 'orderby=Name']; - - try { - const get = this.apiService.getAsync(`EmailTemplates?${odataParts.join('&')}`); - this.loadWhile(get); - const entities = (await get).toList(); - this.#templatesAreDirty = false; - return entities; - } catch (error) { - this.dispatchEvent(new ShowSnackbarEvent(error)); - } - return []; - } - - public async open() { - if (this.#templatesAreDirty) { - this.template = await this.#getTemplatesAsync(); - } - - this.templateId = this.filterController.getValue('template') ?? ''; - - //populate date range - const dateRange = this.filterController.getValue('dateRange') as DateRangeKey; - this.startDate = (dateRange === 'custom' ? this.filterController.getValue('startDate') : DateRanges.get(dateRange)?.startDate()) || ''; - this.endDate = (dateRange === 'custom' ? this.filterController.getValue('endDate') : DateRanges.get(dateRange)?.endDate()) || ''; - - this.dialog.show(); - } - - static styles = [ - css` - :host { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; - } - - md-dialog { - max-width: 550px; - width: calc(100vw - 24px); - - div[inactive] { - font-size: 12px; - line-height: 14px; - opacity: 0.8; - } - - md-outlined-select { - width: 100%; - margin-top: 24px; - } - } - - [hidden] { - display: none !important; - } - `, - ]; - - render() { - return html` - { - e.preventDefault(); - this.filterController.setValue('template', null); - }} - > - content_copy - - - { - e.preventDefault(); - this.filterController.setValue('dateRange', null); - this.filterController.setValue('startDate', null); - this.filterController.setValue('endDate', null); - }} - > - date_range - - - ) => { - dialogZIndexHack(e.target); - dialogOpenNavigationHack(e.target); - }} - @close=${(e: DOMEvent) => { - dialogCloseNavigationHack(e.target); - }} - > -
Filter logs by
-
- ) => { - this.startDate = event.target.startDate || ''; - this.endDate = event.target.endDate || ''; - }} - > - - ) => (this.templateId = e.target.value)} - > - content_copy - - ${this.template.map( - (o) => - html` -
${o.Name}
- ${o.IsExpired ? html`
Inactive
` : ''} - content_copy -
` - )} -
-
-
- this.dialog.close('cancel')}> Close - { - this.filterController.setValue('template', this.templateId || null); - - //set date range - this.filterController.setValue('dateRange', this.dateRangeSelect.range === 'allTime' ? null : this.dateRangeSelect.range); - this.filterController.setValue('startDate', this.dateRangeSelect.range === 'custom' ? this.startDate || null : null); - this.filterController.setValue('endDate', this.dateRangeSelect.range === 'custom' ? this.endDate || null : null); - - this.dialog.close('apply'); - }} - >Apply -
-
- `; - } -} diff --git a/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts b/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts index 0237b64b3..8e1ebe292 100644 --- a/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts +++ b/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts @@ -12,7 +12,7 @@ import { dialogZIndexHack } from '../../titanium/hacks/dialog-zindex-hack'; import { LitElement, PropertyValues, css, html } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { MdDialog } from '@material/web/dialog/dialog'; -import { LoadWhile } from '../../titanium/helpers/load-while'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { FilterController } from '../../titanium/data-table/filter-controller'; import { rangeLabel } from '../../titanium/date-range-selector/types/range-label'; import { TitaniumDateRangeSelector } from '../../titanium/date-range-selector/date-range-selector'; @@ -28,7 +28,12 @@ import { MdOutlinedSelect } from '@material/web/select/outlined-select'; export type FilterKeys = 'template' | 'startDate' | 'endDate' | 'dateRange'; @customElement('leavitt-email-history-viewer-filled-filter-dialog') -export class LeavittEmailHistoryViewerFilledFilterDialog extends LoadWhile(LitElement) { +export class LeavittEmailHistoryViewerFilledFilterDialog extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ type: Boolean }) accessor isActive: boolean; @property({ type: Object }) accessor apiService: ApiService | null; @@ -75,7 +80,7 @@ export class LeavittEmailHistoryViewerFilledFilterDialog extends LoadWhile(LitEl try { const get = this.apiService.getAsync(`EmailTemplates?${odataParts.join('&')}`); - this.loadWhile(get); + this.trackLoadingPromise(get); const entities = (await get).toList(); this.#templatesAreDirty = false; return entities; diff --git a/packages/web/leavitt/email-history-viewer/email-history-viewer-filled.ts b/packages/web/leavitt/email-history-viewer/email-history-viewer-filled.ts index f3a65d87f..c379721ff 100644 --- a/packages/web/leavitt/email-history-viewer/email-history-viewer-filled.ts +++ b/packages/web/leavitt/email-history-viewer/email-history-viewer-filled.ts @@ -19,7 +19,7 @@ import { customElement, property, query, state } from 'lit/decorators.js'; import { a, ellipsis } from '../../titanium/styles/styles'; import { getSearchTokens } from '../../titanium/helpers/get-search-token'; import { DOMEvent } from '../../titanium/types/dom-event'; -import { LoadWhile } from '../../titanium/helpers/load-while'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; import { TitaniumPageControl } from '../../titanium/data-table/page-control'; import { PendingStateEvent } from '../../titanium/types/pending-state-event'; @@ -48,7 +48,12 @@ import ApiService from '../api-service/api-service'; type ItemType = Partial; @customElement('leavitt-email-history-viewer-filled') -export default class LeavittEmailHistoryViewerFilled extends LoadWhile(LitElement) { +export default class LeavittEmailHistoryViewerFilled extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ type: Boolean }) public accessor isActive: boolean; @property({ type: Object }) public accessor apiService: ApiService | null; @property({ type: String }) public accessor path: string; @@ -270,8 +275,8 @@ export default class LeavittEmailHistoryViewerFilled extends LoadWhile(LitElemen } const get = this.apiService?.getAsync(`${this.apiControllerName}/?${odataParts.join('&')}`); - this.loadWhile(get); - this.dataTable.loadWhile(get); + this.trackLoadingPromise(get); + this.dataTable.trackLoadingPromise(get); this.dispatchEvent(new PendingStateEvent(get)); const result = await get; return { items: result.toList(), odataCount: result.odataCount }; diff --git a/packages/web/leavitt/email-history-viewer/email-history-viewer.ts b/packages/web/leavitt/email-history-viewer/email-history-viewer.ts deleted file mode 100644 index c16770ad7..000000000 --- a/packages/web/leavitt/email-history-viewer/email-history-viewer.ts +++ /dev/null @@ -1,367 +0,0 @@ -import '../../titanium/data-table/data-table'; -import '../../titanium/data-table/data-table-item'; -import '../../titanium/data-table/data-table-header'; -import '../../titanium/header/header'; -import '../../titanium/search-input/search-input'; - -import './email-history-view-list-filter-dialog'; -import './view-sent-email-dialog'; -import './view-email-template-info-dialog'; - -import '@material/web/icon/icon'; -import '@material/web/iconbutton/icon-button'; -import '@material/web/iconbutton/filled-tonal-icon-button'; - -import dayjs from 'dayjs/esm'; -import { customElement, property, query, state } from 'lit/decorators.js'; -import { EmailTemplateLog } from '@leavittsoftware/lg-core-typescript'; -import { TitaniumDataTable } from '../../titanium/data-table/data-table'; -import { FilterController } from '../../titanium/data-table/filter-controller'; -import { DateRangeKey } from '../../titanium/date-range-selector/types/date-range-key'; -import { DateRanges } from '../../titanium/date-range-selector/types/date-ranges'; -import { Debouncer } from '../../titanium/helpers/debouncer'; -import { getSearchTokens } from '../../titanium/helpers/get-search-token'; -import { LoadWhile } from '../../titanium/helpers/load-while'; -import { TitaniumSearchInput } from '../../titanium/search-input/search-input'; -import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; -import { a } from '../../titanium/styles/a'; -import { ellipsis } from '../../titanium/styles/ellipsis'; -import { DOMEvent } from '../../titanium/types/dom-event'; -import { LitElement, PropertyValues, css, html, nothing } from 'lit'; -import { repeat } from 'lit/directives/repeat.js'; -import { FilterKeys, LeavittEmailHistoryViewListFilterDialog } from './email-history-view-list-filter-dialog'; -import { LeavittViewSentEmailDialog } from './view-sent-email-dialog'; -import ApiService from '../api-service/api-service'; -import { LeavittViewEmailTemplateInfoDialog } from './view-email-template-info-dialog'; -import { isDevelopment } from '@leavittsoftware/web/titanium/helpers/is-development'; - -/** - * @element leavitt-email-history-viewer - - * @description A component that displays a list of email history for embedding in Leavitt's specific applications. - * @property isActive - Whether the component is active / in view - * @property apiService - The API service to use. This is required to fetch the email history. - * @property path - the route that this component will be available at - */ - -@customElement('leavitt-email-history-viewer') -export default class LeavittEmailHistoryViewer extends LoadWhile(LitElement) { - @property({ type: Boolean }) accessor isActive: boolean; - @property({ type: Object }) accessor apiService: ApiService | null; - @property({ type: String }) accessor path: string; - @property({ type: String }) accessor apiControllerName: string = 'EmailTemplateLogs'; - - // Data table props - @state() private accessor logs: Array> = []; - @state() private accessor selected: Array> = []; - @state() private accessor searchTerm: string = ''; - @state() private accessor resultTotal: number = 0; - @state() private accessor sortDirection: '' | 'asc' | 'desc' = 'desc'; - @state() private accessor sortBy: string = 'SentDate'; - @state() private accessor filterController: FilterController; - - @query('titanium-data-table') private accessor dataTable!: TitaniumDataTable; - @query('leavitt-view-sent-email-dialog') private accessor viewDialog!: LeavittViewSentEmailDialog; - @query('leavitt-email-history-view-list-filter-dialog') private accessor filterModal: LeavittEmailHistoryViewListFilterDialog; - @query('leavitt-view-email-template-info-dialog') private accessor viewEmailTemplateInfoDialog!: LeavittViewEmailTemplateInfoDialog; - - #isDirty: boolean = true; - - constructor() { - super(); - this.filterController = new FilterController(''); - this.filterController.setFilter('dateRange', () => ''); - this.filterController.setFilter('startDate', () => ''); - this.filterController.setFilter('endDate', () => ''); - this.filterController.setFilter('template', (val) => `EmailTemplateId eq ${val}`); - - this.filterController.subscribeToFilterChange(async () => { - if (this.isActive) { - this.dataTable.resetPage(); - this.#reload(); - } else { - this.#isDirty = true; - } - }); - this.filterController.loadFromQueryString(); - } - - updated(changedProps: PropertyValues) { - if (this.isActive && (changedProps.has('isActive') || changedProps.has('apiControllerName') || this.#isDirty)) { - this.#reload(); - } - - if (changedProps.has('path')) { - this.filterController.path = this.path; - } - } - - #reload() { - this.#getLogsAsync(this.searchTerm); - } - - #onSortDirectionChange(e: CustomEvent<'' | 'asc' | 'desc'>) { - this.sortDirection = e.detail; - this.dataTable.resetPage(); - this.#reload(); - } - - #onSortByChange(e: CustomEvent) { - this.sortBy = e.detail; - this.dataTable.resetPage(); - this.#reload(); - } - - #doSearchDebouncer = new Debouncer((searchTerm: string) => this.#getLogsAsync(searchTerm)); - - renderRecipients(recipients: string | null, maxRecipients: number = 1) { - const recipientsList = - recipients - ?.split(',') - .filter((o) => !!o) - .map((o) => o.trim()) - .reverse() ?? []; - - if (recipientsList?.length > maxRecipients) { - return html`${repeat( - recipientsList.slice(0, maxRecipients), - (o) => o, - (o) => html`${o}
` - )} ${recipientsList.length - maxRecipients} more... `; - } - return repeat( - recipientsList, - (o) => o, - (o) => html`${o}
` - ); - } - - async #getLogsAsync(searchTerm: string) { - if (!this.apiService) { - console.warn('No api service provided'); - return; - } - - let filterParts: string[] = []; - const searchTokens = getSearchTokens(searchTerm); - const searchFilter = searchTokens.map((token: string) => `(contains(Subject, '${token}') or contains(Recipients, '${token}'))`).join(' and '); - if (searchTokens.length > 0) { - filterParts.push(`${searchFilter}`); - } - - //Date filters - const dateRange = this.filterController.getValue('dateRange') as DateRangeKey; - const startDate = dateRange === 'custom' ? this.filterController.getValue('startDate') : DateRanges.get(dateRange)?.startDate(); - const endDate = dateRange === 'custom' ? this.filterController.getValue('endDate') : DateRanges.get(dateRange)?.endDate(); - if (startDate) { - filterParts.push(`SentDate ge ${dayjs(startDate).format('YYYY-MM-DD')}`); - } - if (endDate) { - filterParts.push(`SentDate le ${dayjs(endDate).format('YYYY-MM-DD')}`); - } - if (!isDevelopment) { - filterParts.push('IsTestMessage eq false'); - } - - filterParts = [...filterParts, ...this.filterController.getActiveFilterOdata()]; - - const odataParts = [ - `select=Id,Recipients,SentDate,Subject${isDevelopment ? ',IsTestMessage' : ''}`, - 'expand=EmailTemplate(select=Id,Name,IsExpired)', - `top=${await this.dataTable.getTake()}`, - `orderby=${this.sortBy} ${this.sortDirection}`, - `skip=${(await this.dataTable.getTake()) * (await this.dataTable.getPage())}`, - 'count=true', - ]; - if (filterParts.length > 0) { - odataParts.push(`filter=${filterParts.join(' and ')}`); - } - try { - const get = this.apiService.getAsync>(`${this.apiControllerName}/?${odataParts.join('&')}`); - this.dataTable.loadWhile(get); - this.loadWhile(get); - const result = await get; - this.resultTotal = result.odataCount; - this.logs = result.toList(); - } catch (error) { - this.dispatchEvent(new ShowSnackbarEvent(error)); - } finally { - this.#isDirty = false; - } - } - - static styles = [ - ellipsis, - a, - css` - :host { - display: grid; - grid-template-columns: minmax(0, 1fr); - gap: 24px; - } - - header { - display: grid; - gap: 12px; - md-text-button { - justify-self: center; - } - } - - [inactive], - span[time], - span[more] { - font-size: 12px; - line-height: 14px; - opacity: 0.8; - } - - md-filled-tonal-icon-button { - --md-filled-tonal-icon-button-container-height: 32px; - --md-filled-tonal-icon-button-icon-size: 21px; - } - - md-text-button { - text-wrap: auto; - } - - [hidden] { - display: none !important; - } - `, - ]; - - render() { - return html` -
- - - this.viewEmailTemplateInfoDialog.open()}> - chat_info - What emails does this tool send? - -
- >>) => { - this.selected = [...e.detail]; - }} - @paging-changed=${() => this.#reload()} - .count=${this.resultTotal} - .items=${this.logs} - .searchTerm=${this.searchTerm} - > - ) => { - this.searchTerm = e.target.value; - this.dataTable.resetPage(); - this.#doSearchDebouncer.debounce(this.searchTerm); - }} - > - - - - this.filterModal.open()}> - filter_list - - - - - - - - - - - ${isDevelopment - ? html`` - : nothing} - - - ${repeat( - this.logs ?? [], - (item) => item.Id, - (item) => html` - - ${item.SentDate - ? html`${dayjs(item.SentDate).format('MMM DD, YY')}
${dayjs(item.SentDate).format('h:mm A')}` - : '-'}
- ${item.Subject ?? '-'} - ${this.renderRecipients(item.Recipients ?? null)} - -
${item.EmailTemplate?.Name}
- ${item.EmailTemplate?.IsExpired ? html`
Inactive
` : ''}
- ${isDevelopment ? html`
${item.IsTestMessage ? 'Yes' : 'No'}
` : nothing} - this.viewDialog.open(item.Id ?? 0)}>pageview - -
- ` - )} -
- - - `; - } -} diff --git a/packages/web/leavitt/email-history-viewer/view-email-template-info-dialog.ts b/packages/web/leavitt/email-history-viewer/view-email-template-info-dialog.ts index 825a9dcc9..752a7a306 100644 --- a/packages/web/leavitt/email-history-viewer/view-email-template-info-dialog.ts +++ b/packages/web/leavitt/email-history-viewer/view-email-template-info-dialog.ts @@ -6,7 +6,7 @@ import '@material/web/progress/circular-progress'; import { css, html, LitElement, nothing } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { EmailTemplate } from '@leavittsoftware/lg-core-typescript'; -import { LoadWhile } from '../../titanium/helpers/load-while'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { MdDialog } from '@material/web/dialog/dialog'; import { DOMEvent } from '../../titanium/types/dom-event'; import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; @@ -21,7 +21,12 @@ import { h2 } from '../../titanium/styles/h2'; export type CloseReason = 'done'; @customElement('leavitt-view-email-template-info-dialog') -export class LeavittViewEmailTemplateInfoDialog extends LoadWhile(LitElement) { +export class LeavittViewEmailTemplateInfoDialog extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ type: Object }) accessor apiService: ApiService | null; @state() private accessor emailTemplates: Partial[] | null; @@ -55,7 +60,7 @@ export class LeavittViewEmailTemplateInfoDialog extends LoadWhile(LitElement) { try { const get = this.apiService.getAsync>(`EmailTemplates?${odataParts.join('&')}`); - this.loadWhile(get); + this.trackLoadingPromise(get); const result = await get; return result?.entities; } catch (error) { diff --git a/packages/web/leavitt/email-history-viewer/view-sent-email-dialog.ts b/packages/web/leavitt/email-history-viewer/view-sent-email-dialog.ts index 7c9d23007..b82236471 100644 --- a/packages/web/leavitt/email-history-viewer/view-sent-email-dialog.ts +++ b/packages/web/leavitt/email-history-viewer/view-sent-email-dialog.ts @@ -10,7 +10,7 @@ import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { css, html, LitElement, nothing } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { EmailTemplateLog } from '@leavittsoftware/lg-core-typescript'; -import { LoadWhile } from '../../titanium/helpers/load-while'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { MdDialog } from '@material/web/dialog/dialog'; import { DOMEvent } from '../../titanium/types/dom-event'; import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; @@ -23,7 +23,12 @@ import { p } from '../../titanium/styles/p'; export type CloseReason = 'done'; @customElement('leavitt-view-sent-email-dialog') -export class LeavittViewSentEmailDialog extends LoadWhile(LitElement) { +export class LeavittViewSentEmailDialog extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ type: Object }) accessor apiService: ApiService | null; @state() private accessor emailTemplateLogId: number | null; @@ -59,7 +64,7 @@ export class LeavittViewSentEmailDialog extends LoadWhile(LitElement) { try { const get = this.apiService.getAsync>(`EmailTemplateLogs(${emailTemplateLogId})?${odataParts.join('&')}`); - this.loadWhile(get); + this.trackLoadingPromise(get); const result = await get; return result?.entity; } catch (error) { diff --git a/packages/web/leavitt/file-explorer/add-folder-modal.ts b/packages/web/leavitt/file-explorer/add-folder-modal.ts index ce2a7ee14..61c195da3 100644 --- a/packages/web/leavitt/file-explorer/add-folder-modal.ts +++ b/packages/web/leavitt/file-explorer/add-folder-modal.ts @@ -11,11 +11,16 @@ import ApiService from '../api-service/api-service'; import { PendingStateEvent } from '../../titanium/types/pending-state-event'; import { DOMEvent } from '../../titanium/types/dom-event'; import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; -import { LoadWhile } from '../../titanium/helpers/helpers'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; @customElement('leavitt-add-folder-modal') -export class AddFolderModal extends LoadWhile(LitElement) { +export class AddFolderModal extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + /** * Required */ @@ -50,7 +55,7 @@ export class AddFolderModal extends LoadWhile(LitElement) { try { const post = this.apiService.postAsync('FileExplorerFolders?expand=CreatorPerson(select=FullName,ProfilePictureCdnFileName)', dto); this.dispatchEvent(new PendingStateEvent(post)); - this.loadWhile(post); + this.trackLoadingPromise(post); const result = (await post)?.entity; return { diff --git a/packages/web/leavitt/file-explorer/file-explorer.ts b/packages/web/leavitt/file-explorer/file-explorer.ts index 358e9e9aa..4decf16eb 100644 --- a/packages/web/leavitt/file-explorer/file-explorer.ts +++ b/packages/web/leavitt/file-explorer/file-explorer.ts @@ -1,4 +1,4 @@ -import { LoadWhile } from '../../titanium/helpers/load-while'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { css, html, LitElement, nothing, PropertyValues } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import ApiService from '../api-service/api-service'; @@ -11,9 +11,8 @@ import { FileExplorerPathDto, } from '@leavittsoftware/lg-core-typescript'; import { PendingStateEvent } from '../../titanium/types/pending-state-event'; -import { ConfirmDialogOpenEvent } from '../../titanium/confirm-dialog/confirm-dialog-open-event'; -import '../../titanium/confirm-dialog/confirm-dialog'; +import '../../titanium/confirmation-dialog/confirmation-dialog'; import fileExplorerEvents from './events/file-explorer-events'; import '@material/web/icon/icon'; @@ -37,7 +36,7 @@ import { a, ellipsis, h1, h2 } from '../../titanium/styles/styles'; import { formatBytes } from './helpers/format-bytes'; import { CloseMenuEvent, MdMenu, MenuItem } from '@material/web/menu/menu'; -import TitaniumConfirmDialog from '../../titanium/confirm-dialog/confirm-dialog'; +import TitaniumConfirmationDialog from '../../titanium/confirmation-dialog/confirmation-dialog'; import { FileModal } from './file-modal'; import { AddFolderModal } from './add-folder-modal'; import { FolderModal } from './folder-modal'; @@ -56,7 +55,12 @@ import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; * @fires file-deleted - Fired when a file is deleted. */ @customElement('leavitt-file-explorer') -export class LeavittFileExplorer extends LoadWhile(LitElement) { +export class LeavittFileExplorer extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + /** * This is required. */ @@ -108,7 +112,7 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { @query('leavitt-file-modal') private accessor fileDialog: FileModal; @query('input[files]') private accessor fileInput: HTMLInputElement; @query('input[folders]') private accessor folderInput: HTMLInputElement; - @query('titanium-confirm-dialog') private accessor confirmDialog: TitaniumConfirmDialog; + @query('titanium-confirmation-dialog') private accessor confirmationDialog: TitaniumConfirmationDialog; #originalFolderId = 0; @@ -116,14 +120,9 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { //force attribute to reflect this.display = structuredClone(this.display); - this.addEventListener(ConfirmDialogOpenEvent.eventType, async (e: ConfirmDialogOpenEvent) => { - e.stopPropagation(); - this.confirmDialog.handleEvent(e); - }); - this.addEventListener(PendingStateEvent.eventType, async (e: PendingStateEvent) => { e.stopPropagation(); - this.loadWhile(e.detail.promise); + this.trackLoadingPromise(e.detail.promise); }); fileExplorerEvents.subscribe('FileExplorerFileDto', 'Update', (o) => { @@ -169,7 +168,7 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { try { const get = this.apiService?.getAsync(`FileExplorers(${fileExplorerId})/FileExplorerView(folderId=${folderId})`); if (get) { - this.loadWhile(get); + this.trackLoadingPromise(get); } const result = await get; @@ -224,12 +223,11 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { * @internal */ async #deleteSelectedClick() { - const confirmationDialogEvent = new ConfirmDialogOpenEvent( + const result = await this.confirmationDialog.open( 'Please confirm delete', `Deleting folders will delete all of their contents. Are you sure you would like to delete the selected item${this.selected.length === 1 ? '' : 's'}?` ); - this.dispatchEvent(confirmationDialogEvent); - if (await confirmationDialogEvent.dialogResult) { + if (result === 'confirmed') { const items = [...this.selected]; const errorMessageToCount: Map = new Map(); let totalErrorCount = 0; @@ -269,7 +267,7 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { } }) ); - this.loadWhile(requests); + this.trackLoadingPromise(requests); await requests; this.selected = []; this.state = this.folders.length > 0 || this.files.length > 0 ? 'files' : 'no-files'; @@ -365,7 +363,7 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { }); const uploadAll = Throttle.all(requests, { maxInProgress: 4 }); - this.loadWhile(uploadAll); + this.trackLoadingPromise(uploadAll); await uploadAll; if (failedFiles.length > 0) { @@ -408,7 +406,7 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { }); const uploadAll = Throttle.all(requests, { maxInProgress: 4 }); - this.loadWhile(uploadAll); + this.trackLoadingPromise(uploadAll); await uploadAll; if (failedFiles.length > 0) { @@ -864,7 +862,7 @@ export class LeavittFileExplorer extends LoadWhile(LitElement) { > - + `; } } diff --git a/packages/web/leavitt/file-explorer/file-modal.ts b/packages/web/leavitt/file-explorer/file-modal.ts index 958ecafb9..1f42e9368 100644 --- a/packages/web/leavitt/file-explorer/file-modal.ts +++ b/packages/web/leavitt/file-explorer/file-modal.ts @@ -20,11 +20,16 @@ import { MdDialog } from '@material/web/dialog/dialog'; import { DOMEvent } from '../../titanium/types/dom-event'; import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; import ApiService from '../api-service/api-service'; -import { LoadWhile } from '../../titanium/helpers/helpers'; +import { promiseTracking } from '../../titanium/helpers/helpers'; import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; @customElement('leavitt-file-modal') -export class FileModal extends LoadWhile(LitElement) { +export class FileModal extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ attribute: false }) accessor apiService: ApiService | null; @property({ type: Boolean }) accessor enableEditing: boolean = false; @@ -58,7 +63,7 @@ export class FileModal extends LoadWhile(LitElement) { try { const patch = this.apiService.patchAsync(`FileExplorerAttachments(${this.file?.Id})`, dto); - this.loadWhile(patch); + this.trackLoadingPromise(patch); await patch; fileExplorerEvents.dispatch('FileExplorerFileDto', 'Update', { ...this.file, Name: this.fileName }); this.state = 'view'; diff --git a/packages/web/leavitt/file-explorer/folder-modal.ts b/packages/web/leavitt/file-explorer/folder-modal.ts index 544a14a08..e905e9cb2 100644 --- a/packages/web/leavitt/file-explorer/folder-modal.ts +++ b/packages/web/leavitt/file-explorer/folder-modal.ts @@ -16,11 +16,16 @@ import { MdDialog } from '@material/web/dialog/dialog'; import { DOMEvent } from '../../titanium/types/dom-event'; import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; import ApiService from '../api-service/api-service'; -import { LoadWhile } from '../../titanium/helpers/helpers'; +import { promiseTracking } from '../../titanium/helpers/helpers'; import { ShowSnackbarEvent } from '../..//titanium/snackbar/show-snackbar-event'; @customElement('leavitt-folder-modal') -export class FolderModal extends LoadWhile(LitElement) { +export class FolderModal extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ attribute: false }) accessor apiService: ApiService | null; @property({ type: Boolean }) accessor enableEditing: boolean = false; @@ -49,7 +54,7 @@ export class FolderModal extends LoadWhile(LitElement) { try { const patch = this.apiService.patchAsync(`FileExplorerFolders(${this.folder?.Id})`, dto); - this.loadWhile(patch); + this.trackLoadingPromise(patch); await patch; fileExplorerEvents.dispatch('FileExplorerFolder', 'Update', { ...this.folder, Name: this.folderName }); this.state = 'view'; diff --git a/packages/web/leavitt/person-company-select/person-company-select.ts b/packages/web/leavitt/person-company-select/person-company-select.ts index 18f8277f1..0ecf27f7d 100644 --- a/packages/web/leavitt/person-company-select/person-company-select.ts +++ b/packages/web/leavitt/person-company-select/person-company-select.ts @@ -68,7 +68,7 @@ export class LeavittPersonCompanySelect extends TitaniumSingleSelectBase(`${this.apiControllerName}?${oDataParts.join('&')}`, { abortController: this.#abortController }); - this.loadWhile(get); + this.trackLoadingPromise(get); const result = await get; this.showSuggestions(result?.entities ?? [], result?.odataCount ?? 0); diff --git a/packages/web/leavitt/user-feedback/provide-feedback-dialog.ts b/packages/web/leavitt/user-feedback/provide-feedback-dialog.ts index c54c748b6..17d39a39f 100644 --- a/packages/web/leavitt/user-feedback/provide-feedback-dialog.ts +++ b/packages/web/leavitt/user-feedback/provide-feedback-dialog.ts @@ -5,8 +5,8 @@ import '@material/web/button/text-button'; import '@leavittsoftware/web/titanium/snackbar/snackbar-stack'; import { LitElement, css, html } from 'lit'; -import { customElement, property, query } from 'lit/decorators.js'; -import { LoadWhile, isDevelopment } from '../../titanium/helpers/helpers'; +import { customElement, property, query, state } from 'lit/decorators.js'; +import { promiseTracking, isDevelopment } from '../../titanium/helpers/helpers'; import { PendingStateEvent } from '../../titanium/types/pending-state-event'; import { h1, p } from '../../titanium/styles/styles'; import { IssueDto } from '@leavittsoftware/lg-core-typescript'; @@ -21,7 +21,12 @@ import { dialogCloseNavigationHack, dialogOpenNavigationHack } from '../../titan import { AuthZeroLgUserManager } from '../user-manager/auth-zero-lg-user-manager'; @customElement('provide-feedback-dialog') -export class ProvideFeedbackDialog extends LoadWhile(LitElement) { +export class ProvideFeedbackDialog extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ type: Object }) accessor userManager: AuthZeroLgUserManager; @query('md-dialog') private accessor dialog!: MdDialog; @query('titanium-snackbar-stack') private accessor snackbar: SnackbarStack; @@ -57,7 +62,7 @@ export class ProvideFeedbackDialog extends LoadWhile(LitElement) { apiService.addHeader('X-LGAppName', 'IssueTracking'); const post = apiService.postAsync('Issues/ReportIssue', dto, { sendAsFormData: true }); this.dispatchEvent(new PendingStateEvent(post)); - this.loadWhile(post); + this.trackLoadingPromise(post); const entity = (await post).entity; if (!entity) { diff --git a/packages/web/leavitt/user-feedback/report-a-problem-dialog.ts b/packages/web/leavitt/user-feedback/report-a-problem-dialog.ts index 4c6d552e6..ad3c9341e 100644 --- a/packages/web/leavitt/user-feedback/report-a-problem-dialog.ts +++ b/packages/web/leavitt/user-feedback/report-a-problem-dialog.ts @@ -6,8 +6,8 @@ import '../../titanium/snackbar/snackbar-stack'; import '../../titanium/smart-attachment-input/smart-attachment-input'; import { LitElement, css, html } from 'lit'; -import { customElement, property, query } from 'lit/decorators.js'; -import { LoadWhile, isDevelopment } from '../../titanium/helpers/helpers'; +import { customElement, property, query, state } from 'lit/decorators.js'; +import { promiseTracking, isDevelopment } from '../../titanium/helpers/helpers'; import { PendingStateEvent } from '../../titanium/types/pending-state-event'; import { h1, p } from '../../titanium/styles/styles'; import { IssueDto } from '@leavittsoftware/lg-core-typescript'; @@ -23,7 +23,12 @@ import { TitaniumSmartAttachmentInput } from '../../titanium/smart-attachment-in import { AuthZeroLgUserManager } from '../user-manager/auth-zero-lg-user-manager'; @customElement('report-a-problem-dialog') -export class ReportAProblemDialog extends LoadWhile(LitElement) { +export class ReportAProblemDialog extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ type: Object }) accessor userManager: AuthZeroLgUserManager; @query('md-dialog') private accessor dialog!: MdDialog; @query('titanium-snackbar-stack') private accessor snackbar: SnackbarStack; @@ -61,7 +66,7 @@ export class ReportAProblemDialog extends LoadWhile(LitElement) { apiService.addHeader('X-LGAppName', 'IssueTracking'); const post = apiService.postAsync('Issues/ReportIssue', dto, { sendAsFormData: true }); this.dispatchEvent(new PendingStateEvent(post)); - this.loadWhile(post); + this.trackLoadingPromise(post); const entity = (await post).entity; if (!entity) { diff --git a/packages/web/leavitt/user-feedback/user-feedback.ts b/packages/web/leavitt/user-feedback/user-feedback.ts deleted file mode 100644 index 744c32347..000000000 --- a/packages/web/leavitt/user-feedback/user-feedback.ts +++ /dev/null @@ -1,210 +0,0 @@ -import '../../titanium/header/header'; -import '../../titanium/card/card'; -import '../../titanium/smart-attachment-input/smart-attachment-input'; - -import '@material/web/button/filled-tonal-button'; -import '@material/web/textfield/outlined-text-field'; -import '@material/web/tabs/primary-tab'; -import '@material/web/tabs/tabs'; - -import { LitElement, PropertyValues, css, html } from 'lit'; -import { customElement, property, query, state } from 'lit/decorators.js'; -import { LoadWhile, isDevelopment } from '../../titanium/helpers/helpers'; -import { PendingStateEvent } from '../../titanium/types/pending-state-event'; -import { h1, p } from '../../titanium/styles/styles'; -import { IssueDto } from '@leavittsoftware/lg-core-typescript'; -import { TitaniumSmartAttachmentInput } from '../../titanium/smart-attachment-input/smart-attachment-input'; -import ApiService from '../api-service//api-service'; -import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; -import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; -import { AuthZeroLgUserManager } from '../user-manager/auth-zero-lg-user-manager'; - -@customElement('leavitt-user-feedback') -export class LeavittUserFeedback extends LoadWhile(LitElement) { - @property({ type: Object }) accessor userManager: AuthZeroLgUserManager; - @property({ type: Boolean }) accessor isActive: boolean = false; - - @state() private accessor activeIndex: number = 0; - - @query('md-outlined-text-field') private accessor textArea: MdOutlinedTextField; - @query('titanium-smart-attachment-input') private accessor imageInput: TitaniumSmartAttachmentInput | null; - - async updated(changedProps: PropertyValues) { - if (changedProps.has('isActive') && this.isActive) { - this.reset(); - } - } - - reset() { - this.imageInput?.reset(); - this.textArea?.reset(); - } - - async #submitProblem() { - if (!this.textArea.reportValidity() || this.isLoading) { - return; - } - - const dto: IssueDto = { - SiteName: location.hostname, - PathName: window.location.pathname + window.location.search, - IssueType: 'Bug', - Description: this.textArea.value, - Attachments: (this.imageInput?.getFiles() ?? []).map((o) => o.file), - }; - - try { - const apiService = new ApiService(this.userManager); - apiService.baseUrl = isDevelopment ? 'https://devapi3.leavitt.com/' : 'https://api3.leavitt.com/'; - apiService.addHeader('X-LGAppName', 'IssueTracking'); - const post = apiService.postAsync('Issues/ReportIssue', dto, { sendAsFormData: true }); - this.dispatchEvent(new PendingStateEvent(post)); - this.loadWhile(post); - const entity = (await post).entity; - - if (!entity) { - throw new Error('Error submitting problem. Please try again.'); - } else { - this.dispatchEvent( - new ShowSnackbarEvent('', { - overrideTemplate: html`Thank you for bringing this issue to our attention!
-
- Our engineering teams will promptly investigate and address it.`, - }) - ); - this.reset(); - } - } catch (error) { - this.dispatchEvent(new ShowSnackbarEvent(error)); - } - } - - async #submitFeedback() { - if (!this.textArea.reportValidity() || this.isLoading) { - return; - } - - const dto: IssueDto = { - SiteName: location.hostname, - PathName: window.location.pathname + window.location.search, - IssueType: 'Feedback', - Description: this.textArea.value, - Attachments: [], - }; - - try { - const apiService = new ApiService(this.userManager); - apiService.baseUrl = isDevelopment ? 'https://devapi3.leavitt.com/' : 'https://api3.leavitt.com/'; - apiService.addHeader('X-LGAppName', 'IssueTracking'); - const post = apiService.postAsync('Issues/ReportIssue', dto, { sendAsFormData: true }); - this.dispatchEvent(new PendingStateEvent(post)); - this.loadWhile(post); - const entity = (await post).entity; - - if (!entity) { - throw new Error('Error submitting feedback. Please try again.'); - } else { - this.dispatchEvent(new ShowSnackbarEvent('We appreciate your input, and we will promptly conduct a review!')); - this.reset(); - } - } catch (error) { - this.dispatchEvent(new ShowSnackbarEvent(error)); - } - } - - static styles = [ - h1, - p, - css` - :host { - display: grid; - grid-template-columns: minmax(0, 1fr); - gap: 24px; - } - - form { - display: flex; - flex-direction: column; - gap: 24px; - margin: 24px 0; - } - - titanium-card { - padding-top: 0; - } - - md-tabs { - margin-bottom: 16px; - --md-primary-tab-container-shape: 12px; - } - - md-outlined-text-field { - resize: none; - } - - [hidden] { - display: none !important; - } - `, - ]; - - render() { - return html` - - - - { - this.reset(); - this.activeIndex = event.target.activeTabIndex; - }} - > - Report a problem - person_alert - - Provide feedback - rate_review - - - -
- ${this.activeIndex === 0 - ? html`
-

- Please be specific and provide screenshots of the issue if possible in your report. Your report goes directly to our engineering teams so it - can be addressed as soon as possible. -

- - -
` - : html`
-

- User feedback is a valuable tool that empowers our users to share their thoughts, suggestions, and concerns, helping us improve the overall - user experience of our websites and tools. We welcome and appreciate user feedback as it enables us to make informed decisions and enhance our - website based on the needs and expectations of our users. -

-

- Please be specific and provide as much detail as possible in your feedback. Your feedback goes directly to our development teams so it can be - carefully reviewed and planned into the next development cycle. -

- -
`} -
- - (this.activeIndex === 0 ? this.#submitProblem() : this.#submitFeedback())} ?disabled=${this.isLoading} - >Submit - -
- `; - } -} diff --git a/packages/web/titanium/access-denied-page/access-denied-page.ts b/packages/web/titanium/access-denied-page/access-denied-page.ts deleted file mode 100644 index 45f39be5b..000000000 --- a/packages/web/titanium/access-denied-page/access-denied-page.ts +++ /dev/null @@ -1,352 +0,0 @@ -import { css, html, LitElement } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; - -/** - * A pre-styled access denied page - * - * @element titanium-access-denied-page - * - */ -@customElement('titanium-access-denied-page') -export class TitaniumAccessDeniedPage extends LitElement { - /** - * Reason text for the denial of access - */ - @property({ type: String }) accessor message: string = 'You do not have permission to access this application.'; - - static styles = css` - :host { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - - font-family: Roboto, sans-serif; - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - - max-width: 1300px; - } - - header { - flex: 1 1 auto; - margin-right: 24px; - } - - h1 { - font-family: Metropolis, 'Roboto', 'Noto', sans-serif; - font-weight: 600; - font-size: 68px; - line-height: 75px; - margin: 0; - } - - h2 { - font-weight: 400; - margin: 8px 0 0 4px; - max-width: 75%; - } - - p { - font-size: 16px; - margin-top: 8px; - } - - svg { - flex-shrink: 0; - height: 280px; - width: 280px; - } - - @media (max-width: 768px) { - :host { - margin-top: 24px; - } - - h2 { - max-width: inherit; - font-size: 21px; - } - - svg { - height: 120px; - width: 120px; - align-self: flex-start; - } - - h1 { - font-size: 55px; - line-height: 65px; - } - } - `; - - render() { - return html` -
-

Access denied!

-

${this.message}

-
- - - - - - - - - authentication - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - `; - } -} diff --git a/packages/web/titanium/address-input/google-address-input.ts b/packages/web/titanium/address-input/google-address-input.ts index ca13327e0..b586a3701 100644 --- a/packages/web/titanium/address-input/google-address-input.ts +++ b/packages/web/titanium/address-input/google-address-input.ts @@ -126,7 +126,7 @@ export class GoogleAddressInput extends TitaniumSingleSelectBase - { - this.hasFooter = this.assignedElements.some((el) => el.hasAttribute('card-footer')); - this.hasImage = this.assignedElements.some((el) => el.hasAttribute('card-image')); - this.hasMenu = this.assignedElements.some((el) => el.hasAttribute('card-menu')); - }} - > - `; - } -} diff --git a/packages/web/titanium/chip-multi-select/chip-multi-select.ts b/packages/web/titanium/chip-multi-select/chip-multi-select.ts index a9580d0e4..0dc55d7f1 100644 --- a/packages/web/titanium/chip-multi-select/chip-multi-select.ts +++ b/packages/web/titanium/chip-multi-select/chip-multi-select.ts @@ -1,8 +1,7 @@ import { css, LitElement, PropertyValues } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; -import { literal, html } from 'lit/static-html.js'; +import { html } from 'lit'; -import '../../titanium/input-validator/outlined-input-validator'; import '../../titanium/input-validator/filled-input-validator'; /** @@ -66,7 +65,7 @@ export class TitaniumChipMultiSelect extends LitElement { */ @property({ type: Boolean, reflect: true }) accessor disabled: boolean; - @query('titanium-outlined-input-validator, titanium-filled-input-validator') private accessor validator: + @query('titanium-filled-input-validator') private accessor validator: | { checkValidity: () => boolean; reportValidity: () => boolean; reset: () => void } | undefined; @@ -104,7 +103,6 @@ export class TitaniumChipMultiSelect extends LitElement { width: 100%; } - titanium-outlined-input-validator, titanium-filled-input-validator { display: block; width: 100%; @@ -165,9 +163,8 @@ export class TitaniumChipMultiSelect extends LitElement { ]; protected render() { - /* eslint-disable lit/binding-positions, lit/no-invalid-html */ return html` - <${this.filled ? literal`titanium-filled-input-validator` : literal`titanium-outlined-input-validator`} + !this.required || !!this.hasItems} ?required=${this.required} @@ -181,8 +178,7 @@ export class TitaniumChipMultiSelect extends LitElement { ${!this.hasItems ? html` ${this.noItemsText}` : ''} - + `; - /* eslint-enable lit/binding-positions, lit/no-invalid-html */ } } diff --git a/packages/web/titanium/confirm-dialog/confirm-dialog-open-event.ts b/packages/web/titanium/confirm-dialog/confirm-dialog-open-event.ts deleted file mode 100644 index e4c385dc4..000000000 --- a/packages/web/titanium/confirm-dialog/confirm-dialog-open-event.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TemplateResult } from 'lit'; - -export class ConfirmDialogOpenEvent extends Event { - static eventType = 'confirm-dialog-open'; - header: string; - text: string | TemplateResult; - dialogResult: Promise; - resolver: (confirmed: boolean) => void; - - constructor(header: string, text: string | TemplateResult) { - super(ConfirmDialogOpenEvent.eventType, { bubbles: true, composed: true }); - this.header = header; - this.text = text; - this.dialogResult = new Promise((res) => { - this.resolver = res; - }); - } -} diff --git a/packages/web/titanium/confirm-dialog/confirm-dialog.ts b/packages/web/titanium/confirm-dialog/confirm-dialog.ts deleted file mode 100644 index e8cac1bbb..000000000 --- a/packages/web/titanium/confirm-dialog/confirm-dialog.ts +++ /dev/null @@ -1,95 +0,0 @@ -import '@material/web/dialog/dialog'; -import '@material/web/button/text-button'; - -import { css, html, LitElement, TemplateResult } from 'lit'; -import { customElement, query, state } from 'lit/decorators.js'; -import { ConfirmDialogOpenEvent } from './confirm-dialog-open-event'; -import { MdDialog } from '@material/web/dialog/dialog'; -import { DOMEvent } from '../types/dom-event'; -import { p } from '../styles/p'; -import { dialogZIndexHack } from '../hacks/dialog-zindex-hack'; -import { dialogCloseNavigationHack, dialogOpenNavigationHack } from '../hacks/dialog-navigation-hack'; - -@customElement('titanium-confirm-dialog') -export default class TitaniumConfirmDialog extends LitElement { - @state() protected accessor text: string | TemplateResult; - @state() protected accessor headline: string; - @query('md-dialog') protected accessor dialog!: MdDialog; - #resolve: (value: 'confirmed' | 'cancel') => void; - - /** - * This method is used to set up the event listener to capture the confirm dialog open event - * You can skip using this method and set up the event listener yourself (recommended) - */ - listenOn(el: HTMLElement) { - el.addEventListener(ConfirmDialogOpenEvent.eventType, async (event: ConfirmDialogOpenEvent) => { - this.#openDialogWithEvent(event); - }); - } - - /** - * This method is used after capturing the confirm dialog open event - * usually in the same class where the confirm-dialog element is used. - * After capturing the event pass it directly into this method `this.dialog.handleEvent(e);` - */ - async handleEvent(event: ConfirmDialogOpenEvent) { - this.#openDialogWithEvent(event); - } - - #openDialogWithEvent = async (event: ConfirmDialogOpenEvent) => { - this.headline = event.header; - this.text = event.text; - this.dialog.returnValue = ''; - this.dialog.show(); - - const result = await new Promise<'confirmed' | 'cancel'>((resolve) => { - this.#resolve = resolve; - }); - - event.resolver(result === 'confirmed'); - }; - - static styles = [ - p, - css` - p { - margin: 6px 24px 0 24px; - } - - md-dialog { - max-width: 550px; - max-height: calc(100vh - 24px); - } - - b, - strong { - font-weight: 500; - } - `, - ]; - - render() { - return html` - ) => { - dialogZIndexHack(e.target); - dialogOpenNavigationHack(e.target); - }} - @close=${(e: DOMEvent) => { - dialogCloseNavigationHack(e.target); - if (e.target.returnValue === 'confirmed') { - return this.#resolve('confirmed'); - } - return this.#resolve('cancel'); - }} - > -
${this.headline}
-

${this.text}

-
- this.dialog.close('cancel')}>Cancel - this.dialog.close('confirmed')}>Confirm -
-
- `; - } -} diff --git a/packages/web/titanium/data-table/data-table-core-reorder-dialog.ts b/packages/web/titanium/data-table/data-table-core-reorder-dialog.ts index 7aa40f278..99f8a1b88 100644 --- a/packages/web/titanium/data-table/data-table-core-reorder-dialog.ts +++ b/packages/web/titanium/data-table/data-table-core-reorder-dialog.ts @@ -17,14 +17,19 @@ import { DOMEvent } from '../types/dom-event'; import { MdDialog } from '@material/web/dialog/dialog'; import { ItemDropEvent } from './draggable-item-base'; import { repeat } from 'lit/directives/repeat.js'; -import { LoadWhile } from '../helpers/load-while'; +import { promiseTracking } from '../helpers/promise-tracking'; import { ShowSnackbarEvent } from '../snackbar/show-snackbar-event'; import { SnackbarStack } from '@leavittsoftware/web/titanium/snackbar/snackbar-stack'; export type CloseReason = 'apply' | 'cancel'; @customElement('titanium-data-table-core-reorder-dialog') -export class TitaniumDataTableCoreReorderDialog extends LoadWhile(LitElement) { +export class TitaniumDataTableCoreReorderDialog extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @property({ type: Object }) accessor tableMetaData: TitaniumDataTableCoreMetaData | null = null; @property({ type: Object }) accessor supplementalItemStyles: CSSResult | CSSResultGroup | null = null; @@ -148,7 +153,7 @@ export class TitaniumDataTableCoreReorderDialog extends LoadWh _resolve = resolve; _reject = reject; }); - this.loadWhile(saving); + this.trackLoadingPromise(saving); this.dispatchEvent( new CustomEvent<{ resolve: () => void; reject: (reason: any) => void; items: Array }>('reorder-save-request', { detail: { resolve: _resolve, reject: _reject, items: this.items }, diff --git a/packages/web/titanium/data-table/data-table-core.ts b/packages/web/titanium/data-table/data-table-core.ts index 3701bf003..233d82aa1 100644 --- a/packages/web/titanium/data-table/data-table-core.ts +++ b/packages/web/titanium/data-table/data-table-core.ts @@ -21,7 +21,7 @@ import { MdCheckbox } from '@material/web/checkbox/checkbox'; import { repeat } from 'lit/directives/repeat.js'; import { a } from '@leavittsoftware/web/titanium/styles/a'; import { styleMap } from 'lit/directives/style-map.js'; -import { LoadWhile } from '@leavittsoftware/web/titanium/helpers/load-while'; +import { promiseTracking } from '@leavittsoftware/web/titanium/helpers/promise-tracking'; import { niceBadgeStyles } from '../styles/nice-badge'; import { MdIconButton } from '@material/web/iconbutton/icon-button'; import { CloseMenuEvent, MdMenu, MenuItem } from '@material/web/menu/menu'; @@ -78,7 +78,12 @@ export function generateDefaultSortFromMetaData(tableMetaData: } @customElement('titanium-data-table-core') -export class TitaniumDataTableCore extends LoadWhile(LitElement) { +export class TitaniumDataTableCore extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + /** * Current items displayed on the table. */ diff --git a/packages/web/titanium/data-table/data-table-header.ts b/packages/web/titanium/data-table/data-table-header.ts deleted file mode 100644 index 3604039ee..000000000 --- a/packages/web/titanium/data-table/data-table-header.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { css, html, LitElement, PropertyValues } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; - -import '@material/web/icon/icon'; -import '@material/web/ripple/ripple'; -import '@material/web/focus/md-focus-ring'; - -/** - * Material design data table header with styling and sorting capabilities - * - * @element titanium-data-table-header - * - * @fires sort-direction-changed - Fired if sort direction is changed (detail: 'desc' | 'asc') - * @fires sort-by-changed - Fired when the close button is clicked (detail: {string} column name of currently sorted header ) - * - * @cssprop {font} [--titanium-data-table-font-family=Roboto, Noto, sans-serif] - Font family - */ -@customElement('titanium-data-table-header') -export class TitaniumDataTableHeader extends LitElement { - /** - * This displayed header name - */ - @property({ type: String }) accessor title: string; - - /** - * The column name of the currently applied sort - */ - @property({ type: String }) accessor sortBy: string; - - /** - * Optional fixed width of header in px ex. "140px" - */ - @property({ reflect: true, type: String }) accessor width: string; - - /** - * True if header is currently the sorted column. Read-only, do not set. - */ - @property({ type: Boolean, reflect: true }) accessor active: boolean = false; - - /** - * Current sort direction on header. - */ - @property({ type: String, reflect: true, attribute: 'sort-direction' }) accessor sortDirection: 'asc' | 'desc' | ''; - - /** - * Name of header column passed along in sort-by-changed event. Typically the name of the col in the backing DB. ex. first_name - */ - @property({ type: String, attribute: 'column-name' }) accessor columnName: string; - - /** - * Justify header text center - */ - @property({ type: Boolean, reflect: true }) accessor center: boolean = false; - - /** - * Justify header text right; moves sort icon to left. - */ - @property({ type: Boolean, reflect: true }) accessor right: boolean = false; - - /** - * Removes the sort icon - */ - @property({ type: Boolean, reflect: true, attribute: 'no-sort' }) accessor noSort: boolean = false; - - /** - * Set flex 5 on header, default is 3. - */ - @property({ type: Boolean, reflect: true }) accessor large: boolean = false; - - /** - * Only show this header when width is larger - */ - @property({ type: Boolean, reflect: true }) accessor desktop: boolean = false; - - /** - * Sets if view port is small - */ - @property({ type: Boolean, reflect: true }) accessor narrow: boolean = false; - - updated(changedProps: PropertyValues) { - if (changedProps.has('sortBy') && changedProps.get('sortBy') !== this.sortBy) { - this.active = this.sortBy === this.columnName; - } - - if (changedProps.has('width') && changedProps.get('width') !== this.width && this.width) { - this.style.width = this.width; - } - } - - static styles = css` - :host { - display: flex; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - - text-align: left; - } - - button { - display: flex; - flex-direction: row; - align-items: center; - - position: relative; - --md-focus-ring-shape: 0; - - font-family: var(--titanium-data-table-font-family, Roboto, Noto, sans-serif); - font-size: 14px; - padding: 8px; - - /* cancel padding so text aligns */ - margin: 0 -8px; - - line-height: 28px; - font-weight: 500; - height: 100%; - - /* override default button styles */ - text-align: inherit; - - cursor: pointer; - - background-color: inherit; - color: inherit; - - border: none; - outline: none; - } - - :host([right]) { - justify-content: end; - text-align: right; - } - - :host([center]) { - justify-content: center; - text-align: center; - } - - button:focus, - button:active { - outline: none; - box-shadow: none; - } - - :host(:not([width])) { - -ms-flex: 3; - -webkit-flex: 3; - flex: 3; - } - - :host(:not([width])[large]) { - -ms-flex: 5; - -webkit-flex: 5; - flex: 5; - } - - :host([hidden]) { - display: none !important; - } - - :host([no-sort]) button { - cursor: inherit; - } - - :host([center]) button { - margin-left: 22px; - } - - :host([right]) button { - flex-direction: row-reverse; - } - - md-icon { - display: block; - height: 18px; - width: 18px; - font-size: 18px; - margin-left: 4px; - flex-shrink: 0; - transform-origin: center; - transition: transform 150ms ease; - } - - :host([no-sort]) button md-icon { - display: none; - } - - :host([right]) md-icon { - display: block; - margin-left: 0; - margin-right: 4px; - } - - :host([width]) span { - word-break: break-all; - } - - md-icon { - visibility: hidden; - } - - :host([active][sort-direction='asc']) md-icon { - transform: rotate(-180deg); - } - - :host([active][sort-direction='asc']) md-icon, - :host([active][sort-direction='desc']) md-icon { - visibility: visible; - } - - :host([narrow][desktop]) { - display: none; - } - `; - - render() { - return html` - - `; - } -} diff --git a/packages/web/titanium/data-table/data-table-item.ts b/packages/web/titanium/data-table/data-table-item.ts deleted file mode 100644 index c8dbfde2c..000000000 --- a/packages/web/titanium/data-table/data-table-item.ts +++ /dev/null @@ -1,540 +0,0 @@ -import { css, html, LitElement, nothing, PropertyValues } from 'lit'; -import { property, customElement, state } from 'lit/decorators.js'; -import { TitaniumDataTable } from './data-table'; - -import '@material/web/checkbox/checkbox'; -import '@material/web/icon/icon'; - -/** - * A data table element to organize row data and handle row selection. - * - * row-item positioning attributes: - * - right - * - desktop - * - large - * - center - * - width - ex. "140px" - * - * @element titanium-data-table-item - * - * @fires titanium-data-table-item-navigate - Fired on double click of a row. detail: unknown(this.item) - * @fires titanium-data-table-item-selected-changed - Fired when item is selected. detail: { isSelected: boolean, item: unknown } - * @fires titanium-data-table-item-drop - Fired when item is dropped after a drag - * - * @slot default - Main slot that should contain a list of row-item elements - * @slot item-footer - Optional footer content below the row with the row-items - * - * @cssprop {Color} [--md-sys-color-secondary-container] - Row selected color - * @cssprop {Color} [--md-sys-color-on-surface] - Row hover color - * @cssprop {Color} [--md-sys-color-outline-variant] - Bottom division line - * @cssprop [--titanium-data-table-font-family=Roboto, Noto, sans-serif] - Set the font family of the data table item - */ -@customElement('titanium-data-table-item') -export class TitaniumDataTableItem extends LitElement { - /** - * The backing object that is displayed in this row. Sent in navigate and selected events. - */ - @property({ type: Object }) accessor item: unknown; - - /** - * True when row is selected. - */ - @property({ reflect: true, type: Boolean }) accessor selected: boolean = false; - - /** - * Disables ability to select this row. - */ - @property({ type: Boolean, reflect: true, attribute: 'disable-select' }) accessor disableSelect: boolean; - - /** - * Sets if view port is small - */ - @property({ type: Boolean, reflect: true }) accessor narrow: boolean = false; - - /** - * Set to true to make item draggable. When items are dropped, the items in the list's array are sorted accordingly. - * In order to reflect those updates out to the DOM, you will need to call requestUpdate on the items array when - * items are dropped. ex. - * - * this.requestUpdate('items')} ... > - * - */ - @property({ type: Boolean, reflect: true, attribute: 'enable-dragging' }) accessor enableDrag: boolean = false; - - @property({ type: Boolean, reflect: true, attribute: 'nudge-down' }) protected accessor nudgeDown: boolean; - @property({ type: Boolean, reflect: true, attribute: 'nudge-up' }) protected accessor nudgeUp: boolean; - @property({ type: Boolean, reflect: true, attribute: 'dragged' }) protected accessor dragged: boolean; - @property({ type: Boolean, reflect: true, attribute: 'dragging' }) protected accessor dragging: boolean; - - @state() protected accessor nudgeHeight: number; - @state() protected accessor hoverIndex: number | null = null; - @state() protected accessor originIndex: number | null = null; - - protected mouseEvent = (e) => this.#startItemDrag(e, 'mouse'); - protected touchEvent = (e) => { - this.#startItemDrag(e, 'touch'); - }; - - async updated(changed: PropertyValues) { - if (changed.has('enableDrag')) { - if (this.enableDrag) { - this.addEventListener('mousedown', this.mouseEvent); - this.addEventListener('touchstart', this.touchEvent); - } else { - this.removeEventListener('mousedown', this.mouseEvent); - this.removeEventListener('touchstart', this.touchEvent); - } - } - } - - firstUpdated() { - const elements = this.shadowRoot?.querySelector('slot')?.assignedElements(); - elements?.forEach((e) => { - const element = e as HTMLElement; - const width = element.getAttribute('width'); - if (width) { - element.style.width = width; - } - }); - - this.addEventListener('dblclick', () => { - //Force the transition to end on the double-click - /** - * @internal - */ - this.dispatchEvent(new Event('transitionend')); - this.dispatchEvent(new CustomEvent('titanium-data-table-item-navigate', { detail: this.item })); - }); - } - - /** - * @ignore - */ - updateDragProps(dragging: boolean, originIndex: number | null, hoverIndex: number | null, originHeight: number) { - const myIndex = this.items.indexOf(this); - this.nudgeDown = originIndex !== null && hoverIndex !== null && myIndex < originIndex && myIndex >= hoverIndex; - this.nudgeUp = originIndex !== null && hoverIndex !== null && myIndex > originIndex && myIndex <= hoverIndex; - this.dragged = originIndex === myIndex; - this.dragging = dragging; - this.nudgeHeight = originHeight; - } - - /** - * toggles item's checkbox which triggers this.selected to toggle as well - */ - toggleSelected() { - if (this.selected) { - this.deselect(); - } else { - this.select(); - } - } - - /** - * if not already checked, triggers click on checkbox which triggers this.selected to be set as well - */ - select() { - if (!this.selected) { - this.#setSelected(true); - } - } - - /** - * if already checked, triggers click on checkbox which triggers this.selected to be set to false as well - */ - deselect() { - if (this.selected) { - this.#setSelected(false); - } - } - - #setSelected(value: boolean) { - this.selected = value; - this.dispatchEvent(new Event('titanium-data-table-item-selected-changed', { bubbles: true, composed: true })); - } - - protected get dataTable() { - return this.parentElement as TitaniumDataTable; - } - - protected get items() { - return (this.dataTable.itemsSlot?.assignedElements() as TitaniumDataTableItem[]) ?? []; - } - - protected get itemsContainer() { - return this.dataTable.itemsContainer; - } - - /** - * Return index of item over - */ - #getIndexOver(itemEndPositions: number[], hoverPosition: number) { - for (let index = 0; index < itemEndPositions.length; index++) { - const endPosition = itemEndPositions[index]; - if (hoverPosition <= endPosition) { - return index; - } - } - return itemEndPositions.length - 1; - } - - /** - * Given the origin and hover index determine items that will be affected - */ - #determineRange(originIndex: number | null, hoverIndex: number | null) { - //PREF: ONLY UPDATE ITEMS BETWEEN ORIGIN AND HOVER (+1 and -1) - const high = Math.max(hoverIndex ?? 0, originIndex ?? 0) + 1; - let low = Math.min(hoverIndex ?? 0, originIndex ?? 0) - 1; - low = low < 0 ? 0 : low; - - return [high, low]; - } - - #notifySiblingsDrag(originIndex: number | null, hoverIndex: number | null, dragging: boolean, itemHeight: number) { - const range = this.#determineRange(originIndex, hoverIndex); - for (let index = range[1]; index <= range[0]; index++) { - const o = this.items?.[index]; - o?.updateDragProps(dragging, this.originIndex, this.hoverIndex, itemHeight); - } - } - - #notifySiblingDragStop(originIndex: number | null, hoverIndex: number | null) { - const range = this.#determineRange(originIndex, hoverIndex); - for (let index = range[1]; index <= range[0]; index++) { - const o = this.items?.[index]; - o?.updateDragProps(false, null, null, 0); - } - } - - #startItemDrag(event, type: 'touch' | 'mouse') { - //only allow primary mouse for drag - if (type === 'mouse' && event.which !== 1) { - return; - } - - //Prevent native scrolling - event.preventDefault(); - - this.dragging = true; - this.originIndex = this.items.indexOf(this); - - const moveEvent = type === 'touch' ? 'touchmove' : 'mousemove'; - const upEvent = type === 'touch' ? 'touchend' : 'mouseup'; - const containerY = this.itemsContainer?.getBoundingClientRect().top + window.scrollY; - const startY = event.pageY ?? event.touches[0].pageY; - const itemHeight = this.getBoundingClientRect().height - 1; - - //Cache the end positions of each item for variable height list items - let cumulativeSum = 0; - const itemEndPositions = this.items.map((o) => { - cumulativeSum = cumulativeSum + (o.getBoundingClientRect().height - 1); - return cumulativeSum; - }); - - const moveItemHandler = (event) => { - // Translate and keep track of which index we are hovering over. - const pageY = event.pageY ?? event.touches[0].pageY; - const clientY = event.clientY ?? event.touches[0].clientY; - - const itemAbsoluteTop = pageY - containerY; - const transformY = pageY - startY; - - this.style.transform = `translateY(${transformY}px)`; - this.hoverIndex = this.#getIndexOver(itemEndPositions, itemAbsoluteTop); - this.#notifySiblingsDrag(this.originIndex, this.hoverIndex, this.dragging, itemHeight); - - //Scroll on when item approaches bottom/top of viewport - if (clientY < 5) { - scrollBy({ - top: -(window.innerHeight / 5), - behavior: 'smooth', - }); - } else if (clientY < 25) { - scrollBy({ - top: -(window.innerHeight / 10), - behavior: 'smooth', - }); - } - - if (window.innerHeight - clientY < 5) { - scrollBy({ - top: window.innerHeight / 5, - behavior: 'smooth', - }); - } else if (window.innerHeight - clientY < 25) { - scrollBy({ - top: window.innerHeight / 10, - behavior: 'smooth', - }); - } - }; - - const cancelDragHandler = () => { - document.removeEventListener(moveEvent, moveItemHandler); - this.removeEventListener(upEvent, dragCompleteHandler); - this.dragging = false; - - const onTransitionEnd = () => { - this.#notifySiblingDragStop(this.originIndex, this.hoverIndex); - this.originIndex = null; - this.hoverIndex = null; - - this.style.transform = ''; - this.style.transition = ''; - this.removeEventListener('transitionend', onTransitionEnd); - }; - this.addEventListener('transitionend', onTransitionEnd); - - this.style.transition = 'transform 0.1s ease-out'; - this.style.transform = 'translate3d(0, 0, 0)'; - - document.removeEventListener('mouseout', cancelDragHandler); - }; - - const dragCompleteHandler = () => { - this.dragging = false; - this.items.forEach((o) => (o.dragging = false)); - document.removeEventListener(moveEvent, moveItemHandler); - - document.removeEventListener(upEvent, dragCompleteHandler); - if (type === 'mouse') { - document.removeEventListener('mouseout', cancelDragHandler); - } - - // Perform the swap after the item translates to its resting spot. - const onTransitionEnd = () => { - if (this.originIndex !== null && this.hoverIndex !== null) { - /** - * @ignore - */ - this.dispatchEvent(new DataTableItemDropEvent(this.originIndex, this.hoverIndex)); - } - this.#notifySiblingDragStop(this.originIndex, this.hoverIndex); - this.originIndex = null; - this.hoverIndex = null; - - this.style.transform = ''; - this.style.transition = ''; - this.removeEventListener('transitionend', onTransitionEnd); - }; - this.addEventListener('transitionend', onTransitionEnd); - - //Count the nudged items heights to know final transform amount - const finalTransformYUp = this.items - .filter((o) => o.nudgeUp) - .map((o) => (o.getBoundingClientRect().height > 0 ? o.getBoundingClientRect().height - 1 : 0)) - .reduce((a, b) => a + b, 0); - - const finalTransformYDown = this.items - .filter((o) => o.nudgeDown) - .map((o) => -o.getBoundingClientRect().height - 1) - .reduce((a, b) => a + b, 0); - - const finalTransformY = finalTransformYUp !== 0 ? finalTransformYUp : finalTransformYDown; - - // Translate the item to its resting spot. - this.style.transition = 'transform 0.1s ease-out'; - this.style.transform = `translate3d(0, ${finalTransformY}px, 0)`; - }; - - if (type === 'mouse') { - window.addEventListener('mouseout', cancelDragHandler); - } - document.addEventListener(upEvent, dragCompleteHandler); - document.addEventListener(moveEvent, moveItemHandler); - moveItemHandler(event); - } - - static styles = css` - :host { - display: block; - - -webkit-touch-callout: none; - user-select: none; - text-decoration: none; - - font-family: var(--titanium-data-table-font-family, Roboto, Noto, sans-serif); - -webkit-font-smoothing: antialiased; - - transition: none; - margin-top: -1px; - box-sizing: border-box; - border-bottom: 1px var(--md-sys-color-outline-variant) solid; - border-top: 1px var(--md-sys-color-outline-variant) solid; - position: relative; - } - - :host(:not([disable-select])[selected]) { - background-color: var(--md-sys-color-secondary-container); - } - - :host(:not([disable-select]):not([selected]):hover) { - background-color: rgb(from var(--md-sys-color-on-surface, #1d1b20) r g b / 0.08); - } - - :host([enable-dragging]) { - cursor: grab; - } - - md-icon[drag] { - position: absolute; - opacity: 0.3; - right: 7px; - color: var(--md-sys-color-outline, #dadce0); - } - - :host([enable-dragging]:hover) md-icon[drag] { - opacity: 1; - display: block; - } - - :host([dragged]) { - box-shadow: - 0 3px 6px rgba(0, 0, 0, 0.16), - 0 3px 6px rgba(0, 0, 0, 0.23); - transition: none; - overflow: hidden; - z-index: 1 !important; - } - - /* Only have transition under dragging, because we don't want nudged - * items to transition into place once dragging is complete */ - :host([dragging]:not([dragged])) { - transition: transform 0.2s ease-out; - } - - :host main { - display: flex; - flex-direction: row; - gap: 16px; - align-items: center; - min-height: 48px; - } - - /* Fallback :hover style for Firefox support */ - @-moz-document url-prefix() { - :host(:not([disable-select]):not([selected]):hover) { - background-color: color-mix(in srgb, var(--md-sys-color-on-surface, #1d1b20) 8%, transparent); - } - } - - /* Do not apply :hover style on touch devices */ - @media (hover: hover) and (pointer: fine) { - :host([enable-dragging]) div[item-footer] ::slotted(*) { - pointer-events: none; - } - } - - ::slotted(row-item) { - display: block; - font-size: 14px; - line-height: 18px; - font-weight: 400; - padding: 4px 0; - margin: 0; - box-sizing: border-box; - } - - ::slotted(row-item:last-of-type) { - padding-right: 24px; - } - - :host([enable-dragging]) ::slotted(row-item:last-of-type) { - padding-right: 40px; - } - - ::slotted(row-item:not([width])) { - -ms-flex: 3; - -webkit-flex: 3; - flex: 3; - } - - ::slotted(row-item:not([width])[large]) { - -ms-flex: 5; - -webkit-flex: 5; - flex: 5; - } - - ::slotted(row-item[center]) { - text-align: center; - } - - ::slotted(row-item[image]) { - display: inline-flex; - align-items: center; - gap: 12px; - } - - ::slotted(row-item[right]) { - text-align: right; - } - - md-checkbox { - flex-shrink: 0; - align-self: center; - margin: 0 14px 0 20px; - } - - :host([disable-select]) ::slotted(row-item:first-of-type) { - padding-left: 24px; - } - - :host([narrow]) ::slotted(row-item[desktop]) { - display: none; - } - - [hidden] { - display: none; - } - `; - - render() { - return html` - -
- ${this.disableSelect - ? nothing - : html` - e.stopPropagation()} - @touchstart=${(e: TouchEvent) => e.stopPropagation()} - @dblclick=${(e) => e.stopPropagation()} - @click=${(e) => e.stopPropagation()} - @change=${(e) => this.#setSelected(e.target.checked)} - > - `} - - - ${this.enableDrag ? html` drag_indicator` : nothing} -
-
- -
- `; - } -} - -/** - * @class - * @ignore - */ -export class DataTableItemDropEvent extends Event { - static eventType = 'titanium-data-table-item-drop'; - hoverIndex: number; - originIndex: number; - - constructor(originIndex: number, hoverIndex: number) { - super(DataTableItemDropEvent.eventType, { composed: true, bubbles: true }); - this.hoverIndex = hoverIndex; - this.originIndex = originIndex; - } -} diff --git a/packages/web/titanium/data-table/data-table.ts b/packages/web/titanium/data-table/data-table.ts deleted file mode 100644 index d0227c468..000000000 --- a/packages/web/titanium/data-table/data-table.ts +++ /dev/null @@ -1,626 +0,0 @@ -import './page-control'; -import '@material/web/checkbox/checkbox'; -import '@material/web/progress/linear-progress'; -import '@material/web/icon/icon'; - -import { css, html, LitElement } from 'lit'; -import { property, customElement, query, queryAsync, state } from 'lit/decorators.js'; -import { DataTableItemDropEvent, TitaniumDataTableItem } from './data-table-item'; -import { TitaniumDataTableHeader } from './data-table-header'; -import { h1, ellipsis } from '../../titanium/styles/styles'; -import { TitaniumPageControl } from './page-control'; -import { MdCheckbox } from '@material/web/checkbox/checkbox'; -import { niceBadgeStyles } from '../styles/nice-badge'; - -declare const ResizeObserver: any; - -/** - * Material design inspired data table with paging, sorting, multi/single select, table actions, selected actions and more! - * - * @element titanium-data-table - * - * @fires selected-changed - Fired when a row or rows in the data table is selected. detail: array - * @fires titanium-data-table-items-reorder - Fired when table items are resorted by user. - * @fires paging-changed - Fired when take or page is changed by click or keyboard action. - * - * @slot table-actions - item nonspecific table buttons such as add new item - * @slot filter-button - filter button slot - * @slot filters - filter chips slot - * @slot search-button - search button slot - * @slot selected-actions - item specific table buttons such as edit, delete shown when one or more items are selected - * @slot table-headers - slot for table headers (ex. titanium-data-table-header) - * @slot items - slot for table rows (ex. titanium-data-table-item) - * @slot footer - slot for additional footer items. Slotting here overwrites footer-buttons. - * @slot footer-buttons - slot for footer action buttons - * - * @cssprop {Color} [var(--md-sys-color-outline-variant)] - Table border color - * @cssprop {Color} [--titanium-data-table-font-family=Roboto, Noto, sans-serif] - Set the font family used on the data table and paging control - */ -@customElement('titanium-data-table') -export class TitaniumDataTable extends LitElement { - /** - * Table heading / title - */ - @property({ type: String }) accessor header: string; - - /** - * Local storage key. Not required if header is static and unique - */ - @property({ type: String, attribute: 'local-storage-key' }) accessor localStorageKey: string; - - /** - * Available page sizes - */ - @property({ type: Array }) accessor pageSizes: Array = [10, 15, 20, 50]; - - /** - * The default page size before the user changes it - */ - @property({ type: Number, attribute: 'default-page-size' }) accessor defaultPageSize: number = 10; - - /** - * Total number of items in all pages. - */ - @property({ type: Number }) accessor count: number; - - /** - * Current items displayed on the table. - */ - @property({ type: Array }) accessor items: Array = []; - - /** - * Current search term shown in the no result state if no results are found - */ - @property({ type: String }) accessor searchTerm: string; - - /** - * Limits table selection mode to single-select. Default is multi-select. - */ - @property({ type: Boolean, attribute: 'single-select', reflect: true }) accessor singleSelect: boolean; - - /** - * Disables all item selection on the data-table. - */ - @property({ type: Boolean, reflect: true, attribute: 'disable-select' }) accessor disableSelect: boolean = false; - - /** - * Disables paging. - */ - @property({ type: Boolean, attribute: 'disable-paging', reflect: true }) accessor disablePaging: boolean = false; - - /** - * Array of currently selected data table objects - */ - @property({ type: Array }) accessor selected: Array = []; - @query('div[items-slot]') accessor itemsContainer: HTMLDivElement; - @query('slot[name="items"]') accessor itemsSlot: HTMLSlotElement; - - @property({ type: Boolean, reflect: true, attribute: 'narrow' }) protected accessor narrow: boolean = false; - @property({ type: Boolean, attribute: 'has-drag-items', reflect: true }) protected accessor hasDragItems: boolean; - @property({ type: Number, attribute: 'narrow-max-width', reflect: true }) protected accessor narrowMaxWidth: number = 560; - @state() protected accessor isLoading: boolean; - @query('slot[name="table-headers"]') protected accessor tableHeaders: HTMLSlotElement; - @query('md-checkbox') protected accessor checkbox: MdCheckbox; - @queryAsync('titanium-page-control') protected accessor pageControl: Promise; - #openCount = 0; - - /** - * returns internal pageControl's current take - */ - public async getTake(): Promise { - return (await this.pageControl)?.take ?? 0; - } - - /** - * returns internal pageControl's current page - */ - public async getPage(): Promise { - return (await this.pageControl)?.page ?? 0; - } - - /** - * sets internal pageControl's current take - */ - public async setTake(take: number): Promise { - const control = await this.pageControl; - if (control) { - control.take = take; - } - } - - /** - * sets internal pageControl's current page - */ - public async setPage(page: number): Promise { - const control = await this.pageControl; - if (control) { - control.page = page; - } - } - - /** - * resets internal pageControl's current page to 0 - */ - public async resetPage() { - await this.setPage(0); - } - - async firstUpdated() { - if (typeof ResizeObserver === 'function') { - const ro = new ResizeObserver((entries) => { - for (const entry of entries) { - const cr = entry.contentRect; - this.narrow = cr.width < this.narrowMaxWidth; - this.updateChildrenIsNarrow(); - } - }); - - ro.observe(this); - } else { - const mql = window.matchMedia('(max-width: 768px)'); - mql.addEventListener('change', (e) => { - this.narrow = e.matches; - this.updateChildrenIsNarrow(); - }); - this.narrow = mql.matches; - this.updateChildrenIsNarrow(); - } - - this.addEventListener(DataTableItemDropEvent.eventType, (e: DataTableItemDropEvent) => { - e.stopPropagation(); - //HoverIndex cannot be dropped beyond the length of the array - const hoverIndex = Math.min(e.hoverIndex, this.items.length - 1); - - //Ignore if item goes back to where it started - if (hoverIndex !== e.originIndex) { - const temp = this.items[e.originIndex]; - this.items.splice(e.originIndex, 1); - this.items.splice(hoverIndex, 0, temp); - /** - * @ignore - */ - this.dispatchEvent(new DataTableItemsReorderedEvent()); - } - }); - - //When slotted in items change, sync the narrow prop - this.tableHeaders.addEventListener('slotchange', () => this.updateChildrenIsNarrow()); - this.itemsSlot.addEventListener('slotchange', () => this.updateChildrenIsNarrow()); - - await ( - await this.pageControl - )?.updateComplete; - } - - protected updateChildrenIsNarrow() { - this.hasDragItems = (this.itemsSlot.assignedElements() as Array).some((o) => o.enableDrag); - (this.itemsSlot.assignedElements() as Array).forEach((o) => (o.narrow = this.narrow)); - (this.tableHeaders.assignedElements() as Array).forEach((o) => (o.narrow = this.narrow)); - } - - /** - * de-select all table items and clear this.selected - */ - clearSelection() { - this.#deselectAll(); - // Ensure the collection is empty, deselect can cause a race condition - // between deselecting and UI drawing new items. - - if (this.selected.length > 0) { - this.selected = []; - this.#notifySelectedChanged(); - } - } - - updated(changedProps) { - if (changedProps.has('items') && changedProps.get('items') !== this.items) { - // Clear selection when items array changes. - this.clearSelection(); - } - } - - #notifySelectedChanged() { - this.dispatchEvent(new CustomEvent('selected-changed', { composed: true, detail: this.selected })); - } - - /** - * display linear progress bar while promise is active - */ - async loadWhile(promise: Promise) { - this.isLoading = true; - this.#openCount++; - try { - await promise; - } finally { - this.#openCount--; - if (this.#openCount === 0) { - this.isLoading = false; - } - } - } - - #deselectAll() { - this.#getTableItems().forEach((o) => o.deselect()); - } - - /** - * select all table items - */ - selectAll() { - if (!this.singleSelect) { - this.#getTableItems().forEach((o) => o.select()); - } - } - - #getTableItems(): Array { - return (this.itemsSlot.assignedElements() as Array).filter( - (o) => typeof o.select === 'function' && typeof o.deselect === 'function' - ) as Array; - } - - static styles = [ - h1, - ellipsis, - niceBadgeStyles, - css` - :host { - display: flex; - flex-direction: column; - - border: 1px solid var(--md-sys-color-outline-variant); - background-color: var(--md-sys-color-surface); - color: var(--md-sys-color-on-surface); - border-radius: 8px; - font-family: var(--titanium-data-table-font-family, Roboto, Noto, sans-serif); - --titanium-page-control-font-family: var(--titanium-data-table-font-family, Roboto, Noto, sans-serif); - -webkit-font-smoothing: antialiased; - } - - header { - display: flex; - flex-direction: column; - padding-bottom: 12px; - gap: 12px; - border-bottom: 1px solid var(--md-sys-color-outline-variant); - position: relative; - } - - /* HEADER ROW ONE */ - - section[row-one] { - display: grid; - grid: 'head menu' / 1fr auto; - gap: 8px; - padding: 12px 12px 0 12px; - } - - section[row-one] div[head] { - grid-area: head; - } - - section[row-one] div[menu] { - grid-area: menu; - } - - div[search] { - grid-area: search; - } - - /* HEADER ROW TWO */ - - section[row-two] { - display: grid; - grid: 'search-filter add' / 1fr auto; - gap: 8px; - padding: 0 12px 0 20px; - } - - :host([narrow]) section[row-two] { - grid: - 'search-filter ' - 'add' / auto; - } - - section[row-two] div[search-filter] { - grid-area: search-filter; - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; - } - - section[row-two] div[add-button] { - grid-area: add; - justify-self: end; - align-self: end; - } - - h1 { - padding: 12px 12px 0 12px; - } - - selected-actions { - display: grid; - gap: 6px 24px; - grid: 'selected-text buttons'; - background-color: var(--md-sys-color-secondary-container); - position: absolute; - top: 0px; - left: 0px; - right: 0px; - bottom: 0px; - margin: 0 !important; - padding: 0 12px 12px 24px; - align-content: end; - z-index: 1; - } - - selected-actions h2 { - color: var(--md-sys-color-on-secondary-container); - font-size: 18px; - font-weight: 400; - align-self: end; - } - - selected-actions div[buttons] { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0 8px; - justify-content: flex-end; - } - - table-header { - display: flex; - flex-direction: row; - gap: 16px; - min-height: 48px; - border-bottom: 1px solid var(--md-sys-color-outline-variant); - } - - table-header ::slotted(titanium-data-table-header:last-of-type) { - padding-right: 24px; - } - - :host([has-drag-items]) table-header ::slotted(titanium-data-table-header:last-of-type) { - padding-right: 40px; - } - - md-linear-progress { - width: 100%; - margin-top: -4px; - } - - main { - position: relative; - min-height: 48px; - } - - content-veil { - display: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: var(--md-sys-color-scrim); - opacity: 0; - -webkit-transition: opacity 75ms linear; - -o-transition: opacity 75ms linear; - transition: opacity 75ms linear; - z-index: 6; - } - - content-veil[opened] { - opacity: 0.12; - display: block; - } - - table-message { - display: flex; - place-items: center; - justify-content: center; - gap: 8px; - padding: 64px; - - font-size: 14px; - z-index: 10; - line-height: 20px; - border-bottom: 1px solid var(--md-sys-color-outline-variant); - } - - table-message md-icon { - align-self: center; - flex-shrink: 0; - } - - footer { - display: grid; - grid: 'controls footer-slot' / minmax(400px, 1fr) auto; - gap: 24px; - padding: 12px; - align-items: center; - margin-top: -1px; - border-top: 1px solid var(--md-sys-color-outline-variant); - } - - titanium-page-control { - grid-area: controls; - margin-left: 12px; - justify-self: start; - } - - div[footer] { - justify-self: end; - } - - :host([narrow]) footer { - grid: - 'controls' - 'footer-slot' / auto; - } - - :host([disable-paging]) footer { - grid: 'footer-slot' / auto; - } - - footer-buttons { - display: flex; - gap: 12px; - flex-wrap: wrap; - align-items: flex-end; - } - - div[add-button] { - display: flex; - align-items: center; - } - - div[items-slot] { - position: relative; - } - - md-checkbox { - flex-shrink: 0; - align-self: center; - margin: 0 14px 0 20px; - } - - :host([disable-select]) table-header ::slotted(titanium-data-table-header:first-of-type) { - padding-left: 24px; - } - - :host(:not([disable-select])[single-select]) table-header { - padding-left: 68px; - } - - [hidden] { - display: none !important; - } - `, - ]; - - render() { - return html` -
- -
-
-

${this.header}

-
-
- -
-
-
-
- - - -
-
- -
-
-
- - -

${this.selected.length} item${this.selected.length > 1 ? 's' : ''} selected

-
- -
-
-
- - { - e.stopPropagation(); - const dataTableItem = e.target as TitaniumDataTableItem; - - if (dataTableItem.selected) { - if (this.singleSelect) { - this.#getTableItems() - .filter((o) => o.item !== dataTableItem.item) - .forEach((o) => o.deselect()); - } - - this.selected.push(dataTableItem.item); - this.requestUpdate(); - this.#notifySelectedChanged(); - } else { - this.selected.splice(this.selected.indexOf(dataTableItem.item), 1); - this.requestUpdate(); - this.#notifySelectedChanged(); - } - }} - > - - ${this.disableSelect || this.singleSelect - ? '' - : html` - 0} - ?indeterminate=${this.selected.length !== 0 && this.selected.length !== this.items.length} - ?disabled=${this.items.length === 0} - @click=${() => { - if (this.selected.length > 0) { - this.#deselectAll(); - } else { - this.selectAll(); - } - this.checkbox.focus(); - }} - > - `} - - - - -
-
- -
- 0}> - info - ${this.searchTerm === '' || typeof this.searchTerm === 'undefined' || this.searchTerm === null - ? 'No results' - : `Your search of '${this.searchTerm}' did not match any results`} - Loading data... - -
-
-
- - ${this.disablePaging - ? '' - : html` - { - this.dispatchEvent(new CustomEvent('paging-changed', { composed: true })); - }} - > - `} -
- -
-
-
- `; - } -} - -export class DataTableItemsReorderedEvent extends Event { - static eventType = 'titanium-data-table-items-reorder'; - constructor() { - super(DataTableItemsReorderedEvent.eventType); - } -} diff --git a/packages/web/titanium/duration-input/duration-input.ts b/packages/web/titanium/duration-input/duration-input.ts deleted file mode 100644 index a3c11c23d..000000000 --- a/packages/web/titanium/duration-input/duration-input.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { property, customElement } from 'lit/decorators.js'; -import { PropertyValues, css } from 'lit'; - -import dayjs from 'dayjs/esm'; -import duration from 'dayjs/esm/plugin/duration'; -import { ExtendableOutlinedTextField } from '../extendable-outlined-text-field/extendable-outlined-text-field'; -import humanInterval, { durationToString } from './human-interval'; -dayjs.extend(duration); - -/** - * titanium-duration-input is a human readable duration textfield. - * - * @element titanium-duration-input - * - * @fires duration-change The duration can be accessed via event.target.duration - * - */ - -@customElement('titanium-duration-input') -export class TitaniumDurationInput extends ExtendableOutlinedTextField { - /** - * Dayjs duration object. This is the main property you will interact with because the value - * property of this component is actually the human readable string and not the duration you most likely - * want to work with. When changed a duration-change event will be dispatched. - */ - @property({ type: Object }) accessor duration: duration.Duration | null = null; - - @property({ reflect: true, type: String }) accessor autocomplete: string = 'off'; - - @property({ reflect: true, type: Boolean }) accessor spellcheck: boolean = false; - - @property({ reflect: true, type: String }) accessor placeholder: string = '3 hours and 30 minutes'; - @property({ reflect: true, type: String }) accessor label: string = 'Duration'; - - firstUpdated() { - this.addEventListener('change', () => { - this.#customReportValidity(this.input.value); - - const dur = this.#textToInterval(this.input.value); - if (dur?.asMilliseconds() != this.duration?.asMilliseconds()) { - this.duration = dur; - this.dispatchEvent(new Event('duration-change')); - } - }); - } - - static styles = css` - :host { - display: block; - } - - md-outlined-text-field { - width: 100%; - } - `; - - updated(changedProps: PropertyValues) { - if (changedProps.has('duration') && changedProps.get('duration') !== this.duration) { - if (this.duration) { - this.value = durationToString(this.duration); - } else { - this.duration = null; - this.value = ''; - } - } - } - - checkValidity() { - return super.checkValidity() && this.#customCheckValidity(this.input.value); - } - - reportValidity() { - this.#customReportValidity(this.input.value); - return super.reportValidity(); - } - - #customCheckValidity(input: string) { - if (input && !this.#textToInterval(input)) { - return false; - } else { - return true; - } - } - - #customReportValidity(input: string) { - if (!this.#customCheckValidity(input)) { - this.error = true; - this.errorText = 'Duration was entered in an incorrect format. Try "3 hours and 30 minutes"'; - } else { - this.error = false; - this.errorText = ''; - } - } - - #textToInterval(input: string) { - if (!input) { - return null; - } - const ms = humanInterval(input); - return isNaN(ms) ? null : dayjs.duration(ms, 'ms'); - } - - override async reset() { - super.reset(); - this.error = false; - this.errorText = ''; - this.duration = null; - } -} diff --git a/packages/web/titanium/duration-input/outlined-duration-input.ts b/packages/web/titanium/duration-input/outlined-duration-input.ts deleted file mode 100644 index da74b2ff8..000000000 --- a/packages/web/titanium/duration-input/outlined-duration-input.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { property, customElement } from 'lit/decorators.js'; -import { PropertyValues } from 'lit'; -import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; - -import dayjs from 'dayjs/esm'; -import duration from 'dayjs/esm/plugin/duration'; -import humanInterval, { durationToString } from './human-interval'; -dayjs.extend(duration); - -/** - * titanium-outlined-duration-input is a human readable duration textfield. - * - * @element titanium-outlined-duration-input - * - * @fires duration-change The duration can be accessed via event.target.duration - * - */ - -@customElement('titanium-outlined-duration-input') -export class TitaniumOutlinedDurationInput extends MdOutlinedTextField { - /** - * Dayjs duration object. This is the main property you will interact with because the value - * property of this component is actually the human readable string and not the duration you most likely - * want to work with. When changed a duration-change event will be dispatched. - */ - @property({ type: Object }) accessor duration: duration.Duration | null = null; - @property({ type: String }) label: string = 'Duration'; - @property({ type: String }) supportingText: string = 'Enter a duration ex. "3 hours and 30 minutes"'; - @property({ type: String }) autocomplete: string = 'off'; - @property({ type: Boolean }) spellcheck: boolean = false; - - updated(changedProps: PropertyValues) { - if (changedProps.has('duration') && changedProps.get('duration') !== this.duration) { - if (this.duration) { - this.value = durationToString(this.duration); - } else { - this.duration = null; - this.value = ''; - } - } - } - - firstUpdated() { - this.addEventListener('change', () => { - this.reportValidity(); - - const dur = this.#textToInterval(this.value); - if (dur?.asMilliseconds() != this.duration?.asMilliseconds()) { - this.duration = dur; - this.dispatchEvent(new Event('duration-change')); - } - }); - } - - checkValidity() { - return super.checkValidity() && this.#customCheckValidity(this.value); - } - - reportValidity() { - if (!this.#customCheckValidity(this.value)) { - this.error = true; - this.errorText = 'Duration was entered in an incorrect format. Try "3 hours and 30 minutes"'; - } else { - this.error = false; - this.errorText = ''; - } - return super.reportValidity(); - } - - #customCheckValidity(input: string) { - if (input && !this.#textToInterval(input)) { - return false; - } else { - return true; - } - } - - #textToInterval(input: string) { - if (!input) { - return null; - } - const ms = humanInterval(input); - return isNaN(ms) ? null : dayjs.duration(ms, 'ms'); - } - - override async reset() { - super.reset(); - this.error = false; - this.errorText = ''; - this.duration = null; - } -} diff --git a/packages/web/titanium/error-page/error-page.ts b/packages/web/titanium/error-page/error-page.ts deleted file mode 100644 index 6319191ce..000000000 --- a/packages/web/titanium/error-page/error-page.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { css, html, LitElement, TemplateResult } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; - -/** - * A pre-styled error page - * - * @element titanium-error-page - * - */ - -@customElement('titanium-error-page') -export class TitaniumErrorPage extends LitElement { - /** - * Reason text for the error - */ - @property() accessor message: string | TemplateResult<1> = 'We were unable to find the page you are looking for...'; - - static styles = css` - :host { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - - font-family: Roboto, sans-serif; - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - - max-width: 1300px; - } - - header { - flex: 1 1 auto; - margin-right: 24px; - } - - h1 { - font-family: Metropolis, 'Roboto', 'Noto', sans-serif; - font-weight: 600; - font-size: 75px; - line-height: 85px; - margin: 0; - } - - h2 { - font-weight: 400; - margin: 8px 0 0 4px; - max-width: 75%; - } - - img { - flex-shrink: 0; - height: 280px; - width: 280px; - } - - @media (max-width: 768px) { - :host { - margin-top: 24px; - } - - h2 { - max-width: inherit; - font-size: 21px; - } - - img { - height: 120px; - width: 120px; - align-self: flex-start; - } - - h1 { - font-size: 55px; - line-height: 65px; - } - } - `; - - render() { - return html` -
-

Oops!

-

${this.message}

-
- - `; - } -} diff --git a/packages/web/titanium/full-page-loading-indicator/full-page-loading-indicator.ts b/packages/web/titanium/full-page-loading-indicator/full-page-loading-indicator.ts deleted file mode 100644 index 5d3622e92..000000000 --- a/packages/web/titanium/full-page-loading-indicator/full-page-loading-indicator.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { css, html, LitElement } from 'lit'; -import { customElement, state } from 'lit/decorators.js'; -import { PendingStateEvent } from '../types/pending-state-event'; - -import '@material/web/progress/linear-progress'; - -/** - * A simple full-screen veil with loading indicator that uses promise driven pending-state-events - * - * @element titanium-full-page-loading-indicator - * - * - */ -@customElement('titanium-full-page-loading-indicator') -export class TitaniumFullPageLoadingIndicator extends LitElement { - @state() private accessor open: boolean; - - #openDelayTimer: number; - #closeDelayTimer: number; - - //Promises faster than this do not cause the scrim to open at all - //Prevents flicker for fast promises - #openDelay: number = 75; - - // min time scrim has to remain open - #minTimeOpen: number = 350; - #timeOpen: number; - #openCount = 0; - - firstUpdated() { - this.popover = 'manual'; - this.addEventListener('toggle', (e: ToggleEvent) => (this.open = e.newState === 'open')); - - window.addEventListener(PendingStateEvent.eventType, async (e: PendingStateEvent) => { - this.#open(); - this.#openCount++; - try { - await e.detail.promise; - } catch { - // Do nothing, this will be handled by others - } finally { - this.#openCount--; - if (this.#openCount === 0) { - this.#close(); - } - } - }); - } - - #open() { - window.clearTimeout(this.#openDelayTimer); - - //If re-opened while close timer is running, prevent the close - window.clearTimeout(this.#closeDelayTimer); - - this.#openDelayTimer = window.setTimeout(() => { - this.#timeOpen = performance.now(); - if (this.showPopover) { - this.showPopover(); - } else { - this.open = true; - } - this.style.display = 'block'; - }, this.#openDelay); - } - - #close() { - window.clearTimeout(this.#openDelayTimer); - const totalTimeOpened = performance.now() - this.#timeOpen; - const closeDelay = Math.max(this.#minTimeOpen - totalTimeOpened, 0); - - this.#closeDelayTimer = window.setTimeout(() => { - if (this.hidePopover) { - this.hidePopover(); - } else { - this.open = false; - } - this.style.display = 'none'; - }, closeDelay); - } - - static styles = css` - :host { - width: 100%; - height: 100%; - max-width: 100vw; - max-height: 100vh; - - border: 0; - inset: unset; - top: 0; - right: 0; - left: 0; - bottom: 0; - margin: 0; - padding: 0; - background: transparent; - } - - :host::backdrop { - background-color: var(--md-sys-color-scrim, #000); - backdrop-filter: blur(6px); - } - - :host(:popover-open)::backdrop { - opacity: 0.32; - } - - md-linear-progress { - position: absolute; - width: 100%; - top: 0; - right: 0; - left: 0; - } - `; - - render() { - return html` `; - } -} diff --git a/packages/web/titanium/header/header.ts b/packages/web/titanium/header/header.ts deleted file mode 100644 index a025f802b..000000000 --- a/packages/web/titanium/header/header.ts +++ /dev/null @@ -1,145 +0,0 @@ -import '@material/web/icon/icon'; -import '@material/web/iconbutton/icon-button'; - -import { h1, h3 } from '../../titanium/styles/styles'; -import { css, html, LitElement, nothing } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; - -/** - * A pre-styled page header with sub header and optional back button. - * - * @element titanium-header - * - * @fires titanium-header-back-click - Fired when the back button is clicked - * - * @cssprop {Color} [--md-sys-color-on-background] - Header text color - * @cssprop {Color} [--md-sys-color-on-surface-variant] - Sub-header text color - */ -@customElement('titanium-header') -export class TitaniumHeader extends LitElement { - /** - * Header text - */ - @property({ type: String }) accessor header: string; - - /** - * Sub-header text - */ - @property({ type: String }) accessor subHeader: string; - - /** - * Leading header icon - */ - @property({ type: String }) accessor icon: string; - - /** - * Removes the back button - */ - @property({ type: Boolean, reflect: true, attribute: 'no-nav' }) accessor noNav: boolean = false; - - /** - * Lets user override back button behavior - */ - @property({ type: Boolean, reflect: true, attribute: 'disable-default-back-button-behavior' }) accessor disableDefaultBackButtonBehavior: boolean = false; - - #handleBackClick() { - if (this.disableDefaultBackButtonBehavior) { - this.dispatchEvent(new CustomEvent('titanium-header-back-click', { composed: true })); - } else { - window.history.back(); - } - } - - static styles = [ - h1, - h3, - css` - :host { - display: flex; - flex-direction: column; - -webkit-font-smoothing: antialiased; - padding: 0 52px 8px 52px; - position: relative; - } - - :host([no-nav]) md-icon-button { - display: none; - } - - :host([no-nav]) { - padding: 0 0 8px 0; - } - - header { - display: block; - text-align: center; - padding: 0 0 8px 0; - } - - h1 { - display: inline; - font-size: 40px; - line-height: 42px; - font-weight: 200; - - margin: 0; - color: var(--md-sys-color-on-background); - } - - h3 { - color: var(--md-sys-color-on-surface-variant); - font-family: Metropolis, Roboto, Noto, sans-serif; - font-weight: 300; - font-size: 16px; - line-height: 20px; - text-align: center; - } - - md-icon[header-icon] { - display: inline; - vertical-align: text-bottom; - font-size: 40px; - color: var(--md-sys-color-on-background); - margin-right: 8px; - } - - md-icon-button { - position: absolute; - top: 0; - left: 0; - } - - @media (max-width: 920px) { - h1 { - font-size: 30px; - line-height: 32px; - } - - h3 { - font-size: 14px; - line-height: 16px; - } - - md-icon[header-icon] { - font-size: 30px; - } - } - - :host([hidden]) { - display: none; - } - `, - ]; - - render() { - return html` -
- ${this.icon ? html`${this.icon}` : nothing} -

${this.header}

- arrow_back -
- -

${this.subHeader}

- `; - } -} diff --git a/packages/web/titanium/helpers/helpers.ts b/packages/web/titanium/helpers/helpers.ts index 1c45c489a..3d0d8d6dd 100644 --- a/packages/web/titanium/helpers/helpers.ts +++ b/packages/web/titanium/helpers/helpers.ts @@ -5,7 +5,6 @@ export { escapeTerm } from './escape-term'; export { installMediaQueryWatcher } from './install-media-query-watcher'; export { getSearchTokens } from './get-search-token'; export { Debouncer } from './debouncer'; -export { LoadWhile } from './load-while'; export { promiseTracking } from './promise-tracking'; export { join } from './join'; export { delay } from './delay'; diff --git a/packages/web/titanium/helpers/load-while.ts b/packages/web/titanium/helpers/load-while.ts deleted file mode 100644 index 7b76197d4..000000000 --- a/packages/web/titanium/helpers/load-while.ts +++ /dev/null @@ -1,28 +0,0 @@ -export type Constructor = { new (...args: any[]): T }; -export const LoadWhile = >(base: C) => - class extends base { - static get properties() { - return { - isLoading: { type: Boolean }, - }; - } - /** - * @internal - */ - #promiseCount = 0; - isLoading: boolean; - async loadWhile(promise: Promise) { - this.isLoading = true; - this.#promiseCount++; - try { - await promise; - } catch { - // Do nothing, this will be handled by others - } finally { - this.#promiseCount--; - if (this.#promiseCount === 0) { - this.isLoading = false; - } - } - } - }; diff --git a/packages/web/titanium/input-validator/input-validator.ts b/packages/web/titanium/input-validator/input-validator.ts deleted file mode 100644 index ca805acf8..000000000 --- a/packages/web/titanium/input-validator/input-validator.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { customElement, property } from 'lit/decorators.js'; -import { MdOutlinedField } from '@material/web/field/outlined-field'; - -/** - * Input validator to make components use validation consistent with material 3 outlined styling - * - * @element titanium-input-validator - * @slot default - The slotted element should fire the NotifyUserInputEvent when it is ready to be validated - * @example - * (this.collection?.length ?? 0) > 0} validationMessage="Collection must have one item"> - * - * - */ -@customElement('titanium-input-validator') -export class TitaniumInputValidator extends MdOutlinedField { - @property({ type: Boolean }) populated: boolean = true; - @property({ type: Object }) accessor evaluator: () => boolean; - - firstUpdated() { - this.addEventListener('focusin', () => (this.focused = true)); - this.addEventListener('focusout', () => (this.focused = false)); - } - - reset() { - this.error = false; - } - - reportValidity() { - const valid = this.checkValidity(); - this.error = !valid; - - return valid; - } - - checkValidity() { - return !!this.evaluator(); - } -} diff --git a/packages/web/titanium/input-validator/outlined-input-validator.ts b/packages/web/titanium/input-validator/outlined-input-validator.ts deleted file mode 100644 index 3e6356cb3..000000000 --- a/packages/web/titanium/input-validator/outlined-input-validator.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { customElement, property } from 'lit/decorators.js'; -import { MdOutlinedField } from '@material/web/field/outlined-field'; - -/** - * Input validator to make components use validation consistent with material 3 outlined styling - * - * @element titanium-outlined-input-validator - * @slot default - The slotted element should fire the NotifyUserInputEvent when it is ready to be validated - * @example - * (this.collection?.length ?? 0) > 0} validationMessage="Collection must have one item"> - * - * - */ -@customElement('titanium-outlined-input-validator') -export class TitaniumOutlinedInputValidator extends MdOutlinedField { - @property({ type: Boolean }) populated: boolean = true; - @property({ type: Object }) accessor evaluator: () => boolean; - - firstUpdated() { - this.addEventListener('focusin', () => (this.focused = true)); - this.addEventListener('focusout', () => (this.focused = false)); - } - - reset() { - this.error = false; - } - - reportValidity() { - const valid = this.checkValidity(); - this.error = !valid; - - return valid; - } - - checkValidity() { - return !!this.evaluator(); - } -} diff --git a/packages/web/titanium/search-input/search-input.ts b/packages/web/titanium/search-input/search-input.ts deleted file mode 100644 index d7cf7f2d6..000000000 --- a/packages/web/titanium/search-input/search-input.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { PropertyValues, css, html, nothing } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; - -import '@material/web/iconbutton/icon-button'; -import '@material/web/icon/icon'; - -import { ExtendableOutlinedTextField } from '../extendable-outlined-text-field/extendable-outlined-text-field'; - -/** - * A styled input with built-in search and clear icons. . - * - * @element titanium-search-input - * - * @cssprop {Length} --titanium-search-input-expanded-width - Width when input expands - * - */ -@customElement('titanium-search-input') -export class TitaniumSearchInput extends ExtendableOutlinedTextField { - /** - * Whether or not the input should hide the clear button - */ - @property({ type: Boolean, attribute: 'hide-clear-button' }) accessor hideClearButton: boolean = false; - - /** - * Whether the input should prevent collapse. - */ - @property({ type: Boolean, reflect: true, attribute: 'prevent-collapse' }) accessor preventCollapse: boolean = false; - - @property({ type: Boolean, reflect: true, attribute: 'has-value' }) protected accessor hasValue: boolean = true; - - @property({ reflect: true, type: String }) accessor autocomplete: string = 'off'; - - @property({ reflect: true, type: Boolean }) accessor spellcheck: boolean = false; - - async updated(changedProps: PropertyValues) { - if (changedProps.has('value')) { - this.hasValue = !!this.value; - } - } - - static styles = css` - :host { - display: block; - cursor: pointer; - overflow: hidden; - } - - md-outlined-text-field { - width: 48px; - --md-outlined-field-outline-width: 0; - --md-outlined-field-hover-outline-width: 0; - --md-outlined-field-disabled-outline-width: 0; - - -webkit-transition: width 250ms 0ms cubic-bezier(0.4, 0, 0.2, 1); /* Safari */ - transition: width 250ms 0ms cubic-bezier(0.4, 0, 0.2, 1); - --md-outlined-text-field-bottom-space: 11px; - --md-outlined-text-field-top-space: 11px; - } - - :host([has-value]) md-icon-button[search], - :host([prevent-collapse]) md-icon-button[search], - md-outlined-text-field:focus-within md-icon-button[search] { - pointer-events: none; - } - - :host([has-value]) md-outlined-text-field, - :host([prevent-collapse]) md-outlined-text-field, - md-outlined-text-field:focus-within { - --md-outlined-field-outline-width: initial; - --md-outlined-field-disabled-outline-width: initial; - --md-outlined-field-hover-outline-width: initial; - --md-outlined-text-field-container-shape: initial; - - width: var(--titanium-search-input-expanded-width, 258px); - } - `; - - protected override renderMainSlot() { - return html` - - this.focus()} @focus=${() => this.focus()} slot="leading-icon"> - search - - ${!this.hasValue - ? nothing - : html` { - if (this.disabled) { - return; - } - this.input.focus(); - this.value = ''; - this.dispatchEvent(new Event('input')); - }} - > - close`} - `; - } -} diff --git a/packages/web/titanium/service-worker-notifier/service-worker-notifier.ts b/packages/web/titanium/service-worker-notifier/service-worker-notifier.ts deleted file mode 100644 index 23cc04412..000000000 --- a/packages/web/titanium/service-worker-notifier/service-worker-notifier.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { html, LitElement } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; -import { ShowSnackbarEvent } from '../../titanium/snackbar/show-snackbar-event'; - -@customElement('titanium-service-worker-notifier') -export class TitanuimServiceWorkerNotifier extends LitElement { - @property({ type: String }) accessor notificationsStatus: string; - @property({ type: String }) accessor scriptUrl: string = 'service-worker.js'; - - #newWorker: ServiceWorker | null; - #refreshing = false; - - async connectedCallback() { - if ('serviceWorker' in navigator) { - const reg = await navigator.serviceWorker.getRegistration(); - if (reg) { - reg.addEventListener('updatefound', () => { - this.#newWorker = reg.installing; - this.#newWorker?.addEventListener('statechange', () => { - if (this.#newWorker?.state === 'installed' && navigator.serviceWorker.controller) { - this.#showUpdatedSnackbar(); - } - }); - }); - - if (reg.waiting && navigator.serviceWorker.controller) { - this.#newWorker = reg.waiting; - this.#showUpdatedSnackbar(); - } - } - - navigator.serviceWorker.addEventListener('controllerchange', () => { - if (this.#refreshing) { - return; - } - window.location.reload(); - this.#refreshing = true; - }); - } - } - - async #showUpdatedSnackbar() { - await this.dispatchEvent(new ShowSnackbarEvent('Site has been updated', { actionText: 'RELOAD' })); - this.#newWorker?.postMessage({ type: 'SKIP_WAITING' }); - } - - render() { - return html``; - } -} diff --git a/packages/web/titanium/single-select-base/single-select-base.ts b/packages/web/titanium/single-select-base/single-select-base.ts index f7b8d975e..ca60f842f 100644 --- a/packages/web/titanium/single-select-base/single-select-base.ts +++ b/packages/web/titanium/single-select-base/single-select-base.ts @@ -16,7 +16,7 @@ import { DOMEvent } from '../types/dom-event'; import { CloseMenuEvent, MenuItem } from '@material/web/menu/menu'; import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; import { Menu } from '@material/web/menu/internal/menu'; -import { LoadWhile } from '../../titanium/helpers/load-while'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { Identifier } from '../types/identifier-interface'; import { redispatchEvent } from '@material/web/internal/events/redispatch-event'; import { ThemePreference } from '../../leavitt/theme/theme-preference'; @@ -24,7 +24,12 @@ import { html, literal } from 'lit/static-html.js'; import { MdFilledTextField } from '@material/web/textfield/filled-text-field'; @customElement('titanium-single-select-base') -export class TitaniumSingleSelectBase extends ThemePreference(LoadWhile(LitElement)) { +export class TitaniumSingleSelectBase extends ThemePreference(LitElement) { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @state() protected accessor searchTerm: string; @state() protected accessor suggestions: Array = []; diff --git a/packages/web/titanium/smart-attachment-input/crop-and-save-image-dialog.ts b/packages/web/titanium/smart-attachment-input/crop-and-save-image-dialog.ts index 67209ee6d..6f9fd8e7b 100644 --- a/packages/web/titanium/smart-attachment-input/crop-and-save-image-dialog.ts +++ b/packages/web/titanium/smart-attachment-input/crop-and-save-image-dialog.ts @@ -12,7 +12,7 @@ import { property, customElement, query, state } from 'lit/decorators.js'; import { CropperCanvas, CropperImage, CropperSelection, CropperShade } from 'cropperjs'; import { cropperCSS } from './cropper-styles'; import { h1, p } from '../../titanium/styles/styles'; -import { LoadWhile } from '../../titanium/helpers/load-while'; +import { promiseTracking } from '../../titanium/helpers/promise-tracking'; import { MdDialog } from '@material/web/dialog/dialog'; import { DOMEvent } from '../types/dom-event'; import { ifDefined } from 'lit/directives/if-defined.js'; @@ -47,7 +47,12 @@ export declare type SelectionData = { * */ @customElement('crop-and-save-image-dialog') -export class CropAndSaveImageDialog extends LoadWhile(LitElement) { +export class CropAndSaveImageDialog extends LitElement { + @promiseTracking('trackLoadingPromise') + @state() + accessor isLoading = false; + declare trackLoadingPromise: (promise: Promise) => Promise; + @query('md-dialog') accessor dialog: MdDialog; @query('main') accessor main: HTMLElement; @query('cropper-canvas') accessor cropperCanvas: CropperCanvas; @@ -475,25 +480,24 @@ export class CropAndSaveImageDialog extends LoadWhile(LitElement) { }, }); - this.isLoading = true; - await this.updateComplete; - if (!canvas) { return; } - if (this.options?.shape === 'circle') { - this.#applyCircleMask(canvas); - } - const previewBlob = await this.#canvasToBlob(canvas, this.#mimeType, this.options?.outputQuality ?? DEFAULT_IMAGE_QUALITY); - - const previewUrl = URL.createObjectURL(previewBlob); - const file = this.blobToFile(previewBlob, this.#changeFileExtension(this.fileName, this.#extension)); - const save = this.#saveCroppedImageFunc?.(file, previewUrl) || Promise.resolve(); - this.loadWhile(save); - await save; - this.isLoading = false; - this.dialog.close('cropped'); + await this.trackLoadingPromise( + (async () => { + if (this.options?.shape === 'circle') { + this.#applyCircleMask(canvas); + } + const previewBlob = await this.#canvasToBlob(canvas, this.#mimeType, this.options?.outputQuality ?? DEFAULT_IMAGE_QUALITY); + + const previewUrl = URL.createObjectURL(previewBlob); + const file = this.blobToFile(previewBlob, this.#changeFileExtension(this.fileName, this.#extension)); + const save = this.#saveCroppedImageFunc?.(file, previewUrl) || Promise.resolve(); + await save; + this.dialog.close('cropped'); + })() + ); }} >Save diff --git a/packages/web/titanium/youtube-input/youtube-input.ts b/packages/web/titanium/youtube-input/youtube-input.ts deleted file mode 100644 index 81a7c427a..000000000 --- a/packages/web/titanium/youtube-input/youtube-input.ts +++ /dev/null @@ -1,67 +0,0 @@ -import '@material/web/icon/icon'; - -import { html, nothing, css, PropertyValues } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; -import { ExtendableOutlinedTextField } from '../extendable-outlined-text-field/extendable-outlined-text-field'; - -/** - * Material design YouTube url input. - * - * @element titanium-youtube-input - * - */ - -@customElement('titanium-youtube-input') -export class TitaniumYouTubeInput extends ExtendableOutlinedTextField { - @property({ reflect: true, type: String }) accessor autocomplete: string = 'off'; - - @property({ reflect: true, type: Boolean }) accessor spellcheck: boolean = false; - - firstUpdated() { - this.label = this.label || 'YouTube video key'; - this.pattern = '^.{11}$'; - } - - updated(changedProps: PropertyValues) { - if (changedProps.has('value')) { - this.value = this.#stripYouTubeKeyFromUrl(this.input.value); - } - } - - #stripYouTubeKeyFromUrl(value: string) { - // https://stackoverflow.com/a/8260383 - const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/; - const match = value.match(regExp); - const key = match && match[7].length === 11 ? match[7] : false; - // If key stripped from url use key. Otherwise use the value input by user. - return key ? key : value; - } - - override async reset() { - super.reset(); - this.value = ''; - } - - static styles = [ - css` - img { - max-height: 42px; - margin-right: 22px; - border-radius: 8px; - } - - md-outlined-text-field { - width: 100%; - } - `, - ]; - - protected override renderMainSlot() { - return html` - youtube_activity - ${this.value && (this.value?.length || 0) === 11 - ? html`` - : nothing} - `; - } -} From 67c1a23ca68338b8b226378802d46222426f1927 Mon Sep 17 00:00:00 2001 From: Aaron Drabeck Date: Thu, 25 Jun 2026 11:56:57 -0600 Subject: [PATCH 03/14] refactor!: make filled the only variant for dual-style titanium components BREAKING CHANGE: Removes the illed attribute from date-input, date-range-selector, single-select-base (and subclasses), page-control, show-hide, smart-attachment-input, chip-multi-select, chip, and manual-address-dialog. Outlined Material styling is no longer supported; components always render filled controls. Deletes extendable-outlined-text-field. --- .../src/demos/leavitt-company-select-demo.ts | 2 - .../src/demos/leavitt-person-select-demo.ts | 2 - .../src/demos/titanium-address-input-demo.ts | 2 - .../src/demos/titanium-chip-demo.ts | 10 +- .../demos/titanium-chip-multi-select-demo.ts | 1 - .../src/demos/titanium-date-input-demo.ts | 11 +- .../titanium-date-range-selector-demo.ts | 2 +- .../src/demos/titanium-icon-picker-demo.ts | 2 +- .../src/demos/titanium-page-control-demo.ts | 2 +- .../src/demos/titanium-show-hide-demo.ts | 2 +- .../titanium-smart-attachment-input-demo.ts | 6 +- .../leavitt/company-select/company-select.ts | 2 +- ...ail-history-viewer-filled-filter-dialog.ts | 1 - .../email-history-viewer-filled.ts | 1 - .../titanium/address-input/address-input.ts | 2 - .../address-input/manual-address-dialog.ts | 226 +++++++------- .../chip-multi-select/chip-multi-select.ts | 76 ++--- packages/web/titanium/chip/chip.ts | 15 +- .../web/titanium/data-table/page-control.ts | 26 +- .../web/titanium/date-input/date-input.ts | 47 +-- .../date-range-selector.ts | 28 +- .../extendable-outlined-text-field.ts | 277 ------------------ packages/web/titanium/show-hide/show-hide.ts | 15 +- .../single-select-base/single-select-base.ts | 65 ++-- .../smart-attachment-input.ts | 15 +- 25 files changed, 207 insertions(+), 631 deletions(-) delete mode 100644 packages/web/titanium/extendable-outlined-text-field/extendable-outlined-text-field.ts diff --git a/packages/leavittbook/src/demos/leavitt-company-select-demo.ts b/packages/leavittbook/src/demos/leavitt-company-select-demo.ts index 37742175f..fbe96d06b 100644 --- a/packages/leavittbook/src/demos/leavitt-company-select-demo.ts +++ b/packages/leavittbook/src/demos/leavitt-company-select-demo.ts @@ -38,7 +38,6 @@ export class LeavittCompanySelectDemo extends ThemePreference(LitElement) {

Filled

Local searching ) => console.log('selected change 1', e.target.selected)} @@ -164,7 +163,6 @@ export class TitaniumAddressInputDemo extends LitElement { Various chip examples demonstrating different states and configurations

- + alert('click!')}> alert('click!')}> task_alt - alert('click!')}> + alert('click!')}> task_alt @@ -72,7 +72,7 @@ export class TitaniumChipDemo extends LitElement { - (e.target.selected = !e.target.selected)}> + (e.target.selected = !e.target.selected)}> @@ -84,13 +84,13 @@ export class TitaniumChipDemo extends LitElement { task_alt - alert('remove!')} @click=${() => alert('click!')}> + alert('remove!')} @click=${() => alert('click!')}> task_alt - + diff --git a/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts b/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts index 0ff60c6a8..112f38bb3 100644 --- a/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts +++ b/packages/leavittbook/src/demos/titanium-chip-multi-select-demo.ts @@ -53,7 +53,6 @@ export class TitaniumChipMultiSelectDemo extends LitElement {
) => console.log('change', e.target.value)} > @@ -76,14 +75,14 @@ export class TitaniumDateInputDemo extends LitElement { { this.input.reset(); - this.filledInput.reset(); + this.secondInput.reset(); }} >Reset { this.input.reportValidity(); - this.filledInput.reportValidity(); + this.secondInput.reportValidity(); }} >Report validity diff --git a/packages/leavittbook/src/demos/titanium-date-range-selector-demo.ts b/packages/leavittbook/src/demos/titanium-date-range-selector-demo.ts index 0f5acd6c4..e986842eb 100644 --- a/packages/leavittbook/src/demos/titanium-date-range-selector-demo.ts +++ b/packages/leavittbook/src/demos/titanium-date-range-selector-demo.ts @@ -101,7 +101,7 @@ export class TitaniumDateRangeSelectorDemo extends LitElement {

Filled

- +
diff --git a/packages/leavittbook/src/demos/titanium-icon-picker-demo.ts b/packages/leavittbook/src/demos/titanium-icon-picker-demo.ts index 967c92f3a..3cb50b08f 100644 --- a/packages/leavittbook/src/demos/titanium-icon-picker-demo.ts +++ b/packages/leavittbook/src/demos/titanium-icon-picker-demo.ts @@ -37,7 +37,7 @@ export class TitaniumIconPickerDemo extends LitElement {

Filled

Filled icon picker example

- +
diff --git a/packages/leavittbook/src/demos/titanium-page-control-demo.ts b/packages/leavittbook/src/demos/titanium-page-control-demo.ts index 40edfe479..58fa875a1 100644 --- a/packages/leavittbook/src/demos/titanium-page-control-demo.ts +++ b/packages/leavittbook/src/demos/titanium-page-control-demo.ts @@ -70,7 +70,7 @@ export class TitaniumPageControlDemo extends LitElement {

Filled

Filled page control variant

- +
diff --git a/packages/leavittbook/src/demos/titanium-show-hide-demo.ts b/packages/leavittbook/src/demos/titanium-show-hide-demo.ts index f31a69e87..a7743c683 100644 --- a/packages/leavittbook/src/demos/titanium-show-hide-demo.ts +++ b/packages/leavittbook/src/demos/titanium-show-hide-demo.ts @@ -345,7 +345,7 @@ export class TitaniumShowHideDemo extends LitElement {

Filled button example

Read some text

(this.verticalStepValue = event.target.value)} .value=${this.verticalStepValue}> - +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ipsum arcu, semper ac aliquet eu, porttitor vel turpis. Nullam non dolor ac massa pharetra vulputate vel ac libero. In hac habitasse platea dictumst. Praesent lacus mi, vehicula eu euismod sit amet, accumsan porta diff --git a/packages/leavittbook/src/demos/titanium-smart-attachment-input-demo.ts b/packages/leavittbook/src/demos/titanium-smart-attachment-input-demo.ts index 22cac52df..b6cb17699 100644 --- a/packages/leavittbook/src/demos/titanium-smart-attachment-input-demo.ts +++ b/packages/leavittbook/src/demos/titanium-smart-attachment-input-demo.ts @@ -70,7 +70,7 @@ export class TitaniumSmartAttachmentInputDemo extends LitElement { 'xbm', ]; - @query('titanium-smart-attachment-input[filled]') private accessor smartAttachment!: TitaniumSmartAttachmentInput; + @query('titanium-smart-attachment-input[required]') private accessor smartAttachment!: TitaniumSmartAttachmentInput; @query('md-dialog') private accessor dialog!: any; static styles = [ @@ -112,9 +112,9 @@ export class TitaniumSmartAttachmentInputDemo extends LitElement {

-

Filled

+

With options

) => { - const theme = !this.shaped || !this.filled ? this.themePreference : this.shaped && this.filled && this.themePreference === 'dark' ? 'light' : 'dark'; + const theme = !this.shaped ? this.themePreference : this.shaped && this.themePreference === 'dark' ? 'light' : 'dark'; return html` ${company.Name} diff --git a/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts b/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts index 8e1ebe292..cdebe7808 100644 --- a/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts +++ b/packages/web/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.ts @@ -192,7 +192,6 @@ export class LeavittEmailHistoryViewerFilledFilterDialog extends LitElement {
Filter logs by
`; } diff --git a/packages/web/titanium/address-input/manual-address-dialog.ts b/packages/web/titanium/address-input/manual-address-dialog.ts index 415e5304e..89bd6328d 100644 --- a/packages/web/titanium/address-input/manual-address-dialog.ts +++ b/packages/web/titanium/address-input/manual-address-dialog.ts @@ -1,20 +1,15 @@ import '@material/web/icon/icon'; -import '@material/web/select/outlined-select'; import '@material/web/select/filled-select'; import '@material/web/select/select-option'; import '@material/web/dialog/dialog'; -import '@material/web/textfield/outlined-text-field'; import '@material/web/textfield/filled-text-field'; import '@material/web/button/filled-tonal-button'; import '@material/web/button/text-button'; -import { css, LitElement, nothing } from 'lit'; -import { literal, html } from 'lit/static-html.js'; +import { css, html, LitElement, nothing } from 'lit'; import { property, customElement, query, queryAll, state } from 'lit/decorators.js'; import { AddressInputAddress } from './types/address-input-address'; import { MdDialog } from '@material/web/dialog/dialog'; -import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; -import { MdOutlinedSelect } from '@material/web/select/outlined-select'; import { MdFilledTextField } from '@material/web/textfield/filled-text-field'; import { MdFilledSelect } from '@material/web/select/filled-select'; import { DOMEvent } from '../types/dom-event'; @@ -33,7 +28,6 @@ export class ManualAddressDialog extends LitElement { @property({ type: Boolean, attribute: 'show-street2' }) accessor showStreet2: boolean; @property({ type: Boolean, attribute: 'allow-international' }) accessor allowInternational: boolean = false; - @property({ type: Boolean, attribute: 'filled' }) accessor filled: boolean = false; @state() protected accessor street: string = ''; @state() protected accessor street2: string = ''; @@ -43,9 +37,7 @@ export class ManualAddressDialog extends LitElement { @state() protected accessor state: string = ''; @state() protected accessor zip: string = ''; - @queryAll('md-outlined-text-field, md-outlined-select, md-filled-text-field, md-filled-select') protected accessor allInputs: NodeListOf< - MdOutlinedTextField | MdOutlinedSelect | MdFilledTextField | MdFilledSelect - >; + @queryAll('md-filled-text-field, md-filled-select') protected accessor allInputs: NodeListOf; resolve: (value: Partial | null) => void; @@ -130,7 +122,6 @@ export class ManualAddressDialog extends LitElement { ]; render() { - /* eslint-disable lit/binding-positions, lit/no-invalid-html */ return html` ) => { @@ -144,148 +135,146 @@ export class ManualAddressDialog extends LitElement { >
${this.label}
- <${this.filled ? literal`md-filled-text-field` : literal`md-outlined-text-field`} + ) => reportValidityIfError(e.target)} - @change=${(e: DOMEvent) => (this.street = e.target.value)} + @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} + @change=${(e: DOMEvent) => (this.street = e.target.value)} > markunread_mailbox - + ${ this.showStreet2 || (this.country !== 'US' && this.country) - ? html` <${this.filled ? literal`md-filled-text-field` : literal`md-outlined-text-field`} - @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} - label="Street 2/Apartment" - autocomplete="address-line2" - .value=${this.street2 || ''} - @change=${(e: DOMEvent) => (this.street2 = e.target.value)} - > - meeting_room` + ? html`) => reportValidityIfError(e.target)} + label="Street 2/Apartment" + autocomplete="address-line2" + .value=${this.street2 || ''} + @change=${(e: DOMEvent) => (this.street2 = e.target.value)} + > + meeting_room + ` : nothing } - <${this.filled ? literal`md-filled-text-field` : literal`md-outlined-text-field`} + ) => reportValidityIfError(e.target)} - @change=${(e: DOMEvent) => (this.city = e.target.value)} - >location_city) => reportValidityIfError(e.target)} + @change=${(e: DOMEvent) => (this.city = e.target.value)} + >location_city ${ this.showCounty || (this.country !== 'US' && this.country) - ? html`<${this.filled ? literal`md-filled-text-field` : literal`md-outlined-text-field`} - @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} - label="County" - ?required=${!this.allowInternational || this.country === 'US'} - .value=${this.county || ''} - @change=${(e: DOMEvent) => (this.county = e.target.value)} - >explore` + ? html`) => reportValidityIfError(e.target)} + label="County" + ?required=${!this.allowInternational || this.country === 'US'} + .value=${this.county || ''} + @change=${(e: DOMEvent) => (this.county = e.target.value)} + >explore` : nothing } ${ this.allowInternational - ? html`<${this.filled ? literal`md-filled-select` : literal`md-outlined-select`} - @opening=${() => preventDialogOverflow(this.dialog)} - @closing=${() => allowDialogOverflow(this.dialog)} - @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} - label="Country" - autocomplete="country" - required - .value=${this.country || ''} - @change=${(e: DOMEvent) => { - e.stopPropagation(); - this.country = e.target.value; - - if (this.country === 'US') { - // If manually typed state is a valid US state abbreviation or name, preselect it - const foundState = usStates?.find( - (s) => s.abbreviation.toLowerCase() === this.state.toLowerCase() || s.name?.toLowerCase() === this.state.toLowerCase() - ); - this.state = foundState ? foundState?.abbreviation : ''; - } else if (this.country === 'CA') { - // If manually typed state is a valid CA state abbreviation or name, preselect it - const foundState = caStates?.find( - (s) => s.abbreviation.toLowerCase() === this.state.toLowerCase() || s.name?.toLowerCase() === this.state.toLowerCase() - ); - this.state = foundState ? foundState?.abbreviation : ''; - } else { - this.state = ''; - } - }} - > - map - ${countries.map((s) => html`
${s.name}
`)} - ` - : nothing - } - ${ - this.allowInternational && this.country !== 'US' && this.country !== 'CA' - ? html` - <${this.filled ? literal`md-filled-text-field` : literal`md-outlined-text-field`} - label="State/Province" - autocomplete="address-level1" - .value=${this.state || ''} - @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} - @change=${(e: DOMEvent) => (this.state = e.target.value)} - > - location_on - - ` - : html` - <${this.filled ? literal`md-filled-select` : literal`md-outlined-select`} + ? html` preventDialogOverflow(this.dialog)} @closing=${() => allowDialogOverflow(this.dialog)} - @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} - label="State" - autocomplete="address-level1" + @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} + label="Country" + autocomplete="country" required - .value=${this.state || ''} - @change=${(e: DOMEvent) => { + .value=${this.country || ''} + @change=${(e: DOMEvent) => { e.stopPropagation(); - this.state = e.target.value; - if (usStates.some((o) => o.abbreviation.toLowerCase() === this.state.toLowerCase())) { - this.country = 'US'; + this.country = e.target.value; + + if (this.country === 'US') { + const foundState = usStates?.find( + (s) => s.abbreviation.toLowerCase() === this.state.toLowerCase() || s.name?.toLowerCase() === this.state.toLowerCase() + ); + this.state = foundState ? foundState?.abbreviation : ''; + } else if (this.country === 'CA') { + const foundState = caStates?.find( + (s) => s.abbreviation.toLowerCase() === this.state.toLowerCase() || s.name?.toLowerCase() === this.state.toLowerCase() + ); + this.state = foundState ? foundState?.abbreviation : ''; + } else { + this.state = ''; } }} > - location_on + map + ${countries.map((s) => html`
${s.name}
`)} +
` + : nothing + } + ${ + this.allowInternational && this.country !== 'US' && this.country !== 'CA' + ? html` + ) => reportValidityIfError(e.target)} + @change=${(e: DOMEvent) => (this.state = e.target.value)} + > + location_on + + ` + : html` + preventDialogOverflow(this.dialog)} + @closing=${() => allowDialogOverflow(this.dialog)} + @blur=${(e: DOMEvent) => reportValidityIfError(e.target)} + label="State" + autocomplete="address-level1" + required + .value=${this.state || ''} + @change=${(e: DOMEvent) => { + e.stopPropagation(); + this.state = e.target.value; + if (usStates.some((o) => o.abbreviation.toLowerCase() === this.state.toLowerCase())) { + this.country = 'US'; + } + }} + > + location_on - ${usStates.map( - (s) => - html` -
${s.name}
-
United States
-
` - )} - ${caStates.map( - (s) => - html` -
${s.name}
-
Canada
-
` - )} - - ` + ${usStates.map( + (s) => + html` +
${s.name}
+
United States
+
` + )} + ${caStates.map( + (s) => + html` +
${s.name}
+
Canada
+
` + )} +
+ ` } - <${this.filled ? literal`md-filled-text-field` : literal`md-outlined-text-field`} + ) => reportValidityIfError(e.target)} - @change=${(e: DOMEvent) => (this.zip = e.target.value)} - >universal_local) => reportValidityIfError(e.target)} + @change=${(e: DOMEvent) => (this.zip = e.target.value)} + >universal_local @@ -312,6 +301,5 @@ export class ManualAddressDialog extends LitElement {
`; - /* eslint-enable lit/binding-positions, lit/no-invalid-html */ } } diff --git a/packages/web/titanium/chip-multi-select/chip-multi-select.ts b/packages/web/titanium/chip-multi-select/chip-multi-select.ts index 0dc55d7f1..f678f592c 100644 --- a/packages/web/titanium/chip-multi-select/chip-multi-select.ts +++ b/packages/web/titanium/chip-multi-select/chip-multi-select.ts @@ -5,21 +5,17 @@ import { html } from 'lit'; import '../../titanium/input-validator/filled-input-validator'; /** - * Multi select outlined themed input that styles + * Multi select filled themed input that styles * slotted in chips and add button * * @element titanium-chip-multi-select * - * @slot default - Main slot (intended to be a <md-outlined-button> and a list of <md-chip>) + * @slot default - Main slot (intended to be a <md-filled-tonal-button> and a list of <md-chip>) * */ @customElement('titanium-chip-multi-select') export class TitaniumChipMultiSelect extends LitElement { - /** - * Swaps outlined validator for filled validator - */ - @property({ type: Boolean, reflect: true, attribute: 'filled' }) accessor filled: boolean = false; /** * Label of input to display to users */ @@ -101,11 +97,20 @@ export class TitaniumChipMultiSelect extends LitElement { :host { display: block; width: 100%; + + --md-filled-field-container-shape: 16px; + + --md-filled-field-active-indicator-height: 0; + --md-filled-field-error-active-indicator-height: 0; + --md-filled-field-hover-active-indicator-height: 0; + --md-filled-field-focus-active-indicator-height: 0; + --md-filled-field-disabled-active-indicator-height: 0; } titanium-filled-input-validator { display: block; width: 100%; + --md-filled-field-with-label-bottom-space: 12px; } slot-container { @@ -113,51 +118,34 @@ export class TitaniumChipMultiSelect extends LitElement { flex-wrap: wrap; gap: 8px; align-items: center; + margin-top: 6px; } span { font-size: 13px; } - :host([filled]) { - --md-filled-field-container-shape: 16px; - - --md-filled-field-active-indicator-height: 0; - --md-filled-field-error-active-indicator-height: 0; - --md-filled-field-hover-active-indicator-height: 0; - --md-filled-field-focus-active-indicator-height: 0; - --md-filled-field-disabled-active-indicator-height: 0; + ::slotted(md-filled-button), + ::slotted(md-filled-tonal-button) { + --md-filled-button-container-shape: 8px; + --md-filled-button-container-height: 32px; + + --md-filled-button-with-trailing-icon-leading-space: 8px; + --md-filled-button-with-trailing-icon-trailing-space: 16px; + --md-filled-button-with-leading-icon-leading-space: 8px; + --md-filled-button-with-leading-icon-trailing-space: 16px; + + --md-filled-tonal-button-with-trailing-icon-leading-space: 8px; + --md-filled-tonal-button-with-trailing-icon-trailing-space: 16px; + --md-filled-tonal-button-with-leading-icon-leading-space: 8px; + --md-filled-tonal-button-with-leading-icon-trailing-space: 16px; + --md-filled-tonal-button-container-shape: 8px; + --md-filled-tonal-button-container-height: 32px; + } - slot-container { - margin-top: 6px; - } - - titanium-filled-input-validator { - --md-filled-field-with-label-bottom-space: 12px; - } - - ::slotted(md-filled-button), - ::slotted(md-filled-tonal-button) { - --md-filled-button-container-shape: 8px; - --md-filled-button-container-height: 32px; - - --md-filled-button-with-trailing-icon-leading-space: 8px; - --md-filled-button-with-trailing-icon-trailing-space: 16px; - --md-filled-button-with-leading-icon-leading-space: 8px; - --md-filled-button-with-leading-icon-trailing-space: 16px; - - --md-filled-tonal-button-with-trailing-icon-leading-space: 8px; - --md-filled-tonal-button-with-trailing-icon-trailing-space: 16px; - --md-filled-tonal-button-with-leading-icon-leading-space: 8px; - --md-filled-tonal-button-with-leading-icon-trailing-space: 16px; - --md-filled-tonal-button-container-shape: 8px; - --md-filled-tonal-button-container-height: 32px; - } - - ::slotted(md-input-chip) { - background: var(--md-sys-color-surface-container); - --md-sys-color-outline: transparent; - } + ::slotted(md-input-chip) { + background: var(--md-sys-color-surface-container); + --md-sys-color-outline: transparent; } `, ]; diff --git a/packages/web/titanium/chip/chip.ts b/packages/web/titanium/chip/chip.ts index b3e2bbd60..f81f42551 100644 --- a/packages/web/titanium/chip/chip.ts +++ b/packages/web/titanium/chip/chip.ts @@ -58,8 +58,6 @@ export class TitaniumChip extends LitElement { */ @property({ type: Boolean, reflect: true }) accessor disabled: boolean = false; - @property({ type: Boolean, reflect: true }) accessor filled: boolean = false; - @property({ type: Boolean, reflect: true, attribute: 'has-leading-items' }) private accessor hasLeadingItems = false; @property({ type: Boolean, reflect: true, attribute: 'has-trailing-items' }) private accessor hasTrailingItems = false; @@ -100,12 +98,12 @@ export class TitaniumChip extends LitElement { height: inherit; text-align: inherit; - border: 1px solid var(--titanium-chip-outline-color, var(--md-sys-color-outline)); + border: none; border-radius: 8px; --md-focus-ring-shape: 8px; - color: inherit; - background: inherit; + background-color: var(--titanium-chip-filled-background-color, var(--md-sys-color-surface-container)); + color: var(--titanium-chip-filled-color, var(--md-sys-color-on-surface)); width: inherit; outline: none; @@ -119,13 +117,6 @@ export class TitaniumChip extends LitElement { padding: 0 12px; } - :host([filled]) a, - :host([filled]) button { - border: none; - background-color: var(--titanium-chip-filled-background-color, var(--md-sys-color-surface-container)); - color: var(--titanium-chip-filled-color, var(--md-sys-color-on-surface)); - } - :host([selected]) button, :host([selected]) a, :host([has-leading-items]) button, diff --git a/packages/web/titanium/data-table/page-control.ts b/packages/web/titanium/data-table/page-control.ts index 67476d31c..9c7588724 100644 --- a/packages/web/titanium/data-table/page-control.ts +++ b/packages/web/titanium/data-table/page-control.ts @@ -1,11 +1,9 @@ -import { css, LitElement } from 'lit'; +import { css, html, LitElement } from 'lit'; import { property, customElement, queryAsync } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; -import { MdOutlinedSelect } from '@material/web/select/outlined-select.js'; -import { literal, html } from 'lit/static-html.js'; +import { MdFilledSelect } from '@material/web/select/filled-select.js'; import '@material/web/iconbutton/icon-button'; -import '@material/web/select/outlined-select.js'; import '@material/web/select/filled-select.js'; import '@material/web/select/select-option.js'; @@ -55,12 +53,7 @@ export class TitaniumPageControl extends LitElement { */ @property({ type: Boolean }) accessor disabled: boolean; - /** - * Swaps out outlined select for a filled select. - */ - @property({ type: Boolean, attribute: 'filled' }) accessor filled: boolean = false; - - @queryAsync('md-select') protected accessor select: MdOutlinedSelect; + @queryAsync('md-select') protected accessor select: MdFilledSelect; /** * Gets or sets take value and assigns it to local storage. @@ -141,13 +134,6 @@ export class TitaniumPageControl extends LitElement { display: flex; } - md-outlined-select { - min-width: 100px; - --md-outlined-field-top-space: 4px; - --md-outlined-field-bottom-space: 4px; - --md-outlined-select-text-field-container-shape: 8px; - } - md-filled-select { min-width: 100px; overflow: hidden; @@ -171,11 +157,10 @@ export class TitaniumPageControl extends LitElement { `; render() { - /* eslint-disable lit/binding-positions, lit/no-invalid-html */ return html`
${this.label}
- <${this.filled ? literal`md-filled-select` : literal`md-outlined-select`} + { e.stopPropagation(); @@ -191,7 +176,7 @@ export class TitaniumPageControl extends LitElement {
${o}
` )} - +
${this.#getPageStats(this.page, this.count)} @@ -203,6 +188,5 @@ export class TitaniumPageControl extends LitElement {
`; - /* eslint-enable lit/binding-positions, lit/no-invalid-html */ } } diff --git a/packages/web/titanium/date-input/date-input.ts b/packages/web/titanium/date-input/date-input.ts index 6dfad6a3f..8e7ec7415 100644 --- a/packages/web/titanium/date-input/date-input.ts +++ b/packages/web/titanium/date-input/date-input.ts @@ -5,12 +5,11 @@ import { redispatchEvent } from '@material/web/internal/events/redispatch-event' import { stringConverter } from '@material/web/internal/controller/string-converter'; -import '@material/web/field/outlined-field'; import '@material/web/field/filled-field'; import '@material/web/icon/icon'; import '@material/web/iconbutton/icon-button'; import { Field } from '@material/web/field/internal/field'; -import { html, literal } from 'lit/static-html.js'; +import { html } from 'lit'; /** * A date input the works in Firefox, Safari and Chrome @@ -88,11 +87,6 @@ export class TitaniumDateInput extends LitElement { @property() accessor label = ''; - /** - * Swaps out outlined text field for a filled text field. - */ - @property({ type: Boolean, reflect: true, attribute: 'filled' }) accessor filled: boolean = false; - @property({ type: Boolean, reflect: true }) accessor required = false; /** @@ -262,7 +256,7 @@ export class TitaniumDateInput extends LitElement { this.nativeErrorText = this.validationMessage; if (prevMessage === this.getErrorText()) { - (this.shadowRoot?.querySelector(this.filled ? 'md-filled-field' : 'md-outlined-field') as Field)?.reannounceError(); + (this.shadowRoot?.querySelector('md-filled-field') as Field)?.reannounceError(); } return valid; @@ -295,7 +289,7 @@ export class TitaniumDateInput extends LitElement { display: block; } - :host([filled]) { + :host { --md-filled-field-container-shape: 16px; --md-filled-field-active-indicator-height: 0; @@ -307,12 +301,6 @@ export class TitaniumDateInput extends LitElement { --md-filled-field-label-text-populated-line-height: 14px; } - :host(:not([filled])) { - --md-outlined-field-top-space: 15px; - --md-outlined-field-bottom-space: 15px; - } - - md-outlined-field, md-filled-field { width: 100%; } @@ -329,14 +317,7 @@ export class TitaniumDateInput extends LitElement { /* Safari Only */ _::-webkit-full-page-media, _:future, - input { - padding-top: 14px; - padding-bottom: 7px; - } - - :host([filled]) _::-webkit-full-page-media, - :host([filled]) _:future, - :host([filled]) input { + :host input { padding-top: 21px; padding-bottom: 0; } @@ -355,14 +336,9 @@ export class TitaniumDateInput extends LitElement { input { min-width: 100px; - padding-bottom: 10px; - padding-top: 16px; - height: 30px; - } - - :host([filled]) input { padding-bottom: 3px; padding-top: 23px; + height: 30px; } } @@ -380,21 +356,15 @@ export class TitaniumDateInput extends LitElement { display: none; } - :host([filled]) { + :host { --md-filled-field-label-text-populated-line-height: 16px; } - - :host(:not([filled])) { - --md-outlined-field-top-space: 16px; - --md-outlined-field-bottom-space: 16px; - } } `; protected render() { - /* eslint-disable lit/binding-positions, lit/no-invalid-html */ return html` - <${this.filled ? literal`md-filled-field` : literal`md-outlined-field`} + - + `; - /* eslint-enable lit/binding-positions, lit/no-invalid-html */ } } diff --git a/packages/web/titanium/date-range-selector/date-range-selector.ts b/packages/web/titanium/date-range-selector/date-range-selector.ts index f0ae07f9f..021dc0683 100644 --- a/packages/web/titanium/date-range-selector/date-range-selector.ts +++ b/packages/web/titanium/date-range-selector/date-range-selector.ts @@ -1,5 +1,4 @@ import '@material/web/icon/icon'; -import '@material/web/field/outlined-field'; import '@material/web/field/filled-field'; import '@material/web/menu/menu'; import '@material/web/button/text-button'; @@ -10,7 +9,7 @@ import '@material/web/list/list-item'; import '../date-input/date-input'; import { css, LitElement, nothing } from 'lit'; -import { html, literal } from 'lit/static-html.js'; +import { html } from 'lit'; import { property, customElement, query, state } from 'lit/decorators.js'; import dayjs from 'dayjs/esm'; import { DateRangeOption } from './types/date-range-option'; @@ -63,11 +62,6 @@ export class TitaniumDateRangeSelector extends LitElement { */ @property({ type: Boolean, reflect: true }) accessor disabled: boolean = false; - /** - * Whether or not the input should be filled - */ - @property({ type: Boolean, reflect: true }) accessor filled: boolean = false; - /** * Override default ranges with custom options. Needs to contain, at least, 'allTime'. */ @@ -130,7 +124,7 @@ export class TitaniumDateRangeSelector extends LitElement { position: relative; } - :host([filled]) { + :host { --md-menu-container-shape: 16px; --md-filled-field-container-shape: 16px; @@ -174,10 +168,6 @@ export class TitaniumDateRangeSelector extends LitElement { gap: 12px; padding: 12px; - border-radius: 0 0 4px 4px; - } - - :host([filled]) menu-actions { border-radius: 0 0 16px 16px; } @@ -191,13 +181,11 @@ export class TitaniumDateRangeSelector extends LitElement { margin-right: 16px; } - md-filled-field, - md-outlined-field { + md-filled-field { width: 100%; } - md-filled-field md-icon, - md-outlined-field md-icon { + md-filled-field md-icon { margin: 0 12px; } @@ -274,9 +262,8 @@ export class TitaniumDateRangeSelector extends LitElement { } render() { - /* eslint-disable lit/binding-positions, lit/no-invalid-html */ return html` - <${this.filled ? literal`md-filled-field` : literal`md-outlined-field`} + ${this.#getRange(this.range)?.icon || 'date_range'} ${this.open ? 'arrow_drop_up' : 'arrow_drop_down'} - + ) => { this.proposedStartDate = e.target.value ?? ''; @@ -389,7 +375,6 @@ export class TitaniumDateRangeSelector extends LitElement { end-date label="To" type=${this.type} - .filled=${this.filled} .value=${this.proposedEndDate ?? ''} @change=${async (e: DOMEvent) => { this.proposedEndDate = e.target.value ?? ''; @@ -429,6 +414,5 @@ export class TitaniumDateRangeSelector extends LitElement { `; - /* eslint-enable lit/binding-positions, lit/no-invalid-html */ } } diff --git a/packages/web/titanium/extendable-outlined-text-field/extendable-outlined-text-field.ts b/packages/web/titanium/extendable-outlined-text-field/extendable-outlined-text-field.ts deleted file mode 100644 index 31564dd9b..000000000 --- a/packages/web/titanium/extendable-outlined-text-field/extendable-outlined-text-field.ts +++ /dev/null @@ -1,277 +0,0 @@ -import '@material/web/textfield/outlined-text-field'; - -import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; -import { LitElement, html } from 'lit'; -import { property, query } from 'lit/decorators.js'; -import { stringConverter } from '@material/web/internal/controller/string-converter'; -import { TextFieldType, UnsupportedTextFieldType } from '@material/web/textfield/internal/text-field'; -import { DOMEvent } from '../types/dom-event'; -import { redispatchEvent } from '@material/web/internal/events/redispatch-event'; - -export class ExtendableOutlinedTextField extends LitElement { - @query('md-outlined-text-field') accessor input: MdOutlinedTextField; - - /** - * Gets or sets whether or not the text field is in a visually invalid state. - * - * This error state overrides the error state controlled by - * `reportValidity()`. - */ - @property({ type: Boolean, reflect: true }) accessor error = false; - - @property({ type: Boolean, reflect: true }) accessor disabled = false; - - /** - * The error message that replaces supporting text when `error` is true. If - * `errorText` is an empty string, then the supporting text will continue to - * show. - * - * This error message overrides the error message displayed by - * `reportValidity()`. - */ - @property({ attribute: 'error-text' }) accessor errorText = ''; - - @property() accessor label = ''; - - @property({ type: Boolean, reflect: true }) accessor required = false; - - /** - * The current value of the text field. It is always a string. - */ - @property() accessor value = ''; - - /** - * An optional prefix to display before the input value. - */ - @property({ attribute: 'prefix-text' }) accessor prefixText = ''; - - /** - * An optional suffix to display after the input value. - */ - @property({ attribute: 'suffix-text' }) accessor suffixText = ''; - - /** - * Whether or not the text field has a leading icon. Used for SSR. - */ - @property({ type: Boolean, attribute: 'has-leading-icon' }) - accessor hasLeadingIcon = false; - - /** - * Whether or not the text field has a trailing icon. Used for SSR. - */ - @property({ type: Boolean, attribute: 'has-trailing-icon' }) - accessor hasTrailingIcon = false; - - /** - * Conveys additional information below the text field, such as how it should - * be used. - */ - @property({ attribute: 'supporting-text' }) accessor supportingText = ''; - - /** - * Override the input text CSS `direction`. Useful for RTL languages that use - * LTR notation for fractions. - */ - @property({ attribute: 'text-direction' }) accessor textDirection = ''; - - /** - * The number of rows to display for a `type="textarea"` text field. - * Defaults to 2. - */ - @property({ type: Number }) accessor rows = 2; - - /** - * The number of cols to display for a `type="textarea"` text field. - * Defaults to 20. - */ - @property({ type: Number }) accessor cols = 20; - - // properties - @property({ reflect: true }) override accessor inputMode = ''; - - /** - * Defines the greatest value in the range of permitted values. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#max - */ - @property() accessor max = ''; - - /** - * The maximum number of characters a user can enter into the text field. Set - * to -1 for none. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#maxlength - */ - @property({ type: Number }) accessor maxLength = -1; - - /** - * Defines the most negative value in the range of permitted values. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#min - */ - @property() accessor min = ''; - - /** - * The minimum number of characters a user can enter into the text field. Set - * to -1 for none. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#minlength - */ - @property({ type: Number }) accessor minLength = -1; - - /** - * A regular expression that the text field's value must match to pass - * constraint validation. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#pattern - */ - @property() accessor pattern = ''; - - @property({ reflect: true, converter: stringConverter }) accessor placeholder = ''; - - /** - * Indicates whether or not a user should be able to edit the text field's - * value. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#readonly - */ - @property({ type: Boolean, reflect: true }) accessor readOnly = false; - - /** - * Indicates that input accepts multiple email addresses. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email#multiple - */ - @property({ type: Boolean, reflect: true }) accessor multiple = false; - - /** - * Returns or sets the element's step attribute, which works with min and max - * to limit the increments at which a numeric or date-time value can be set. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#step - */ - @property() accessor step = ''; - - /** - * The `` type to use, defaults to "text". The type greatly changes how - * the text field behaves. - * - * Text fields support a limited number of `` types: - * - * - text - * - textarea - * - email - * - number - * - password - * - search - * - tel - * - url - * - * See - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types - * for more details on each input type. - */ - @property({ reflect: true }) accessor type: TextFieldType | UnsupportedTextFieldType = 'text'; - - /** - * Describes what, if any, type of autocomplete functionality the input - * should provide. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete - */ - @property({ reflect: true, type: String }) accessor autocomplete = ''; - - /** - * Describes what, if any, type of spellcheck functionality the input - * should provide. - * - * https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck - */ - @property({ reflect: true, type: Boolean }) accessor spellcheck; - - checkValidity() { - return this.input.checkValidity(); - } - - reportValidity() { - return this.input.reportValidity(); - } - - select() { - this.input.select(); - } - - setCustomValidity(error: string) { - this.input.setCustomValidity(error); - } - - setRangeText(replacement: string, start: number, end: number, selectionMode?: SelectionMode) { - this.input.setRangeText(replacement, start, end, selectionMode); - } - - setSelectionRange(start: number | null, end: number | null, direction?: 'forward' | 'backward' | 'none') { - this.input.setSelectionRange(start, end, direction); - } - - stepDown(stepDecrement?: number) { - this.input.stepDown(stepDecrement); - } - - stepUp(stepDecrement?: number) { - this.input.stepUp(stepDecrement); - } - - reset() { - this.input.reset(); - } - - focus() { - this.input.focus(); - } - - protected renderMainSlot() { - return html` - - - - `; - } - - render() { - return html` - ) => (this.value = e.target.value)} - @blur=${(e) => redispatchEvent(this, e)} - @focus=${(e) => redispatchEvent(this, e)} - @change=${(e) => redispatchEvent(this, e)} - @invalid=${(e) => redispatchEvent(this, e)} - .disabled=${this.disabled} - .required=${this.required} - .error=${this.error} - .autocomplete=${this.autocomplete} - .spellcheck=${this.spellcheck} - .errorText=${this.errorText} - .hasLeadingIcon=${this.hasLeadingIcon} - .hasTrailingIcon=${this.hasTrailingIcon} - .label=${this.label} - .max=${this.max} - .maxLength=${this.maxLength} - .minLength=${this.minLength} - .pattern=${this.pattern} - .placeholder=${this.placeholder} - .prefixText=${this.prefixText} - .readOnly=${this.readOnly} - .rows=${this.rows} - .step=${this.step} - .suffixText=${this.suffixText} - .supportingText=${this.supportingText} - .textDirection=${this.textDirection} - .type=${this.type} - .value=${this.value} - > - ${this.renderMainSlot()} - - `; - } -} diff --git a/packages/web/titanium/show-hide/show-hide.ts b/packages/web/titanium/show-hide/show-hide.ts index 3f18b2e6a..1d03aa611 100644 --- a/packages/web/titanium/show-hide/show-hide.ts +++ b/packages/web/titanium/show-hide/show-hide.ts @@ -1,8 +1,6 @@ -import { css, LitElement, PropertyValues } from 'lit'; -import { literal, html } from 'lit/static-html.js'; +import { css, html, LitElement, PropertyValues } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; -import '@material/web/button/outlined-button'; import '@material/web/button/filled-button'; /** @@ -31,10 +29,6 @@ export default class TitaniumShowHide extends LitElement { @property({ type: Boolean, reflect: true, attribute: 'collapsed' }) accessor collapsed: boolean = true; @property({ type: Boolean, reflect: true, attribute: 'has-hidden-items' }) protected accessor hasHiddenItems: boolean = false; @property({ type: Number }) accessor hiddenItemCount: number = 0; - /** - * Swaps out outlined button for a filled button when true - */ - @property({ type: Boolean, attribute: 'filled' }) accessor filled: boolean = false; @query('items-container') protected accessor itemsContainer: HTMLElement; @query('collapsed-box') protected accessor collapsedContainer: HTMLElement; @@ -101,7 +95,6 @@ export default class TitaniumShowHide extends LitElement { gap: var(--titanium-show-hide-gap, 8px); } - md-outlined-button, md-filled-button { max-width: 160px; width: 100%; @@ -116,7 +109,6 @@ export default class TitaniumShowHide extends LitElement { ]; render() { - /* eslint-disable lit/binding-positions, lit/no-invalid-html */ return html`