Skip to content

Commit 43cebcb

Browse files
authored
Merge pull request #212 from spalmer25/ot_tip-container-vertical-positioning
Ot_tip improve container vertical positioning
2 parents fb94ecd + d4ce8fd commit 43cebcb

File tree

2 files changed

+98
-64
lines changed

2 files changed

+98
-64
lines changed

src/widgets/ot_tip.eliom

Lines changed: 84 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -27,88 +27,109 @@ open Js_of_ocaml]
2727

2828
let%client display ?(container_a = [a_class ["ot-tip-container"]])
2929
?(filter_a = [a_class ["ot-tip-filter"]])
30-
?(side : [`Center | `Left | `Right] = `Center)
30+
?(position :
31+
[`Forced_top | `Top | `Ratio of float | `Bottom | `Forced_bottom] =
32+
`Ratio 0.5) ?(side : [`Center | `Left | `Right] = `Center)
3133
~(origin : Dom_html.element Js.t) ?(onopen = fun _ _ -> ())
3234
?(onclose = fun _ _ -> ())
3335
~(content :
3436
(unit -> unit)
3537
-> [< Html_types.div_content_fun > `Div] Eliom_content.Html.elt list) ()
3638
=
3739
let close = ref @@ fun () -> () in
38-
let b = origin in
3940
let container =
4041
D.div ~a:container_a
4142
@@ (div ~a:[a_class ["ot-tip-src"]] [] :: content (fun () -> !close ()))
4243
in
43-
let m = To_dom.of_element container in
44-
let mb = b##getBoundingClientRect in
45-
let w = mb##.right -. mb##.left in
46-
let bb = b##getBoundingClientRect in
47-
let top = int_of_float bb##.top in
48-
let bottom =
49-
Dom_html.document##.documentElement##.clientHeight
50-
- int_of_float bb##.bottom
44+
let container_elt = To_dom.of_element container in
45+
let d_height =
46+
float
47+
@@ Js.Optdef.get Dom_html.window##.innerHeight
48+
@@ fun () -> Dom_html.document##.documentElement##.clientHeight
5149
in
52-
let left = int_of_float bb##.left in
53-
let right =
54-
Dom_html.document##.documentElement##.clientWidth - int_of_float bb##.right
50+
let d_width =
51+
float
52+
@@ Js.Optdef.get Dom_html.window##.innerWidth
53+
@@ fun () -> Dom_html.document##.documentElement##.clientWidth
5554
in
55+
let o_bounds = origin##getBoundingClientRect in
56+
let o_left = o_bounds##.left in
57+
let o_right = o_bounds##.right in
58+
let o_to_right = d_width -. o_right in
59+
let o_top = o_bounds##.top in
60+
let o_to_top = d_height -. o_top in
61+
let o_bottom = o_bounds##.bottom in
62+
let o_to_bottom = d_height -. o_bottom in
63+
let o_width = o_right -. o_left in
64+
let o_center_to_left = (o_right +. o_left) /. 2. in
65+
let o_center_to_right = d_width -. o_center_to_left in
66+
let container_ready = Ot_nodeready.nodeready container_elt in
67+
let when_container_ready get_from_container use_it =
68+
Lwt.(async @@ fun () -> container_ready >|= get_from_container >|= use_it)
69+
in
70+
let get_c_height () = float container_elt##.offsetHeight in
71+
let get_half_c_width () = float (container_elt##.offsetWidth / 2) in
72+
let c_style = container_elt##.style in
5673
let print_px x = Js.string (Printf.sprintf "%gpx" x) in
57-
m##.style##.minWidth := print_px w;
58-
(if top < bottom
59-
then (
60-
let top = print_px bb##.bottom in
61-
m##.style##.top := top;
62-
m##.classList##add (Js.string "ot-tip-top"))
63-
else
64-
let bottom =
65-
print_px
66-
(float_of_int Dom_html.document##.documentElement##.clientHeight
67-
-. bb##.top)
68-
in
69-
m##.style##.bottom := bottom;
70-
m##.classList##add (Js.string "ot-tip-bottom"));
74+
let c_add_class class_ = Manip.Class.add container class_ in
75+
c_style##.minWidth := print_px o_width;
76+
let put_on_top () =
77+
c_style##.top := print_px 0.;
78+
c_add_class "ot-tip-bottom"
79+
in
80+
let put_c_below_o () =
81+
c_style##.top := print_px o_bottom;
82+
c_add_class "ot-tip-top"
83+
in
84+
let put_c_above_o () =
85+
c_style##.bottom := print_px o_to_top;
86+
c_add_class "ot-tip-bottom"
87+
in
88+
let when_container_ready_and_in f =
89+
when_container_ready get_c_height @@ fun c_height ->
90+
let enough_space_below_o = c_height < o_to_bottom in
91+
let enough_space_above_o = c_height < o_top in
92+
match enough_space_below_o, enough_space_above_o with
93+
| false, false -> put_on_top ()
94+
| false, true -> put_c_above_o ()
95+
| true, false -> put_c_below_o ()
96+
| true, true -> f ()
97+
in
98+
(match position with
99+
| `Forced_top -> put_c_above_o ()
100+
| `Top -> when_container_ready_and_in put_c_above_o
101+
| `Ratio r ->
102+
when_container_ready_and_in (fun () ->
103+
if (1. -. r) *. o_top < r *. o_to_bottom
104+
then put_c_below_o ()
105+
else put_c_above_o ())
106+
| `Bottom -> when_container_ready_and_in put_c_below_o
107+
| `Forced_bottom -> put_c_below_o ());
71108
(match side with
72109
| `Left ->
73-
let right =
74-
float_of_int Dom_html.document##.documentElement##.clientWidth
75-
-. bb##.right
76-
in
77-
m##.style##.right := print_px right;
78-
Manip.Class.add container "ot-tip-left"
110+
c_style##.right := print_px o_to_right;
111+
c_add_class "ot-tip-left"
79112
| `Right ->
80-
let left = bb##.left in
81-
m##.style##.left := print_px left;
82-
Manip.Class.add container "ot-tip-right"
113+
c_style##.left := print_px o_left;
114+
c_add_class "ot-tip-right"
83115
| `Center ->
84-
if right < left
116+
if o_to_right < o_left
85117
then (
86-
let right =
87-
float_of_int Dom_html.document##.documentElement##.clientWidth
88-
-. ((bb##.right +. bb##.left) /. 2.)
89-
in
90-
m##.style##.right := print_px right;
91-
Lwt.async @@ fun () ->
92-
let%lwt () = Ot_nodeready.nodeready m in
93-
let off = float (m##.offsetWidth / 2) in
94-
if off <= right -. 1.
95-
then (
96-
m##.style##.right := print_px (right -. off);
97-
Manip.Class.add container "ot-tip-center")
98-
else Manip.Class.add container "ot-tip-left";
99-
Lwt.return_unit)
100-
else
101-
let left = (bb##.right +. bb##.left) /. 2. in
102-
m##.style##.left := print_px left;
103-
Lwt.async @@ fun () ->
104-
let%lwt () = Ot_nodeready.nodeready m in
105-
let off = float (m##.offsetWidth / 2) in
106-
if off <= left -. 1.
107-
then (
108-
m##.style##.left := print_px (left -. off);
109-
Manip.Class.add container "ot-tip-center")
110-
else Manip.Class.add container "ot-tip-right";
111-
Lwt.return_unit);
118+
c_style##.right := print_px o_center_to_right;
119+
when_container_ready get_half_c_width (fun half_c_width ->
120+
if half_c_width <= o_center_to_right -. 1.
121+
then (
122+
c_style##.right := print_px (o_center_to_right -. half_c_width);
123+
c_add_class "ot-tip-center")
124+
else c_add_class "ot-tip-left"))
125+
else (
126+
c_style##.left := print_px o_center_to_left;
127+
when_container_ready get_half_c_width (fun half_c_width ->
128+
if half_c_width <= o_center_to_left -. 1.
129+
then (
130+
c_style##.left := print_px (o_center_to_left -. half_c_width);
131+
c_add_class "ot-tip-center")
132+
else c_add_class "ot-tip-right")));
112133
let filter =
113134
D.div ~a:(a_onclick (fun _ -> !close ()) :: filter_a) [container]
114135
in

src/widgets/ot_tip.eliomi

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,19 @@ open Js_of_ocaml
3939
[menu_a]: menu attributes default is [ [ a_class ["ot-drp-menu"] ] ]
4040
and will be overriden if you provide this argument.
4141
42-
[side]: specify how the tip whould be positioned with respect to
42+
[position]: specify how the tip whould be positioned horizontally with respect
43+
to the [origin] element. By default, the tip is above the [origin] element
44+
when there is more space above than below the [origin] element and vice versa.
45+
When position is [`Forced_top] or [`Forced_bottom], the tip is always
46+
above (resp. the below) the [origin] element.
47+
When position is [`Top] or [`Bottom], the tip is above (resp. the below)
48+
the [origin] element unless the tip is off the screen and in this case
49+
the tip will be below (resp. the above) the [origin] element.
50+
When position is [`Ratio r], the tip is below the [origin] element if
51+
the [origin] element is on the top [r] part of the screen otherwise the tip
52+
will be above the [origin] element.
53+
54+
[side]: specify how the tip whould be positioned vertically with respect to
4355
the [origin] element. By default, the tip is centered; if it would
4456
not fit on screen, its right hand side or left hand side is aligned
4557
with the middle of the [origin] element. When side is [`Left] or
@@ -63,6 +75,7 @@ val display
6375
-> ?filter_a:
6476
[< Html_types.div_attrib > `Class `OnClick] Eliom_content.Html.attrib
6577
list
78+
-> ?position:[`Forced_top | `Top | `Ratio of float | `Bottom | `Forced_bottom]
6679
-> ?side:[`Left | `Right | `Center]
6780
-> origin:Dom_html.element Js.t
6881
-> ?onopen:

0 commit comments

Comments
 (0)