Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
0410f6c196 | |||
24aa7588c5 | |||
b46fe8fe93 | |||
b47c2053b5 | |||
16bf8001ae | |||
792e77f824 |
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-09-18 - 1.12.0 - feat(dees-stepper)
|
||||
Revamp dees-stepper: modern styling, new steps and improved navigation/validation
|
||||
|
||||
- Visual refresh for dees-stepper: updated card shapes, shadows, refined borders and stronger selected-state visuals for a modern shadcn-inspired look
|
||||
- Improved transitions and animations (transform, box-shadow, filter) for smoother step selection and show/hide behavior
|
||||
- Expanded default/demo steps: replaced small sample with a richer multi-step flow (Account Setup, Profile Details, Contact Information, Team Size, Goals, Brand Preferences, Integrations, Review & Launch)
|
||||
- Enhanced step interactions: safer goNext/goBack handling with boundary checks and reset of validation flags to avoid stale validation state
|
||||
- Better toolbar/controls placement for stepper demo (spacing, counters, accessible back control) and improved keyboard/UX affordances
|
||||
- Minor documentation and meta updates: readme.plan.md extended with dees-stepper plan items and added .claude/settings.local.json
|
||||
|
||||
## 2025-09-18 - 1.11.8 - fix(ci)
|
||||
Add local tool permissions config to allow running pnpm scripts and enable mcp__zen__chat
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-catalog",
|
||||
"version": "1.11.8",
|
||||
"version": "1.12.0",
|
||||
"private": false,
|
||||
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
||||
"main": "dist_ts_web/index.js",
|
||||
@@ -10,7 +10,8 @@
|
||||
"test": "tstest test/ --web --verbose --timeout 30 --logfile",
|
||||
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element --production --bundler esbuild",
|
||||
"watch": "tswatch element",
|
||||
"buildDocs": "tsdoc"
|
||||
"buildDocs": "tsdoc",
|
||||
"postinstall": "node scripts/update-monaco-version.cjs"
|
||||
},
|
||||
"author": "Lossless GmbH",
|
||||
"license": "MIT",
|
||||
|
BIN
readme.plan.md
BIN
readme.plan.md
Binary file not shown.
44
scripts/update-monaco-version.cjs
Executable file
44
scripts/update-monaco-version.cjs
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const projectRoot = path.resolve(__dirname, '..');
|
||||
|
||||
function resolveMonacoPackageJson() {
|
||||
try {
|
||||
const resolvedPath = require.resolve('monaco-editor/package.json', {
|
||||
paths: [projectRoot],
|
||||
});
|
||||
return resolvedPath;
|
||||
} catch (error) {
|
||||
console.error('[dees-editor] Unable to resolve monaco-editor/package.json');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getMonacoVersion() {
|
||||
const monacoPackagePath = resolveMonacoPackageJson();
|
||||
const monacoPackage = require(monacoPackagePath);
|
||||
if (!monacoPackage.version) {
|
||||
throw new Error('[dees-editor] monaco-editor/package.json does not expose a version field');
|
||||
}
|
||||
return monacoPackage.version;
|
||||
}
|
||||
|
||||
function writeVersionModule(version) {
|
||||
const targetDir = path.join(projectRoot, 'ts_web', 'elements', 'dees-editor');
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
const targetFile = path.join(targetDir, 'version.ts');
|
||||
const fileContent = `// Auto-generated by scripts/update-monaco-version.cjs\nexport const MONACO_VERSION = '${version}';\n`;
|
||||
fs.writeFileSync(targetFile, fileContent, 'utf8');
|
||||
console.log(`[dees-editor] Wrote ${path.relative(projectRoot, targetFile)} with monaco-editor@${version}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const version = getMonacoVersion();
|
||||
writeVersionModule(version);
|
||||
} catch (error) {
|
||||
console.error('[dees-editor] Failed to update Monaco version module.');
|
||||
console.error(error instanceof Error ? error.message : error);
|
||||
process.exitCode = 1;
|
||||
}
|
28
test/test.dashboardgrid-layout.node.ts
Normal file
28
test/test.dashboardgrid-layout.node.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
|
||||
import {
|
||||
resolveWidgetPlacement,
|
||||
collectCollisions,
|
||||
} from '../ts_web/elements/dees-dashboardgrid/layout.ts';
|
||||
import type { DashboardWidget } from '../ts_web/elements/dees-dashboardgrid/types.ts';
|
||||
|
||||
tap.test('dashboardgrid does not overlap widgets after swap attempt', async () => {
|
||||
const widgets: DashboardWidget[] = [
|
||||
{ id: 'w0', x: 6, y: 5, w: 1, h: 3 },
|
||||
{ id: 'w1', x: 6, y: 1, w: 1, h: 3 },
|
||||
{ id: 'w2', x: 3, y: 0, w: 2, h: 2 },
|
||||
{ id: 'w3', x: 9, y: 0, w: 1, h: 2 },
|
||||
{ id: 'w4', x: 4, y: 3, w: 1, h: 2 },
|
||||
];
|
||||
|
||||
const placement = resolveWidgetPlacement(widgets, 'w0', { x: 6, y: 3 }, 12);
|
||||
expect(placement).toBeTruthy();
|
||||
|
||||
const layout = placement!.widgets;
|
||||
for (const widget of layout) {
|
||||
const collisions = collectCollisions(layout, widget, widget.x, widget.y, widget.w, widget.h);
|
||||
expect(collisions).toBeEmptyArray();
|
||||
}
|
||||
});
|
||||
|
||||
export default tap.start();
|
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-catalog',
|
||||
version: '1.11.8',
|
||||
version: '1.12.0',
|
||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||
}
|
||||
|
@@ -159,21 +159,169 @@ export const demoFunc = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Enhanced logging for reflow events
|
||||
let lastPlaceholderPosition = null;
|
||||
let moveEventCounter = 0;
|
||||
|
||||
// Helper function to log grid state
|
||||
const logGridState = (eventName: string, details?: any) => {
|
||||
const layout = grid.getLayout();
|
||||
console.group(`🔄 ${eventName} [Event #${++moveEventCounter}]`);
|
||||
console.log('Timestamp:', new Date().toISOString());
|
||||
console.log('Grid Configuration:', {
|
||||
columns: grid.columns,
|
||||
cellHeight: grid.cellHeight,
|
||||
margin: grid.margin,
|
||||
editable: grid.editable,
|
||||
activeBreakpoint: grid.activeBreakpoint
|
||||
});
|
||||
console.log('Current Layout:', layout);
|
||||
console.log('Widget Count:', layout.length);
|
||||
console.log('Grid Bounds:', {
|
||||
totalWidgets: grid.widgets.length,
|
||||
maxY: Math.max(...layout.map(w => w.y + w.h)),
|
||||
occupied: layout.map(w => `${w.id}: (${w.x},${w.y}) ${w.w}x${w.h}`).join(', ')
|
||||
});
|
||||
if (details) {
|
||||
console.log('Event Details:', details);
|
||||
}
|
||||
console.groupEnd();
|
||||
};
|
||||
|
||||
// Monitor placeholder position changes using MutationObserver
|
||||
const placeholderObserver = new MutationObserver(() => {
|
||||
const placeholder = grid.shadowRoot?.querySelector('.placeholder') as HTMLElement;
|
||||
if (placeholder) {
|
||||
const currentPosition = {
|
||||
left: placeholder.style.left,
|
||||
top: placeholder.style.top,
|
||||
width: placeholder.style.width,
|
||||
height: placeholder.style.height
|
||||
};
|
||||
|
||||
if (JSON.stringify(currentPosition) !== JSON.stringify(lastPlaceholderPosition)) {
|
||||
console.group('📍 Placeholder Position Changed');
|
||||
console.log('Previous:', lastPlaceholderPosition);
|
||||
console.log('Current:', currentPosition);
|
||||
|
||||
// Extract grid coordinates from style
|
||||
const gridInfo = grid.shadowRoot?.querySelector('.grid-container');
|
||||
if (gridInfo) {
|
||||
console.log('Grid Container Dimensions:', {
|
||||
width: gridInfo.clientWidth,
|
||||
height: gridInfo.clientHeight
|
||||
});
|
||||
}
|
||||
console.groupEnd();
|
||||
lastPlaceholderPosition = currentPosition;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start observing the shadow DOM for placeholder changes
|
||||
if (grid.shadowRoot) {
|
||||
placeholderObserver.observe(grid.shadowRoot, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['style']
|
||||
});
|
||||
}
|
||||
|
||||
// Log initial state
|
||||
logGridState('Initial Grid State');
|
||||
|
||||
grid.addEventListener('widget-move', (e: CustomEvent) => {
|
||||
console.log('Widget moved:', e.detail.widget, 'Displaced:', e.detail.displaced);
|
||||
logGridState('Widget Move', {
|
||||
widget: e.detail.widget,
|
||||
displaced: e.detail.displaced,
|
||||
swappedWith: e.detail.swappedWith
|
||||
});
|
||||
});
|
||||
|
||||
grid.addEventListener('widget-resize', (e: CustomEvent) => {
|
||||
console.log('Widget resized:', e.detail.widget, 'Displaced:', e.detail.displaced);
|
||||
logGridState('Widget Resize', {
|
||||
widget: e.detail.widget,
|
||||
displaced: e.detail.displaced,
|
||||
swappedWith: e.detail.swappedWith
|
||||
});
|
||||
});
|
||||
|
||||
grid.addEventListener('widget-remove', (e: CustomEvent) => {
|
||||
console.log('Widget removed:', e.detail.widget);
|
||||
logGridState('Widget Remove', {
|
||||
removedWidget: e.detail.widget
|
||||
});
|
||||
updateStatus();
|
||||
});
|
||||
|
||||
grid.addEventListener('layout-change', () => {
|
||||
console.log('Layout changed:', grid.getLayout());
|
||||
logGridState('Layout Change');
|
||||
updateStatus();
|
||||
});
|
||||
|
||||
// Monitor during drag/resize operations using pointer events
|
||||
grid.addEventListener('pointerdown', (e: PointerEvent) => {
|
||||
const isHeader = (e.target as HTMLElement).closest('.widget-header');
|
||||
const isResizeHandle = (e.target as HTMLElement).closest('.resize-handle');
|
||||
|
||||
if (isHeader || isResizeHandle) {
|
||||
console.group(`🎯 Interaction Started: ${isHeader ? 'Drag' : 'Resize'}`);
|
||||
console.log('Target Widget:', (e.target as HTMLElement).closest('.widget')?.getAttribute('data-widget-id'));
|
||||
console.log('Pointer Position:', { x: e.clientX, y: e.clientY });
|
||||
console.groupEnd();
|
||||
|
||||
// Track pointer move during interaction
|
||||
const handlePointerMove = (moveEvent: PointerEvent) => {
|
||||
const widget = (e.target as HTMLElement).closest('.widget');
|
||||
if (widget) {
|
||||
console.log(`↔️ Pointer Move:`, {
|
||||
widgetId: widget.getAttribute('data-widget-id'),
|
||||
position: { x: moveEvent.clientX, y: moveEvent.clientY },
|
||||
delta: {
|
||||
x: moveEvent.clientX - e.clientX,
|
||||
y: moveEvent.clientY - e.clientY
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerUp = () => {
|
||||
console.group('🏁 Interaction Ended');
|
||||
logGridState('Final State After Interaction');
|
||||
console.groupEnd();
|
||||
document.removeEventListener('pointermove', handlePointerMove);
|
||||
document.removeEventListener('pointerup', handlePointerUp);
|
||||
};
|
||||
|
||||
document.addEventListener('pointermove', handlePointerMove);
|
||||
document.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
});
|
||||
|
||||
// Log when widgets are added
|
||||
const originalAddWidget = grid.addWidget.bind(grid);
|
||||
grid.addWidget = (widget: any, autoPosition?: boolean) => {
|
||||
console.group('➕ Adding Widget');
|
||||
console.log('New Widget:', widget);
|
||||
console.log('Auto Position:', autoPosition);
|
||||
const result = originalAddWidget(widget, autoPosition);
|
||||
logGridState('After Widget Added');
|
||||
console.groupEnd();
|
||||
return result;
|
||||
};
|
||||
|
||||
// Log compact operations
|
||||
const originalCompact = grid.compact.bind(grid);
|
||||
grid.compact = (direction?: string) => {
|
||||
console.group('🗜️ Compacting Grid');
|
||||
console.log('Direction:', direction || 'vertical');
|
||||
logGridState('Before Compact');
|
||||
const result = originalCompact(direction);
|
||||
logGridState('After Compact');
|
||||
console.groupEnd();
|
||||
return result;
|
||||
};
|
||||
|
||||
updateStatus();
|
||||
}}>
|
||||
<style>
|
||||
|
@@ -413,20 +413,42 @@ export class DeesDashboardgrid extends DeesElement {
|
||||
|
||||
const layoutSource = this.widgets;
|
||||
this.previewWidgets = null;
|
||||
|
||||
// Always validate the final position, don't rely on lastPlacement from drag
|
||||
const target = this.placeholderPosition ?? dragState.start;
|
||||
const placement =
|
||||
dragState.lastPlacement ??
|
||||
resolveWidgetPlacement(
|
||||
layoutSource,
|
||||
dragState.widgetId,
|
||||
{ x: target.x, y: target.y },
|
||||
this.columns,
|
||||
dragState.previousPosition,
|
||||
);
|
||||
const placement = resolveWidgetPlacement(
|
||||
layoutSource,
|
||||
dragState.widgetId,
|
||||
{ x: target.x, y: target.y },
|
||||
this.columns,
|
||||
dragState.previousPosition,
|
||||
);
|
||||
|
||||
if (placement) {
|
||||
this.commitPlacement(placement, dragState.widgetId, 'widget-move');
|
||||
// Verify that the placement doesn't result in overlapping widgets
|
||||
const finalWidget = placement.widgets.find(w => w.id === dragState.widgetId);
|
||||
if (finalWidget) {
|
||||
const hasOverlap = placement.widgets.some(w => {
|
||||
if (w.id === dragState.widgetId) return false;
|
||||
return (
|
||||
finalWidget.x < w.x + w.w &&
|
||||
finalWidget.x + finalWidget.w > w.x &&
|
||||
finalWidget.y < w.y + w.h &&
|
||||
finalWidget.y + finalWidget.h > w.y
|
||||
);
|
||||
});
|
||||
|
||||
if (!hasOverlap) {
|
||||
this.commitPlacement(placement, dragState.widgetId, 'widget-move');
|
||||
} else {
|
||||
// Return to start position if overlap detected
|
||||
this.widgets = this.widgets.map(widget =>
|
||||
widget.id === dragState.widgetId ? { ...widget, x: dragState.start.x, y: dragState.start.y } : widget,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Return to start position if no valid placement
|
||||
this.widgets = this.widgets.map(widget =>
|
||||
widget.id === dragState.widgetId ? { ...widget, x: dragState.start.x, y: dragState.start.y } : widget,
|
||||
);
|
||||
|
@@ -161,10 +161,23 @@ export const resolveWidgetPlacement = (
|
||||
if (!other.locked && !other.noMove && other.w === moving.w && other.h === moving.h) {
|
||||
const otherClone = sourceWidgets.find(widget => widget.id === other.id);
|
||||
if (otherClone) {
|
||||
const swapTarget = previousPosition ?? original;
|
||||
// Use the original position of the moving widget for a clean swap
|
||||
// This prevents the "snapping together" issue where both widgets end up at the same position
|
||||
const swapTarget = original;
|
||||
const previousOtherPosition = { x: otherClone.x, y: otherClone.y };
|
||||
otherClone.x = swapTarget.x;
|
||||
otherClone.y = swapTarget.y;
|
||||
return { widgets: sourceWidgets, movedWidgets: [moving.id, otherClone.id], swappedWith: otherClone.id };
|
||||
|
||||
const swapValid =
|
||||
collectCollisions(sourceWidgets, moving, moving.x, moving.y, moving.w, moving.h).length === 0 &&
|
||||
collectCollisions(sourceWidgets, otherClone, otherClone.x, otherClone.y, otherClone.w, otherClone.h).length === 0;
|
||||
|
||||
if (swapValid) {
|
||||
return { widgets: sourceWidgets, movedWidgets: [moving.id, otherClone.id], swappedWith: otherClone.id };
|
||||
}
|
||||
|
||||
otherClone.x = previousOtherPosition.x;
|
||||
otherClone.y = previousOtherPosition.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import {
|
||||
cssManager,
|
||||
} from '@design.estate/dees-element';
|
||||
import * as domtools from '@design.estate/dees-domtools';
|
||||
import { MONACO_VERSION } from './version.js';
|
||||
|
||||
import type * as monaco from 'monaco-editor';
|
||||
|
||||
@@ -80,10 +81,11 @@ export class DeesEditor extends DeesElement {
|
||||
): Promise<void> {
|
||||
super.firstUpdated(_changedProperties);
|
||||
const container = this.shadowRoot.getElementById('container');
|
||||
const monacoCdnBase = `https://cdn.jsdelivr.net/npm/monaco-editor@${MONACO_VERSION}`;
|
||||
|
||||
if (!DeesEditor.monacoDeferred) {
|
||||
DeesEditor.monacoDeferred = domtools.plugins.smartpromise.defer();
|
||||
const scriptUrl = `https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/loader.js`;
|
||||
const scriptUrl = `${monacoCdnBase}/min/vs/loader.js`;
|
||||
const script = document.createElement('script');
|
||||
script.src = scriptUrl;
|
||||
script.onload = () => {
|
||||
@@ -94,7 +96,7 @@ export class DeesEditor extends DeesElement {
|
||||
await DeesEditor.monacoDeferred.promise;
|
||||
|
||||
(window as any).require.config({
|
||||
paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor/min/vs' },
|
||||
paths: { vs: `${monacoCdnBase}/min/vs` },
|
||||
});
|
||||
(window as any).require(['vs/editor/editor.main'], async () => {
|
||||
const editor = ((window as any).monaco.editor as typeof monaco.editor).create(container, {
|
||||
@@ -109,7 +111,7 @@ export class DeesEditor extends DeesElement {
|
||||
this.editorDeferred.resolve(editor);
|
||||
});
|
||||
const css = await (
|
||||
await fetch('https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/editor/editor.main.css')
|
||||
await fetch(`${monacoCdnBase}/min/vs/editor/editor.main.css`)
|
||||
).text();
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.textContent = css;
|
2
ts_web/elements/dees-editor/version.ts
Normal file
2
ts_web/elements/dees-editor/version.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// Auto-generated by scripts/update-monaco-version.cjs
|
||||
export const MONACO_VERSION = '0.52.2';
|
@@ -36,30 +36,130 @@ export class DeesStepper extends DeesElement {
|
||||
<dees-stepper
|
||||
.steps=${[
|
||||
{
|
||||
title: 'Whats your name?',
|
||||
title: 'Account Setup',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text
|
||||
key="email"
|
||||
label="Your Email"
|
||||
value="hello@something.com"
|
||||
disabled
|
||||
></dees-input-text>
|
||||
<dees-input-text key="firstName" required label="Vorname"></dees-input-text>
|
||||
<dees-input-text key="lastName" required label="Nachname"></dees-input-text>
|
||||
<dees-form-submit>Next</dees-form-submit>
|
||||
<dees-input-text key="email" label="Work Email" required></dees-input-text>
|
||||
<dees-input-text key="password" label="Create Password" type="password" required></dees-input-text>
|
||||
<dees-form-submit>Continue</dees-form-submit>
|
||||
</dees-form>
|
||||
`,
|
||||
validationFunc: async (stepperArg, elementArg) => {
|
||||
const deesForm = elementArg.querySelector('dees-form');
|
||||
deesForm.addEventListener('formData', (eventArg) => {
|
||||
stepperArg.goNext();
|
||||
});
|
||||
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Whats your mobile number?',
|
||||
content: html``,
|
||||
title: 'Profile Details',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text key="firstName" label="First Name" required></dees-input-text>
|
||||
<dees-input-text key="lastName" label="Last Name" required></dees-input-text>
|
||||
<dees-form-submit>Continue</dees-form-submit>
|
||||
</dees-form>
|
||||
`,
|
||||
validationFunc: async (stepperArg, elementArg) => {
|
||||
const deesForm = elementArg.querySelector('dees-form');
|
||||
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Contact Information',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-phone key="phone" label="Mobile Number" required></dees-input-phone>
|
||||
<dees-input-text key="company" label="Company"></dees-input-text>
|
||||
<dees-form-submit>Continue</dees-form-submit>
|
||||
</dees-form>
|
||||
`,
|
||||
validationFunc: async (stepperArg, elementArg) => {
|
||||
const deesForm = elementArg.querySelector('dees-form');
|
||||
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Team Size',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-dropdown
|
||||
key="teamSize"
|
||||
label="How big is your team?"
|
||||
.options=${[
|
||||
{ label: '1-5', value: '1-5' },
|
||||
{ label: '6-20', value: '6-20' },
|
||||
{ label: '21-50', value: '21-50' },
|
||||
{ label: '51+', value: '51+' },
|
||||
]}
|
||||
required
|
||||
></dees-input-dropdown>
|
||||
<dees-form-submit>Continue</dees-form-submit>
|
||||
</dees-form>
|
||||
`,
|
||||
validationFunc: async (stepperArg, elementArg) => {
|
||||
const deesForm = elementArg.querySelector('dees-form');
|
||||
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Goals',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-multitoggle
|
||||
key="goal"
|
||||
label="Main objective"
|
||||
.options=${[
|
||||
{ label: 'Onboarding', value: 'onboarding' },
|
||||
{ label: 'Analytics', value: 'analytics' },
|
||||
{ label: 'Automation', value: 'automation' },
|
||||
]}
|
||||
required
|
||||
></dees-input-multitoggle>
|
||||
<dees-form-submit>Continue</dees-form-submit>
|
||||
</dees-form>
|
||||
`,
|
||||
validationFunc: async (stepperArg, elementArg) => {
|
||||
const deesForm = elementArg.querySelector('dees-form');
|
||||
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Brand Preferences',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text key="brandColor" label="Primary brand color"></dees-input-text>
|
||||
<dees-input-text key="tone" label="Preferred tone (e.g. friendly, formal)"></dees-input-text>
|
||||
<dees-form-submit>Continue</dees-form-submit>
|
||||
</dees-form>
|
||||
`,
|
||||
validationFunc: async (stepperArg, elementArg) => {
|
||||
const deesForm = elementArg.querySelector('dees-form');
|
||||
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Integrations',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-list
|
||||
key="integrations"
|
||||
label="Integrations in use"
|
||||
placeholder="Add integration"
|
||||
></dees-input-list>
|
||||
<dees-form-submit>Continue</dees-form-submit>
|
||||
</dees-form>
|
||||
`,
|
||||
validationFunc: async (stepperArg, elementArg) => {
|
||||
const deesForm = elementArg.querySelector('dees-form');
|
||||
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Review & Launch',
|
||||
content: html`
|
||||
<dees-panel>
|
||||
<p>Almost there! Review your selections and launch whenever you're ready.</p>
|
||||
</dees-panel>
|
||||
`,
|
||||
},
|
||||
] as IStep[]}
|
||||
></dees-stepper>
|
||||
@@ -99,30 +199,33 @@ export class DeesStepper extends DeesElement {
|
||||
position: relative;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
transition: all 0.7s ease-in-out;
|
||||
transition: transform 0.35s ease, box-shadow 0.35s ease, filter 0.35s ease, border 0.35s ease;
|
||||
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')};
|
||||
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')};
|
||||
margin: auto;
|
||||
margin-bottom: 20px;
|
||||
filter: opacity(0.5) grayscale(1);
|
||||
box-shadow: 0px 0px 3px #00000010;
|
||||
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)')};
|
||||
user-select: none;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.step.selected {
|
||||
border-top: 1px solid #e4002b;
|
||||
pointer-events: all;
|
||||
filter: opacity(1) grayscale(0);
|
||||
box-shadow: 0px 0px 5px #00000010;
|
||||
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)')};
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
.step.hiddenStep {
|
||||
filter: opacity(0);
|
||||
transform: translateY(16px);
|
||||
}
|
||||
|
||||
.step:last-child {
|
||||
@@ -130,40 +233,50 @@ export class DeesStepper extends DeesElement {
|
||||
}
|
||||
|
||||
.step .stepCounter {
|
||||
color: #999;
|
||||
color: ${cssManager.bdTheme('#64748b', '#a1a1aa')};
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
padding: 10px 15px;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
padding: 6px 14px;
|
||||
font-size: 12px;
|
||||
border-bottom-left-radius: 3px;
|
||||
background: ${cssManager.bdTheme('#00000008', '#ffffff08')};
|
||||
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)')};
|
||||
}
|
||||
|
||||
.step .goBack {
|
||||
color: #999;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
padding: 10px 15px;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
border-bottom-right-radius: 3px;
|
||||
background: ${cssManager.bdTheme('#00000008', '#ffffff08')};
|
||||
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;
|
||||
}
|
||||
|
||||
.step .goBack:hover {
|
||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
||||
background: ${cssManager.bdTheme('#00000012', colors.dark.blue)};
|
||||
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);
|
||||
}
|
||||
|
||||
.step .goBack:active {
|
||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
||||
background: ${cssManager.bdTheme('#00000012', colors.dark.blueActive)};
|
||||
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)')};
|
||||
}
|
||||
|
||||
.step .goBack span {
|
||||
transition: all 0.2s;
|
||||
transition: transform 0.2s ease;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -173,15 +286,16 @@ export class DeesStepper extends DeesElement {
|
||||
|
||||
.step .title {
|
||||
text-align: center;
|
||||
padding-top: 50px;
|
||||
padding-top: 64px;
|
||||
font-family: 'Geist Sans', sans-serif;
|
||||
font-size: 22px;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.step .content {
|
||||
padding: 20px;
|
||||
padding: 24px 28px 32px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
@@ -270,7 +384,14 @@ export class DeesStepper extends DeesElement {
|
||||
|
||||
public async goBack() {
|
||||
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
|
||||
this.selectedStep = this.steps[currentIndex - 1];
|
||||
if (currentIndex <= 0) {
|
||||
return;
|
||||
}
|
||||
const currentStep = this.steps[currentIndex];
|
||||
currentStep.validationFuncCalled = false;
|
||||
const previousStep = this.steps[currentIndex - 1];
|
||||
previousStep.validationFuncCalled = false;
|
||||
this.selectedStep = previousStep;
|
||||
await this.domtoolsPromise;
|
||||
await this.domtools.convenience.smartdelay.delayFor(100);
|
||||
this.selectedStep.onReturnToStepFunc?.(this, this.shadowRoot.querySelector('.selected'));
|
||||
@@ -278,6 +399,13 @@ export class DeesStepper extends DeesElement {
|
||||
|
||||
public goNext() {
|
||||
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
|
||||
this.selectedStep = this.steps[currentIndex + 1];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ export * from './dees-contextmenu.js';
|
||||
export * from './dees-dataview-codebox.js';
|
||||
export * from './dees-dataview-statusobject.js';
|
||||
export * from './dees-dashboardgrid/index.js';
|
||||
export * from './dees-editor.js';
|
||||
export * from './dees-editor/dees-editor.js';
|
||||
export * from './dees-editor-markdown.js';
|
||||
export * from './dees-editor-markdownoutlet.js';
|
||||
export * from './dees-form-submit.js';
|
||||
|
Reference in New Issue
Block a user