@colisweb/rescript-toolkit 4.13.0 → 4.14.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colisweb/rescript-toolkit",
3
- "version": "4.13.0",
3
+ "version": "4.14.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "clean": "rescript clean",
@@ -31,6 +31,7 @@ module Layout = Toolkit__Ui_Layout
31
31
  module SpinnerFullScreen = Toolkit__Ui_SpinnerFullScreen
32
32
  module MultiSelect = Toolkit__Ui_MultiSelect
33
33
  module MultiSelectWithValidation = Toolkit__Ui_MultiSelectWithValidation
34
+ module SelectWithValidation = Toolkit__Ui_SelectWithValidation
34
35
  module Notice = Toolkit__Ui_Notice
35
36
  module NativeDatePicker = Toolkit__Ui_NativeDatePicker
36
37
  module ErrorBoundary = Toolkit__Ui_ErrorBoundary
@@ -0,0 +1,150 @@
1
+ open ReactIntl
2
+
3
+ type item = {
4
+ itemLabel?: React.element,
5
+ label: string,
6
+ value: string,
7
+ }
8
+
9
+ type options = array<item>
10
+
11
+ module Footer = {
12
+ @react.component
13
+ let make = (~onCancel, ~onValidateClick) => {
14
+ let dropdownContext = React.useContext(Toolkit__Ui_Dropdown.dropdownContext)
15
+
16
+ <footer className="bg-white p-1 flex flex-row justify-between">
17
+ <Toolkit__Ui_Button
18
+ type_="button"
19
+ onClick={_ => {
20
+ dropdownContext.hide()
21
+ onCancel()
22
+ }}>
23
+ <FormattedMessage defaultMessage={"Annuler"} />
24
+ </Toolkit__Ui_Button>
25
+ <Toolkit__Ui_Button
26
+ color=#primary
27
+ type_="button"
28
+ onClick={_ => {
29
+ onValidateClick()
30
+ dropdownContext.hide()
31
+ }}>
32
+ <FormattedMessage defaultMessage={"Valider"} />
33
+ </Toolkit__Ui_Button>
34
+ </footer>
35
+ }
36
+ }
37
+
38
+ module Options = {
39
+ @react.component
40
+ let make = (~options, ~deferredSearch, ~itemClassName, ~setSelectedOption, ~selectedOption) => {
41
+ options
42
+ ->Array.keep(({label}) =>
43
+ deferredSearch == "" ||
44
+ label->Toolkit__Primitives.String.normalizeForSearch->Js.String2.includes(deferredSearch)
45
+ )
46
+ ->Array.mapWithIndex((i, item) => {
47
+ let {label, value} = item
48
+
49
+ <div
50
+ key={`multiselectoption-${label}-${value}-${i->Int.toString}`}
51
+ className={cx([
52
+ "group flex flex-row items-center gap-2 pt-3 text-left relative",
53
+ i > 0 ? "mt-3" : "",
54
+ itemClassName,
55
+ ])}>
56
+ <Toolkit__Ui_Radio
57
+ value
58
+ className="w-full flex-shrink-0 relative"
59
+ checked={selectedOption->Option.mapWithDefault(false, selectedOption => {
60
+ item.label == selectedOption.label && item.value == selectedOption.value
61
+ })}
62
+ onChange={_ => {
63
+ setSelectedOption(_ => {
64
+ Some(item)
65
+ })
66
+ }}>
67
+ {item.itemLabel->Option.getWithDefault(label->React.string)}
68
+ </Toolkit__Ui_Radio>
69
+ </div>
70
+ })
71
+ ->React.array
72
+ }
73
+ }
74
+
75
+ @react.component
76
+ let make = (
77
+ ~options: options,
78
+ ~placeholder: React.element,
79
+ ~buttonClassName="",
80
+ ~dropdownClassName="",
81
+ ~itemClassName="",
82
+ ~searchPlaceholder: option<string>=?,
83
+ ~allowFilter=true,
84
+ ~defaultValue: option<item>=?,
85
+ ~onValidate: item => unit,
86
+ ~disabled: option<bool>=?,
87
+ ~onCancel: option<unit => unit>=?,
88
+ ) => {
89
+ let (selectedOption, setSelectedOption) = React.useState(() => defaultValue)
90
+ let previousDefaultValue = Toolkit__Hooks.usePrevious(defaultValue)
91
+ let (search, setSearch) = React.useState(() => "")
92
+ let deferredSearch = React.useDeferredValue(search)
93
+ let allowFilter = options->Array.length > 5 && allowFilter
94
+
95
+ React.useEffect2(() => {
96
+ switch (previousDefaultValue, defaultValue) {
97
+ | (Some(Some(v)), Some(v2)) if v !== v2 => setSelectedOption(_ => defaultValue)
98
+ | _ => ()
99
+ }
100
+
101
+ None
102
+ }, (previousDefaultValue, defaultValue))
103
+
104
+ <Toolkit__Ui_Dropdown
105
+ ?disabled
106
+ buttonClassName
107
+ onClose={_ => setSelectedOption(_ => defaultValue)}
108
+ dropdownClassName
109
+ position=#bottom
110
+ label={switch selectedOption {
111
+ | None =>
112
+ <p className="flex flex-row gap-2 w-full items-center relative">
113
+ <span className="ml-1"> {placeholder} </span>
114
+ <span className="absolute inset-y-0 right-0 flex items-center">
115
+ <ReactIcons.FaAngleDown />
116
+ </span>
117
+ </p>
118
+ | Some({label}) =>
119
+ <div className="table table-fixed w-full" title={label}>
120
+ <span className="table-cell truncate text-left"> {label->React.string} </span>
121
+ </div>
122
+ }}>
123
+ <div className="py-2 pl-2 pr-1 max-h-[300px] overflow-y-scroll">
124
+ {allowFilter
125
+ ? <div className="mb-3">
126
+ <Toolkit__Ui_TextInput
127
+ id="search"
128
+ autoFocus={true}
129
+ placeholder=?{searchPlaceholder}
130
+ allowWhiteSpace={true}
131
+ value={search}
132
+ onChange={event => {
133
+ let target = event->ReactEvent.Form.currentTarget
134
+
135
+ setSearch(_ => target["value"]->Toolkit__Primitives.String.normalizeForSearch)
136
+ }}
137
+ />
138
+ </div>
139
+ : React.null}
140
+ <Options deferredSearch options setSelectedOption selectedOption itemClassName />
141
+ </div>
142
+ <Footer
143
+ onCancel={() => {
144
+ setSelectedOption(_ => defaultValue)
145
+ onCancel->Option.forEach(fn => fn())
146
+ }}
147
+ onValidateClick={() => selectedOption->Option.forEach(onValidate)}
148
+ />
149
+ </Toolkit__Ui_Dropdown>
150
+ }
@@ -0,0 +1,17 @@
1
+ type item = {itemLabel?: React.element, label: string, value: string}
2
+ type options = array<item>
3
+
4
+ @react.component
5
+ let make: (
6
+ ~options: options,
7
+ ~placeholder: React.element,
8
+ ~buttonClassName: string=?,
9
+ ~dropdownClassName: string=?,
10
+ ~itemClassName: string=?,
11
+ ~searchPlaceholder: string=?,
12
+ ~allowFilter: bool=?,
13
+ ~defaultValue: item=?,
14
+ ~onValidate: item => unit,
15
+ ~disabled: bool=?,
16
+ ~onCancel: unit => unit=?,
17
+ ) => React.element
@@ -46,6 +46,8 @@ module DomElement = {
46
46
  bottom: float,
47
47
  }
48
48
  @send external getBoundingClientRect: Dom.element => rect = "getBoundingClientRect"
49
+
50
+ @send external focus: Dom.element => unit = "focus"
49
51
  }
50
52
 
51
53
  @val
@@ -33,6 +33,7 @@ module DomElement: {
33
33
  bottom: float,
34
34
  }
35
35
  let getBoundingClientRect: Dom.element => rect
36
+ let focus: Dom.element => unit
36
37
  }
37
38
 
38
39
  let innerWidth: int