@colisweb/rescript-toolkit 1.27.0 → 1.28.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
|
@@ -132,7 +132,7 @@ let useClipboard = (~onCopyNotificationMessage: option<string>=?, value: string)
|
|
|
132
132
|
let (hasCopied, setHasCopied) = React.useState(() => false)
|
|
133
133
|
let onCopy = React.useCallback1(() => {
|
|
134
134
|
onCopyNotificationMessage->Option.forEach(message =>
|
|
135
|
-
Toolkit__Ui_Snackbar.show(~title=message, ~variant=#success, ())
|
|
135
|
+
Toolkit__Ui_Snackbar.show(~title=message, ~variant=#success, ())->ignore
|
|
136
136
|
)
|
|
137
137
|
|
|
138
138
|
let didCopy = BsCopyToClipboard.copy(value)
|
|
@@ -1,30 +1,43 @@
|
|
|
1
|
-
type rec state = {
|
|
2
|
-
isVisible: bool,
|
|
3
|
-
options: option<options>,
|
|
4
|
-
}
|
|
1
|
+
type rec state = {list: array<options>}
|
|
5
2
|
and options = {
|
|
3
|
+
id: id,
|
|
4
|
+
isVisible: bool,
|
|
6
5
|
title: string,
|
|
7
6
|
description: option<React.element>,
|
|
8
7
|
closable: bool,
|
|
9
8
|
variant: variant,
|
|
10
9
|
timeout: int,
|
|
11
10
|
}
|
|
11
|
+
and id
|
|
12
12
|
and variant = [#success | #warning | #danger]
|
|
13
13
|
|
|
14
14
|
type action =
|
|
15
15
|
| Show(options)
|
|
16
|
-
| Hide
|
|
16
|
+
| Hide(id)
|
|
17
|
+
| Remove(id)
|
|
17
18
|
|
|
18
|
-
let store = Restorative.createStore({
|
|
19
|
+
let store = Restorative.createStore({list: []}, (state, action) =>
|
|
19
20
|
switch action {
|
|
20
|
-
| Show(options) => {
|
|
21
|
-
| Hide => {
|
|
21
|
+
| Show(options) => {list: state.list->Array.concat([options])}
|
|
22
|
+
| Hide(id) => {
|
|
23
|
+
list: state.list->Array.map(e =>
|
|
24
|
+
if e.id === id {
|
|
25
|
+
{...e, isVisible: false}
|
|
26
|
+
} else {
|
|
27
|
+
e
|
|
28
|
+
}
|
|
29
|
+
),
|
|
30
|
+
}
|
|
31
|
+
| Remove(id) => {list: state.list->Array.keep(e => e.id !== id)}
|
|
22
32
|
}
|
|
23
33
|
)
|
|
24
34
|
|
|
25
|
-
let show = (~title, ~description=?, ~closable=true, ~variant, ~timeout=5000, ()) =>
|
|
35
|
+
let show = (~title, ~description=?, ~closable=true, ~variant, ~timeout=5000, ()) => {
|
|
36
|
+
let id: id = Js.Math.random()->Js.Float.toString->Obj.magic
|
|
26
37
|
store.dispatch(
|
|
27
38
|
Show({
|
|
39
|
+
id: id,
|
|
40
|
+
isVisible: true,
|
|
28
41
|
title: title,
|
|
29
42
|
description: description,
|
|
30
43
|
closable: closable,
|
|
@@ -32,76 +45,95 @@ let show = (~title, ~description=?, ~closable=true, ~variant, ~timeout=5000, ())
|
|
|
32
45
|
timeout: timeout,
|
|
33
46
|
}),
|
|
34
47
|
)
|
|
35
|
-
|
|
48
|
+
id
|
|
49
|
+
}
|
|
36
50
|
|
|
37
|
-
|
|
51
|
+
let remove = id => store.dispatch(Remove(id))
|
|
52
|
+
let hide = id => {
|
|
53
|
+
store.dispatch(Hide(id))
|
|
54
|
+
Js.Global.setTimeout(() => {
|
|
55
|
+
remove(id)
|
|
56
|
+
}, 160)->ignore
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module Item = {
|
|
38
60
|
@react.component
|
|
39
|
-
let make = () => {
|
|
40
|
-
let {isVisible,
|
|
41
|
-
let timeoutRef = React.useRef(None)
|
|
61
|
+
let make = (~options) => {
|
|
62
|
+
let {id, isVisible, variant, title, description, closable, timeout} = options
|
|
42
63
|
|
|
43
|
-
React.
|
|
44
|
-
if isVisible {
|
|
45
|
-
timeoutRef.current->Option.map(timeoutRef => timeoutRef->Js.Global.clearTimeout)->ignore
|
|
64
|
+
let (mounted, setMounted) = React.useState(() => false)
|
|
46
65
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
66
|
+
React.useEffect0(() => {
|
|
67
|
+
setMounted(_ => true)
|
|
68
|
+
let timeoutId = Js.Global.setTimeout(() => {
|
|
69
|
+
hide(id)
|
|
70
|
+
}, timeout)
|
|
71
|
+
Some(() => Js.Global.clearTimeout(timeoutId))
|
|
72
|
+
})
|
|
51
73
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
74
|
+
<div
|
|
75
|
+
className={cx([
|
|
76
|
+
"bg-white p-3 rounded shadow transition duration-500 ease-in-out mt-3 transform",
|
|
77
|
+
{
|
|
78
|
+
open Css
|
|
79
|
+
style(list{bottom(40->px)})
|
|
80
|
+
},
|
|
81
|
+
mounted ? "-translate-y-14 opacity-1" : "opacity-0",
|
|
82
|
+
isVisible ? "opacity-100 z-50" : "opacity-0 translate-x-[200px]",
|
|
83
|
+
{
|
|
84
|
+
switch variant {
|
|
85
|
+
| #success => "bg-success-500 text-white"
|
|
86
|
+
| #warning => "bg-warning-500 text-white"
|
|
87
|
+
| #danger => "bg-danger-500 text-white"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
])}>
|
|
91
|
+
<div className="flex flex-gap-3">
|
|
92
|
+
<div>
|
|
93
|
+
{switch variant {
|
|
94
|
+
| #success => <BsReactIcons.MdCheckCircle size=28 />
|
|
95
|
+
| #warning => <BsReactIcons.MdWarning size=28 />
|
|
96
|
+
| #danger => <BsReactIcons.MdError size=28 />
|
|
97
|
+
}}
|
|
98
|
+
</div>
|
|
99
|
+
<div>
|
|
100
|
+
<strong> {title->React.string} </strong>
|
|
101
|
+
{description->Option.mapWithDefault(React.null, description =>
|
|
102
|
+
<p
|
|
103
|
+
className={cx([
|
|
104
|
+
"text-sm",
|
|
105
|
+
{
|
|
106
|
+
open Css
|
|
107
|
+
style(list{maxWidth(250->px)})
|
|
108
|
+
},
|
|
109
|
+
])}>
|
|
110
|
+
description
|
|
111
|
+
</p>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
{closable
|
|
115
|
+
? <button className="self-start" onClick={_ => hide(id)}>
|
|
116
|
+
<BsReactIcons.MdClose />
|
|
117
|
+
</button>
|
|
118
|
+
: React.null}
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module Provider = {
|
|
125
|
+
@react.component
|
|
126
|
+
let make = () => {
|
|
127
|
+
let {list} = store.useStore()
|
|
56
128
|
|
|
57
129
|
<ReachUi.Portal>
|
|
58
130
|
<div
|
|
59
|
-
className=
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
isVisible ? "opacity-100 translate-y-0 z-50" : "opacity-0 translate-y-10",
|
|
66
|
-
options->Option.mapWithDefault("", options =>
|
|
67
|
-
switch options.variant {
|
|
68
|
-
| #success => "bg-success-500 text-white"
|
|
69
|
-
| #warning => "bg-warning-500 text-white"
|
|
70
|
-
| #danger => "bg-danger-500 text-white"
|
|
71
|
-
}
|
|
72
|
-
),
|
|
73
|
-
])}>
|
|
74
|
-
{options->Option.mapWithDefault(React.null, options =>
|
|
75
|
-
<div className="flex flex-gap-3">
|
|
76
|
-
<div>
|
|
77
|
-
{switch options.variant {
|
|
78
|
-
| #success => <BsReactIcons.MdCheckCircle size=28 />
|
|
79
|
-
| #warning => <BsReactIcons.MdWarning size=28 />
|
|
80
|
-
| #danger => <BsReactIcons.MdError size=28 />
|
|
81
|
-
}}
|
|
82
|
-
</div>
|
|
83
|
-
<div>
|
|
84
|
-
<strong> {options.title->React.string} </strong>
|
|
85
|
-
{options.description->Option.mapWithDefault(React.null, description =>
|
|
86
|
-
<p
|
|
87
|
-
className={cx([
|
|
88
|
-
"text-sm",
|
|
89
|
-
{
|
|
90
|
-
open Css
|
|
91
|
-
style(list{maxWidth(250->px)})
|
|
92
|
-
},
|
|
93
|
-
])}>
|
|
94
|
-
description
|
|
95
|
-
</p>
|
|
96
|
-
)}
|
|
97
|
-
</div>
|
|
98
|
-
{options.closable
|
|
99
|
-
? <button className="self-start" onClick={_ => hide()}>
|
|
100
|
-
<BsReactIcons.MdClose />
|
|
101
|
-
</button>
|
|
102
|
-
: React.null}
|
|
103
|
-
</div>
|
|
104
|
-
)}
|
|
131
|
+
className="fixed right-0 top-0 transform translate-y-14 h-full p-4 flex flex-col items-end justify-end">
|
|
132
|
+
{list
|
|
133
|
+
->Array.map(e => {
|
|
134
|
+
<Item key={e.id->Obj.magic} options=e />
|
|
135
|
+
})
|
|
136
|
+
->React.array}
|
|
105
137
|
</div>
|
|
106
138
|
</ReachUi.Portal>
|
|
107
139
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
type variant = [#success | #warning | #danger]
|
|
2
|
+
type id
|
|
2
3
|
|
|
3
4
|
let show: (
|
|
4
5
|
~title: string,
|
|
@@ -7,8 +8,8 @@ let show: (
|
|
|
7
8
|
~variant: variant,
|
|
8
9
|
~timeout: int=?, // default: 5000
|
|
9
10
|
unit,
|
|
10
|
-
) =>
|
|
11
|
-
let hide:
|
|
11
|
+
) => id
|
|
12
|
+
let hide: id => unit
|
|
12
13
|
|
|
13
14
|
module Provider: {
|
|
14
15
|
@react.component
|