@colisweb/rescript-toolkit 2.24.0 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/ui/Toolkit__Ui.res
CHANGED
|
@@ -25,6 +25,7 @@ module Reference = Toolkit__Ui_Reference
|
|
|
25
25
|
module RichText = Toolkit__Ui_RichText
|
|
26
26
|
module ProgressBar = Toolkit__Ui_ProgressBar
|
|
27
27
|
module Dropdown = Toolkit__Ui_Dropdown
|
|
28
|
+
module DropdownList = Toolkit__Ui_DropdownList
|
|
28
29
|
module Layout = Toolkit__Ui_Layout
|
|
29
30
|
module SpinnerFullScreen = Toolkit__Ui_SpinnerFullScreen
|
|
30
31
|
module MultiSelect = Toolkit__Ui_MultiSelect
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
type position = [
|
|
2
|
+
| #bottom
|
|
3
|
+
| #top
|
|
4
|
+
]
|
|
5
|
+
|
|
6
|
+
type item = {
|
|
7
|
+
icon: React.element,
|
|
8
|
+
label: React.element,
|
|
9
|
+
onClick: unit => unit,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@react.component
|
|
13
|
+
let make = (
|
|
14
|
+
~label: React.element,
|
|
15
|
+
~dropdownClassName="",
|
|
16
|
+
~buttonClassName="",
|
|
17
|
+
~containerClassName="",
|
|
18
|
+
~defaultIsOpen=false,
|
|
19
|
+
~buttonColor: Toolkit__Ui_Button.color=#white,
|
|
20
|
+
~buttonSize: Toolkit__Ui_Button.size=#md,
|
|
21
|
+
~buttonVariant: Toolkit__Ui_Button.variant=#default,
|
|
22
|
+
~position=#bottom,
|
|
23
|
+
~items: array<item>,
|
|
24
|
+
) => {
|
|
25
|
+
let dropdownRef = React.useRef(Js.Nullable.null)
|
|
26
|
+
let buttonRef = React.useRef(Js.Nullable.null)
|
|
27
|
+
let (position, _setPosition) = React.useState(() => position)
|
|
28
|
+
let {isOpen, hide, toggle} = Toolkit__Hooks.useDisclosure(~defaultIsOpen, ())
|
|
29
|
+
let {isXs} = Toolkit__Hooks.useMediaQuery()
|
|
30
|
+
|
|
31
|
+
Toolkit__Hooks.useOnClickOutside(dropdownRef, _ => {
|
|
32
|
+
hide()
|
|
33
|
+
})
|
|
34
|
+
let (adjustmentStyle, setAdjustmentStyle) = React.useState(() => None)
|
|
35
|
+
|
|
36
|
+
React.useEffect1(() => {
|
|
37
|
+
if isOpen && !isXs {
|
|
38
|
+
Js.Global.setTimeout(() => {
|
|
39
|
+
buttonRef.current
|
|
40
|
+
->Js.Nullable.toOption
|
|
41
|
+
->Option.forEach(dom => {
|
|
42
|
+
let buttonRect = dom->Browser.DomElement.getBoundingClientRect
|
|
43
|
+
let dropdownRect =
|
|
44
|
+
dropdownRef.current
|
|
45
|
+
->Js.Nullable.toOption
|
|
46
|
+
->Option.map(Browser.DomElement.getBoundingClientRect)
|
|
47
|
+
|
|
48
|
+
let calculatedTop = switch position {
|
|
49
|
+
| #bottom => buttonRect.top +. buttonRect.height +. 10.
|
|
50
|
+
| #top => {
|
|
51
|
+
let base = buttonRect.top -. 10.
|
|
52
|
+
switch dropdownRect {
|
|
53
|
+
| None => base
|
|
54
|
+
| Some({height}) => {
|
|
55
|
+
let newBase = base -. height /. 2.
|
|
56
|
+
newBase < 0. ? buttonRect.top +. buttonRect.height +. 10. : newBase
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let calculatedLeft = {
|
|
63
|
+
let baseContainerCenter = buttonRect.left +. buttonRect.width /. 2.
|
|
64
|
+
|
|
65
|
+
switch dropdownRect {
|
|
66
|
+
| None => baseContainerCenter
|
|
67
|
+
| Some({width}) => {
|
|
68
|
+
let baseCalculated = baseContainerCenter -. width /. 2.
|
|
69
|
+
let hasOverflowLeft = baseCalculated < 0.
|
|
70
|
+
let hasOverflowRight =
|
|
71
|
+
baseContainerCenter +. width /. 2. > Browser.innerWidth->Int.toFloat
|
|
72
|
+
|
|
73
|
+
if hasOverflowLeft {
|
|
74
|
+
0.
|
|
75
|
+
} else if hasOverflowRight {
|
|
76
|
+
let tmp = baseContainerCenter +. width /. 2.
|
|
77
|
+
|
|
78
|
+
baseCalculated -. (tmp -. Browser.innerWidth->Int.toFloat)
|
|
79
|
+
} else {
|
|
80
|
+
baseCalculated
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setAdjustmentStyle(_ => Some(
|
|
87
|
+
ReactDOM.Style.make(
|
|
88
|
+
~top=`${calculatedTop->Js.Float.toString}px`,
|
|
89
|
+
~left=`${calculatedLeft->Js.Float.toString}px`,
|
|
90
|
+
~opacity="1",
|
|
91
|
+
(),
|
|
92
|
+
),
|
|
93
|
+
))
|
|
94
|
+
})
|
|
95
|
+
}, 16)->ignore
|
|
96
|
+
} else {
|
|
97
|
+
setAdjustmentStyle(_ => None)
|
|
98
|
+
}
|
|
99
|
+
None
|
|
100
|
+
}, [isOpen, isXs])
|
|
101
|
+
|
|
102
|
+
<div className={cx(["relative", containerClassName])}>
|
|
103
|
+
<Toolkit__Ui_Button
|
|
104
|
+
variant=buttonVariant
|
|
105
|
+
buttonRef={ReactDOM.Ref.domRef(buttonRef)}
|
|
106
|
+
size=buttonSize
|
|
107
|
+
type_="button"
|
|
108
|
+
color=buttonColor
|
|
109
|
+
onClick={event => {
|
|
110
|
+
switch dropdownRef.current->Js.Nullable.toOption {
|
|
111
|
+
| None => toggle()
|
|
112
|
+
| Some(domRef) => {
|
|
113
|
+
let isInDropdown = ReactEvent.Mouse.currentTarget(event)["contains"](. domRef)
|
|
114
|
+
if !isInDropdown {
|
|
115
|
+
toggle()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}}
|
|
120
|
+
className={cx([buttonClassName, "dropdown-button"])}>
|
|
121
|
+
label
|
|
122
|
+
{isOpen
|
|
123
|
+
? <ReachUi.Portal>
|
|
124
|
+
<div
|
|
125
|
+
ref={ReactDOM.Ref.domRef(dropdownRef)}
|
|
126
|
+
className={cx([
|
|
127
|
+
"dropdown",
|
|
128
|
+
"top-0 left-0 p-2 transform transition-opacity duration-150 ease-in-out shadow rounded text-base font-normal text-neutral-700",
|
|
129
|
+
isXs ? "fixed !w-full !h-full z-40" : "absolute z-20 bg-white w-60 opacity-0",
|
|
130
|
+
dropdownClassName,
|
|
131
|
+
])}
|
|
132
|
+
style={adjustmentStyle->Option.getWithDefault(ReactDOM.Style.make())}>
|
|
133
|
+
{isXs
|
|
134
|
+
? <div
|
|
135
|
+
className="bg-neutral-500 absolute left-0 top-0 w-full h-full opacity-50 z-30"
|
|
136
|
+
/>
|
|
137
|
+
: React.null}
|
|
138
|
+
<div className={cx([isXs ? "z-40 absolute left-0 bottom-0 w-full" : ""])}>
|
|
139
|
+
<div
|
|
140
|
+
className={cx([
|
|
141
|
+
"flex flex-col",
|
|
142
|
+
isXs ? "bg-white p-2 mx-2 shadow rounded-t-lg" : "",
|
|
143
|
+
])}>
|
|
144
|
+
{items
|
|
145
|
+
->Array.mapWithIndex((i, {icon, label, onClick}) => {
|
|
146
|
+
<div
|
|
147
|
+
onClick={_ => onClick()}
|
|
148
|
+
className={cx([
|
|
149
|
+
"flex flex-row gap-4 items-center py-2 hover:bg-neutral-100 focus:bg-neutral-100 cursor-pointer",
|
|
150
|
+
i == 0 ? "" : "border-t",
|
|
151
|
+
])}
|
|
152
|
+
key={i->Int.toString}>
|
|
153
|
+
{icon} {label}
|
|
154
|
+
</div>
|
|
155
|
+
})
|
|
156
|
+
->React.array}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</ReachUi.Portal>
|
|
161
|
+
: React.null}
|
|
162
|
+
</Toolkit__Ui_Button>
|
|
163
|
+
</div>
|
|
164
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type position = [
|
|
2
|
+
| #bottom
|
|
3
|
+
| #top
|
|
4
|
+
]
|
|
5
|
+
|
|
6
|
+
type item = {
|
|
7
|
+
icon: React.element,
|
|
8
|
+
label: React.element,
|
|
9
|
+
onClick: unit => unit,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@react.component
|
|
13
|
+
let make: (
|
|
14
|
+
~label: React.element,
|
|
15
|
+
~dropdownClassName: string=?,
|
|
16
|
+
~buttonClassName: string=?,
|
|
17
|
+
~containerClassName: string=?,
|
|
18
|
+
~defaultIsOpen: bool=?,
|
|
19
|
+
~buttonColor: Toolkit__Ui_Button.color=?,
|
|
20
|
+
~buttonSize: Toolkit__Ui_Button.size=?,
|
|
21
|
+
~buttonVariant: Toolkit__Ui_Button.variant=?,
|
|
22
|
+
~position: position=?,
|
|
23
|
+
~items: array<item>,
|
|
24
|
+
) => React.element
|