willba-component-library 0.2.71 → 0.2.73

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.
@@ -1,20 +1,18 @@
1
1
  import React from 'react'
2
2
  import { useTranslation } from 'react-i18next'
3
- import { useMediaQuery } from 'react-responsive'
4
3
 
5
- import useTheme, { Palette } from '../../themes/useTheme'
4
+ import useTheme from '../../themes/useTheme'
6
5
  import '../../themes/Default.css'
7
- import { Calendar, SubmitButton, CloseButton } from '../../core/components'
6
+ import { Calendar, CloseButton } from '../../core/components'
8
7
  import {
9
8
  useAwaitRender,
10
9
  useCloseFilterSection,
11
10
  useUpdateTranslations,
12
11
  } from '../../core/hooks'
13
- import { nightsCount, parseDate } from '../../core/components/calendar/utils'
14
- import { IconsSvg } from '../../assets/IconsSvg'
15
12
 
16
13
  import { FilterCalendarTypes } from './FilterCalendarTypes'
17
14
  import { useFilterCalendar } from './hooks/useFilterCalendar'
15
+ import { Footer } from './components/Footer'
18
16
 
19
17
  import './FilterCalendar.css'
20
18
 
@@ -37,7 +35,6 @@ export default function FilterCalendar({
37
35
  // Translations
38
36
  useUpdateTranslations({ language })
39
37
  const { t } = useTranslation()
40
- const isMobile = useMediaQuery({ maxWidth: 690 })
41
38
 
42
39
  const {
43
40
  setCalendarRange,
@@ -70,10 +67,6 @@ export default function FilterCalendar({
70
67
  handleSelectedFilter: setToggleCalendar,
71
68
  })
72
69
 
73
- const nights = nightsCount({
74
- calendarRange: calendarRange,
75
- })
76
-
77
70
  return (
78
71
  <div className={`will-root`} style={themePalette}>
79
72
  {toggleCalendar && (
@@ -109,90 +102,16 @@ export default function FilterCalendar({
109
102
  />
110
103
  </div>
111
104
  <div className="will-calendar-footer">
112
- <div className="will-calendar-footer-actions-wrapper">
113
- <div className="will-calendar-footer-dates">
114
- {calendarHasError ? (
115
- <span>
116
- {t(`common:errors.calendarErrors.checkInAvailabilityGuide`)}
117
- </span>
118
- ) : (
119
- <div>
120
- <span>
121
- {parseDate({
122
- date: calendarRange?.from,
123
- dateFormat: 'EEEEEE d.M.yyyy',
124
- language,
125
- }) || t('common:checkIn')}
126
- </span>
127
- <span className="will-calendar-footer-dates-separator">
128
- -
129
- </span>
130
- <span>
131
- {parseDate({
132
- date: calendarRange?.to,
133
- dateFormat: 'EEEEEE d.M.yyyy',
134
- language,
135
- }) || t('common:checkOut')}
136
- </span>
137
- </div>
138
- )}
139
-
140
- <span className="will-calendar-footer-booked">
141
- {calendarHasError
142
- ? t(`filterBar:calendar.minNights`)
143
- : nights
144
- ? `${nights} ${t(
145
- `common:${nights === 1 ? 'night' : 'nights'}`
146
- )}`
147
- : ''}
148
- </span>
149
- </div>
150
-
151
- {calendarHasError &&
152
- isMobile &&
153
- renderCalendarErrorMessage({
154
- palette,
155
- message: t(
156
- `common:errors.calendarErrors.checkInAvailabilityError`
157
- ),
158
- })}
159
-
160
- <div className="will-calendar-footer-actions">
161
- <SubmitButton
162
- onClick={handleClearDates}
163
- disabled={!calendarRange?.from}
164
- label={t(`common:clearDates`)}
165
- variant="text"
166
- />
167
- </div>
168
- </div>
169
-
170
- {calendarHasError &&
171
- !isMobile &&
172
- renderCalendarErrorMessage({
173
- palette,
174
- message: t(
175
- `common:errors.calendarErrors.checkInAvailabilityError`
176
- ),
177
- })}
105
+ <Footer
106
+ calendarHasError={calendarHasError}
107
+ calendarRange={calendarRange}
108
+ handleClearDates={handleClearDates}
109
+ language={language}
110
+ palette={palette}
111
+ />
178
112
  </div>
179
113
  </div>
180
114
  )}
181
115
  </div>
182
116
  )
183
117
  }
184
-
185
- /////////
186
-
187
- const renderCalendarErrorMessage = ({
188
- message,
189
- palette,
190
- }: {
191
- message?: string
192
- palette: Palette
193
- }) => (
194
- <div className="will-calendar-footer-error">
195
- <IconsSvg fill={palette?.error || 'inherit'} size={25} icon="warning" />
196
- <span style={{ marginLeft: '10px' }}>{message || ''}</span>
197
- </div>
198
- )
@@ -0,0 +1,114 @@
1
+ import React from 'react'
2
+ import { useTranslation } from 'react-i18next'
3
+ import { useMediaQuery } from 'react-responsive'
4
+ import { DateRange } from 'react-day-picker'
5
+
6
+ import { Palette } from '../../../themes/useTheme'
7
+ import { nightsCount, parseDate } from '../../../core/components/calendar/utils'
8
+ import { IconsSvg } from '../../../assets/IconsSvg'
9
+ import { SubmitButton } from '../../../core/components'
10
+
11
+ export const Footer = ({
12
+ calendarHasError,
13
+ calendarRange,
14
+ handleClearDates,
15
+ language,
16
+ palette,
17
+ }: {
18
+ calendarHasError: boolean
19
+ calendarRange?: DateRange
20
+ handleClearDates: () => void
21
+ language?: string
22
+ palette: Palette
23
+ }) => {
24
+ const { t } = useTranslation()
25
+ const isMobile = useMediaQuery({ maxWidth: 600 })
26
+
27
+ const nights = nightsCount({
28
+ calendarRange: calendarRange,
29
+ })
30
+
31
+ return (
32
+ <>
33
+ <div className="will-calendar-footer-actions-wrapper">
34
+ <div className="will-calendar-footer-dates">
35
+ {calendarHasError ? (
36
+ <span>
37
+ {t(`common:errors.calendarErrors.checkInAvailabilityGuide`)}
38
+ </span>
39
+ ) : (
40
+ <div>
41
+ <span>
42
+ {parseDate({
43
+ date: calendarRange?.from,
44
+ dateFormat: 'EEEEEE d.M.yyyy',
45
+ language,
46
+ }) || t('common:checkIn')}
47
+ </span>
48
+ <span className="will-calendar-footer-dates-separator">-</span>
49
+ <span>
50
+ {parseDate({
51
+ date: calendarRange?.to,
52
+ dateFormat: 'EEEEEE d.M.yyyy',
53
+ language,
54
+ }) || t('common:checkOut')}
55
+ </span>
56
+ </div>
57
+ )}
58
+
59
+ <span className="will-calendar-footer-booked">
60
+ {calendarHasError
61
+ ? `${1} ${t(`common:night`)} min`
62
+ : nights
63
+ ? `${nights} ${t(`common:${nights === 1 ? 'night' : 'nights'}`)}`
64
+ : ''}
65
+ </span>
66
+ </div>
67
+
68
+ <div className="will-calendar-footer-error">
69
+ {calendarHasError &&
70
+ isMobile &&
71
+ renderCalendarErrorMessage({
72
+ palette,
73
+ message: t(
74
+ `common:errors.calendarErrors.checkInAvailabilityError`
75
+ ),
76
+ })}
77
+ </div>
78
+
79
+ <div className="will-calendar-footer-actions">
80
+ <SubmitButton
81
+ onClick={handleClearDates}
82
+ disabled={!calendarRange?.from}
83
+ label={t(`common:clearDates`)}
84
+ variant="text"
85
+ />
86
+ </div>
87
+ </div>
88
+
89
+ <div className="will-calendar-footer-error">
90
+ {calendarHasError &&
91
+ !isMobile &&
92
+ renderCalendarErrorMessage({
93
+ palette,
94
+ message: t(`common:errors.calendarErrors.checkInAvailabilityError`),
95
+ })}
96
+ </div>
97
+ </>
98
+ )
99
+ }
100
+
101
+ /////////
102
+
103
+ const renderCalendarErrorMessage = ({
104
+ message,
105
+ palette,
106
+ }: {
107
+ message?: string
108
+ palette: Palette
109
+ }) => (
110
+ <>
111
+ <IconsSvg fill={palette?.error || 'inherit'} size={25} icon="warning" />
112
+ <span>{message || ''}</span>
113
+ </>
114
+ )
@@ -24,6 +24,8 @@
24
24
  color: var(--will-black);
25
25
  text-decoration: underline;
26
26
  font-weight: 500;
27
+ font-size: 15px;
28
+ padding: 0 10px;
27
29
  }
28
30
 
29
31
  .will-filter-bar-submit-button span {
@@ -6,9 +6,8 @@
6
6
 
7
7
  /* Calendar overrides */
8
8
 
9
-
10
9
  .will-calendar-filter-container .DayPicker {
11
- font-size: 25px; /* Adjust this value to make the DayPicker bigger */
10
+ font-size: 25px;
12
11
  }
13
12
 
14
13
  .will-calendar-filter-container .rdp-month {
@@ -67,7 +66,8 @@
67
66
  }
68
67
 
69
68
  .will-calendar-filter-container .rdp-cell .rdp-button[disabled] {
70
- opacity: 0.5;
69
+ color: var(--will-transparent-black);
70
+ opacity: 1
71
71
  }
72
72
 
73
73
  @media (max-width: 960px) {
@@ -85,7 +85,7 @@
85
85
  }
86
86
  }
87
87
 
88
- /* Tooltip */
88
+ /* Tooltips */
89
89
  .will-root .will-calendar-filter-container .will-calendar-tooltip,
90
90
  .will-root .will-calendar-filter-container .will-calendar-tooltip-check-out,
91
91
  .will-root .will-calendar-filter-container .will-calendar-tooltip-overlapping-date,
@@ -158,15 +158,11 @@
158
158
 
159
159
  /* No active selection */
160
160
 
161
-
162
-
163
-
164
- /* ---- */
165
-
166
161
  .will-root .will-calendar-filter-container .no-active-selection-start,
167
162
  .will-root .will-calendar-filter-container .no-active-selection-mid,
168
163
  .will-root .will-calendar-filter-container .no-active-selection-end {
169
164
  position: initial;
165
+
170
166
  }
171
167
 
172
168
  .will-root .will-calendar-filter-container .no-active-selection-start::before,
@@ -179,7 +175,6 @@
179
175
  border: 3px dashed var(--will-grey);
180
176
  }
181
177
 
182
-
183
178
  .will-root .will-calendar-filter-container .no-active-selection-start::before {
184
179
  border-right: none;
185
180
  border-top-left-radius: 50%;
@@ -202,7 +197,6 @@
202
197
  color: inherit;
203
198
  }
204
199
 
205
-
206
200
  /* Overlapping date */
207
201
 
208
202
  .will-root .will-calendar-filter-container .overlapping-date {
@@ -214,4 +208,17 @@
214
208
  cursor: not-allowed;
215
209
  }
216
210
 
211
+ @media (max-width: 600px) {
212
+ /* Tooltips */
213
+ .will-root .will-calendar-filter-container .will-calendar-tooltip,
214
+ .will-root .will-calendar-filter-container .will-calendar-tooltip-check-out,
215
+ .will-root .will-calendar-filter-container .will-calendar-tooltip-overlapping-date,
216
+ .will-root .will-calendar-filter-container .will-calendar-tooltip-check-out-only,
217
+ .will-root .will-calendar-filter-container .will-calendar-tooltip-check-in-only {
218
+ top: -70px;
219
+ white-space: wrap;
220
+ max-width: 120px;
221
+ }
222
+ }
223
+
217
224
 
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useEffect } from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import { startOfDay } from 'date-fns'
3
3
  import { fi, enUS } from 'date-fns/locale'
4
4
  import { useTranslation } from 'react-i18next'
@@ -118,6 +118,7 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
118
118
  rangeContext,
119
119
  calendarRange,
120
120
  calendarHasError,
121
+ disabledDates: newDisableCalendarDates?.disabledDates,
121
122
  })
122
123
 
123
124
  return (
@@ -119,5 +119,7 @@ export const useCalendarTooltips = ({ showFeedback }: Props) =>
119
119
  tooltipClonesCheckOut.forEach((clone) => clone.remove())
120
120
  tooltipClonesSpinner.forEach((clone) => clone.remove())
121
121
  tooltipClonesOverlappingDates.forEach((clone) => clone.remove())
122
+ tooltipClonesCheckInOnly.forEach((clone) => clone.remove())
123
+ tooltipClonesCheckOutOnly.forEach((clone) => clone.remove())
122
124
  }
123
125
  })
@@ -85,7 +85,7 @@ export const useUpdateDisabledDates = ({
85
85
 
86
86
  // Extract overlapping dates ( dates that are only available for checkout )
87
87
  if (newOverlappingDates.length) {
88
- setOverlappingDate((prev = []) => [...prev, ...newOverlappingDates])
88
+ setOverlappingDate([...newOverlappingDates])
89
89
  }
90
90
 
91
91
  const newDisableCalendarDates = {
@@ -103,5 +103,7 @@ export const useUpdateDisabledDates = ({
103
103
  updateCalendarDefaultMonth,
104
104
  ])
105
105
 
106
+ console.log('1111', overlappingDate)
107
+
106
108
  return { newDisableCalendarDates, overlappingDate, lastPossibleCheckout }
107
109
  }
@@ -1,14 +1,22 @@
1
1
  import { useEffect } from 'react'
2
2
  import { DateRange } from 'react-day-picker'
3
- import { isAfter, isEqual, isBefore, endOfDay } from 'date-fns'
3
+ import {
4
+ isAfter,
5
+ isEqual,
6
+ isBefore,
7
+ endOfDay,
8
+ isWithinInterval,
9
+ areIntervalsOverlapping,
10
+ } from 'date-fns'
4
11
 
5
- import { RangeContext } from '../CalendarTypes'
12
+ import { DisableCalendarDates, RangeContext } from '../CalendarTypes'
6
13
 
7
14
  type Props = {
8
15
  setCalendarHasError?: (arg: boolean) => void
9
16
  rangeContext?: RangeContext
10
17
  calendarRange?: DateRange
11
18
  calendarHasError?: boolean
19
+ disabledDates?: DisableCalendarDates['disabledDates']
12
20
  }
13
21
 
14
22
  // Case: If the selected dates do not overlap with the rangeContext during continuous selection,
@@ -20,34 +28,59 @@ export const checkForContinuousSelection = ({
20
28
  rangeContext,
21
29
  calendarRange,
22
30
  calendarHasError,
31
+ disabledDates,
23
32
  }: Props) => {
24
- const calendarRangeFrom = calendarRange?.from || null
25
- const calendarRangeTo = calendarRange?.to || null
26
- const rangeContextFrom = rangeContext?.from || null
27
- const rangeContextTo = rangeContext?.to || null
33
+ const calendarRangeFrom = calendarRange?.from && endOfDay(calendarRange.from)
34
+ const calendarRangeTo = calendarRange?.to && endOfDay(calendarRange.to)
35
+ const rangeContextFrom = rangeContext?.from && endOfDay(rangeContext.from)
36
+ const rangeContextTo = rangeContext?.to && endOfDay(rangeContext.to)
28
37
 
29
38
  // Checking if rangeFrom is equal to or before rangeContextTo
30
39
  const startIsEqualOrBeforeRangeContextEnd =
31
40
  calendarRangeFrom && rangeContextTo
32
- ? isBefore(endOfDay(calendarRangeFrom), endOfDay(rangeContextTo)) ||
33
- isEqual(endOfDay(calendarRangeFrom), endOfDay(rangeContextTo))
41
+ ? isBefore(calendarRangeFrom, rangeContextTo) ||
42
+ isEqual(calendarRangeFrom, rangeContextTo)
34
43
  : null
35
44
 
36
45
  // Checking if rangeTo is equal to or after rangeContextFrom
37
46
  const endIsEqualOrAfterRangeContextStart =
38
47
  calendarRangeTo && rangeContextFrom && rangeContextTo
39
- ? isAfter(endOfDay(calendarRangeTo), endOfDay(rangeContextFrom)) ||
40
- isEqual(endOfDay(calendarRangeTo), endOfDay(rangeContextFrom))
48
+ ? isAfter(calendarRangeTo, rangeContextFrom) ||
49
+ isEqual(calendarRangeTo, rangeContextFrom)
41
50
  : null
42
51
 
52
+ // Check if selection overlapping unavailable dates
53
+ const selectionOverlappingUnavailableDate = disabledDates?.find((range) => {
54
+ const rangeFrom = endOfDay(range.from)
55
+ const rangeTo = endOfDay(range.to)
56
+
57
+ return (
58
+ rangeContextFrom &&
59
+ rangeContextTo &&
60
+ areIntervalsOverlapping(
61
+ { start: rangeContextFrom, end: rangeContextTo },
62
+ { start: rangeFrom, end: rangeTo }
63
+ ) &&
64
+ calendarRangeFrom &&
65
+ calendarRangeTo &&
66
+ areIntervalsOverlapping(
67
+ { start: calendarRangeFrom, end: calendarRangeTo },
68
+ { start: rangeFrom, end: rangeTo }
69
+ )
70
+ )
71
+ })
72
+
43
73
  useEffect(() => {
44
74
  if (
45
75
  (rangeContext &&
46
76
  calendarRangeFrom &&
47
77
  !startIsEqualOrBeforeRangeContextEnd) ||
48
- (rangeContext && calendarRangeTo && !endIsEqualOrAfterRangeContextStart)
78
+ (rangeContext &&
79
+ calendarRangeTo &&
80
+ !endIsEqualOrAfterRangeContextStart) ||
81
+ selectionOverlappingUnavailableDate
49
82
  ) {
50
- setCalendarHasError && !calendarHasError && setCalendarHasError(true)
83
+ !!(setCalendarHasError && !calendarHasError) && setCalendarHasError(true)
51
84
  }
52
85
  })
53
86
  }
@@ -1,4 +1,4 @@
1
- import { addDays, endOfDay } from 'date-fns'
1
+ import { addDays, endOfDay, isAfter, isBefore } from 'date-fns'
2
2
  import { DateRange, Matcher } from 'react-day-picker'
3
3
 
4
4
  import { DisableCalendarDates, RangeContext } from '../CalendarTypes'
@@ -47,6 +47,27 @@ export const handleCalendarModifiers = ({
47
47
  const rangeContextFrom = rangeContext?.from && endOfDay(rangeContext.from)
48
48
  const rangeContextTo = rangeContext?.to && endOfDay(rangeContext.to)
49
49
 
50
+ const filteredOverlappingDates = overlappingDate
51
+ ? overlappingDate
52
+ .filter((date) => {
53
+ const dateFrom = date.from ? endOfDay(date.from) : null
54
+ const isBeforeRange =
55
+ dateFrom &&
56
+ findFirstPossibleRangeContextCheckIn?.checkIn &&
57
+ isBefore(dateFrom, findFirstPossibleRangeContextCheckIn?.checkIn)
58
+ const isAfterRange =
59
+ dateFrom &&
60
+ findLastPossibleRangeContextCheckOut?.lastCheckOut &&
61
+ isAfter(
62
+ dateFrom,
63
+ endOfDay(findLastPossibleRangeContextCheckOut?.lastCheckOut)
64
+ )
65
+
66
+ return !(isBeforeRange || isAfterRange)
67
+ })
68
+ .map((date) => ({ from: date.from }))
69
+ : []
70
+
50
71
  return {
51
72
  booked: disabledDatesByPage.length
52
73
  ? disabledDatesByPage
@@ -70,11 +91,10 @@ export const handleCalendarModifiers = ({
70
91
  ? [{ after: calendarRangeFrom }]
71
92
  : [],
72
93
 
73
- overlappingDate: [
74
- ...(!calendarRangeFrom && !!overlappingDate?.length
75
- ? overlappingDate.map((date) => ({ from: date.from }))
76
- : []),
77
- ],
94
+ overlappingDate:
95
+ !calendarRangeFrom && !!filteredOverlappingDates.length
96
+ ? filteredOverlappingDates.map((date) => ({ from: date.from }))
97
+ : [],
78
98
 
79
99
  noActiveSelectionStart: rangeContextFrom || [],
80
100
 
@@ -12,8 +12,8 @@
12
12
  "checkInOnly": "Check-in only",
13
13
  "errors": {
14
14
  "calendarErrors": {
15
- "checkInAvailabilityError": "Check-in available for second room only with connection dates",
16
- "checkInAvailabilityGuide": "Start or end day need connection for previous reservation"
15
+ "checkInAvailabilityError": "All room reservations in a single purchase must be for the same dates.",
16
+ "checkInAvailabilityGuide": "If you need reservations for different dates, please make a separate purchase."
17
17
  }
18
18
  }
19
19
  }
@@ -9,11 +9,11 @@
9
9
  "noCheckIn": "Huone ei saatavilla",
10
10
  "noCheckOut": "Uloskirjaus ei saatavilla",
11
11
  "checkOutOnly": "Vain uloskirjaus",
12
- "checkInOnly": "Check-in only",
12
+ "checkInOnly": "Vain sisäänkirjautuminen",
13
13
  "errors": {
14
14
  "calendarErrors": {
15
- "checkInAvailabilityError": "Check-in available for second room only with connection dates",
16
- "checkInAvailabilityGuide": "Start or end day need connection for previous reservation"
15
+ "checkInAvailabilityError": "Yhdellä ostolla tehdyt huonevaraukset tulee olla samalle ajankohdalle.",
16
+ "checkInAvailabilityGuide": "Jos tarvitset huonevarauksia eri ajankohdille, ole hyvä ja tee uusi osto."
17
17
  }
18
18
  }
19
19
  }
@@ -21,21 +21,23 @@
21
21
  --will-onahau: #CDEEFF;
22
22
  --will-text: #5A5959;
23
23
  --will-charcoal-blue: #384265;
24
- --will-transparent-white: rgba(255, 255, 255, 0.30);
25
- --will-transparent-black: rgba(171, 167, 175, 0.30);
26
24
  --will-error: #d32f2f;
27
25
 
28
- /*Color mix*/
26
+ /* Transparent */
27
+ --will-transparent-black: rgba(0, 0, 0, 0.5);
28
+ --will-transparent-white: rgba(255, 255, 255, 0.30);
29
+ --will-transparent-lavender: rgba(171, 167, 175, 0.30);
30
+
31
+ /* Color mix */
29
32
  --will-primary-lighter: color-mix(in srgb, var(--will-primary), white 50%);
30
33
  --will-primary-lightest: color-mix(in srgb, var(--will-primary), white 80%);
31
34
 
32
35
 
33
- /* Confines */
36
+ /* Shadows */
34
37
  --will-box-shadow-dark: 0px 2px 12px 2px #a1a1a180;
35
38
  --will-box-shadow-light: 0px 2px 12px 2px #bcb9b980;
36
39
 
37
40
  /* Breakpoints */
38
-
39
41
  --will-lg: 1140px;
40
42
  --will-md: 960px;
41
43
  --will-sm: 600px;