Skip to content

Commit a747eb2

Browse files
committed
lets go
1 parent 8988a65 commit a747eb2

20 files changed

+475
-403
lines changed

packages/floating-ui-svelte/src/components/floating-arrow.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
8080
// https://github.com/floating-ui/floating-ui/issues/2932
8181
watch(
82-
() => context.elements.floating,
82+
() => context.floating,
8383
(floatingEl) => {
8484
if (!floatingEl) return;
8585
if (getComputedStyle(floatingEl).direction === "rtl") {
@@ -149,9 +149,7 @@
149149
});
150150
</script>
151151

152-
<!-- FIXME: extend styleObjectToString type to accept `rest.styles` -->
153-
154-
{#if context.elements.floating}
152+
{#if context.floating}
155153
<svg
156154
bind:this={ref}
157155
{...rest}

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

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { isHTMLElement } from "@floating-ui/utils/dom";
2-
import { isMouseLikePointerType } from "../internal/dom.js";
2+
import { isMouseLikePointerType, isPointerType } from "../internal/dom.js";
33
import { isTypeableElement } from "../internal/is-typeable-element.js";
44
import type { FloatingContext } from "./use-floating.svelte.js";
55
import type { MaybeGetter, ReferenceType } from "../types.js";
66
import { extract } from "../internal/extract.js";
7+
import type { ElementProps } from "./use-interactions.svelte.js";
78

89
interface UseClickOptions {
910
/**
@@ -43,6 +44,14 @@ interface UseClickOptions {
4344
* @default true
4445
*/
4546
keyboardHandlers?: MaybeGetter<boolean>;
47+
48+
/**
49+
* If already open from another event such as the `useHover()` Hook,
50+
* determines whether to keep the floating element open when clicking the
51+
* reference element for the first time.
52+
* @default true
53+
*/
54+
stickIfOpen?: boolean;
4655
}
4756

4857
function isButtonTarget(event: KeyboardEvent) {
@@ -53,34 +62,45 @@ function isSpaceIgnored(element: ReferenceType | null) {
5362
return isTypeableElement(element);
5463
}
5564

56-
class ClickInteraction {
65+
const pointerTypes = ["mouse", "pen", "touch"] as const;
66+
67+
type PointerType = (typeof pointerTypes)[number];
68+
69+
class ClickInteraction implements ElementProps {
5770
#enabled = $derived.by(() => extract(this.options?.enabled, true));
5871
#eventOption = $derived.by(() => extract(this.options?.event, "click"));
5972
#toggle = $derived.by(() => extract(this.options?.toggle, true));
6073
#ignoreMouse = $derived.by(() => extract(this.options?.ignoreMouse, false));
74+
#stickIfOpen = $derived.by(() => extract(this.options?.stickIfOpen, true));
6175
#keyboardHandlers = $derived.by(() =>
6276
extract(this.options?.keyboardHandlers, true),
6377
);
64-
#pointerType: PointerEvent["pointerType"] | undefined = undefined;
78+
#pointerType: PointerType | undefined = undefined;
6579
#didKeyDown = false;
6680

6781
constructor(
6882
private readonly context: FloatingContext,
6983
private readonly options: UseClickOptions = {},
7084
) {}
7185

86+
#onpointerdown = (event: PointerEvent) => {
87+
if (!isPointerType(event.pointerType)) return;
88+
this.#pointerType = event.pointerType;
89+
};
90+
7291
#onmousedown = (event: MouseEvent) => {
92+
// Ignore all buttons except for the "main" button.
93+
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
7394
if (event.button !== 0) return;
95+
if (this.#eventOption === "click") return;
7496
if (isMouseLikePointerType(this.#pointerType, true) && this.#ignoreMouse) {
7597
return;
7698
}
7799

78-
if (this.#eventOption === "click") return;
79-
80100
if (
81101
this.context.open &&
82102
this.#toggle &&
83-
(this.context.data.openEvent
103+
(this.context.data.openEvent && this.#stickIfOpen
84104
? this.context.data.openEvent.type === "mousedown"
85105
: true)
86106
) {
@@ -92,10 +112,6 @@ class ClickInteraction {
92112
}
93113
};
94114

95-
#onpointerdown = (event: PointerEvent) => {
96-
this.#pointerType = event.pointerType;
97-
};
98-
99115
#onclick = (event: MouseEvent) => {
100116
if (this.#eventOption === "mousedown" && this.#pointerType) {
101117
this.#pointerType = undefined;
@@ -109,7 +125,7 @@ class ClickInteraction {
109125
if (
110126
this.context.open &&
111127
this.#toggle &&
112-
(this.context.data.openEvent
128+
(this.context.data.openEvent && this.#stickIfOpen
113129
? this.context.data.openEvent.type === "click"
114130
: true)
115131
) {
@@ -165,7 +181,11 @@ class ClickInteraction {
165181
}
166182
};
167183

168-
readonly reference = $derived.by(() => {
184+
get reference() {
185+
return this.#reference;
186+
}
187+
188+
#reference = $derived.by(() => {
169189
if (!this.#enabled) return {};
170190
return {
171191
onpointerdown: this.#onpointerdown,
@@ -175,10 +195,6 @@ class ClickInteraction {
175195
onkeyup: this.#onkeyup,
176196
};
177197
});
178-
179-
get enabled() {
180-
return this.#enabled;
181-
}
182198
}
183199

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

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import { getChildren } from "../internal/get-children.js";
2121
import { on } from "svelte/events";
2222
import { executeCallbacks } from "../internal/execute-callbacks.js";
2323
import { extract } from "../internal/extract.js";
24+
import { watch } from "../internal/watch.svelte.js";
25+
import type { ElementProps } from "./use-interactions.svelte.js";
2426

2527
const bubbleHandlerKeys = {
2628
pointerdown: "onpointerdown",
@@ -119,7 +121,7 @@ interface UseDismissOptions {
119121
>;
120122
}
121123

122-
class DismissInteraction {
124+
class DismissInteraction implements ElementProps {
123125
#enabled = $derived.by(() => extract(this.options.enabled, true));
124126
#escapeKey = $derived.by(() => extract(this.options.escapeKey, true));
125127
#unstable_outsidePress = $derived.by(() => this.options.outsidePress ?? true);
@@ -137,7 +139,6 @@ class DismissInteraction {
137139
);
138140
#bubbles = $derived.by(() => extract(this.options.bubbles));
139141
#capture = $derived.by(() => extract(this.options.capture));
140-
141142
#outsidePressFn = $derived.by(() =>
142143
typeof this.#unstable_outsidePress === "function"
143144
? this.#unstable_outsidePress
@@ -264,8 +265,7 @@ class DismissInteraction {
264265
};
265266
});
266267

267-
$effect(() => {
268-
[this.#outsidePress, this.#outsidePressEvent];
268+
watch([() => this.#outsidePress, () => this.#outsidePressEvent], () => {
269269
this.#insideTree = false;
270270
});
271271
}
@@ -454,7 +454,7 @@ class DismissInteraction {
454454
getTarget(event)?.addEventListener(this.#outsidePressEvent, callback);
455455
}
456456

457-
readonly reference = $derived.by(() => {
457+
#reference = $derived.by(() => {
458458
if (!this.#enabled) return {};
459459
return {
460460
onkeydown: this.#closeOnEscapeKeyDown,
@@ -471,7 +471,7 @@ class DismissInteraction {
471471
};
472472
});
473473

474-
readonly floating = $derived.by(() => {
474+
#floating = $derived.by(() => {
475475
if (!this.#enabled) return {};
476476
return {
477477
onkeydown: this.#closeOnEscapeKeyDown,
@@ -487,8 +487,12 @@ class DismissInteraction {
487487
};
488488
});
489489

490-
get enabled() {
491-
return this.#enabled;
490+
get reference() {
491+
return this.#reference;
492+
}
493+
494+
get floating() {
495+
return this.#floating;
492496
}
493497
}
494498

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,6 @@ class FloatingRootContextOptions {
7171
this.reference.current = this.referenceProp;
7272
this.floating.current = this.floatingProp;
7373

74-
$effect.pre(() => {
75-
console.log("REFERENCE IN FLOATING ROOT", this.reference.current);
76-
console.log("REFERENCE PROP IN FLOATING ROOT", this.referenceProp);
77-
});
78-
7974
$effect.pre(() => {
8075
this.reference.current = this.referenceProp;
8176
});

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

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ interface UseFloatingOptions<RT extends ReferenceType = ReferenceType> {
9696
nodeId?: MaybeGetter<string>;
9797
}
9898

99+
/**
100+
* Reactive options for the `useFloating` hook.
101+
*/
102+
// This enables us to not have to pass around a bunch of getters and setters internally.
99103
class FloatingOptions<RT extends ReferenceType = ReferenceType> {
100104
open: ReadableBox<boolean>;
101105
placement: ReadableBox<Placement>;
@@ -155,21 +159,16 @@ class FloatingOptions<RT extends ReferenceType = ReferenceType> {
155159
this.reference.current = this.referenceProp;
156160
this.floating.current = this.floatingProp;
157161

158-
$effect(() => {
159-
console.log("REFERENCE IN FLOATING OPTIONS", this.reference.current);
160-
console.log("REFERENCE PROP IN FLOATING OPTIONS", this.referenceProp);
161-
});
162-
163-
$effect(() => {
164-
this.floating.current = this.floatingProp;
165-
});
166-
167-
$effect(() => {
168-
this.reference.current = this.referenceProp;
162+
$effect.pre(() => {
163+
if (this.floatingProp) {
164+
this.floating.current = this.floatingProp;
165+
}
169166
});
170167

171-
$effect(() => {
172-
console.log("OPEN STATE:", this.open.current);
168+
$effect.pre(() => {
169+
if (this.referenceProp) {
170+
this.reference.current = this.referenceProp;
171+
}
173172
});
174173
}
175174
}
@@ -288,11 +287,10 @@ class FloatingState<RT extends ReferenceType = ReferenceType> {
288287
#rootContext: FloatingRootContext<RT>;
289288
#position: PositionState<RT>;
290289
#positionReference = $state<ReferenceType | null>(null);
291-
#_domReference = $state<NarrowedElement<RT> | null>(null);
292290
#derivedDomReference = $derived.by(
293291
() =>
294292
(this.#rootContext.domReference ||
295-
this.#_domReference) as NarrowedElement<RT>,
293+
this.options.reference.current) as NarrowedElement<RT>,
296294
);
297295
#tree: FloatingTreeType<RT> | null;
298296
context: FloatingContext<RT>;
@@ -302,6 +300,7 @@ class FloatingState<RT extends ReferenceType = ReferenceType> {
302300
open: () => options.open.current ?? true,
303301
reference: () => options.reference.current,
304302
floating: () => options.floating.current,
303+
onOpenChange: options.onOpenChange,
305304
});
306305

307306
this.#rootContext =
@@ -342,7 +341,6 @@ class FloatingState<RT extends ReferenceType = ReferenceType> {
342341
}
343342

344343
set reference(node: RT | null) {
345-
console.log("setting reference!", node);
346344
if (isElement(node) || node === null) {
347345
this.options.reference.current = node;
348346
}

0 commit comments

Comments
 (0)