Skip to content

Commit 165aeea

Browse files
committed
feat(async-stack): enhanceDialog helper
1 parent 4d0fa85 commit 165aeea

6 files changed

Lines changed: 179 additions & 128 deletions

File tree

.changeset/fluffy-gifts-love.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@svelte-put/async-stack': minor
3+
---
4+
5+
export an `enhanceDialog` helper for convenient usage with `HTMLDialogElement`

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"stylelint-config-clean-order": "^6.1.0",
4343
"stylelint-config-html": "^1.1.0",
4444
"stylelint-config-standard": "^36.0.1",
45-
"svelte": "5.6.2",
45+
"svelte": "5.37.1",
4646
"svelte-check": "^4.1.1",
4747
"tslib": "^2.8.1",
4848
"turbo": "^2.3.3",

packages/async-stack/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
".": {
1111
"types": "./types/index.d.ts",
1212
"import": "./src/index.js"
13+
},
14+
"./helpers": {
15+
"types": "./types/index.d.ts",
16+
"import": "./src/helpers/index.js"
1317
}
1418
},
1519
"publishConfig": {
@@ -62,7 +66,7 @@
6266
"@internals/tsconfig": "workspace:*"
6367
},
6468
"peerDependencies": {
65-
"svelte": "^5.1.0"
69+
"svelte": "^5.29.0"
6670
},
6771
"volta": {
6872
"extends": "../../package.json"
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { createAttachmentKey } from 'svelte/attachments';
2+
3+
/**
4+
* @template Resolved
5+
* @typedef EnhanceDialogOptions
6+
* @property {Resolved} [defaultReturnValue] default return value when dialog is closed, helpful to capture method="dialog" form / inputs without the need for additional JavaScript
7+
* @property {'animationend'} [delayResolution] delay resolution, helpful for exit animation and such
8+
*/
9+
10+
/**
11+
* @typedef EnhanceDialogAttributes
12+
* @property {(e: CustomEvent<void>) => void} [onclickbackdrop] fired when clicked on backdrop
13+
*/
14+
15+
/**
16+
* enhance an `HTMLDialogElement` when used as component for `StackItem`. This will:
17+
* 1. call `showModal()` on the dialog is mounted,
18+
* 2. capture `form.returnValue` when integrated with method="dialog" form / inputs,
19+
* 3. close the dialog when backdrop is clicked.
20+
* @template Resolved
21+
* @param {import('../stack-item.svelte').StackItem<import('svelte').Component<import('../types.public').StackItemProps<Resolved>>, Resolved>} item
22+
* @param {EnhanceDialogOptions<Resolved>} [options]
23+
* @returns {import('svelte/elements').HTMLDialogAttributes & EnhanceDialogAttributes}
24+
*/
25+
export function enhanceDialog(item, options) {
26+
/** @type {undefined | (() => void)} */
27+
let resumeResolution = undefined;
28+
if (options?.delayResolution) {
29+
item.onResolve(() => new Promise((resolve) => (resumeResolution = resolve)));
30+
}
31+
32+
return {
33+
/** @param {Event} event */
34+
onanimationend(event) {
35+
const dialog = /** @type {HTMLDialogElement} */ (event.target);
36+
if (dialog.open) return;
37+
if (options?.delayResolution === 'animationend') {
38+
resumeResolution?.();
39+
}
40+
},
41+
42+
/** @param {Event} event */
43+
onclose: function (event) {
44+
const dialog = /** @type {HTMLDialogElement} */ (event.target);
45+
// if dialog is setup with "method=dialog" form / inputs
46+
// this will help capture without the need for JavaScript
47+
item.resolve(
48+
/** @type {Resolved} */ (dialog.returnValue) || options?.defaultReturnValue || undefined,
49+
);
50+
},
51+
52+
/**
53+
* @param {HTMLDialogElement} element
54+
*/
55+
[createAttachmentKey()]: function (element) {
56+
const dialog = /** @type {HTMLDialogElement} */ (element);
57+
dialog.showModal();
58+
},
59+
60+
onclickbackdrop,
61+
onclick,
62+
};
63+
}
64+
65+
/**
66+
* @param {MouseEvent} event
67+
*/
68+
function onclick(event) {
69+
const dialog = /** @type {HTMLDialogElement} */ (event.currentTarget);
70+
const rect = /** @type {HTMLDialogElement} */ (event.target).getBoundingClientRect();
71+
if (!event.clientX || !event.clientY) return; // not a mouse event (probably triggered by keyboard)
72+
if (
73+
rect.left > event.clientX ||
74+
rect.right < event.clientX ||
75+
rect.top > event.clientY ||
76+
rect.bottom < event.clientY
77+
) {
78+
dialog.dispatchEvent(new CustomEvent('clickbackdrop'));
79+
}
80+
}
81+
82+
/**
83+
* @param {CustomEvent} event
84+
*/
85+
function onclickbackdrop(event) {
86+
const dialog = /** @type {HTMLDialogElement} */ (event.currentTarget);
87+
dialog.close();
88+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './enhance-dialog.js';

0 commit comments

Comments
 (0)