import * as plugins from './00plugins.js'; import * as colors from './00colors.js'; import { DeesElement, customElement, html, css, unsafeCSS, type CSSResult, cssManager, property, type TemplateResult, } from '@design.estate/dees-element'; import * as domtools from '@design.estate/dees-domtools'; export interface IStep { title: string; content: TemplateResult; validationFunc?: (stepper: DeesStepper, htmlElement: HTMLElement) => Promise; onReturnToStepFunc?: (stepper: DeesStepper, htmlElement: HTMLElement) => Promise; validationFuncCalled?: boolean; } declare global { interface HTMLElementTagNameMap { 'dees-stepper': DeesStepper; } } @customElement('dees-stepper') export class DeesStepper extends DeesElement { public static demo = () => html` Next `, validationFunc: async (stepperArg, elementArg) => { const deesForm = elementArg.querySelector('dees-form'); deesForm.addEventListener('formData', (eventArg) => { stepperArg.goNext(); }); }, }, { title: 'Whats your mobile number?', content: html``, }, ] as IStep[]} > `; @property({ type: Array, }) public steps: IStep[] = []; @property({ type: Object, }) public selectedStep: IStep; constructor() { super(); } public static styles = [ cssManager.defaultStyles, css` :host { position: absolute; width: 100%; height: 100%; } .stepperContainer { position: absolute; width: 100%; height: 100%; background: ${cssManager.bdTheme('#eeeeeb', '#000')}; overflow: hidden; } .step { position: relative; pointer-events: none; overflow: hidden; transition: all 0.7s ease-in-out; max-width: 500px; min-height: 300px; border-radius: 16px; background: ${cssManager.bdTheme('#ffffff', '#181818')}; border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#181818')}; color: ${cssManager.bdTheme('#333', '#fff')}; margin: auto; margin-bottom: 20px; filter: opacity(0.5) grayscale(1); box-shadow: 0px 0px 3px #00000010; user-select: none; } .step.selected { border-top: 1px solid #e4002b; pointer-events: all; filter: opacity(1) grayscale(0); box-shadow: 0px 0px 5px #00000010; user-select: auto; } .step.hiddenStep { filter: opacity(0); } .step:last-child { margin-bottom: 100vh; } .step .stepCounter { color: #999; position: absolute; top: 0px; right: 0px; padding: 10px 15px; font-size: 12px; border-bottom-left-radius: 3px; background: ${cssManager.bdTheme('#00000008', '#ffffff08')}; } .step .goBack { color: #999; cursor: default; position: absolute; top: 0px; left: 0px; padding: 10px 15px; font-size: 12px; border-bottom-right-radius: 3px; background: ${cssManager.bdTheme('#00000008', '#ffffff08')}; } .step .goBack:hover { color: ${cssManager.bdTheme('#333', '#fff')}; background: ${cssManager.bdTheme('#00000012', colors.dark.blue)}; } .step .goBack:active { color: ${cssManager.bdTheme('#333', '#fff')}; background: ${cssManager.bdTheme('#00000012', colors.dark.blueActive)}; } .step .goBack span { transition: all 0.2s; display: inline-block; } .step .goBack:hover span { transform: translateX(-2px); } .step .title { text-align: center; padding-top: 50px; font-family: 'Geist Sans', sans-serif; font-size: 22px; font-weight: 500; } .step .content { padding: 20px; } `, ]; public render() { return html`
${this.steps.map( (stepArg) => html`
${this.getIndexOfStep(stepArg) > 0 ? html`
<- go to previous step
` : ``}
Step ${this.steps.findIndex((elementArg) => elementArg === stepArg) + 1} of ${this.steps.length}
${stepArg.title}
${stepArg.content}
` )}
`; } public getIndexOfStep = (stepArg: IStep): number => { return this.steps.findIndex((stepArg2) => stepArg === stepArg2); }; public async firstUpdated() { await this.domtoolsPromise; await this.domtools.convenience.smartdelay.delayFor(0); this.selectedStep = this.steps[0]; this.setScrollStatus(); } public async updated() { this.setScrollStatus(); } public scroller: typeof domtools.plugins.SweetScroll.prototype; public async setScrollStatus() { const stepperContainer: HTMLElement = this.shadowRoot.querySelector('.stepperContainer'); const firstStepElement: HTMLElement = this.shadowRoot.querySelector('.step'); const selectedStepElement: HTMLElement = this.shadowRoot.querySelector('.selected'); if (!selectedStepElement) { return; } if (!stepperContainer.style.paddingTop) { stepperContainer.style.paddingTop = `${ stepperContainer.offsetHeight / 2 - selectedStepElement.offsetHeight / 2 }px`; } console.log('Setting scroll status'); console.log(selectedStepElement); const scrollPosition = selectedStepElement.offsetTop - stepperContainer.offsetHeight / 2 + selectedStepElement.offsetHeight / 2; console.log(scrollPosition); const domtoolsInstance = await domtools.DomTools.setupDomTools(); if (!this.scroller) { this.scroller = new domtools.plugins.SweetScroll( { vertical: true, horizontal: false, easing: 'easeInOutExpo', duration: 700, }, stepperContainer ); } if (!this.selectedStep.validationFuncCalled && this.selectedStep.validationFunc) { this.selectedStep.validationFuncCalled = true; await this.selectedStep.validationFunc(this, selectedStepElement); } this.scroller.to(scrollPosition); } public async goBack() { const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep); this.selectedStep = this.steps[currentIndex - 1]; await this.domtoolsPromise; await this.domtools.convenience.smartdelay.delayFor(100); this.selectedStep.onReturnToStepFunc?.(this, this.shadowRoot.querySelector('.selected')); } public goNext() { const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep); this.selectedStep = this.steps[currentIndex + 1]; } }