@carbon/ibm-products-web-components
Version:
Carbon for IBM Products Web Components
259 lines (255 loc) • 10.5 kB
JavaScript
/**
* Copyright IBM Corp. 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { __decorate } from 'tslib';
import { nothing, html } from 'lit';
import { property, state } from 'lit/decorators.js';
import { prefix } from '../../globals/settings.js';
import HostListenerMixin from '@carbon/web-components/es/globals/mixins/host-listener.js';
import { carbonElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js';
import '@carbon/web-components/es/components/button/index.js';
import styles from './interstitial-screen-footer.scss.js';
import { interstitialDetailsSignal } from './interstitial-screen-context.js';
import { SignalWatcher } from '@lit-labs/signals';
import '@carbon/web-components/es/components/inline-loading/inline-loading.js';
import { CDSModalFooter } from '@carbon/web-components/es/index.js';
import ArrowRight from '@carbon/web-components/es/icons/arrow--right/16.js';
/**
* @license
*
* Copyright IBM Corp. 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const blockClass = `${prefix}--interstitial-screen`;
/**
* interstitial-screen-footer for footer section
* @element c4p-interstitial-screen-footer
* @fires c4p-on-action - The name of the custom event fired just before the action. You can use this event to perform any async/sync validation.
* if you pass async-action = true, the component will wait for an external listener (e.g., a form validation or confirmation modal) to resolve the proceed()
* callback before continuing. When asyncAction is true, you must listen for the eventOnBeforeAction event and call the proceed() function
* (either synchronously or with a promise) to allow navigation.
*/
let CDSInterstitialScreenFooter = class CDSInterstitialScreenFooter extends SignalWatcher(HostListenerMixin(CDSModalFooter)) {
constructor() {
super(...arguments);
/**
* The label for the Next button.
*/
this.nextButtonLabel = 'Next';
/**
* The label for the Previous button.
*/
this.previousButtonLabel = 'Back';
/**
* The label for the skip button.
*/
this.skipButtonLabel = 'Skip';
/**
* The label for the start button.
*/
this.startButtonLabel = 'Get Started';
this.slot = 'footer';
this.handleSkip = () => this.handleAction('skip');
this.handleStart = () => this.handleAction('start');
this.handleClickNext = () => this.handleAction('next');
this.handleClickPrev = () => this.handleAction('back');
this.handleAction = async (actionType) => {
var _a;
this.loadingAction = actionType;
const { currentStep, stepDetails } = interstitialDetailsSignal.get();
const stepCount = stepDetails.length;
let resolvePromise;
const proceedPromise = new Promise((resolve) => {
resolvePromise = resolve;
});
const customEvent = new CustomEvent(this.constructor.eventOnBeforeAction, {
bubbles: true,
composed: true,
cancelable: true,
detail: {
step: currentStep,
stepCount: stepCount,
proceed: (allow) => {
Promise.resolve(allow).then(resolvePromise);
},
},
});
const eventNotCanceled = this.dispatchEvent(customEvent);
if (!eventNotCanceled) {
return;
}
const canProceed = this.asyncAction ? await proceedPromise : true;
this.loadingAction = '';
if (canProceed) {
const { carouselAPI } = interstitialDetailsSignal.get();
if (actionType == 'next') {
carouselAPI.next();
}
else if (actionType === 'back') {
carouselAPI.prev();
}
else {
(_a = this._handleUserInitiatedClose) === null || _a === void 0 ? void 0 : _a.call(this, actionType);
}
}
};
}
updated(_changedProperties) {
if (_changedProperties.size === 0) {
// This logic ensures the start/next button receives focus when focus is lost from the "next" or "back" buttons
// during step navigation—particularly when those buttons are not rendered.
this.updateComplete.then(() => {
const { stepDetails, currentStep } = interstitialDetailsSignal.get();
const isMultiStep = Array.isArray(stepDetails) && stepDetails.length > 0;
const lastStepIndex = (stepDetails === null || stepDetails === void 0 ? void 0 : stepDetails.length) - 1;
if (!isMultiStep) {
return;
}
const focusButton = (selector) => {
var _a;
const btn = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(selector);
btn === null || btn === void 0 ? void 0 : btn.focus();
};
if (currentStep === lastStepIndex) {
focusButton(`.${prefix}--interstitial-screen--start-btn`);
}
else if (currentStep === 0) {
focusButton(`.${prefix}--interstitial-screen--next-btn`);
}
});
}
}
_handleUserInitiatedClose(triggeredBy) {
const init = {
bubbles: true,
cancelable: true,
composed: true,
detail: {
triggeredBy,
},
};
this.dispatchEvent(new CustomEvent(this.constructor.eventRequestClose, init));
}
render() {
const { stepDetails, currentStep, disableActions } = interstitialDetailsSignal.get();
const { start, next, back, skip } = disableActions;
const isMulti = (stepDetails === null || stepDetails === void 0 ? void 0 : stepDetails.length) > 0;
const progStepCeil = (stepDetails === null || stepDetails === void 0 ? void 0 : stepDetails.length) - 1;
return html `<slot>
<div class="${blockClass}--footer">
${isMulti
? html `
<cds-button
class="${blockClass}--skip-btn"
kind="ghost"
size="lg"
title="${this.skipButtonLabel}"
="${this.handleSkip}"
?disabled="${skip}"
>
${this.skipButtonLabel}
</cds-button>
`
: ''}
<div class="${blockClass}--footer-controls">
${isMulti && currentStep > 0
? html `
<cds-button
class="${blockClass}--prev-btn"
kind="secondary"
size="lg"
title="${this.previousButtonLabel}"
?disabled="${back}"
="${this.handleClickPrev}"
>
${this.previousButtonLabel}
${this.loadingAction === 'back'
? html ` <cds-inline-loading slot="icon" aria-live="off">
</cds-inline-loading>`
: nothing}
</cds-button>
`
: nothing}
${isMulti && currentStep < progStepCeil
? html `
<cds-button
class="${blockClass}--next-btn"
kind="primary"
size="lg"
title="${this.nextButtonLabel}"
?disabled="${next}"
="${this.handleClickNext}"
>
${this.nextButtonLabel}
${this.loadingAction === 'next'
? html ` <cds-inline-loading slot="icon" aria-live="off">
</cds-inline-loading>`
: html ` ${ArrowRight({ slot: 'icon' })}`}
</cds-button>
`
: nothing}
${(isMulti && currentStep === progStepCeil) || !isMulti
? html `
<cds-button
class="${blockClass}--start-btn"
kind="primary"
size="lg"
title="${this.startButtonLabel}"
?disabled="${start}"
="${this.handleStart}"
>
${this.startButtonLabel}
${this.loadingAction === 'start'
? html ` <cds-inline-loading slot="icon" aria-live="off">
</cds-inline-loading>`
: nothing}
</cds-button>
`
: nothing}
</div>
</div>
</slot>`;
}
static get eventRequestClose() {
return `${prefix}-request-close`;
}
/**
* The name of the custom event fired just before the action.
*/
static get eventOnBeforeAction() {
return `${prefix}-on-action`;
}
};
CDSInterstitialScreenFooter.styles = styles;
__decorate([
property({ reflect: true })
], CDSInterstitialScreenFooter.prototype, "nextButtonLabel", void 0);
__decorate([
property({ reflect: true })
], CDSInterstitialScreenFooter.prototype, "previousButtonLabel", void 0);
__decorate([
property({ reflect: true })
], CDSInterstitialScreenFooter.prototype, "skipButtonLabel", void 0);
__decorate([
property({ reflect: true })
], CDSInterstitialScreenFooter.prototype, "startButtonLabel", void 0);
__decorate([
property({ reflect: true })
], CDSInterstitialScreenFooter.prototype, "slot", void 0);
__decorate([
property({ type: Boolean, reflect: true, attribute: 'async-action' })
], CDSInterstitialScreenFooter.prototype, "asyncAction", void 0);
__decorate([
state()
], CDSInterstitialScreenFooter.prototype, "loadingAction", void 0);
CDSInterstitialScreenFooter = __decorate([
carbonElement(`${prefix}-interstitial-screen-footer`)
], CDSInterstitialScreenFooter);
var CDSInterstitialScreenFooter$1 = CDSInterstitialScreenFooter;
export { CDSInterstitialScreenFooter$1 as default };
//# sourceMappingURL=interstitial-screen-footer.js.map