Files
dees-catalog/ts_web/elements/dees-stepper/dees-stepper.ts

281 lines
8.6 KiB
TypeScript
Raw Normal View History

import * as plugins from '../00plugins.js';
import * as colors from '../00colors.js';
2024-01-18 02:08:19 +01:00
2021-09-01 21:48:22 +02:00
import {
DeesElement,
customElement,
html,
css,
unsafeCSS,
2023-08-07 20:02:18 +02:00
type CSSResult,
2021-09-01 21:48:22 +02:00
cssManager,
property,
2023-08-07 20:02:18 +02:00
type TemplateResult,
2023-08-07 19:13:29 +02:00
} from '@design.estate/dees-element';
2021-08-29 17:10:25 +02:00
2023-08-07 19:13:29 +02:00
import * as domtools from '@design.estate/dees-domtools';
import { stepperDemo } from './dees-stepper.demo.js';
2021-08-29 17:10:25 +02:00
2021-09-01 21:48:22 +02:00
export interface IStep {
title: string;
content: TemplateResult;
2021-09-09 00:35:10 +02:00
validationFunc?: (stepper: DeesStepper, htmlElement: HTMLElement) => Promise<any>;
2021-09-10 15:51:30 +02:00
onReturnToStepFunc?: (stepper: DeesStepper, htmlElement: HTMLElement) => Promise<any>;
2021-09-09 00:35:10 +02:00
validationFuncCalled?: boolean;
2021-09-01 21:48:22 +02:00
}
2021-09-09 00:02:35 +02:00
declare global {
interface HTMLElementTagNameMap {
'dees-stepper': DeesStepper;
}
}
2021-08-29 17:10:25 +02:00
@customElement('dees-stepper')
export class DeesStepper extends DeesElement {
public static demo = stepperDemo;
2021-08-29 17:10:25 +02:00
2021-09-01 21:48:22 +02:00
@property({
type: Array,
})
2021-09-14 17:31:16 +02:00
public steps: IStep[] = [];
2021-08-29 17:10:25 +02:00
@property({
2021-09-01 21:48:22 +02:00
type: Object,
2021-08-29 17:10:25 +02:00
})
2021-09-01 21:48:22 +02:00
public selectedStep: IStep;
constructor() {
super();
}
2021-08-29 17:10:25 +02:00
public static styles = [
cssManager.defaultStyles,
css`
:host {
position: absolute;
width: 100%;
height: 100%;
}
.stepperContainer {
position: absolute;
width: 100%;
height: 100%;
2021-09-01 21:48:22 +02:00
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
2021-08-29 17:10:25 +02:00
overflow: hidden;
}
.step {
2021-09-01 21:48:22 +02:00
position: relative;
2021-09-01 22:43:31 +02:00
pointer-events: none;
2021-09-01 21:48:22 +02:00
overflow: hidden;
transition: transform 0.35s ease, box-shadow 0.35s ease, filter 0.35s ease, border 0.35s ease;
2021-09-01 22:43:31 +02:00
max-width: 500px;
2021-08-29 17:10:25 +02:00
min-height: 300px;
border-radius: 18px;
background: ${cssManager.bdTheme('#ffffff', '#18181b')};
border: 1px solid ${cssManager.bdTheme('rgba(226, 232, 240, 0.9)', 'rgba(63, 63, 70, 0.85)')};
color: ${cssManager.bdTheme('#0f172a', '#f5f5f5')};
2021-08-29 17:10:25 +02:00
margin: auto;
margin-bottom: 20px;
filter: opacity(0.55) saturate(0.85);
box-shadow: ${cssManager.bdTheme('0 20px 40px -25px rgba(15, 23, 42, 0.45)', '0 20px 36px -22px rgba(15, 23, 42, 0.65)')};
2021-09-10 15:56:37 +02:00
user-select: none;
background-clip: padding-box;
2021-09-01 21:48:22 +02:00
}
.step.selected {
2021-09-01 22:43:31 +02:00
pointer-events: all;
filter: opacity(1) saturate(1);
transform: translateY(-6px);
border: 1px solid ${cssManager.bdTheme(colors.dark.blue, colors.dark.blue)};
box-shadow: ${cssManager.bdTheme('0 28px 60px -30px rgba(15, 23, 42, 0.42)', '0 26px 55px -28px rgba(37, 99, 235, 0.6)')};
2021-09-10 15:56:37 +02:00
user-select: auto;
2021-09-01 21:48:22 +02:00
}
2021-08-29 17:10:25 +02:00
2021-09-01 22:43:31 +02:00
.step.hiddenStep {
filter: opacity(0);
transform: translateY(16px);
2021-09-01 22:43:31 +02:00
}
2021-09-15 00:45:14 +02:00
.step:last-child {
2021-09-14 17:31:16 +02:00
margin-bottom: 100vh;
}
2021-09-01 21:48:22 +02:00
.step .stepCounter {
color: ${cssManager.bdTheme('#64748b', '#a1a1aa')};
2021-09-01 21:48:22 +02:00
position: absolute;
top: 12px;
right: 12px;
padding: 6px 14px;
2021-09-01 21:48:22 +02:00
font-size: 12px;
border-radius: 999px;
background: ${cssManager.bdTheme('rgba(226, 232, 240, 0.5)', 'rgba(63, 63, 70, 0.45)')};
border: 1px solid ${cssManager.bdTheme('rgba(226, 232, 240, 0.7)', 'rgba(63, 63, 70, 0.6)')};
2021-09-01 22:43:31 +02:00
}
.step .goBack {
position: absolute;
top: 12px;
left: 12px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
2021-09-01 22:43:31 +02:00
font-size: 12px;
font-weight: 500;
border-radius: 999px;
border: 1px solid ${cssManager.bdTheme('rgba(226, 232, 240, 0.9)', 'rgba(63, 63, 70, 0.85)')};
background: ${cssManager.bdTheme('rgba(255, 255, 255, 0.9)', 'rgba(39, 39, 42, 0.85)')};
color: ${cssManager.bdTheme('#475569', '#d4d4d8')};
cursor: pointer;
transition: border 0.2s ease, color 0.2s ease, background 0.2s ease, transform 0.2s ease;
2021-09-01 22:43:31 +02:00
}
.step .goBack:hover {
color: ${cssManager.bdTheme('#0f172a', '#fafafa')};
border-color: ${cssManager.bdTheme(colors.dark.blue, colors.dark.blue)};
background: ${cssManager.bdTheme('rgba(226, 232, 240, 0.95)', 'rgba(63, 63, 70, 0.7)')};
transform: translateX(-2px);
2024-01-18 02:08:19 +01:00
}
.step .goBack:active {
color: ${cssManager.bdTheme('#0f172a', '#fafafa')};
border-color: ${cssManager.bdTheme(colors.dark.blueActive, colors.dark.blueActive)};
background: ${cssManager.bdTheme('rgba(226, 232, 240, 0.85)', 'rgba(63, 63, 70, 0.6)')};
2024-01-18 02:08:19 +01:00
}
.step .goBack span {
transition: transform 0.2s ease;
2024-01-18 02:08:19 +01:00
display: inline-block;
}
.step .goBack:hover span {
transform: translateX(-2px);
2021-09-01 21:48:22 +02:00
}
.step .title {
text-align: center;
padding-top: 64px;
font-family: 'Geist Sans', sans-serif;
font-size: 24px;
font-weight: 600;
letter-spacing: -0.01em;
color: inherit;
2021-08-29 17:10:25 +02:00
}
2021-09-01 22:43:31 +02:00
.step .content {
padding: 24px 28px 32px;
2021-09-01 22:43:31 +02:00
}
2021-09-01 21:48:22 +02:00
`,
];
2021-08-29 17:10:25 +02:00
2021-09-01 21:48:22 +02:00
public render() {
2021-08-29 17:10:25 +02:00
return html`
<div class="stepperContainer">
2021-09-01 21:48:22 +02:00
${this.steps.map(
(stepArg) =>
2021-09-14 17:31:16 +02:00
html`<div
class="step ${stepArg === this.selectedStep
? 'selected'
: null} ${this.getIndexOfStep(stepArg) > this.getIndexOfStep(this.selectedStep)
? 'hiddenStep'
: ''}"
>
${this.getIndexOfStep(stepArg) > 0
2024-01-18 02:08:19 +01:00
? html`<div class="goBack" @click=${this.goBack}><span style="font-family: Inter"><-</span> go to previous step</div>`
2021-09-14 17:31:16 +02:00
: ``}
<div class="stepCounter">
Step ${this.steps.findIndex((elementArg) => elementArg === stepArg) + 1} of
${this.steps.length}
</div>
2021-09-01 22:43:31 +02:00
<div class="title">${stepArg.title}</div>
2021-09-01 21:48:22 +02:00
<div class="content">${stepArg.content}</div>
</div> `
)}
2021-08-29 17:10:25 +02:00
</div>
`;
}
2021-09-01 21:48:22 +02:00
2021-09-01 22:43:31 +02:00
public getIndexOfStep = (stepArg: IStep): number => {
2021-09-14 17:31:16 +02:00
return this.steps.findIndex((stepArg2) => stepArg === stepArg2);
};
2021-09-01 22:43:31 +02:00
2023-04-12 02:47:45 +02:00
public async firstUpdated() {
await this.domtoolsPromise;
await this.domtools.convenience.smartdelay.delayFor(0);
2021-09-01 21:48:22 +02:00
this.selectedStep = this.steps[0];
this.setScrollStatus();
}
2023-04-12 02:47:45 +02:00
public async updated() {
this.setScrollStatus();
2021-09-01 21:48:22 +02:00
}
2021-09-01 22:44:43 +02:00
public scroller: typeof domtools.plugins.SweetScroll.prototype;
2021-09-01 21:48:22 +02:00
public async setScrollStatus() {
const stepperContainer: HTMLElement = this.shadowRoot.querySelector('.stepperContainer');
const firstStepElement: HTMLElement = this.shadowRoot.querySelector('.step');
const selectedStepElement: HTMLElement = this.shadowRoot.querySelector('.selected');
2023-04-12 02:47:45 +02:00
if (!selectedStepElement) {
return;
}
2021-09-01 22:43:31 +02:00
if (!stepperContainer.style.paddingTop) {
2021-09-14 17:31:16 +02:00
stepperContainer.style.paddingTop = `${
stepperContainer.offsetHeight / 2 - selectedStepElement.offsetHeight / 2
}px`;
2021-09-01 22:43:31 +02:00
}
2021-09-01 21:48:22 +02:00
console.log('Setting scroll status');
console.log(selectedStepElement);
2021-09-14 17:31:16 +02:00
const scrollPosition =
selectedStepElement.offsetTop -
stepperContainer.offsetHeight / 2 +
selectedStepElement.offsetHeight / 2;
2021-09-01 21:48:22 +02:00
console.log(scrollPosition);
2021-09-14 17:31:16 +02:00
const domtoolsInstance = await domtools.DomTools.setupDomTools();
2021-09-01 21:48:22 +02:00
if (!this.scroller) {
2021-09-14 17:31:16 +02:00
this.scroller = new domtools.plugins.SweetScroll(
{
vertical: true,
horizontal: false,
2022-12-07 02:28:31 +01:00
easing: 'easeInOutExpo',
duration: 700,
2021-09-14 17:31:16 +02:00
},
stepperContainer
);
2021-09-01 21:48:22 +02:00
}
2021-09-09 00:35:10 +02:00
if (!this.selectedStep.validationFuncCalled && this.selectedStep.validationFunc) {
this.selectedStep.validationFuncCalled = true;
await this.selectedStep.validationFunc(this, selectedStepElement);
}
2021-09-01 21:48:22 +02:00
this.scroller.to(scrollPosition);
}
2021-09-10 15:51:30 +02:00
public async goBack() {
2021-09-14 17:31:16 +02:00
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
if (currentIndex <= 0) {
return;
}
const currentStep = this.steps[currentIndex];
currentStep.validationFuncCalled = false;
const previousStep = this.steps[currentIndex - 1];
previousStep.validationFuncCalled = false;
this.selectedStep = previousStep;
2021-09-10 15:51:30 +02:00
await this.domtoolsPromise;
await this.domtools.convenience.smartdelay.delayFor(100);
this.selectedStep.onReturnToStepFunc?.(this, this.shadowRoot.querySelector('.selected'));
2021-09-01 21:48:22 +02:00
}
public goNext() {
2021-09-14 17:31:16 +02:00
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
if (currentIndex < 0 || currentIndex >= this.steps.length - 1) {
return;
}
const currentStep = this.steps[currentIndex];
currentStep.validationFuncCalled = false;
const nextStep = this.steps[currentIndex + 1];
nextStep.validationFuncCalled = false;
this.selectedStep = nextStep;
2021-09-01 21:48:22 +02:00
}
}