From 0727447c9e5ebd0eb93b496f9e14298811fd65bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dabiel=20Gonz=C3=A1lez=20Ramos?= Date: Mon, 10 Feb 2025 20:37:05 +0200 Subject: [PATCH 1/6] refactor: load cally calendar library from CDN instead of bundling --- .../components/date-picker/bq-date-picker.tsx | 88 ++++++++++--------- .../date-picker/bq-date-picker.types.ts | 17 ++++ .../date-picker/helper/callyLibrary.ts | 81 +++++++++++++++++ packages/beeq/src/global.d.ts | 10 --- 4 files changed, 146 insertions(+), 50 deletions(-) create mode 100644 packages/beeq/src/components/date-picker/helper/callyLibrary.ts diff --git a/packages/beeq/src/components/date-picker/bq-date-picker.tsx b/packages/beeq/src/components/date-picker/bq-date-picker.tsx index d28110b3e..096cfd9d2 100644 --- a/packages/beeq/src/components/date-picker/bq-date-picker.tsx +++ b/packages/beeq/src/components/date-picker/bq-date-picker.tsx @@ -11,9 +11,9 @@ import { State, Watch, } from '@stencil/core'; -import { CalendarDate } from 'cally'; -import { DATE_PICKER_TYPE, DaysOfWeek, TDatePickerType } from './bq-date-picker.types'; +import { DATE_PICKER_TYPE, DaysOfWeek, TCalendarDate, TDatePickerType } from './bq-date-picker.types'; +import { isCallyLibraryLoaded, loadCallyLibrary } from './helper/callyLibrary'; import { Placement } from '../../services/interfaces'; import { hasSlotContent, @@ -160,7 +160,7 @@ export class BqDatePicker { // Own Properties // ==================== - private callyElem?: InstanceType; + private callyElem?: TCalendarDate; private inputElem?: HTMLInputElement; private labelElem?: HTMLElement; private prefixElem?: HTMLElement; @@ -310,6 +310,7 @@ export class BqDatePicker { @Watch('value') handleValueChange() { const { formatDisplayValue, internals, value } = this; + if (!isCallyLibraryLoaded()) return; internals.setFormValue(!isNil(value) ? `${value}` : undefined); this.updateFormValidity(); @@ -354,12 +355,18 @@ export class BqDatePicker { // ===================================== async connectedCallback() { - if (!isClient()) return; + if (!isClient() || isCallyLibraryLoaded()) return; - await import('cally'); + try { + console.log('Loading Cally library...'); + await loadCallyLibrary(); + } catch (error) { + console.error(error); + } } - componentWillLoad() { + async componentDidLoad() { + this.handleSlotChange(); this.handleValueChange(); } @@ -437,10 +444,11 @@ export class BqDatePicker { }; private setFocusedDate = () => { - if (!this.callyElem) return; + const { callyElem, formatFocusedDate, value } = this; + if (!(callyElem && isCallyLibraryLoaded())) return; // We need to set the focused date in the calendar component via a ref // because it doesn't work when passed as a prop (the Cally element does not re-render) - this.focusedDate = this.value ? this.formatFocusedDate(this.value) : new Date().toLocaleDateString('fr-CA'); + this.focusedDate = value ? formatFocusedDate(value) : new Date().toLocaleDateString('fr-CA'); this.callyElem.focusedDate = this.focusedDate; }; @@ -496,19 +504,15 @@ export class BqDatePicker { ev.stopPropagation(); }; - private handleLabelSlotChange = () => { + private handleSlotChange = () => { this.hasLabel = hasSlotContent(this.labelElem); - }; - - private handlePrefixSlotChange = () => { this.hasPrefix = hasSlotContent(this.prefixElem); - }; - - private handleSuffixSlotChange = () => { this.hasSuffix = hasSlotContent(this.suffixElem); }; - private generateCalendarMonth = (offset?: number, className = ''): Element => { + private generateCalendarMonth = (offset?: number, className = ''): Element | null => { + if (!isCallyLibraryLoaded()) return null; + return ( { + if (!isCallyLibraryLoaded()) return []; + if (this.type === 'range' || (this.type === 'multi' && this.months)) { return Array.from({ length: this.months }, (_, i) => { const offset = i > 0 ? i : undefined; @@ -625,7 +631,7 @@ export class BqDatePicker { ref={(labelElem: HTMLSpanElement) => (this.labelElem = labelElem)} part="label" > - + {/* Select date picker dropdown */} (this.prefixElem = spanElem)} part="prefix" > - + {/* HTML Input */} (this.suffixElem = spanElem)} part="suffix" > - + - (this.callyElem = elem as InstanceType)} - > - - + {isCallyLibraryLoaded() && ( + (this.callyElem = elem as TCalendarDate)} + > + + -
{this.generateCalendarMonths()}
-
+
{this.generateCalendarMonths()}
+
+ )}
); diff --git a/packages/beeq/src/components/date-picker/bq-date-picker.types.ts b/packages/beeq/src/components/date-picker/bq-date-picker.types.ts index 410a1f142..9648b03fd 100644 --- a/packages/beeq/src/components/date-picker/bq-date-picker.types.ts +++ b/packages/beeq/src/components/date-picker/bq-date-picker.types.ts @@ -2,3 +2,20 @@ export type DaysOfWeek = 0 | 1 | 2 | 3 | 4 | 5 | 6; export const DATE_PICKER_TYPE = ['single', 'multi', 'range'] as const; export type TDatePickerType = (typeof DATE_PICKER_TYPE)[number]; + +export type TCalendarDate = { + focusedDate: string; + value: string; + min?: string; + max?: string; + months?: number; + tentative?: string; + pageBy?: 'single' | 'months'; + firstDayOfWeek?: number; + showOutsideDays?: boolean; + isDateDisallowed?: (date: Date) => boolean; + locale?: string; + onChange?: (ev: Event) => void; + onRangestart?: (ev: CustomEvent) => void; + onRangeend?: (ev: CustomEvent) => void; +}; diff --git a/packages/beeq/src/components/date-picker/helper/callyLibrary.ts b/packages/beeq/src/components/date-picker/helper/callyLibrary.ts new file mode 100644 index 000000000..413bbd441 --- /dev/null +++ b/packages/beeq/src/components/date-picker/helper/callyLibrary.ts @@ -0,0 +1,81 @@ +/* -------------------------------------------------------------------------- */ +/* Cally Library loader helper */ +/* -------------------------------------------------------------------------- */ + +const CALLY_SCRIPT_ATTRIBUTE = 'data-cally-library' as const; +//❗ Make sure to update the version here when the library releases a new version +const CALLY_LIB_VERSION = '0.8.0' as const; +/** + * ❗ Make sure to update the hash here if the `CALLY_LIB_VERSION` is updated + * This hash is used to ensure the integrity of the library when loading it + * from a CDN. + * @see https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity + */ +const CALLY_LIB_HASH = 'sha384-giuY/f8D3+ehgOTmQMr4HvrreOITDmvXsZuDCJ1csQ+3dURHA24NqRd8lkSI6uGF' as const; + +let isLibraryLoaded = false; + +/** + * Checks if the Cally library is already loaded in the document + * @returns {boolean} True if the library is loaded + */ +export const isCallyLibraryLoaded = (): boolean => { + return isLibraryLoaded || document.querySelector(`script[${CALLY_SCRIPT_ATTRIBUTE}]`) !== null; +}; + +/** + * Loads the Cally library for the date picker component + * @throws {Error} If the library fails to load + * @return {Promise} + */ +export const loadCallyLibrary = async (): Promise => { + if (isCallyLibraryLoaded()) return; + + try { + await loadScript({ + type: 'module', + src: `https://unpkg.com/cally@${CALLY_LIB_VERSION}/dist/cally.js`, + attributes: { + [CALLY_SCRIPT_ATTRIBUTE]: '', + crossOrigin: 'anonymous', + integrity: CALLY_LIB_HASH, + }, + }); + + isLibraryLoaded = true; + } catch (error) { + isLibraryLoaded = false; + throw new Error(`Failed to load the cally library: ${error instanceof Error ? error.message : 'Unknown error'}`); + } +}; + +interface ScriptConfig { + type: string; + src: string; + attributes?: Record; +} + +/** + * Helper function to load a script with given configuration + * @param {Object} config - Script configuration + * @param {string} config.type - The type of the script + * @param {string} config.src - The source URL of the script + * @param {Object} [config.attributes] - Additional attributes to set on the script element + * @returns {Promise} + */ +const loadScript = ({ type, src, attributes = {} }: ScriptConfig): Promise => { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.type = type; + script.src = src; + + Object.entries(attributes).forEach(([key, value]) => { + script.setAttribute(key, value); + }); + + script.onload = () => resolve(); + script.onerror = (error: Event) => reject(error); + + document.head.appendChild(script); + }); +}; diff --git a/packages/beeq/src/global.d.ts b/packages/beeq/src/global.d.ts index 7f7405027..34941a838 100644 --- a/packages/beeq/src/global.d.ts +++ b/packages/beeq/src/global.d.ts @@ -1,24 +1,14 @@ import type { JSXBase } from '@stencil/core/internal'; -import type { CalendarDateProps, CalendarMonthProps, CalendarMultiProps, CalendarRangeProps } from 'cally'; /* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-ignore: this imported file declaration is generated by the build process // eslint-disable-next-line import-x/no-unresolved import type { CustomElements } from '../cem/beeq'; -type EventName = T extends `on${infer Rest}` ? `on${Capitalize>}` : T; -type MapEvents = { - [K in keyof T as EventName]: T[K]; -}; - declare module '@stencil/core' { // eslint-disable-next-line @typescript-eslint/no-namespace export namespace JSX { interface IntrinsicElements extends CustomElements { - 'calendar-multi': CalendarMultiProps & JSXBase.HTMLAttributes; - 'calendar-range': MapEvents & JSXBase.HTMLAttributes; - 'calendar-date': CalendarDateProps & JSXBase.HTMLAttributes; - 'calendar-month': CalendarMonthProps & JSXBase.HTMLAttributes; // Extend the global slot HTML element to include the id and class attributes slot: JSXBase.SlotAttributes & { id?: string; class?: string }; } From b579e4bc339c844acf004891ec51e37999e432fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dabiel=20Gonz=C3=A1lez=20Ramos?= Date: Tue, 11 Feb 2025 09:53:05 +0200 Subject: [PATCH 2/6] chore(deps): remove cally from beeq dependencies --- package-lock.json | 18 +----------------- packages/beeq/package.json | 3 +-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 70dd99a73..4d16dd57a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15363,12 +15363,6 @@ "node": ">= 4.0.0" } }, - "node_modules/atomico": { - "version": "1.79.2", - "resolved": "https://registry.npmjs.org/atomico/-/atomico-1.79.2.tgz", - "integrity": "sha512-mshhLRMeIltNYbnQnqgnrvJ/uDa8XDfTQcjw3ymOygQqwHIQ4Sp0LcNYMCbACkV3DtV+eDXb9szwU4qMUuGwYQ==", - "license": "MIT" - }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -16451,15 +16445,6 @@ "node": ">=6" } }, - "node_modules/cally": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cally/-/cally-0.7.2.tgz", - "integrity": "sha512-ej7AfqMe3ACjAzqvkiH0ZHRSQzRRsoJlQwpvm6oevkEbMrgFmlxkBXdkxnGj4AvG743KGV/3sw2Ya85cbWqWew==", - "license": "MIT", - "dependencies": { - "atomico": "^1.76.1" - } - }, "node_modules/camel-case": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", @@ -43575,8 +43560,7 @@ "dependencies": { "@floating-ui/core": "^1.6.9", "@floating-ui/dom": "^1.6.13", - "@stencil/core": "^4.25.1", - "cally": "^0.7.2" + "@stencil/core": "^4.25.1" } }, "packages/beeq-angular": { diff --git a/packages/beeq/package.json b/packages/beeq/package.json index 27168b77b..2b7e710cf 100644 --- a/packages/beeq/package.json +++ b/packages/beeq/package.json @@ -15,8 +15,7 @@ "dependencies": { "@floating-ui/core": "^1.6.9", "@floating-ui/dom": "^1.6.13", - "@stencil/core": "^4.25.1", - "cally": "^0.7.2" + "@stencil/core": "^4.25.1" }, "repository": { "type": "git", From 250223ae0cb6955a338fe244e51a0094aab5ca8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dabiel=20Gonz=C3=A1lez=20Ramos?= Date: Tue, 11 Feb 2025 12:15:45 +0200 Subject: [PATCH 3/6] refactor: streamline path resolution in stencil.config.ts --- packages/beeq/stencil.config.ts | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/beeq/stencil.config.ts b/packages/beeq/stencil.config.ts index 1a312c700..3eb42dfdd 100644 --- a/packages/beeq/stencil.config.ts +++ b/packages/beeq/stencil.config.ts @@ -10,16 +10,17 @@ import tailwind, { PluginConfigOpts, tailwindHMR } from 'stencil-tailwind-plugin import { angularValueAccessorBindings, generateCustomElementsJson, vueComponentModels } from './src/tools'; import tailwindConf from '../../tailwind.config'; +const namespace = 'beeq'; +const componentCorePackage = `@${namespace}/core`; +const customElementsDir = 'dist/components'; +const resolvePath = (path: string) => resolve(__dirname, path).replace(/\\/g, '/'); + const tailwindOpts: PluginConfigOpts = { - postcss: resolve(__dirname, '../../postcss.config.js').replace(/\\/g, '/'), + postcss: resolvePath('../../postcss.config.js'), tailwindConf: tailwindConf, stripComments: true, }; -const namespace = 'beeq'; -const componentCorePackage = `@${namespace}/core`; -const customElementsDir = 'dist/components'; - export const config: Config = { namespace, taskQueue: 'async', @@ -28,14 +29,11 @@ export const config: Config = { env: { BEEQ_ASSETS_BASE_PATH: process.env.BEEQ_ASSETS_BASE_PATH, }, - globalStyle: resolve(__dirname, './src/global/styles/default.scss').replace(/\\/g, '/'), + globalStyle: resolvePath('./src/global/styles/default.scss'), plugins: [ sass({ - includePaths: [ - resolve(__dirname, '../../node_modules').replace(/\\/g, '/'), - resolve(__dirname, 'src/global/styles').replace(/\\/g, '/'), - ], - injectGlobalPaths: [resolve(__dirname, 'src/global/styles/mixins/index.scss').replace(/\\/g, '/')], + includePaths: [resolvePath('../../node_modules'), resolvePath('src/global/styles')], + injectGlobalPaths: [resolvePath('src/global/styles/mixins/index.scss')], outputStyle: 'compressed', sourceMap: true, sourceMapEmbed: true, @@ -75,37 +73,39 @@ export const config: Config = { serviceWorker: null, // disable service workers }, angular({ + /* --------------------------- Angular output target -------------------------- */ componentCorePackage, outputType: 'component', // Generate many component wrappers tied to a single Angular module (lazy/hydrated approach) - directivesProxyFile: resolve(__dirname, '../beeq-angular/src/directives/components.ts').replace(/\\/g, '/'), - directivesArrayFile: resolve(__dirname, '../beeq-angular/src/directives/index.ts').replace(/\\/g, '/'), + directivesProxyFile: resolvePath('../beeq-angular/src/directives/components.ts'), + directivesArrayFile: resolvePath('../beeq-angular/src/directives/index.ts'), valueAccessorConfigs: angularValueAccessorBindings, inlineProperties: true, customElementsDir, }), angular({ + /* -------------------- Angular Standalone output target -------------------- */ componentCorePackage, outputType: 'standalone', // Generate a component with the standalone flag set to true. - directivesProxyFile: resolve(__dirname, '../beeq-angular/standalone/src/directives/components.ts').replace( - /\\/g, - '/', - ), - directivesArrayFile: resolve(__dirname, '../beeq-angular/standalone/src/directives/index.ts').replace(/\\/g, '/'), + directivesProxyFile: resolvePath('../beeq-angular/standalone/src/directives/components.ts'), + directivesArrayFile: resolvePath('../beeq-angular/standalone/src/directives/index.ts'), valueAccessorConfigs: angularValueAccessorBindings, customElementsDir, }), react({ - outDir: resolve(__dirname, '../beeq-react/src/').replace(/\\/g, '/'), + /* --------------------------- React output target -------------------------- */ + outDir: resolvePath('../beeq-react/src/'), customElementsDir, }), react({ - outDir: resolve(__dirname, '../beeq-react/ssr').replace(/\\/g, '/'), + /* ------------------------- React SSR output target ------------------------ */ + outDir: resolvePath('../beeq-react/ssr'), hydrateModule: '@beeq/core/dist/hydrate', customElementsDir, }), vue({ + /* ---------------------------- Vue output target --------------------------- */ componentCorePackage, - proxiesFile: resolve(__dirname, '../beeq-vue/src/components.ts').replace(/\\/g, '/'), + proxiesFile: resolvePath('../beeq-vue/src/components.ts'), includeImportCustomElements: true, includePolyfills: false, includeDefineCustomElements: false, From ddb104829af863e75718ad1b4e7c60e8effdb71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dabiel=20Gonz=C3=A1lez=20Ramos?= Date: Tue, 11 Feb 2025 14:14:31 +0200 Subject: [PATCH 4/6] fix: listen slot change on componentDidLoad, otherwise the changes won't be applied on SSR --- .../src/components/accordion/bq-accordion.tsx | 10 ++++---- .../beeq/src/components/alert/bq-alert.tsx | 11 ++++----- .../beeq/src/components/badge/bq-badge.tsx | 8 +++++-- .../components/breadcrumb/bq-breadcrumb.tsx | 4 ++++ .../beeq/src/components/button/bq-button.tsx | 4 ++++ .../components/date-picker/bq-date-picker.tsx | 1 - .../beeq/src/components/dialog/bq-dialog.tsx | 11 ++++----- .../beeq/src/components/drawer/bq-drawer.tsx | 4 ++++ .../components/empty-state/bq-empty-state.tsx | 13 ++++++----- .../beeq/src/components/input/bq-input.tsx | 23 ++++++++----------- .../notification/bq-notification.tsx | 13 ++++++----- .../beeq/src/components/option/bq-option.tsx | 13 ++++++----- .../components/page-title/bq-page-title.tsx | 4 ++++ .../beeq/src/components/select/bq-select.tsx | 22 ++++++------------ .../src/components/spinner/bq-spinner.tsx | 2 ++ packages/beeq/src/components/tab/bq-tab.tsx | 4 ++++ packages/beeq/src/components/tag/bq-tag.tsx | 4 ++++ .../src/components/textarea/bq-textarea.tsx | 10 ++++---- packages/beeq/src/shared/utils/slot.ts | 2 ++ 19 files changed, 89 insertions(+), 74 deletions(-) diff --git a/packages/beeq/src/components/accordion/bq-accordion.tsx b/packages/beeq/src/components/accordion/bq-accordion.tsx index c357f1b17..7042747ea 100644 --- a/packages/beeq/src/components/accordion/bq-accordion.tsx +++ b/packages/beeq/src/components/accordion/bq-accordion.tsx @@ -237,6 +237,7 @@ export class BqAccordion { componentDidLoad() { this.handleJsAnimation(); this.handleExpandedChange(); + this.handleSlotChange(); } // Listeners @@ -286,11 +287,8 @@ export class BqAccordion { this.bqBlur.emit(this.el); }; - private handlePrefixSlotChange = () => { + private handleSlotChange = () => { this.hasPrefix = hasSlotContent(this.prefixElem, 'prefix'); - }; - - private handleSuffixSlotChange = () => { this.hasSuffix = hasSlotContent(this.suffixElem, 'suffix'); }; @@ -336,7 +334,7 @@ export class BqAccordion { class={{ 'bq-accordion__header--prefix': true, '!hidden': !this.hasPrefix }} part="prefix" > - +
@@ -346,7 +344,7 @@ export class BqAccordion { class={{ 'bq-accordion__header--suffix': true, '!hidden': !this.hasSuffix }} part="suffix" > - +
{ + private handleSlotChange = () => { this.hasContent = hasSlotContent(this.bodyElem, 'body'); - }; - - private handleFooterSlotChange = () => { this.hasFooter = hasSlotContent(this.footerElem, 'footer'); }; @@ -356,7 +355,7 @@ export class BqAlert { ref={(div) => (this.bodyElem = div)} part="body" > - +
{/* FOOTER */} @@ -365,7 +364,7 @@ export class BqAlert { ref={(div) => (this.footerElem = div)} part="footer" > - + diff --git a/packages/beeq/src/components/badge/bq-badge.tsx b/packages/beeq/src/components/badge/bq-badge.tsx index 26159b97b..78e2199d2 100644 --- a/packages/beeq/src/components/badge/bq-badge.tsx +++ b/packages/beeq/src/components/badge/bq-badge.tsx @@ -94,6 +94,10 @@ export class BqBadge { this.handleSizePropChange(); } + componentDidLoad() { + this.handleSlotChange(); + } + disconnectedCallback() { this.observer?.disconnect(); } @@ -113,7 +117,7 @@ export class BqBadge { // These methods cannot be called from the host element. // ======================================================= - private onSlotChange = () => { + private handleSlotChange = () => { const slot = this.slot; if (isNil(slot)) return; @@ -159,7 +163,7 @@ export class BqBadge { part="base" > (this.spanElement = element)} class="text-xs font-bold leading-small" part="number"> - + diff --git a/packages/beeq/src/components/breadcrumb/bq-breadcrumb.tsx b/packages/beeq/src/components/breadcrumb/bq-breadcrumb.tsx index 05ca476ab..507855c69 100644 --- a/packages/beeq/src/components/breadcrumb/bq-breadcrumb.tsx +++ b/packages/beeq/src/components/breadcrumb/bq-breadcrumb.tsx @@ -62,6 +62,10 @@ export class BqBreadcrumb { // Ordered by their natural call order // ===================================== + componentDidLoad() { + this.handleSlotChange(); + } + // Listeners // ============== diff --git a/packages/beeq/src/components/button/bq-button.tsx b/packages/beeq/src/components/button/bq-button.tsx index aa740feff..2ab26e645 100644 --- a/packages/beeq/src/components/button/bq-button.tsx +++ b/packages/beeq/src/components/button/bq-button.tsx @@ -180,6 +180,10 @@ export class BqButton { this.checkPropValues(); } + componentDidLoad() { + this.handleSlotChange(); + } + // Listeners // ============== diff --git a/packages/beeq/src/components/date-picker/bq-date-picker.tsx b/packages/beeq/src/components/date-picker/bq-date-picker.tsx index 096cfd9d2..ba921121c 100644 --- a/packages/beeq/src/components/date-picker/bq-date-picker.tsx +++ b/packages/beeq/src/components/date-picker/bq-date-picker.tsx @@ -358,7 +358,6 @@ export class BqDatePicker { if (!isClient() || isCallyLibraryLoaded()) return; try { - console.log('Loading Cally library...'); await loadCallyLibrary(); } catch (error) { console.error(error); diff --git a/packages/beeq/src/components/dialog/bq-dialog.tsx b/packages/beeq/src/components/dialog/bq-dialog.tsx index a0456c742..1c4d3350f 100644 --- a/packages/beeq/src/components/dialog/bq-dialog.tsx +++ b/packages/beeq/src/components/dialog/bq-dialog.tsx @@ -186,7 +186,9 @@ export class BqDialog { } componentDidLoad() { + this.handleSlotChange(); this.handleOpenChange(); + this.closeSlotElem = this.el.shadowRoot.querySelector('slot[name="button-close"]'); this.closeSlotElem?.addEventListener('click', () => this.hide()); } @@ -302,11 +304,8 @@ export class BqDialog { this.el.classList.remove(this.OPEN_CSS_CLASS); }; - private handleContentSlotChange = () => { + private handleSlotChange = () => { this.hasContent = hasSlotContent(this.contentElem); - }; - - private handleFooterSlotChange = () => { this.hasFooter = hasSlotContent(this.footerElem, 'footer'); }; @@ -360,7 +359,7 @@ export class BqDialog { ref={(mainElem) => (this.contentElem = mainElem)} part="body" > - +
(this.footerElem = footerElem)} part="footer" > - +
); diff --git a/packages/beeq/src/components/drawer/bq-drawer.tsx b/packages/beeq/src/components/drawer/bq-drawer.tsx index e1040bae4..4a2fb3474 100644 --- a/packages/beeq/src/components/drawer/bq-drawer.tsx +++ b/packages/beeq/src/components/drawer/bq-drawer.tsx @@ -183,6 +183,10 @@ export class BqDrawer { this.handlePlacementChange(); } + componentDidLoad() { + this.handleFooterSlotChange(); + } + // Listeners // ============== diff --git a/packages/beeq/src/components/empty-state/bq-empty-state.tsx b/packages/beeq/src/components/empty-state/bq-empty-state.tsx index 5bbe140ec..a164f5288 100644 --- a/packages/beeq/src/components/empty-state/bq-empty-state.tsx +++ b/packages/beeq/src/components/empty-state/bq-empty-state.tsx @@ -80,6 +80,10 @@ export class BqEmptyState { this.checkPropValues(); } + componentDidLoad() { + this.handleSlotChange(); + } + // Listeners // ============== @@ -95,11 +99,8 @@ export class BqEmptyState { // These methods cannot be called from the host element. // ======================================================= - private handleContentSlotChange = () => { + private handleSlotChange = () => { this.hasBody = hasSlotContent(this.bodyElem, 'body'); - }; - - private handleFooterSlotChange = () => { this.hasFooter = hasSlotContent(this.footerElem, 'footer'); }; @@ -153,10 +154,10 @@ export class BqEmptyState { ref={(div) => (this.bodyElem = div)} part="body" > - +
(this.footerElem = div)} part="footer"> - +
); diff --git a/packages/beeq/src/components/input/bq-input.tsx b/packages/beeq/src/components/input/bq-input.tsx index 51d85b10f..74fe528f8 100644 --- a/packages/beeq/src/components/input/bq-input.tsx +++ b/packages/beeq/src/components/input/bq-input.tsx @@ -293,6 +293,10 @@ export class BqInput { this.handleValueChange(); } + componentDidLoad() { + this.handleSlotChange(); + } + formAssociatedCallback() { this.setFormValue(this.value?.toString()); this.updateFormValidity(); @@ -404,19 +408,10 @@ export class BqInput { internals?.setValidity({}); }; - private handleLabelSlotChange = () => { + private handleSlotChange = () => { this.hasLabel = hasSlotContent(this.labelElem); - }; - - private handlePrefixSlotChange = () => { this.hasPrefix = hasSlotContent(this.prefixElem); - }; - - private handleSuffixSlotChange = () => { this.hasSuffix = hasSlotContent(this.suffixElem); - }; - - private handleHelperTextSlotChange = () => { this.hasHelperText = hasSlotContent(this.helperTextElem); }; @@ -435,7 +430,7 @@ export class BqInput { ref={(labelElem) => (this.labelElem = labelElem)} part="label" > - + {/* Input control group */}
(this.prefixElem = spanElem)} part="prefix" > - + {/* HTML Input */} (this.suffixElem = spanElem)} part="suffix" > - +
{/* Helper text */} @@ -521,7 +516,7 @@ export class BqInput { ref={(divElem) => (this.helperTextElem = divElem)} part="helper-text" > - + ); diff --git a/packages/beeq/src/components/notification/bq-notification.tsx b/packages/beeq/src/components/notification/bq-notification.tsx index 5b92eae85..f61a6c47f 100644 --- a/packages/beeq/src/components/notification/bq-notification.tsx +++ b/packages/beeq/src/components/notification/bq-notification.tsx @@ -191,6 +191,10 @@ export class BqNotification { this.handleTimeout(); } + componentDidLoad() { + this.handleSlotChange(); + } + // Listeners // ============== @@ -280,11 +284,8 @@ export class BqNotification { this.bqAfterClose.emit(); }; - private handleContentSlotChange = () => { + private handleSlotChange = () => { this.hasContent = hasSlotContent(this.bodyElem); - }; - - private handleFooterSlotChange = () => { this.hasFooter = hasSlotContent(this.footerElem, 'footer'); }; @@ -366,7 +367,7 @@ export class BqNotification { ref={(div) => (this.bodyElem = div)} part="body" > - + {/* FOOTER */} @@ -375,7 +376,7 @@ export class BqNotification { ref={(div) => (this.footerElem = div)} part="footer" > - + diff --git a/packages/beeq/src/components/option/bq-option.tsx b/packages/beeq/src/components/option/bq-option.tsx index 66422e5f6..af2631fd1 100644 --- a/packages/beeq/src/components/option/bq-option.tsx +++ b/packages/beeq/src/components/option/bq-option.tsx @@ -110,6 +110,10 @@ export class BqOption { // Ordered by their natural call order // ===================================== + componentDidLoad() { + this.handleSlotChange(); + } + // Listeners // ============== @@ -161,11 +165,8 @@ export class BqOption { this.bqClick.emit(this.el); }; - private onSlotChange = () => { + private handleSlotChange = () => { this.hasPrefix = hasSlotContent(this.prefixElem, 'prefix'); - }; - - private handleSuffixSlotChange = () => { this.hasSuffix = hasSlotContent(this.suffixElem, 'suffix'); }; @@ -206,7 +207,7 @@ export class BqOption { ref={(elem) => (this.prefixElem = elem)} part="prefix" > - + @@ -219,7 +220,7 @@ export class BqOption { ref={(elem) => (this.suffixElem = elem)} part="suffix" > - + diff --git a/packages/beeq/src/components/page-title/bq-page-title.tsx b/packages/beeq/src/components/page-title/bq-page-title.tsx index 278560db9..3eece0818 100644 --- a/packages/beeq/src/components/page-title/bq-page-title.tsx +++ b/packages/beeq/src/components/page-title/bq-page-title.tsx @@ -88,6 +88,10 @@ export class BqPageTitle { // Ordered by their natural call order // ===================================== + componentDidLoad() { + this.handleSlotChange(); + } + // Listeners // ============== diff --git a/packages/beeq/src/components/select/bq-select.tsx b/packages/beeq/src/components/select/bq-select.tsx index d4f8002e9..d3ba0acc8 100644 --- a/packages/beeq/src/components/select/bq-select.tsx +++ b/packages/beeq/src/components/select/bq-select.tsx @@ -292,10 +292,11 @@ export class BqSelect { } componentDidLoad() { + this.handleSlotChange(); + if (this.multiple && Array.isArray(this.value)) { this.selectedOptions = this.options.filter((item) => this.value.includes(item.value)); } - this.handleValueChange(); } @@ -468,19 +469,10 @@ export class BqSelect { this.bqSelect.emit({ value: this.value, item }); }; - private handleLabelSlotChange = () => { + private handleSlotChange = () => { this.hasLabel = hasSlotContent(this.labelElem); - }; - - private handlePrefixSlotChange = () => { this.hasPrefix = hasSlotContent(this.prefixElem); - }; - - private handleSuffixSlotChange = () => { this.hasSuffix = hasSlotContent(this.suffixElem); - }; - - private handleHelperTextSlotChange = () => { this.hasHelperText = hasSlotContent(this.helperTextElem); }; @@ -626,7 +618,7 @@ export class BqSelect { ref={(labelElem: HTMLSpanElement) => (this.labelElem = labelElem)} part="label" > - + {/* Select dropdown */} (this.prefixElem = spanElem)} part="prefix" > - +
{/* Display selected values using BqTags for multiple selection */} @@ -720,7 +712,7 @@ export class BqSelect { ref={(spanElem: HTMLSpanElement) => (this.suffixElem = spanElem)} part="suffix" > - + @@ -744,7 +736,7 @@ export class BqSelect { ref={(divElem: HTMLDivElement) => (this.helperTextElem = divElem)} part="helper-text" > - +
); diff --git a/packages/beeq/src/components/spinner/bq-spinner.tsx b/packages/beeq/src/components/spinner/bq-spinner.tsx index df8f9c45f..79be73cb7 100644 --- a/packages/beeq/src/components/spinner/bq-spinner.tsx +++ b/packages/beeq/src/components/spinner/bq-spinner.tsx @@ -117,6 +117,8 @@ export class BqSpinner { } componentDidLoad() { + this.handleSlotChange(); + this.handleIconSlotChange(); this.setIconSize(); } diff --git a/packages/beeq/src/components/tab/bq-tab.tsx b/packages/beeq/src/components/tab/bq-tab.tsx index 6ca154e21..20a7bb872 100644 --- a/packages/beeq/src/components/tab/bq-tab.tsx +++ b/packages/beeq/src/components/tab/bq-tab.tsx @@ -136,6 +136,10 @@ export class BqTab { this.checkPropValues(); } + componentDidLoad() { + this.handleIconSlotChange(); + } + // Listeners // ============== diff --git a/packages/beeq/src/components/tag/bq-tag.tsx b/packages/beeq/src/components/tag/bq-tag.tsx index 049b4a93d..b69cb2940 100644 --- a/packages/beeq/src/components/tag/bq-tag.tsx +++ b/packages/beeq/src/components/tag/bq-tag.tsx @@ -150,6 +150,10 @@ export class BqTag { this.checkPropValues(); } + componentDidLoad() { + this.handleSlotChange(); + } + // Listeners // ============== diff --git a/packages/beeq/src/components/textarea/bq-textarea.tsx b/packages/beeq/src/components/textarea/bq-textarea.tsx index c67bbcf1d..a0f16ff18 100644 --- a/packages/beeq/src/components/textarea/bq-textarea.tsx +++ b/packages/beeq/src/components/textarea/bq-textarea.tsx @@ -246,6 +246,7 @@ export class BqTextarea { // ===================================== componentDidLoad() { + this.handleSlotChange(); this.handleValueChange(); } @@ -331,11 +332,8 @@ export class BqTextarea { inputElem.style.height = `${inputElem.scrollHeight}px`; }; - private handleLabelSlotChange = () => { + private handleSlotChange = () => { this.hasLabel = hasSlotContent(this.labelElem); - }; - - private handleHelperTextSlotChange = () => { this.hasHelperText = hasSlotContent(this.helperTextElem); }; @@ -380,7 +378,7 @@ export class BqTextarea { ref={(label: HTMLLabelElement) => (this.labelElem = label)} part="label" > - +