-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.ts
134 lines (123 loc) · 4.73 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* eslint-disable @typescript-eslint/ban-ts-comment */
//@ts-ignore
import type { ComputePositionConfig, ComputePositionReturn, FloatingElement, Middleware, Padding, ReferenceElement, VirtualElement } from "./core/index.js";
//@ts-ignore
import { arrow as arrowCore } from "./core";
import { autoUpdate as _autoUpdate, computePosition, type AutoUpdateOptions, type MiddlewareState } from "./dom/index.js";
import type { Readable, Writable } from "svelte/store";
import { get } from "svelte/store";
import { onDestroy, tick } from 'svelte';
export type ComputeConfig = Partial<ComputePositionConfig> & {
onComputed?: (computed: ComputePositionReturn) => void
/**
* false: Don't initialize autoUpdate;
* true: Standard autoUpdate values from the documentation;
* object: All as in the autoUpdate documentation. Your parameters are added to the default ones;
* @default true
*/
autoUpdate?: boolean | Partial<AutoUpdateOptions>
};
export type UpdatePosition = (contentOptions?: Omit<ComputeConfig, 'autoUpdate'>) => void;
export type ReferenceAction = (node: HTMLElement | Writable<VirtualElement> | VirtualElement) => void;
export type ContentAction = (node: HTMLElement, contentOptions?: ComputeConfig) => void;
export type ArrowOptions = { padding?: Padding, element: Writable<HTMLElement | undefined | null> };
export function createFloatingActions(initOptions?: ComputeConfig): [ReferenceAction, ContentAction, UpdatePosition] {
let referenceElement: ReferenceElement;
let floatingElement: FloatingElement;
const defaultOptions:Partial<ComputeConfig> = {
autoUpdate: true
}
let options: ComputeConfig | undefined = initOptions;
const getOptions = (mixin?:object):ComputeConfig => {
return {...defaultOptions, ...(initOptions || {}), ...(mixin || {}) }
}
const updatePosition:UpdatePosition = (updateOptions) => {
if (referenceElement && floatingElement) {
options = getOptions(updateOptions);
computePosition(referenceElement, floatingElement, options)
.then(v => {
Object.assign(floatingElement.style, {
position: v.strategy,
left: `${v.x}px`,
top: `${v.y}px`,
});
options?.onComputed && options.onComputed(v);
});
}
}
const referenceAction: ReferenceAction = node => {
if ('subscribe' in node) {
setupVirtualElementObserver(node);
return {};
} else {
referenceElement = node;
updatePosition();
}
}
const contentAction: ContentAction = (node, contentOptions) => {
let autoUpdateDestroy:ReturnType<typeof _autoUpdate> | undefined
floatingElement = node;
options = getOptions(contentOptions);
setTimeout(() => updatePosition(contentOptions), 0) //tick doesn't work
updatePosition(contentOptions)
const destroyAutoUpdate = () => {
if (autoUpdateDestroy) {
autoUpdateDestroy()
autoUpdateDestroy = undefined
}
}
const initAutoUpdate = ({autoUpdate} = options || {}):typeof autoUpdateDestroy => {
destroyAutoUpdate()
if(autoUpdate !== false) {
tick().then(() => {
return _autoUpdate(referenceElement, floatingElement, () => updatePosition(options), (autoUpdate === true ? {} : autoUpdate));
})
}
return
}
autoUpdateDestroy = initAutoUpdate()
return {
update(contentOptions:Parameters<typeof contentAction>[1]) {
updatePosition(contentOptions)
autoUpdateDestroy = initAutoUpdate(contentOptions)
},
destroy() {
destroyAutoUpdate()
}
}
}
const setupVirtualElementObserver = (node: Readable<VirtualElement>) => {
const unsubscribe = node.subscribe(($node) => {
if (referenceElement === undefined) {
referenceElement = $node;
updatePosition();
} else {
// Preserve the reference to the virtual element.
Object.assign(referenceElement, $node);
updatePosition();
}
});
onDestroy(unsubscribe);
};
return [
referenceAction,
contentAction,
updatePosition
]
}
export function arrow(options: ArrowOptions): Middleware {
return {
name: "arrow",
options,
fn(args:MiddlewareState) {
const element = get(options.element);
if (element) {
return arrowCore({
element,
padding: options.padding
}).fn(args);
}
return {};
}
}
}