@colisweb/rescript-toolkit 4.12.1 → 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/locale/fr.json +30 -5
- package/package.json +2 -2
- package/src/form/Toolkit__Form.res +35 -110
- package/src/ui/Toolkit__Ui.res +1 -0
- package/src/ui/Toolkit__Ui_SelectWithValidation.res +150 -0
- package/src/ui/Toolkit__Ui_SelectWithValidation.resi +17 -0
- package/src/ui/styles.css +77 -126
- package/src/ui/tailwind-init.css +3 -0
- package/src/vendors/Browser.res +2 -0
- package/src/vendors/Browser.resi +1 -0
- package/src/vendors/ReactDayPicker.res +601 -85
- package/src/vendors/ReactDayPicker.resi +132 -0
- package/src/ui/styles.scss +0 -371
package/locale/fr.json
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"id": "_0475def8",
|
|
4
|
+
"defaultMessage": "Fin",
|
|
5
|
+
"message": "Fin"
|
|
6
|
+
},
|
|
2
7
|
{
|
|
3
8
|
"id": "_04eadf92",
|
|
4
9
|
"defaultMessage": "L'email est invalide.",
|
|
@@ -39,11 +44,6 @@
|
|
|
39
44
|
"defaultMessage": "Volume total",
|
|
40
45
|
"message": "Volume total"
|
|
41
46
|
},
|
|
42
|
-
{
|
|
43
|
-
"id": "_2f765479",
|
|
44
|
-
"defaultMessage": "Choisir",
|
|
45
|
-
"message": "Choisir"
|
|
46
|
-
},
|
|
47
47
|
{
|
|
48
48
|
"id": "_337be526",
|
|
49
49
|
"defaultMessage": "Jeudi",
|
|
@@ -64,6 +64,16 @@
|
|
|
64
64
|
"defaultMessage": "Samedi",
|
|
65
65
|
"message": "Samedi"
|
|
66
66
|
},
|
|
67
|
+
{
|
|
68
|
+
"id": "_4df46058",
|
|
69
|
+
"defaultMessage": "Semaine {number}",
|
|
70
|
+
"message": "Semaine {number}"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "_4f2a1035",
|
|
74
|
+
"defaultMessage": "Début",
|
|
75
|
+
"message": "Début"
|
|
76
|
+
},
|
|
67
77
|
{
|
|
68
78
|
"id": "_5689ac4d",
|
|
69
79
|
"defaultMessage": "Masquer le mot de passe",
|
|
@@ -74,6 +84,11 @@
|
|
|
74
84
|
"defaultMessage": "Non",
|
|
75
85
|
"message": "Non"
|
|
76
86
|
},
|
|
87
|
+
{
|
|
88
|
+
"id": "_6a661ab6",
|
|
89
|
+
"defaultMessage": "Aujourd'hui",
|
|
90
|
+
"message": "Aujourd'hui"
|
|
91
|
+
},
|
|
77
92
|
{
|
|
78
93
|
"id": "_6ef34790",
|
|
79
94
|
"defaultMessage": "Une erreur est survenue lors de la recherche",
|
|
@@ -94,11 +109,21 @@
|
|
|
94
109
|
"defaultMessage": "<lat></lat>, <lng></lng>",
|
|
95
110
|
"message": "<lat></lat>, <lng></lng>"
|
|
96
111
|
},
|
|
112
|
+
{
|
|
113
|
+
"id": "_7cd2a589",
|
|
114
|
+
"defaultMessage": "Cette semaine",
|
|
115
|
+
"message": "Cette semaine"
|
|
116
|
+
},
|
|
97
117
|
{
|
|
98
118
|
"id": "_7e01b0f2",
|
|
99
119
|
"defaultMessage": "Réinitialiser",
|
|
100
120
|
"message": "Réinitialiser"
|
|
101
121
|
},
|
|
122
|
+
{
|
|
123
|
+
"id": "_7e345c99",
|
|
124
|
+
"defaultMessage": "Ce mois",
|
|
125
|
+
"message": "Ce mois"
|
|
126
|
+
},
|
|
102
127
|
{
|
|
103
128
|
"id": "_8f8eb0df",
|
|
104
129
|
"defaultMessage": "Vendredi",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colisweb/rescript-toolkit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rescript clean",
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@colisweb/bs-react-intl-extractor-bin": "0.12.2",
|
|
31
|
-
"@colisweb/react-day-picker": "7.4.16",
|
|
32
31
|
"@colisweb/restorative": "1.0.0",
|
|
33
32
|
"@datadog/browser-rum": "4.46.0",
|
|
34
33
|
"@headlessui/react": "1.7.16",
|
|
@@ -53,6 +52,7 @@
|
|
|
53
52
|
"react": "18.2.0",
|
|
54
53
|
"react-big-calendar": "1.5.2",
|
|
55
54
|
"react-datepicker": "3.8.0",
|
|
55
|
+
"react-day-picker": "8.8.0",
|
|
56
56
|
"react-dom": "18.2.0",
|
|
57
57
|
"react-error-boundary": "4.0.10",
|
|
58
58
|
"react-helmet": "6.1.0",
|
|
@@ -638,107 +638,7 @@ module Make = (StateLenses: Config) => {
|
|
|
638
638
|
}
|
|
639
639
|
|
|
640
640
|
module DatePicker = {
|
|
641
|
-
|
|
642
|
-
@react.component
|
|
643
|
-
let make = (
|
|
644
|
-
~value,
|
|
645
|
-
~handleChange,
|
|
646
|
-
~containerClassName,
|
|
647
|
-
~placeholder,
|
|
648
|
-
~disabledBefore: option<Js.Date.t>=?,
|
|
649
|
-
~revalidate,
|
|
650
|
-
) => {
|
|
651
|
-
let (date, setDate) = React.useState((): option<Js.Date.t> => value)
|
|
652
|
-
let intl = useIntl()
|
|
653
|
-
|
|
654
|
-
<div>
|
|
655
|
-
<Toolkit__Ui_PortalDropdown
|
|
656
|
-
buttonClassName="!border-0 w-full !p-0"
|
|
657
|
-
label={switch value {
|
|
658
|
-
| None =>
|
|
659
|
-
<div className={cx(["text-left w-full", containerClassName])}>
|
|
660
|
-
<div className="border p-2 rounded text-neutral-400 font-normal">
|
|
661
|
-
{placeholder->Option.getWithDefault(
|
|
662
|
-
<FormattedMessage defaultMessage={"Choisissez une date"} />,
|
|
663
|
-
)}
|
|
664
|
-
</div>
|
|
665
|
-
</div>
|
|
666
|
-
| Some(date) =>
|
|
667
|
-
<div
|
|
668
|
-
className={cx([
|
|
669
|
-
"border rounded-lg p-2 flex flex-row items-center justify-between gap-6 font-normal text-neutral-700 w-full",
|
|
670
|
-
containerClassName,
|
|
671
|
-
])}>
|
|
672
|
-
<span>
|
|
673
|
-
<FormattedDate
|
|
674
|
-
value={date} weekday=#long day=#numeric month=#long year=#numeric
|
|
675
|
-
/>
|
|
676
|
-
</span>
|
|
677
|
-
<div
|
|
678
|
-
className="border inline-flex items-center justify-center w-8 h-8 rounded border-primary-700 text-primary-700">
|
|
679
|
-
<ReactIcons.FaPencilAlt size={18} />
|
|
680
|
-
</div>
|
|
681
|
-
</div>
|
|
682
|
-
}}>
|
|
683
|
-
{disclosure => {
|
|
684
|
-
<div className="flex flex-col">
|
|
685
|
-
<ReactDayPicker.ReactDayPicker.DayPicker
|
|
686
|
-
className="cw-Datepicker"
|
|
687
|
-
showOutsideDays=true
|
|
688
|
-
modifiers={disabledBefore->Option.mapWithDefault(
|
|
689
|
-
Js.Obj.empty()->Obj.magic,
|
|
690
|
-
disabledBefore => {
|
|
691
|
-
{
|
|
692
|
-
"disabled": {
|
|
693
|
-
"before": disabledBefore,
|
|
694
|
-
},
|
|
695
|
-
}->Obj.magic
|
|
696
|
-
},
|
|
697
|
-
)}
|
|
698
|
-
locale={intl->Intl.locale}
|
|
699
|
-
months=Toolkit__LocalesHelpers.DatePicker.Fr.months
|
|
700
|
-
weekdaysShort=Toolkit__LocalesHelpers.DatePicker.Fr.weekdaysShort
|
|
701
|
-
firstDayOfWeek=1
|
|
702
|
-
selectedDays=(date->Obj.magic, {from: Js.Nullable.null, to_: Js.Nullable.null})
|
|
703
|
-
onDayClick={(
|
|
704
|
-
(day, modifiers) => {
|
|
705
|
-
let disabled: option<bool> = (modifiers->Obj.magic)["disabled"]
|
|
706
|
-
|
|
707
|
-
switch disabled {
|
|
708
|
-
| Some(true) => ()
|
|
709
|
-
| _ => setDate(_ => Some(day))
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
)->Obj.magic}
|
|
713
|
-
/>
|
|
714
|
-
<div className="flex flex-row justify-between">
|
|
715
|
-
<Toolkit__Ui_Button
|
|
716
|
-
type_="button"
|
|
717
|
-
disabled={date->Option.isNone}
|
|
718
|
-
onClick={_ => {
|
|
719
|
-
revalidate()
|
|
720
|
-
setDate(_ => None)
|
|
721
|
-
}}>
|
|
722
|
-
<FormattedMessage defaultMessage={"Réinitialiser"} />
|
|
723
|
-
</Toolkit__Ui_Button>
|
|
724
|
-
<Toolkit__Ui_Button
|
|
725
|
-
color=#primary
|
|
726
|
-
type_="button"
|
|
727
|
-
disabled={date->Option.isNone}
|
|
728
|
-
onClick={_ => {
|
|
729
|
-
disclosure.hide()
|
|
730
|
-
revalidate()
|
|
731
|
-
handleChange(date)
|
|
732
|
-
}}>
|
|
733
|
-
<FormattedMessage defaultMessage={"Choisir"} />
|
|
734
|
-
</Toolkit__Ui_Button>
|
|
735
|
-
</div>
|
|
736
|
-
</div>
|
|
737
|
-
}}
|
|
738
|
-
</Toolkit__Ui_PortalDropdown>
|
|
739
|
-
</div>
|
|
740
|
-
}
|
|
741
|
-
}
|
|
641
|
+
open ReactDayPicker
|
|
742
642
|
@react.component
|
|
743
643
|
let make = (
|
|
744
644
|
~field,
|
|
@@ -751,14 +651,7 @@ module Make = (StateLenses: Config) => {
|
|
|
751
651
|
) => {
|
|
752
652
|
<Field
|
|
753
653
|
field
|
|
754
|
-
render={({handleChange, value, error
|
|
755
|
-
let revalidate = () => {
|
|
756
|
-
switch state {
|
|
757
|
-
| Pristine => ()
|
|
758
|
-
| _ => validate()
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
654
|
+
render={({handleChange, value, error}) => {
|
|
762
655
|
<>
|
|
763
656
|
{switch label {
|
|
764
657
|
| None => React.null
|
|
@@ -771,7 +664,39 @@ module Make = (StateLenses: Config) => {
|
|
|
771
664
|
label
|
|
772
665
|
</Toolkit__Ui_Label>
|
|
773
666
|
}}
|
|
774
|
-
<
|
|
667
|
+
<ReactDayPicker.SingleDayPickerInput
|
|
668
|
+
?value
|
|
669
|
+
onChange={v => handleChange(v)}
|
|
670
|
+
allowEmpty=?isOptional
|
|
671
|
+
buttonClassName="w-full"
|
|
672
|
+
labelClassName={cx([
|
|
673
|
+
"flex flex-row items-center gap-4 font-normal text-neutral-700 w-full",
|
|
674
|
+
containerClassName,
|
|
675
|
+
])}
|
|
676
|
+
modifiers=?{disabledBefore->Option.map((disabledBefore): ReactDayPicker.modifiers => {
|
|
677
|
+
disabled: [
|
|
678
|
+
ReactDayPicker.Matcher.interval({
|
|
679
|
+
before: disabledBefore,
|
|
680
|
+
}),
|
|
681
|
+
],
|
|
682
|
+
})}
|
|
683
|
+
placeholder={placeholder->Option.getWithDefault(
|
|
684
|
+
<FormattedMessage defaultMessage="Choisissez une date" />,
|
|
685
|
+
)}
|
|
686
|
+
labelFormatter={date => {
|
|
687
|
+
<>
|
|
688
|
+
<span>
|
|
689
|
+
<FormattedDate
|
|
690
|
+
value={date} weekday=#long day=#numeric month=#long year=#numeric
|
|
691
|
+
/>
|
|
692
|
+
</span>
|
|
693
|
+
<div
|
|
694
|
+
className="border inline-flex items-center justify-center w-8 h-8 rounded border-primary-700 text-primary-700">
|
|
695
|
+
<ReactIcons.FaPencilAlt size={18} />
|
|
696
|
+
</div>
|
|
697
|
+
</>
|
|
698
|
+
}}
|
|
699
|
+
/>
|
|
775
700
|
<ErrorMessage ?error />
|
|
776
701
|
</>
|
|
777
702
|
}}
|
package/src/ui/Toolkit__Ui.res
CHANGED
|
@@ -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
|