Skip to content

Commit 45979d4

Browse files
committed
click dismiss focus progress
1 parent ddfd345 commit 45979d4

File tree

9 files changed

+280
-316
lines changed

9 files changed

+280
-316
lines changed

packages/floating-ui-svelte/src/hooks/use-click.svelte.ts

Lines changed: 106 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -66,117 +66,115 @@ class ClickState {
6666
private readonly options: UseClickOptions = {},
6767
) {}
6868

69-
get reference() {
70-
if (!this.#enabled) {
71-
return {};
69+
#onmousedown = (event: MouseEvent) => {
70+
if (event.button !== 0) return;
71+
if (isMouseLikePointerType(this.#pointerType, true) && this.#ignoreMouse) {
72+
return;
7273
}
74+
75+
if (this.#eventOption === "click") return;
76+
77+
if (
78+
this.context.open &&
79+
this.#toggle &&
80+
(this.context.data.openEvent
81+
? this.context.data.openEvent.type === "mousedown"
82+
: true)
83+
) {
84+
this.context.onOpenChange(false, event, "click");
85+
} else {
86+
// Prevent stealing focus from the floating element
87+
event.preventDefault();
88+
this.context.onOpenChange(true, event, "click");
89+
}
90+
};
91+
92+
#onpointerdown = (event: PointerEvent) => {
93+
this.#pointerType = event.pointerType;
94+
};
95+
96+
#onclick = (event: MouseEvent) => {
97+
if (this.#eventOption === "mousedown" && this.#pointerType) {
98+
this.#pointerType = undefined;
99+
return;
100+
}
101+
102+
if (isMouseLikePointerType(this.#pointerType, true) && this.#ignoreMouse) {
103+
return;
104+
}
105+
106+
if (
107+
this.context.open &&
108+
this.#toggle &&
109+
(this.context.data.openEvent
110+
? this.context.data.openEvent.type === "click"
111+
: true)
112+
) {
113+
this.context.onOpenChange(false, event, "click");
114+
} else {
115+
this.context.onOpenChange(true, event, "click");
116+
}
117+
};
118+
119+
#onkeydown = (event: KeyboardEvent) => {
120+
this.#pointerType = undefined;
121+
122+
if (
123+
event.defaultPrevented ||
124+
!this.#keyboardHandlers ||
125+
isButtonTarget(event)
126+
) {
127+
return;
128+
}
129+
130+
if (
131+
event.key === " " &&
132+
!isSpaceIgnored(this.context.elements.domReference)
133+
) {
134+
// Prevent scrolling
135+
event.preventDefault();
136+
this.#didKeyDown = true;
137+
}
138+
139+
if (event.key === "Enter") {
140+
if (this.context.open && this.#toggle) {
141+
this.context.onOpenChange(false, event, "click");
142+
} else {
143+
this.context.onOpenChange(true, event, "click");
144+
}
145+
}
146+
};
147+
148+
#onkeyup = (event: KeyboardEvent) => {
149+
if (
150+
event.defaultPrevented ||
151+
!this.#keyboardHandlers ||
152+
isButtonTarget(event) ||
153+
isSpaceIgnored(this.context.elements.domReference)
154+
) {
155+
return;
156+
}
157+
158+
if (event.key === " " && this.#didKeyDown) {
159+
this.#didKeyDown = false;
160+
if (this.context.open && this.#toggle) {
161+
this.context.onOpenChange(false, event, "click");
162+
} else {
163+
this.context.onOpenChange(true, event, "click");
164+
}
165+
}
166+
};
167+
168+
readonly reference = $derived.by(() => {
169+
if (!this.#enabled) return {};
73170
return {
74-
onpointerdown: (event: PointerEvent) => {
75-
this.#pointerType = event.pointerType;
76-
},
77-
onmousedown: (event: MouseEvent) => {
78-
if (event.button !== 0) {
79-
return;
80-
}
81-
82-
if (
83-
isMouseLikePointerType(this.#pointerType, true) &&
84-
this.#ignoreMouse
85-
) {
86-
return;
87-
}
88-
89-
if (this.#eventOption === "click") {
90-
return;
91-
}
92-
93-
if (
94-
this.context.open &&
95-
this.#toggle &&
96-
(this.context.data.openEvent
97-
? this.context.data.openEvent.type === "mousedown"
98-
: true)
99-
) {
100-
this.context.onOpenChange(false, event, "click");
101-
} else {
102-
// Prevent stealing focus from the floating element
103-
event.preventDefault();
104-
this.context.onOpenChange(true, event, "click");
105-
}
106-
},
107-
onclick: (event: MouseEvent) => {
108-
if (this.#eventOption === "mousedown" && this.#pointerType) {
109-
this.#pointerType = undefined;
110-
return;
111-
}
112-
113-
if (
114-
isMouseLikePointerType(this.#pointerType, true) &&
115-
this.#ignoreMouse
116-
) {
117-
return;
118-
}
119-
120-
if (
121-
this.context.open &&
122-
this.#toggle &&
123-
(this.context.data.openEvent
124-
? this.context.data.openEvent.type === "click"
125-
: true)
126-
) {
127-
this.context.onOpenChange(false, event, "click");
128-
} else {
129-
this.context.onOpenChange(true, event, "click");
130-
}
131-
},
132-
onkeydown: (event: KeyboardEvent) => {
133-
this.#pointerType = undefined;
134-
135-
if (
136-
event.defaultPrevented ||
137-
!this.#keyboardHandlers ||
138-
isButtonTarget(event)
139-
) {
140-
return;
141-
}
142-
if (
143-
event.key === " " &&
144-
!isSpaceIgnored(this.context.elements.reference)
145-
) {
146-
// Prevent scrolling
147-
event.preventDefault();
148-
this.#didKeyDown = true;
149-
}
150-
151-
if (event.key === "Enter") {
152-
if (this.context.open && this.#toggle) {
153-
this.context.onOpenChange(false, event, "click");
154-
} else {
155-
this.context.onOpenChange(true, event, "click");
156-
}
157-
}
158-
},
159-
onkeyup: (event: KeyboardEvent) => {
160-
if (
161-
event.defaultPrevented ||
162-
!this.#keyboardHandlers ||
163-
isButtonTarget(event) ||
164-
isSpaceIgnored(this.context.elements.reference)
165-
) {
166-
return;
167-
}
168-
169-
if (event.key === " " && this.#didKeyDown) {
170-
this.#didKeyDown = false;
171-
if (this.context.open && this.#toggle) {
172-
this.context.onOpenChange(false, event, "click");
173-
} else {
174-
this.context.onOpenChange(true, event, "click");
175-
}
176-
}
177-
},
171+
onpointerdown: this.#onpointerdown,
172+
onmousedown: this.#onmousedown,
173+
onclick: this.#onclick,
174+
onkeydown: this.#onkeydown,
175+
onkeyup: this.#onkeyup,
178176
};
179-
}
177+
});
180178
}
181179

182180
function useClick(context: FloatingContext, options: UseClickOptions = {}) {

packages/floating-ui-svelte/src/hooks/use-dismiss.svelte.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { FloatingTreeType } from "../types.js";
1919
import { useFloatingTree } from "../components/floating-tree/hooks.svelte.js";
2020
import { getChildren } from "../internal/get-children.js";
2121
import { on } from "svelte/events";
22+
import { executeCallbacks } from "../internal/execute-callbacks.js";
2223

2324
const bubbleHandlerKeys = {
2425
pointerdown: "onpointerdown",
@@ -251,9 +252,7 @@ class DismissState {
251252
}
252253

253254
return () => {
254-
for (const listener of listenersToRemove) {
255-
listener();
256-
}
255+
executeCallbacks(...listenersToRemove);
257256
window.clearTimeout(compositionTimeout);
258257
};
259258
});
@@ -448,7 +447,7 @@ class DismissState {
448447
getTarget(event)?.addEventListener(this.#outsidePressEvent, callback);
449448
}
450449

451-
reference = $derived.by(() => {
450+
readonly reference = $derived.by(() => {
452451
if (!this.#enabled) return {};
453452
return {
454453
onkeydown: this.#closeOnEscapeKeyDown,
@@ -465,7 +464,7 @@ class DismissState {
465464
};
466465
});
467466

468-
floating = $derived.by(() => {
467+
readonly floating = $derived.by(() => {
469468
if (!this.#enabled) return {};
470469
return {
471470
onkeydown: this.#closeOnEscapeKeyDown,

packages/floating-ui-svelte/src/hooks/use-floating-root-context.svelte.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,11 @@ class FloatingRootContext<RT extends ReferenceType = ReferenceType> {
7373
}
7474
}
7575
this.#positionReference = options.elements.reference;
76+
this.onOpenChange = this.onOpenChange.bind(this);
77+
this.setPositionReference = this.setPositionReference.bind(this);
7678
}
7779

78-
onOpenChange: OnOpenChange = (open, event, reason) => {
80+
onOpenChange(open: boolean, event?: Event, reason?: OpenChangeReason) {
7981
this.data.openEvent = open ? event : undefined;
8082
this.events.emit("openchange", {
8183
open,
@@ -84,11 +86,11 @@ class FloatingRootContext<RT extends ReferenceType = ReferenceType> {
8486
nested: this.#nested,
8587
});
8688
this.options.onOpenChange?.(open, event, reason);
87-
};
89+
}
8890

89-
setPositionReference = (node: ReferenceElement | null) => {
91+
setPositionReference(node: ReferenceElement | null) {
9092
this.#positionReference = node;
91-
};
93+
}
9294

9395
get elements() {
9496
const _this = this;

packages/floating-ui-svelte/src/hooks/use-floating.svelte.ts

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,11 @@ import type {
77
OpenChangeReason,
88
ReferenceType,
99
} from "../types.js";
10-
import {
11-
FloatingRootContext,
12-
useFloatingRootContext,
13-
} from "./use-floating-root-context.svelte.js";
10+
import { FloatingRootContext } from "./use-floating-root-context.svelte.js";
1411
import {
1512
PositionState,
1613
type UsePositionOptions,
1714
} from "./use-position.svelte.js";
18-
import { untrack } from "svelte";
1915

2016
interface UseFloatingOptions<RT extends ReferenceType = ReferenceType>
2117
extends Omit<UsePositionOptions<RT>, "elements"> {
@@ -44,27 +40,6 @@ interface UseFloatingOptions<RT extends ReferenceType = ReferenceType>
4440
nodeId?: string;
4541
}
4642

47-
// interface UseFloatingReturn<RT extends ReferenceType = ReferenceType>
48-
// extends UsePositionReturn {
49-
// /**
50-
// * `FloatingContext`
51-
// */
52-
// context: FloatingContext<RT>;
53-
54-
// /**
55-
// * Set the position reference outside of the `elements`
56-
// * object.
57-
// */
58-
// refs: {
59-
// setPositionReference(node: ReferenceType | null): void;
60-
// };
61-
62-
// /**
63-
// * The floating elements.
64-
// */
65-
// elements: ExtendedElements<RT>;
66-
// }
67-
6843
type FloatingContextOptions<RT extends ReferenceType = ReferenceType> = {
6944
floating: FloatingState<RT>;
7045
floatingOptions: UseFloatingOptions<RT>;
@@ -192,9 +167,11 @@ class FloatingState<RT extends ReferenceType = ReferenceType> {
192167
node.context = this.context;
193168
}
194169
});
170+
171+
this.setPositionReference = this.setPositionReference.bind(this);
195172
}
196173

197-
setPositionReference = (node: ReferenceType | null) => {
174+
setPositionReference(node: ReferenceType | null) {
198175
const computedPositionReference = isElement(node)
199176
? {
200177
getBoundingClientRect: () => node.getBoundingClientRect(),
@@ -203,7 +180,7 @@ class FloatingState<RT extends ReferenceType = ReferenceType> {
203180
: node;
204181

205182
this.#positionReference = computedPositionReference;
206-
};
183+
}
207184

208185
get placement() {
209186
return this.#position.data.placement;

0 commit comments

Comments
 (0)