Compare commits

..

8 Commits

Author SHA1 Message Date
59610f463e v3.73.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 10:44:17 +00:00
c1672bb8ae feat(dees-label): expand dees-label demo coverage and update supporting dependencies 2026-04-12 10:44:17 +00:00
3e101840a6 v3.72.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 09:24:23 +00:00
e87898ab82 fix(dees-stepper): improve stepper exit animation timing for cancel confirmation flow 2026-04-12 09:24:23 +00:00
8a1be59a51 v3.72.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-11 20:04:37 +00:00
a3b2ace88d feat(dees-stepper): add configurable cancellation flow with confirmation modal 2026-04-11 20:04:37 +00:00
c34037265e v3.71.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-11 16:20:34 +00:00
8c230fe3af fix(dees-modal): move modal content scrolling into dees-tile so long content stays scrollable with pinned header and actions 2026-04-11 16:20:34 +00:00
10 changed files with 409 additions and 78 deletions

View File

@@ -0,0 +1 @@
{"sessionId":"4b0f0a7f-f187-40a3-a38b-cb9a7e877011","pid":3110692,"acquiredAt":1775914414249}

View File

@@ -1,5 +1,33 @@
# Changelog
## 2026-04-12 - 3.73.0 - feat(dees-label)
expand dees-label demo coverage and update supporting dependencies
- replace the minimal dees-label demo with a structured showcase for basic, required, description, combined, and empty-label states
- add themed demo styling and inline annotations to better document component behavior
- update @design.estate/dees-wcctools, lucide, and @types/node dependency versions
## 2026-04-12 - 3.72.1 - fix(dees-stepper)
improve stepper exit animation timing for cancel confirmation flow
- Animate step tiles downward with fade-out during teardown instead of only fading the container
- Delay stepper destruction briefly after dismissing the confirmation modal so both exit transitions render smoothly
- Increase teardown delay to match the updated exit animation duration
## 2026-04-11 - 3.72.0 - feat(dees-stepper)
add configurable cancellation flow with confirmation modal
- adds a cancelable option to control whether steppers can be dismissed
- shows a confirmation modal when canceling via the new Cancel button or overlay backdrop
- updates footer button rendering and separators to support the new cancel action consistently
## 2026-04-11 - 3.71.1 - fix(dees-modal)
move modal content scrolling into dees-tile so long content stays scrollable with pinned header and actions
- Update dees-tile content area to use vertical scrolling when constrained by a max-height while keeping horizontal overflow clipped.
- Remove duplicate scrolling styles from dees-modal and rely on the shared tile container behavior.
- Add modal demo cases for long article, list, and form content to verify internal scrolling.
## 2026-04-11 - 3.71.0 - feat(dees-stepper)
add footer menu actions with form-aware step validation

View File

@@ -1,6 +1,6 @@
{
"name": "@design.estate/dees-catalog",
"version": "3.71.0",
"version": "3.73.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",
@@ -18,7 +18,7 @@
"dependencies": {
"@design.estate/dees-domtools": "^2.5.4",
"@design.estate/dees-element": "^2.2.4",
"@design.estate/dees-wcctools": "^3.8.0",
"@design.estate/dees-wcctools": "^3.8.2",
"@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-brands-svg-icons": "^7.2.0",
"@fortawesome/free-regular-svg-icons": "^7.2.0",
@@ -35,10 +35,10 @@
"@tiptap/starter-kit": "^2.23.0",
"@tsclass/tsclass": "^9.5.0",
"echarts": "^5.6.0",
"lightweight-charts": "^5.1.0",
"highlight.js": "11.11.1",
"ibantools": "^4.5.1",
"lucide": "^0.577.0",
"lightweight-charts": "^5.1.0",
"lucide": "^1.8.0",
"monaco-editor": "0.55.1",
"pdfjs-dist": "^4.10.38",
"xterm": "^5.3.0",
@@ -50,7 +50,7 @@
"@git.zone/tstest": "^3.6.3",
"@git.zone/tswatch": "^3.3.2",
"@push.rocks/projectinfo": "^5.1.0",
"@types/node": "^25.5.0"
"@types/node": "^25.6.0"
},
"files": [
"ts/**/*",

65
pnpm-lock.yaml generated
View File

@@ -15,8 +15,8 @@ importers:
specifier: ^2.2.4
version: 2.2.4
'@design.estate/dees-wcctools':
specifier: ^3.8.0
version: 3.8.0
specifier: ^3.8.2
version: 3.8.2
'@fortawesome/fontawesome-svg-core':
specifier: ^7.2.0
version: 7.2.0
@@ -75,8 +75,8 @@ importers:
specifier: ^5.1.0
version: 5.1.0
lucide:
specifier: ^0.577.0
version: 0.577.0
specifier: ^1.8.0
version: 1.8.0
monaco-editor:
specifier: 0.55.1
version: 0.55.1
@@ -106,8 +106,8 @@ importers:
specifier: ^5.1.0
version: 5.1.0
'@types/node':
specifier: ^25.5.0
version: 25.5.0
specifier: ^25.6.0
version: 25.6.0
packages:
@@ -323,8 +323,8 @@ packages:
'@design.estate/dees-element@2.2.4':
resolution: {integrity: sha512-O9cA6flBMMd+pBwMQrZXwAWel9yVxgokolb+Em6gvkXxPJ0P/B5UDn4Vc2d4ts3ta55PTBm+l2dPeDVGx/bl7Q==}
'@design.estate/dees-wcctools@3.8.0':
resolution: {integrity: sha512-CC14iVKUrguzD9jIrdPBd9fZ4egVJEZMxl5y8iy0l7WLumeoYvGsoXj5INVkRPLRVLqziIdi4Je1hXqHt2NU+g==}
'@design.estate/dees-wcctools@3.8.2':
resolution: {integrity: sha512-A55XHeWExxxojdERAmedrZeyTGeK01ax5ct46VbjMeH65HbgBiTF4EOHfS6rjdTp+9VD3vXd0efhzyOxOS6uFw==}
'@emnapi/core@1.8.1':
resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
@@ -2121,11 +2121,11 @@ packages:
'@types/node@16.9.1':
resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==}
'@types/node@22.19.15':
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
'@types/node@22.19.17':
resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==}
'@types/node@25.5.0':
resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
'@types/node@25.6.0':
resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==}
'@types/randomatic@3.1.5':
resolution: {integrity: sha512-VCwCTw6qh1pRRw+5rNTAwqPmf6A+hdrkdM7dBpZVmhl7g+em3ONXlYK/bWPVKqVGMWgP0d1bog8Vc/X6zRwRRQ==}
@@ -3054,6 +3054,9 @@ packages:
lucide@0.577.0:
resolution: {integrity: sha512-PpC/m5eOItp/WU/GlQPFBXDOhq6HibL73KzYP37OX3LM7VmzWQF8voEj8QRWUFvy9FIKfeDQkWYoyS1D/MdWFA==}
lucide@1.8.0:
resolution: {integrity: sha512-JjV/QnadgFLj1Pyu9IKl0lknrolFEzo04B64QcYLLeRzZl/iEHpdbSrRRKbyXcv45SZNv+WGjIUCT33e7xHO6Q==}
make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
@@ -4002,8 +4005,8 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
undici-types@7.19.2:
resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==}
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
@@ -4708,7 +4711,7 @@ snapshots:
dependencies:
'@design.estate/dees-domtools': 2.5.4
'@design.estate/dees-element': 2.2.4
'@design.estate/dees-wcctools': 3.8.0
'@design.estate/dees-wcctools': 3.8.2
'@fortawesome/fontawesome-svg-core': 7.2.0
'@fortawesome/free-brands-svg-icons': 7.2.0
'@fortawesome/free-regular-svg-icons': 7.2.0
@@ -4784,7 +4787,7 @@ snapshots:
- supports-color
- vue
'@design.estate/dees-wcctools@3.8.0':
'@design.estate/dees-wcctools@3.8.2':
dependencies:
'@design.estate/dees-domtools': 2.5.4
'@design.estate/dees-element': 2.2.4
@@ -5257,7 +5260,7 @@ snapshots:
'@inquirer/figures': 1.0.15
'@inquirer/type': 2.0.0
'@types/mute-stream': 0.0.4
'@types/node': 22.19.15
'@types/node': 22.19.17
'@types/wrap-ansi': 3.0.0
ansi-escapes: 4.3.2
cli-width: 4.1.0
@@ -7235,7 +7238,7 @@ snapshots:
'@types/clean-css@4.2.11':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
source-map: 0.6.1
'@types/debug@4.1.12':
@@ -7245,7 +7248,7 @@ snapshots:
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 25.5.0
'@types/node': 25.6.0
'@types/hast@3.0.4':
dependencies:
@@ -7265,7 +7268,7 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
'@types/linkify-it@5.0.0': {}
@@ -7288,21 +7291,21 @@ snapshots:
'@types/mute-stream@0.0.4':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
'@types/node-forge@1.3.14':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
'@types/node@16.9.1': {}
'@types/node@22.19.15':
'@types/node@22.19.17':
dependencies:
undici-types: 6.21.0
'@types/node@25.5.0':
'@types/node@25.6.0':
dependencies:
undici-types: 7.18.2
undici-types: 7.19.2
'@types/randomatic@3.1.5': {}
@@ -7314,11 +7317,11 @@ snapshots:
'@types/tar-stream@3.1.4':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
'@types/through2@2.0.41':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
'@types/trusted-types@2.0.7': {}
@@ -7344,11 +7347,11 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 25.5.0
'@types/node': 25.6.0
optional: true
'@ungap/structured-clone@1.3.0': {}
@@ -8300,6 +8303,8 @@ snapshots:
lucide@0.577.0: {}
lucide@1.8.0: {}
make-dir@3.1.0:
dependencies:
semver: 6.3.1
@@ -9570,7 +9575,7 @@ snapshots:
undici-types@6.21.0: {}
undici-types@7.18.2: {}
undici-types@7.19.2: {}
unified@11.0.5:
dependencies:

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@design.estate/dees-catalog',
version: '3.71.0',
version: '3.73.0',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
}

View File

@@ -1,7 +1,128 @@
import { html, cssManager } from '@design.estate/dees-element';
import { html, css, cssManager } from '@design.estate/dees-element';
export const demoFunc = () => {
return html`
<dees-label .label=${'a label'}></dees-label>
`;
}
export const demoFunc = () => html`
<style>
${css`
.demo-container {
display: flex;
flex-direction: column;
gap: 24px;
padding: 24px;
max-width: 800px;
margin: 0 auto;
}
.demo-section {
background: ${cssManager.bdTheme('#f8f9fa', '#1a1a1a')};
border-radius: 8px;
padding: 24px;
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#333')};
}
.demo-section h3 {
margin-top: 0;
margin-bottom: 16px;
color: ${cssManager.bdTheme('#333', '#fff')};
}
.demo-section p {
color: ${cssManager.bdTheme('#666', '#999')};
margin-bottom: 16px;
font-size: 13px;
}
.label-grid {
display: flex;
flex-direction: column;
gap: 24px;
}
.label-row {
display: flex;
align-items: baseline;
gap: 16px;
}
.label-row .annotation {
font-size: 12px;
font-family: monospace;
color: ${cssManager.bdTheme('#888', '#666')};
flex-shrink: 0;
min-width: 200px;
}
`}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Basic Label</h3>
<p>A simple text label with no additional indicators.</p>
<div class="label-grid">
<div class="label-row">
<span class="annotation">label="Username"</span>
<dees-label .label=${'Username'}></dees-label>
</div>
<div class="label-row">
<span class="annotation">label="Email Address"</span>
<dees-label .label=${'Email Address'}></dees-label>
</div>
</div>
</div>
<div class="demo-section">
<h3>Required Indicator</h3>
<p>When <code>required</code> is set, a red asterisk appears after the label text.</p>
<div class="label-grid">
<div class="label-row">
<span class="annotation">required=${'{true}'}</span>
<dees-label .label=${'Full Name'} .required=${true}></dees-label>
</div>
<div class="label-row">
<span class="annotation">required=${'{false}'} (default)</span>
<dees-label .label=${'Nickname'} .required=${false}></dees-label>
</div>
</div>
</div>
<div class="demo-section">
<h3>Description (Info Icon)</h3>
<p>When <code>description</code> is set, an info icon appears next to the label. Hover over it to see the tooltip.</p>
<div class="label-grid">
<div class="label-row">
<span class="annotation">description="..."</span>
<dees-label .label=${'API Key'} .description=${'Your API key can be found in the developer settings dashboard.'}></dees-label>
</div>
<div class="label-row">
<span class="annotation">short description</span>
<dees-label .label=${'Region'} .description=${'Select your nearest datacenter.'}></dees-label>
</div>
</div>
</div>
<div class="demo-section">
<h3>Required + Description</h3>
<p>Both indicators can be combined. The asterisk appears first, then the info icon.</p>
<div class="label-grid">
<div class="label-row">
<span class="annotation">required + description</span>
<dees-label .label=${'Password'} .required=${true} .description=${'Must be at least 8 characters with one uppercase letter and one number.'}></dees-label>
</div>
<div class="label-row">
<span class="annotation">required + description</span>
<dees-label .label=${'Email Address'} .required=${true} .description=${'We will send a verification link to this address.'}></dees-label>
</div>
</div>
</div>
<div class="demo-section">
<h3>Empty Label</h3>
<p>When <code>label</code> is empty or not set, nothing is rendered. The element below has no label text:</p>
<div class="label-grid">
<div class="label-row">
<span class="annotation">label="" (empty)</span>
<dees-label .label=${''}></dees-label>
</div>
</div>
</div>
</div>
`;

View File

@@ -18,6 +18,7 @@ import { themeDefaultStyles } from '../../00theme.js';
import { cssGeistFontFamily } from '../../00fonts.js';
import { zIndexRegistry } from '../../00zindex.js';
import { DeesWindowLayer } from '../../00group-overlay/dees-windowlayer/dees-windowlayer.js';
import { DeesModal } from '../../00group-overlay/dees-modal/dees-modal.js';
import type { DeesForm } from '../../00group-form/dees-form/dees-form.js';
import '../dees-tile/dees-tile.js';
@@ -45,15 +46,16 @@ export class DeesStepper extends DeesElement {
public static async createAndShow(optionsArg: {
steps: IStep[];
cancelable?: boolean;
}): Promise<DeesStepper> {
const body = document.body;
const stepper = new DeesStepper();
stepper.steps = optionsArg.steps;
stepper.overlay = true;
if (optionsArg.cancelable !== undefined) {
stepper.cancelable = optionsArg.cancelable;
}
stepper.windowLayer = await DeesWindowLayer.createAndShow({ blur: true });
stepper.windowLayer.addEventListener('click', async () => {
await stepper.destroy();
});
body.append(stepper.windowLayer);
body.append(stepper);
@@ -81,6 +83,18 @@ export class DeesStepper extends DeesElement {
})
accessor overlay: boolean = false;
/**
* When true (default), the stepper renders a Cancel button in every step's
* footer, and clicking the backdrop (overlay mode) triggers the same cancel
* confirmation flow. Set to false for forced flows where the user must
* complete the stepper — no Cancel button, no backdrop dismissal.
*/
@property({
type: Boolean,
reflect: true,
})
accessor cancelable: boolean = true;
@property({ type: Number, attribute: false })
accessor stepperZIndex: number = 1000;
@@ -140,9 +154,14 @@ export class DeesStepper extends DeesElement {
height: 100vh;
}
.stepperContainer.predestroy {
opacity: 0;
transition: opacity 0.2s ease-in;
/* Exit animation — reverse of the entry. Tiles slide DOWN + fade out,
mirroring the .entrance slide-up. The transition override is needed
because dees-tile.step has its own 0.7s transition for step selection
which would otherwise make the exit sluggish. */
.stepperContainer.predestroy dees-tile.step {
transform: translateY(16px);
filter: opacity(0);
transition: transform 0.25s, filter 0.2s;
}
dees-tile.step {
@@ -280,15 +299,17 @@ export class DeesStepper extends DeesElement {
transition: all 0.15s ease;
background: transparent;
border: none;
border-left: 1px solid var(--dees-color-border-subtle);
color: var(--dees-color-text-muted);
white-space: nowrap;
display: flex;
align-items: center;
}
.bottomButtons .bottomButton:first-child {
border-left: none;
/* Border-left separator on every button EXCEPT the first one.
Uses general sibling so the stepHint (if rendered on the left) does
not shift which button counts as "first" and create a phantom border. */
.bottomButtons .bottomButton ~ .bottomButton {
border-left: 1px solid var(--dees-color-border-subtle);
}
.bottomButtons .bottomButton:hover {
@@ -347,6 +368,7 @@ export class DeesStepper extends DeesElement {
<div
class="stepperContainer ${this.overlay ? 'overlay' : ''}"
style=${this.overlay ? `z-index: ${this.stepperZIndex};` : ''}
@click=${this.handleOutsideClick}
>
${this.steps.map((stepArg, stepIndex) => {
const isSelected = stepArg === this.selectedStep;
@@ -370,23 +392,27 @@ export class DeesStepper extends DeesElement {
<div class="title">${stepArg.title}</div>
<div class="content">${stepArg.content}</div>
</div>
${stepArg.menuOptions && stepArg.menuOptions.length > 0
? html`<div slot="footer" class="bottomButtons">
${isSelected && this.activeForm !== null && !this.activeFormValid
? html`<div class="stepHint">Complete form to continue</div>`
: ''}
${stepArg.menuOptions.map((actionArg, actionIndex) => {
const isPrimary = actionIndex === stepArg.menuOptions!.length - 1;
const isDisabled = isPrimary && this.activeForm !== null && !this.activeFormValid;
return html`
<div
class="bottomButton ${isPrimary ? 'primary' : ''} ${isDisabled ? 'disabled' : ''}"
@click=${() => this.handleMenuOptionClick(actionArg, isPrimary)}
>${actionArg.name}</div>
`;
})}
</div>`
: ''}
<div slot="footer" class="bottomButtons">
${isSelected && this.activeForm !== null && !this.activeFormValid
? html`<div class="stepHint">Complete form to continue</div>`
: ''}
${this.cancelable
? html`<div
class="bottomButton"
@click=${() => this.handleCancelRequest()}
>Cancel</div>`
: ''}
${stepArg.menuOptions?.map((actionArg, actionIndex) => {
const isPrimary = actionIndex === stepArg.menuOptions!.length - 1;
const isDisabled = isPrimary && this.activeForm !== null && !this.activeFormValid;
return html`
<div
class="bottomButton ${isPrimary ? 'primary' : ''} ${isDisabled ? 'disabled' : ''}"
@click=${() => this.handleMenuOptionClick(actionArg, isPrimary)}
>${actionArg.name}</div>
`;
}) ?? ''}
</div>
</dees-tile>`;
})}
</div>
@@ -556,11 +582,83 @@ export class DeesStepper extends DeesElement {
await optionArg.action(this);
}
/**
* Currently-open confirmation modal (if any). Prevents double-stacking when
* the user clicks the backdrop or the Cancel button while a confirm modal
* is already visible. The reference may become stale (point to a destroyed
* modal) if the user dismisses the confirm modal by clicking its own
* backdrop — so handleCancelRequest() uses isConnected to detect that.
*/
private cancelConfirmModal?: DeesModal;
/**
* Click handler on .stepperContainer. Mirrors dees-modal.handleOutsideClick:
* when the user clicks the empty backdrop area (target === stepperContainer,
* not any descendant tile), trigger the cancel confirmation flow. Clicks
* that originate inside a step tile have a different event.target and are
* ignored here.
*/
private handleOutsideClick(eventArg: MouseEvent) {
if (!this.overlay) return;
if (!this.cancelable) return;
eventArg.stopPropagation();
const stepperContainer = this.shadowRoot!.querySelector('.stepperContainer');
if (eventArg.target === stepperContainer) {
this.handleCancelRequest();
}
}
/**
* Shown by both the backdrop click and the Cancel button in the footer.
* Presents a dees-modal asking the user to confirm cancellation. If they
* confirm, the stepper and window layer are destroyed; otherwise the
* confirm modal is dismissed and the stepper stays open.
*
* The isConnected check on the cached reference handles the case where the
* user dismissed the previous confirm modal by clicking ITS OWN backdrop —
* dees-modal.handleOutsideClick calls destroy() directly, bypassing our
* action callbacks, so our cached reference would be stale without this
* fallback check.
*/
public async handleCancelRequest() {
if (!this.cancelable) return;
if (this.cancelConfirmModal && this.cancelConfirmModal.isConnected) return;
this.cancelConfirmModal = undefined;
this.cancelConfirmModal = await DeesModal.createAndShow({
heading: 'Cancel setup?',
width: 'small',
content: html`
<p style="margin: 0;">
Are you sure you want to cancel? Any progress on the current step will be lost.
</p>
`,
menuOptions: [
{
name: 'Continue setup',
action: async (modal) => {
this.cancelConfirmModal = undefined;
await modal!.destroy();
},
},
{
name: 'Yes, cancel',
action: async (modal) => {
this.cancelConfirmModal = undefined;
modal!.destroy(); // fire-and-forget — starts the confirm modal fade
const domtools = await this.domtoolsPromise;
await domtools.convenience.smartdelay.delayFor(100);
await this.destroy();
},
},
],
});
}
public async destroy() {
const domtools = await this.domtoolsPromise;
const container = this.shadowRoot!.querySelector('.stepperContainer');
container?.classList.add('predestroy');
await domtools.convenience.smartdelay.delayFor(200);
await domtools.convenience.smartdelay.delayFor(250);
if (this.parentElement) {
this.parentElement.removeChild(this);
}

View File

@@ -87,14 +87,24 @@ export class DeesTile extends DeesElement {
color: var(--dees-color-text-secondary);
}
/* --- Content: the rounded inset --- */
/* --- Content: the rounded inset ---
Uses overflow-y: auto so that when a consumer (e.g. dees-modal) caps
the tile with max-height, long content scrolls inside the tile
instead of being clipped. For consumers without max-height
(e.g. dees-stepper), the tile grows with content and the scroll
never activates. Horizontal overflow stays clipped to preserve the
rounded corners. */
.tile-content {
flex: 1;
position: relative;
border-radius: 8px;
border-top: 1px solid var(--dees-color-border-subtle);
border-bottom: 1px solid var(--dees-color-border-subtle);
overflow: hidden;
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: var(--dees-color-scrollbar-thumb) transparent;
}
.tile-content.no-footer {

View File

@@ -352,5 +352,80 @@ export const demoFunc = () => html`
});
}}>Test Responsive</dees-button>
</div>
<div class="demo-section">
<h3>Scrollable Content</h3>
<p>When content exceeds the modal's max-height (<code>calc(100vh - 80px)</code>), the tile caps at that height and the content area scrolls inside. The heading and bottom buttons stay pinned.</p>
<div class="button-grid">
<dees-button @click=${() => {
DeesModal.createAndShow({
heading: 'Long Article',
width: 'medium',
content: html`
<h4 style="margin-top: 0;">Lorem ipsum dolor sit amet</h4>
${Array.from({ length: 40 }, (_, i) => html`
<p>
<strong>§ ${i + 1}.</strong>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
`)}
`,
menuOptions: [{
name: 'Cancel',
action: async (modal) => modal!.destroy()
}, {
name: 'Accept',
action: async (modal) => modal!.destroy()
}],
});
}}>Long Article</dees-button>
<dees-button @click=${() => {
DeesModal.createAndShow({
heading: 'Long List',
width: 'small',
content: html`
<p>Selected items:</p>
<ul style="padding-left: 20px; margin: 0;">
${Array.from({ length: 80 }, (_, i) => html`
<li style="padding: 4px 0;">Item ${i + 1} — option label</li>
`)}
</ul>
`,
menuOptions: [{
name: 'Done',
action: async (modal) => modal!.destroy()
}],
});
}}>Long List</dees-button>
<dees-button @click=${() => {
DeesModal.createAndShow({
heading: 'Tall Form',
width: 'medium',
content: html`
<dees-form>
${Array.from({ length: 25 }, (_, i) => html`
<dees-input-text .label=${`Field ${i + 1}`}></dees-input-text>
`)}
</dees-form>
`,
menuOptions: [{
name: 'Cancel',
action: async (modal) => modal!.destroy()
}, {
name: 'Submit',
action: async (modal) => modal!.destroy()
}],
});
}}>Tall Form</dees-button>
</div>
</div>
</div>
`

View File

@@ -271,13 +271,6 @@ export class DeesModal extends DeesElement {
font-size: 14px;
}
.content {
overflow-y: auto;
overflow-x: hidden;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: var(--dees-color-scrollbar-thumb) transparent;
}
.bottomButtons {
display: flex;
flex-direction: row;