Source file update_position.ml
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
open! Core
open Js_of_ocaml
open Bindings
module Side = struct
type t =
| Top
| Bottom
| Left
| Right
let to_string = function
| Top -> "top"
| Bottom -> "bottom"
| Left -> "left"
| Right -> "right"
;;
let of_placement = function
| Placement.Top -> Top
| Top_start -> Top
| Top_end -> Top
| Bottom -> Bottom
| Bottom_start -> Bottom
| Bottom_end -> Bottom
| Right -> Right
| Right_start -> Right
| Right_end -> Right
| Left -> Left
| Left_start -> Left
| Left_end -> Left
;;
let flip = function
| Top -> Bottom
| Bottom -> Top
| Left -> Right
| Right -> Left
;;
end
module Accessors = struct
let floating_arrow_top = "--floatingArrowTop"
let floating_arrow_left = "--floatingArrowLeft"
let floating_available_height = "--floatingAvailableHeight"
let floating_available_width = "--floatingAvailableWidth"
let data_floating_placement = "data-floating-placement"
let data_floating_arrow_placement = "data-floating-arrow-placement"
let floating_styling =
let module Style =
[%css
stylesheet
{|
@layer {
.floating {
width: max-content;
top: 0;
left: 0;
max-height: %{`Var floating_available_height#Css_gen.Length};
max-width: %{`Var floating_available_width#Css_gen.Length};
}
}
|}]
in
Style.floating
;;
let arrow_container =
[%css
{|
left: %{`Var floating_arrow_left#Css_gen.Length};
top: %{`Var floating_arrow_top#Css_gen.Length};
position: absolute;
display:flex;
align-items:center;
justify-content: center;
z-index: -1000;
&[data-floating-arrow-placement=top] {
top: 0;
transform: translateY(-50%);
}
&[data-floating-arrow-placement=bottom] {
bottom: 0;
transform: translateY(50%) rotate(180deg);
}
&[data-floating-arrow-placement=left] {
left: 0;
transform: translateX(-50%) rotate(-90deg);
}
&[data-floating-arrow-placement=right] {
right: 0;
transform: translateX(50%) rotate(90deg) ;
}
|}]
;;
end
let placement side alignment =
match side, alignment with
| Side.Top, None -> Placement.Top
| Top, Some Alignment.Start -> Top_start
| Top, Some End -> Top_end
| Bottom, None -> Bottom
| Bottom, Some Start -> Bottom_start
| Bottom, Some End -> Bottom_end
| Right, None -> Right
| Right, Some Start -> Right_start
| Right, Some End -> Right_end
| Left, None -> Left
| Left, Some Start -> Left_start
| Left, Some End -> Left_end
;;
let set_style element property value =
element##.style##setProperty (Js.string property) (Js.string value) Js.undefined
|> (ignore : Js.js_string Js.t -> unit)
;;
let remove_style element property =
element##.style##removeProperty (Js.string property)
|> (ignore : Js.js_string Js.t -> unit)
;;
let set_or_remove_style element property = function
| None -> remove_style element property
| Some value -> set_style element property value
;;
let format_px px = Virtual_dom.Dom_float.to_string_fixed 8 px ^ "px"
let single_update
~anchor
~(floating : Dom_html.element Js.t)
~arrow_selector
side
alignment
(offset : Offset.t)
strategy
=
let padding = if Float.(offset.main_axis > 0.) then Some offset.main_axis else None in
let offset_middleware =
if Float.(offset.main_axis > 0.) || Float.(offset.cross_axis <> 0.)
then [ Middleware.Offset.create offset ]
else []
in
let placement, placement_middleware =
match side with
| None ->
let auto_placement = Middleware.Auto_placement.create { alignment; padding } in
Some Placement.Top, [ auto_placement ]
| Some side ->
( Some (placement side alignment)
, [ Middleware.Flip.create { padding }
; Middleware.Shift.create
{ padding
; limiter =
Middleware.Shift.Limiter.create { main_axis = true; cross_axis = true }
}
] )
in
let arrow_element =
let%bind.Option arrow_selector = arrow_selector in
floating##querySelector (Js.string arrow_selector) |> Js.Opt.to_option
in
let arrow_middleware =
match arrow_element with
| None -> []
| Some arrow_element ->
[ Middleware.Arrow.create { element = arrow_element; padding } ]
in
let size_middleware =
[ Middleware.Size.create
{ apply =
(fun { available_height; available_width } ->
set_style
floating
Accessors.floating_available_height
(format_px available_height);
set_style
floating
Accessors.floating_available_width
(format_px available_width))
}
]
in
let middleware =
offset_middleware @ placement_middleware @ size_middleware @ arrow_middleware
in
Compute_position.create ~anchor ~floating { placement; strategy; middleware }
|> fun x ->
Compute_position.then_
x
(fun { Compute_position.Then_args.x; y; placement; middleware_data; _ } ->
let side = Side.of_placement placement in
set_style floating "top" (format_px y);
set_style floating "left" (format_px x);
floating##setAttribute
(Js.string Accessors.data_floating_placement)
(Js.string (Side.to_string side));
match middleware_data, arrow_element with
| Some { arrow = Some { x; y } }, Some arrow_element ->
set_or_remove_style
arrow_element
Accessors.floating_arrow_top
(Option.map y ~f:format_px);
set_or_remove_style
arrow_element
Accessors.floating_arrow_left
(Option.map x ~f:format_px);
arrow_element##setAttribute
(Js.string Accessors.data_floating_arrow_placement)
(Js.string (Side.to_string (Side.flip side)))
| _ -> ())
;;