Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: use stacks in useEscapeKeydown & introduce escapeBehavior prop #1142

Merged
merged 40 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift click to select a range
fd42c1b
fix(escape-keydown): using a stack for correctly handling nested elem…
anatolzak Mar 30, 2024
8e3b2a5
update docs description
anatolzak Mar 30, 2024
8042899
fix escape keydown behavior in all builders
anatolzak Mar 30, 2024
bb41131
test(escape-keydown): add tests for sibling elements with different …
anatolzak Mar 30, 2024
9a08d77
add changeset
anatolzak Mar 30, 2024
2877afe
fix(dialog): should not add dialog to escape-keydown stack until is open
anatolzak Mar 30, 2024
9ac37f5
Merge branch 'develop' into fix/escape-keydown-nested-elements
anatolzak Mar 31, 2024
c50f81c
improve type safety in tests
anatolzak Mar 31, 2024
2a31851
fix types in escape keydown tests
anatolzak Mar 31, 2024
368cace
fix menubar tests
anatolzak Mar 31, 2024
453f4a1
feat(escape-keydown): change `closeOnEscape` to `escapeBehavior` & en…
anatolzak Apr 1, 2024
a9cc1dd
add tests for new escape keydown action
anatolzak Apr 1, 2024
d16393d
docs: update escape behavior description
anatolzak Apr 1, 2024
e9f099d
update `closeOnEscape` references to `escapeBehavior`
anatolzak Apr 1, 2024
df92431
changeset
anatolzak Apr 1, 2024
afc292c
remove unnecessary changes
anatolzak Apr 1, 2024
51d8538
fix escape keydown tests
anatolzak Apr 1, 2024
bd6a3bc
Merge branch 'develop' into fix/escape-keydown-nested-elements
anatolzak Apr 1, 2024
1098c14
Merge branch 'develop' into fix/escape-keydown-nested-elements
anatolzak Apr 1, 2024
dba7c36
prettier
anatolzak Apr 1, 2024
3db9691
Merge branch 'fix/escape-keydown-nested-elements' of https://github.c…
anatolzak Apr 1, 2024
f518f38
Merge branch 'develop' into fix/escape-keydown-nested-elements
anatolzak Apr 1, 2024
f54957c
fix tests
anatolzak Apr 1, 2024
17988c8
Merge branch 'develop' into fix/escape-keydown-nested-elements
anatolzak Apr 1, 2024
73eade6
allow changing `escapeBehavior` without reseting position of element …
anatolzak Apr 2, 2024
acb267d
test: allow changing `escapeBehavior` without reseting position of el…
anatolzak Apr 2, 2024
0c1a6cd
Merge branch 'fix/escape-keydown-nested-elements' of https://github.c…
anatolzak Apr 2, 2024
7a73dc8
Merge branch 'develop' into fix/escape-keydown-nested-elements
anatolzak Apr 5, 2024
54777c8
feat: add escape behavior types: `defer-otherwise-close` and `defer-o…
anatolzak Apr 5, 2024
cbe8b33
add tests for new escape behavior types: `defer-otherwise-close` and …
anatolzak Apr 5, 2024
39e373e
docs: add descriptions for new escape behavior types: `defer-otherwis…
anatolzak Apr 5, 2024
9212f2a
change builder `escapeBehavior` JSDoc for new escape behavior types: …
anatolzak Apr 5, 2024
fac04c2
fix: landing page popover `escapeBehavior` prop value
anatolzak Apr 5, 2024
7831713
fix tests for new escapeBehavior values `defer-otherwise-close` and `…
anatolzak Apr 5, 2024
e8c0de5
fix svelte-check
anatolzak Apr 5, 2024
0cc0f25
update escape keydown tests function name
anatolzak Apr 5, 2024
4e727bb
revert dialog content actions execution order
anatolzak Apr 8, 2024
c108bb1
Merge branch 'develop' into fix/escape-keydown-nested-elements
anatolzak Apr 16, 2024
d963b4f
lint
anatolzak Apr 16, 2024
63d3110
lint
anatolzak Apr 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: add escape behavior types: defer-otherwise-close and `defer-o…
…therwise-ignore`
  • Loading branch information
anatolzak committed Apr 5, 2024
commit 54777c8149849dc33a2b039cd63e9113974c91d3
41 changes: 32 additions & 9 deletions src/lib/internal/actions/escape-keydown/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 20,28 @@ export const useEscapeKeydown = ((node, config = {}) => {
layers.set(node, behaviorType);

const onKeyDown = (e: KeyboardEvent) => {
if (e.key !== kbd.ESCAPE || !isHighestLayerEscapeKey(node)) return;
if (e.key !== kbd.ESCAPE || !isResponsibleEscapeLayer(node)) return;
const target = e.target;
if (!isHTMLElement(target)) return;

const $behaviorType = behaviorType.get();
e.preventDefault();
if ($behaviorType !== 'close' || shouldIgnoreEvent(e, options.ignore)) return;
options.handler?.(e);
if (shouldIgnoreEvent(e, options.ignore)) return;
if (shouldInvokeResponsibleLayerHandler(behaviorType.get())) {
options.handler?.(e);
}
};

unsub = executeCallbacks(
addEventListener(document, 'keydown', onKeyDown, { passive: false }),
effect(behaviorType, ($behaviorType) => {
if ($behaviorType === 'close') node.dataset.escapee = '';
else delete node.dataset.escapee;
if (
$behaviorType === 'close' ||
($behaviorType === 'defer-otherwise-close' && [...layers.keys()][0] === node)
) {
node.dataset.escapee = '';
} else {
delete node.dataset.escapee;
}
}),
behaviorType.destroy || noop
);
Expand All @@ -52,9 59,21 @@ export const useEscapeKeydown = ((node, config = {}) => {
};
}) satisfies Action<HTMLElement, EscapeKeydownConfig>;

const isHighestLayerEscapeKey = (node: HTMLElement): boolean => {
const topMostLayer = [...layers].findLast(([_, behaviorType]) => behaviorType.get() !== 'defer');
return !!topMostLayer && topMostLayer[0] === node;
const isResponsibleEscapeLayer = (node: HTMLElement): boolean => {
const layersArr = [...layers];
/**
* We first check if we can find a top layer with `close` or `ignore`.
* If that top layer was found and matches the provided node, then the node is
* responsible for the escape. Otherwise, we know that all layers defer so
* the first layer is the responsible one.
*/
const topMostLayer = layersArr.findLast(([_, behaviorType]) => {
const $behaviorType = behaviorType.get();
return $behaviorType === 'close' || $behaviorType === 'ignore';
});
if (topMostLayer) return topMostLayer[0] === node;
const [firstLayerNode] = layersArr[0];
return firstLayerNode === node;
};

const shouldIgnoreEvent = (e: KeyboardEvent, ignore: EscapeKeydownConfig['ignore']): boolean => {
Expand All @@ -65,3 84,7 @@ const shouldIgnoreEvent = (e: KeyboardEvent, ignore: EscapeKeydownConfig['ignore
}
return false;
};

const shouldInvokeResponsibleLayerHandler = (behaviorType: EscapeBehaviorType) => {
return behaviorType === 'close' || behaviorType === 'defer-otherwise-close';
};
9 changes: 7 additions & 2 deletions src/lib/internal/actions/escape-keydown/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 1,18 @@
import type { WithGet } from '$lib/internal/helpers/withGet.js';
import type { Readable } from 'svelte/store';

export type EscapeBehaviorType = 'close' | 'defer' | 'ignore';
export type EscapeBehaviorType =
| 'close'
| 'defer-otherwise-close'
| 'defer-otherwise-ignore'
| 'ignore';

export type EscapeKeydownConfig = {
/**
* Escape behavior type.
* `close`: Closes the element immediately.
* `defer`: Delegates the action to the parent floating element.
* `defer-otherwise-close`: Delegates the action to the parent floating element. If no parent is found, it closes the element.
* `defer-otherwise-ignore`: Delegates the action to the parent floating element. If no parent is found, nothing is done.
* `ignore`: Prevents the element from closing and also blocks the parent element from closing in response to the Escape key.
*
* @defaultValue `close`
Expand Down