@colisweb/rescript-toolkit 4.9.3 → 4.10.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.9.3",
3
+ "version": "4.10.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "clean": "rescript clean",
@@ -36,3 +36,4 @@ module ErrorBoundary = Toolkit__Ui_ErrorBoundary
36
36
  module WeekDateFilter = Toolkit__Ui_WeekDateFilter
37
37
  module Coordinates = Toolkit__Ui_Coordinates
38
38
  module Autocomplete = Toolkit__Ui_Autocomplete
39
+ module WeekdayNavigation = Toolkit__Ui_WeekdayNavigation
@@ -0,0 +1,195 @@
1
+ open ReactIntl
2
+
3
+ type rec state = {
4
+ period: period,
5
+ selectedDay: option<Js.Date.t>,
6
+ }
7
+ and period = {
8
+ start: Js.Date.t,
9
+ end_: Js.Date.t,
10
+ }
11
+
12
+ type action =
13
+ | PreviousPeriod
14
+ | NextPeriod
15
+ | UpdateSelectedDay(option<Js.Date.t>)
16
+
17
+ let updateQueryParams = (state: state) => {
18
+ RescriptReactRouter.replace(
19
+ Qs.stringifyWithParams(
20
+ {
21
+ "start": Some(state.period.start),
22
+ "selectedDay": state.selectedDay,
23
+ }->Obj.magic,
24
+ Qs.makeParams(
25
+ ~addQueryPrefix=true,
26
+ ~serializeDate=d => d->DateFns.formatWithPattern("yyyy-MM-dd"),
27
+ (),
28
+ ),
29
+ ),
30
+ )
31
+ }
32
+
33
+ /*
34
+ * The key is a formatted date
35
+ * ex:
36
+ * {
37
+ * "2023-03-03": 2
38
+ * }
39
+ */
40
+ type dayCounter = Js.Dict.t<int>
41
+
42
+ @react.component
43
+ let make = (
44
+ ~startOfWeek: option<Js.Date.t>=?,
45
+ ~selectedDay: option<Js.Date.t>=?,
46
+ ~className="",
47
+ ~counters: option<dayCounter>=?,
48
+ ~children,
49
+ ) => {
50
+ let {isSm} = Toolkit__Hooks.useMediaQuery()
51
+
52
+ let periodLength = isSm ? 7 : 3
53
+ let ({period, selectedDay}, dispatch) = ReactUpdate.useReducerWithMapState(
54
+ (state, action) =>
55
+ switch action {
56
+ | PreviousPeriod =>
57
+ UpdateWithSideEffects(
58
+ {
59
+ period: {
60
+ start: state.period.start->DateFns.subDays(periodLength)->DateFns.startOfDay,
61
+ end_: state.period.start->DateFns.subDays(1),
62
+ },
63
+ selectedDay: state.selectedDay->Option.map(_ =>
64
+ state.period.start->DateFns.subDays(periodLength)->DateFns.startOfDay
65
+ ),
66
+ },
67
+ ({state}) => {
68
+ updateQueryParams(state)
69
+
70
+ None
71
+ },
72
+ )
73
+ | NextPeriod =>
74
+ UpdateWithSideEffects(
75
+ {
76
+ period: {
77
+ start: state.period.end_->DateFns.addDays(1),
78
+ end_: state.period.end_->DateFns.addDays(periodLength),
79
+ },
80
+ selectedDay: state.selectedDay->Option.map(_ => state.period.end_->DateFns.addDays(1)),
81
+ },
82
+ ({state}) => {
83
+ updateQueryParams(state)
84
+
85
+ None
86
+ },
87
+ )
88
+ | UpdateSelectedDay(date) =>
89
+ UpdateWithSideEffects(
90
+ {...state, selectedDay: date},
91
+ ({state}) => {
92
+ updateQueryParams(state)
93
+ None
94
+ },
95
+ )
96
+ },
97
+ () => {
98
+ let defaultPeriod: period = {
99
+ let today = Js.Date.make()
100
+ let timeSlotStart = isSm ? DateFns.startOfWeek(today, {weekStartsOn: 1}) : today
101
+
102
+ let timeSlotEnd = isSm
103
+ ? DateFns.endOfWeek(today, {weekStartsOn: 1})
104
+ : today->DateFns.addDays(2)
105
+ {
106
+ start: timeSlotStart,
107
+ end_: timeSlotEnd,
108
+ }
109
+ }
110
+
111
+ {
112
+ period: startOfWeek->Option.mapWithDefault(defaultPeriod, start => {
113
+ start,
114
+ end_: start->DateFns.addDays(periodLength - 1),
115
+ }),
116
+ selectedDay: selectedDay->Option.isSome
117
+ ? selectedDay
118
+ : isSm
119
+ ? None
120
+ : Some(startOfWeek->Option.getWithDefault(defaultPeriod.start)),
121
+ }
122
+ },
123
+ )
124
+ let intl = useIntl()
125
+
126
+ <div className>
127
+ <div className="border-neutral-300 flex justify-center items-center py-3">
128
+ <Toolkit__Ui_IconButton
129
+ size=#xs
130
+ variant=#outline
131
+ color=#neutral
132
+ onClick={_ => dispatch(PreviousPeriod)}
133
+ ariaLabel="previous week"
134
+ icon={<ReactIcons.MdKeyboardArrowLeft size=30 />}
135
+ />
136
+ <div className="flex items-center mx-2">
137
+ {DateFns.eachDayOfInterval({
138
+ start: period.start,
139
+ end_: period.end_,
140
+ })
141
+ ->Array.mapWithIndex((i, day) => {
142
+ let formattedDay =
143
+ intl->Intl.formatDateWithOptions(day, dateTimeFormatOptions(~weekday=#long, ()))
144
+
145
+ let isSelected =
146
+ selectedDay->Option.mapWithDefault(false, sDay => DateFns.isSameDay(sDay, day))
147
+ <React.Fragment key={i->Int.toString ++ "-day"}>
148
+ {i == 0 || day->DateFns.isFirstDayOfMonth
149
+ ? <p className="text-xs text-neutral-700 uppercase">
150
+ <FormattedDate value=day month=#short />
151
+ </p>
152
+ : React.null}
153
+ <div
154
+ onClick={_ => dispatch(UpdateSelectedDay(isSm && isSelected ? None : Some(day)))}
155
+ className="flex flex-col items-stretch w-16 mx-1 font-display">
156
+ <p
157
+ className={cx([
158
+ "flex flex-col items-center justify-center uppercase text-xs w-full rounded-sm leading-tight py-1",
159
+ isSelected ? "text-white bg-primary-700" : "text-primary-700 bg-primary-50",
160
+ ])}>
161
+ <span>
162
+ {formattedDay
163
+ ->Js.String2.slice(~from=0, ~to_=4)
164
+ ->Js.String2.concat(formattedDay->Js.String2.length > 4 ? "." : "")
165
+ ->React.string}
166
+ </span>
167
+ <span className="text-sm font-semibold">
168
+ <FormattedDate value=day day=#"2-digit" />
169
+ </span>
170
+ </p>
171
+ <p
172
+ className="text-xs text-info-500 bg-info-50 text-center mt-1 font-semibold rounded-sm">
173
+ {counters
174
+ ->Option.flatMap(counters =>
175
+ counters->Js.Dict.get(day->DateFns.formatWithPattern("yyyy-MM-dd"))
176
+ )
177
+ ->Option.mapWithDefault(React.null, React.int)}
178
+ </p>
179
+ </div>
180
+ </React.Fragment>
181
+ })
182
+ ->React.array}
183
+ </div>
184
+ <Toolkit__Ui_IconButton
185
+ size=#xs
186
+ variant=#outline
187
+ color=#neutral
188
+ onClick={_ => dispatch(NextPeriod)}
189
+ ariaLabel="next week"
190
+ icon={<ReactIcons.MdKeyboardArrowRight size=30 />}
191
+ />
192
+ </div>
193
+ {children({period, selectedDay})}
194
+ </div>
195
+ }