willba-component-library 0.2.80 → 0.2.82

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.
Files changed (54) hide show
  1. package/lib/components/FilterBar/FilterBar.stories.d.ts +1 -1
  2. package/lib/components/FilterBar/FilterBarTypes.d.ts +2 -2
  3. package/lib/components/FilterCalendar/FilterCalendar.d.ts +1 -1
  4. package/lib/components/FilterCalendar/FilterCalendar.stories.d.ts +3 -1
  5. package/lib/components/FilterCalendar/hooks/useFilterCalendar.d.ts +10 -3
  6. package/lib/core/components/buttons/submit-button/SubmitButton.d.ts +3 -1
  7. package/lib/core/components/calendar/CalendarTypes.d.ts +9 -2
  8. package/lib/index.d.ts +11 -4
  9. package/lib/index.esm.js +896 -268
  10. package/lib/index.esm.js.map +1 -1
  11. package/lib/index.js +896 -268
  12. package/lib/index.js.map +1 -1
  13. package/lib/index.umd.js +896 -268
  14. package/lib/index.umd.js.map +1 -1
  15. package/lib/themes/useTheme.d.ts +2 -0
  16. package/package.json +1 -1
  17. package/src/assets/IconsSvg.tsx +68 -0
  18. package/src/components/FilterBar/FilterBar.stories.tsx +2 -1
  19. package/src/components/FilterBar/FilterBar.tsx +2 -2
  20. package/src/components/FilterBar/FilterBarTypes.ts +1 -1
  21. package/src/components/FilterBar/components/buttons/tab-button/TabButton.css +1 -1
  22. package/src/components/FilterBar/hooks/useFilterBar.tsx +1 -1
  23. package/src/components/FilterCalendar/FilterCalendar.css +26 -10
  24. package/src/components/FilterCalendar/FilterCalendar.stories.tsx +522 -153
  25. package/src/components/FilterCalendar/FilterCalendar.tsx +25 -54
  26. package/src/components/FilterCalendar/FilterCalendarTypes.ts +0 -1
  27. package/src/components/FilterCalendar/components/Footer.tsx +96 -0
  28. package/src/components/FilterCalendar/hooks/useFilterCalendar.ts +94 -4
  29. package/src/core/components/buttons/submit-button/SubmitButton.css +16 -2
  30. package/src/core/components/buttons/submit-button/SubmitButton.tsx +6 -5
  31. package/src/core/components/calendar/Calendar.css +71 -18
  32. package/src/core/components/calendar/Calendar.tsx +132 -342
  33. package/src/core/components/calendar/CalendarTypes.ts +10 -3
  34. package/src/core/components/calendar/hooks/index.ts +3 -0
  35. package/src/core/components/calendar/hooks/useCalendarLoadingSpinner.tsx +19 -0
  36. package/src/core/components/calendar/hooks/useCalendarTooltips.tsx +125 -0
  37. package/src/core/components/calendar/hooks/useUpdateDisabledDates.tsx +107 -0
  38. package/src/core/components/calendar/utils/calendarSelectionRules.tsx +228 -0
  39. package/src/core/components/calendar/utils/checkForContinuousSelection.tsx +86 -0
  40. package/src/core/components/calendar/utils/disabledDatesByPage.tsx +36 -0
  41. package/src/core/components/calendar/utils/handleCalendarModifiers.tsx +147 -0
  42. package/src/core/components/calendar/utils/handleRangeContextDisabledDates.tsx +75 -0
  43. package/src/core/components/calendar/utils/index.ts +8 -0
  44. package/src/locales/en/common.json +7 -1
  45. package/src/locales/en/filterBar.json +2 -1
  46. package/src/locales/fi/common.json +7 -1
  47. package/src/locales/fi/filterBar.json +2 -1
  48. package/src/themes/Default.css +12 -3
  49. package/src/themes/useTheme.tsx +3 -0
  50. package/src/assets/SpinnerSvg.tsx +0 -40
  51. package/src/core/utils/index.ts +0 -3
  52. /package/src/core/{utils → components/calendar/utils}/nightsCount.tsx +0 -0
  53. /package/src/core/{utils → components/calendar/utils}/parseDate.tsx +0 -0
  54. /package/src/core/{utils → components/calendar/utils}/parseDates.tsx +0 -0
@@ -0,0 +1,147 @@
1
+ import { addDays, endOfDay, isAfter, isBefore } from 'date-fns'
2
+ import { DateRange, Matcher } from 'react-day-picker'
3
+
4
+ import { DisableCalendarDates, RangeContext } from '../CalendarTypes'
5
+
6
+ type Props = {
7
+ newDisableCalendarDates?: DisableCalendarDates
8
+ calendarRange?: DateRange
9
+ disabledDatesByPage: {
10
+ from: Date
11
+ to: Date
12
+ }[]
13
+ disabledDates?: Matcher[]
14
+ lastPossibleCheckout?: Matcher
15
+ overlappingDate?: DateRange[]
16
+ rangeContext?: RangeContext
17
+ findFirstPossibleRangeContextCheckIn?: NonNullable<
18
+ DisableCalendarDates['availableDates']
19
+ >['0']
20
+ findLastPossibleRangeContextCheckOut?: NonNullable<
21
+ DisableCalendarDates['availableDates']
22
+ >['0']
23
+ firstPossibleRangeContextCheckIn: Matcher[]
24
+ lastPossibleRangeContextCheckOut: Matcher[]
25
+ currentSelectionLastCheckoutDate?: NonNullable<
26
+ DisableCalendarDates['availableDates']
27
+ >['0']
28
+ }
29
+
30
+ export const handleCalendarModifiers = ({
31
+ newDisableCalendarDates,
32
+ calendarRange,
33
+ disabledDatesByPage,
34
+ disabledDates,
35
+ overlappingDate,
36
+ rangeContext,
37
+ firstPossibleRangeContextCheckIn,
38
+ lastPossibleRangeContextCheckOut,
39
+ findFirstPossibleRangeContextCheckIn,
40
+ findLastPossibleRangeContextCheckOut,
41
+ currentSelectionLastCheckoutDate,
42
+ lastPossibleCheckout,
43
+ }: Props) => {
44
+ // Parse data
45
+ const calendarRangeFrom = calendarRange?.from && endOfDay(calendarRange.from)
46
+ const calendarRangeTo = calendarRange?.to && endOfDay(calendarRange.to)
47
+ const rangeContextFrom = rangeContext?.from && endOfDay(rangeContext.from)
48
+ const rangeContextTo = rangeContext?.to && endOfDay(rangeContext.to)
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
+
71
+ return {
72
+ booked: disabledDatesByPage.length
73
+ ? disabledDatesByPage
74
+ : disabledDates?.length
75
+ ? [
76
+ lastPossibleCheckout || [],
77
+ ...disabledDates,
78
+ ...firstPossibleRangeContextCheckIn,
79
+ ...lastPossibleRangeContextCheckOut,
80
+ ]
81
+ : newDisableCalendarDates?.disabledDates?.length
82
+ ? [
83
+ lastPossibleCheckout || [],
84
+ ...newDisableCalendarDates?.disabledDates,
85
+ ...firstPossibleRangeContextCheckIn,
86
+ ...lastPossibleRangeContextCheckOut,
87
+ ]
88
+ : [],
89
+
90
+ disabledAfterCheckIn: calendarRangeFrom
91
+ ? [{ after: calendarRangeFrom }]
92
+ : [],
93
+
94
+ overlappingDate:
95
+ !calendarRangeFrom && !!filteredOverlappingDates.length
96
+ ? filteredOverlappingDates.map((date) => ({ from: date.from }))
97
+ : [],
98
+
99
+ noActiveSelectionStart: rangeContextFrom || [],
100
+
101
+ noActiveSelectionMid: [
102
+ rangeContextFrom && rangeContextTo
103
+ ? { after: rangeContextFrom, before: rangeContextTo }
104
+ : [],
105
+ ],
106
+
107
+ noActiveSelectionEnd: rangeContextTo || [],
108
+
109
+ checkoutOptionsMid: [
110
+ ...(calendarRangeFrom &&
111
+ !calendarRangeTo &&
112
+ currentSelectionLastCheckoutDate?.lastCheckOut
113
+ ? [
114
+ {
115
+ after: calendarRangeFrom,
116
+ before: addDays(currentSelectionLastCheckoutDate.lastCheckOut, 1),
117
+ },
118
+ ]
119
+ : []),
120
+ ],
121
+
122
+ checkInOnly: [
123
+ ...(findFirstPossibleRangeContextCheckIn?.checkIn &&
124
+ rangeContext &&
125
+ rangeContextFrom
126
+ ? [
127
+ {
128
+ from: findFirstPossibleRangeContextCheckIn.checkIn,
129
+ to: addDays(rangeContextFrom, -1),
130
+ },
131
+ ]
132
+ : []),
133
+ ],
134
+
135
+ checkOutOnly: [
136
+ ...(findLastPossibleRangeContextCheckOut?.checkIn &&
137
+ findLastPossibleRangeContextCheckOut.lastCheckOut
138
+ ? [
139
+ {
140
+ from: addDays(findLastPossibleRangeContextCheckOut.checkIn, 1),
141
+ to: findLastPossibleRangeContextCheckOut.lastCheckOut,
142
+ },
143
+ ]
144
+ : []),
145
+ ],
146
+ }
147
+ }
@@ -0,0 +1,75 @@
1
+ import { DateRange, Matcher } from 'react-day-picker'
2
+ import { isEqual, endOfDay } from 'date-fns'
3
+
4
+ import { DisableCalendarDates, RangeContext } from '../CalendarTypes'
5
+
6
+ type Props = {
7
+ rangeContext?: RangeContext
8
+ availableDates?: DisableCalendarDates['availableDates']
9
+ calendarRange?: DateRange
10
+ }
11
+
12
+ export const handleRangeContextDisabledDates = ({
13
+ rangeContext,
14
+ availableDates,
15
+ calendarRange,
16
+ }: Props) => {
17
+ let findFirstPossibleRangeContextCheckIn:
18
+ | NonNullable<DisableCalendarDates['availableDates']>['0']
19
+ | undefined
20
+ let findLastPossibleRangeContextCheckOut:
21
+ | NonNullable<DisableCalendarDates['availableDates']>['0']
22
+ | undefined
23
+
24
+ let firstPossibleRangeContextCheckIn: Matcher[] = []
25
+ let lastPossibleRangeContextCheckOut: Matcher[] = []
26
+
27
+ // Parse data
28
+ const rangeContextFrom = rangeContext?.from && endOfDay(rangeContext.from)
29
+ const rangeContextTo = rangeContext?.to && endOfDay(rangeContext.to)
30
+ const calendarRangeFrom = calendarRange?.from && endOfDay(calendarRange.from)
31
+
32
+ if (rangeContext && availableDates?.length) {
33
+ // Find the first possible check-in after the last date gap till the range context
34
+
35
+ findFirstPossibleRangeContextCheckIn = availableDates?.find(
36
+ (date) =>
37
+ rangeContextFrom &&
38
+ endOfDay(date.checkIn) < rangeContextFrom &&
39
+ endOfDay(date.lastCheckOut) >= rangeContextFrom
40
+ )
41
+
42
+ if (findFirstPossibleRangeContextCheckIn?.checkIn) {
43
+ firstPossibleRangeContextCheckIn.push({
44
+ before: findFirstPossibleRangeContextCheckIn.checkIn,
45
+ })
46
+ }
47
+
48
+ // Find the last possible checkout before the first date gap till the range context
49
+ findLastPossibleRangeContextCheckOut = availableDates?.find(
50
+ (date) =>
51
+ rangeContextTo && isEqual(rangeContextTo, endOfDay(date.checkIn))
52
+ )
53
+
54
+ if (findLastPossibleRangeContextCheckOut?.checkIn) {
55
+ lastPossibleRangeContextCheckOut.push({
56
+ after: findLastPossibleRangeContextCheckOut.lastCheckOut,
57
+ })
58
+ }
59
+ }
60
+
61
+ // Get last possible check-out dates for current check-in
62
+ const currentSelectionLastCheckoutDate = availableDates?.find((date) => {
63
+ return calendarRangeFrom
64
+ ? isEqual(endOfDay(date.checkIn), calendarRangeFrom)
65
+ : false
66
+ })
67
+
68
+ return {
69
+ findFirstPossibleRangeContextCheckIn,
70
+ findLastPossibleRangeContextCheckOut,
71
+ firstPossibleRangeContextCheckIn,
72
+ lastPossibleRangeContextCheckOut,
73
+ currentSelectionLastCheckoutDate,
74
+ }
75
+ }
@@ -0,0 +1,8 @@
1
+ export { parseDates } from './parseDates'
2
+ export { parseDate } from './parseDate'
3
+ export { nightsCount } from './nightsCount'
4
+ export { calendarSelectionRules } from './calendarSelectionRules'
5
+ export { disabledDatesByPage } from './disabledDatesByPage'
6
+ export { handleCalendarModifiers } from './handleCalendarModifiers'
7
+ export { handleRangeContextDisabledDates } from './handleRangeContextDisabledDates'
8
+ export { checkForContinuousSelection } from './checkForContinuousSelection'
@@ -8,5 +8,11 @@
8
8
  "clearDates": "Clear dates",
9
9
  "noCheckIn": "Room not available",
10
10
  "noCheckOut": "Check-out not available",
11
- "checkOutOnly": "Check-out only"
11
+ "checkOutOnly": "Check-out only",
12
+ "checkInOnly": "Check-in only",
13
+ "errors": {
14
+ "calendarErrors": {
15
+ "checkInAvailabilityError": "All room reservations in a single purchase must be for the same dates. If you need reservations for different dates, please make a separate purchase."
16
+ }
17
+ }
12
18
  }
@@ -7,7 +7,8 @@
7
7
  "endDate": "End date",
8
8
  "title": "Calendar",
9
9
  "checkoutOnly": "Check-out only",
10
- "hasDisableDates": "Contains unavailable dates"
10
+ "hasDisableDates": "Contains unavailable dates",
11
+ "minNights": "2 nights min"
11
12
  },
12
13
  "guests": {
13
14
  "label": "Number of guests",
@@ -8,6 +8,12 @@
8
8
  "clearDates": "Tyhjennä",
9
9
  "noCheckIn": "Huone ei saatavilla",
10
10
  "noCheckOut": "Uloskirjaus ei saatavilla",
11
- "checkOutOnly": "Vain uloskirjaus"
11
+ "checkOutOnly": "Vain uloskirjaus",
12
+ "checkInOnly": "Vain sisäänkirjaus",
13
+ "errors": {
14
+ "calendarErrors": {
15
+ "checkInAvailabilityError": "Yhdellä ostolla tehdyt huonevaraukset tulee olla samalle ajankohdalle. Jos tarvitset huonevarauksia eri ajankohdille, ole hyvä ja tee uusi osto."
16
+ }
17
+ }
12
18
  }
13
19
 
@@ -7,7 +7,8 @@
7
7
  "endDate": "Loppu",
8
8
  "title": "Kalenteri",
9
9
  "checkoutOnly": "Check-out only",
10
- "hasDisableDates": "Contains unavailable dates"
10
+ "hasDisableDates": "Contains unavailable dates",
11
+ "minNights": "2 nights min"
11
12
  },
12
13
  "guests": {
13
14
  "label": "Vierasmäärä",
@@ -15,21 +15,30 @@
15
15
  --will-primary: #374269;
16
16
  --will-secondary: #374269;
17
17
  --will-grey: #ABA7AF;
18
+ --will-light-grey: #C8C8C8;
18
19
  --will-white: #fff;
19
20
  --will-white-transparent: #ffffffcf;
20
21
  --will-black: #000;
21
22
  --will-onahau: #CDEEFF;
22
23
  --will-text: #5A5959;
23
24
  --will-charcoal-blue: #384265;
25
+ --will-error: #d32f2f;
26
+
27
+ /* Transparent */
28
+ --will-transparent-black: rgba(0, 0, 0, 0.5);
24
29
  --will-transparent-white: rgba(255, 255, 255, 0.30);
25
- --will-transparent-black: rgba(171, 167, 175, 0.30);
30
+ --will-transparent-lavender: rgba(171, 167, 175, 0.30);
31
+
32
+ /* Color mix */
33
+ --will-primary-lighter: color-mix(in srgb, var(--will-primary), white 50%);
34
+ --will-primary-lightest: color-mix(in srgb, var(--will-primary), white 80%);
26
35
 
27
- /* Confines */
36
+
37
+ /* Shadows */
28
38
  --will-box-shadow-dark: 0px 2px 12px 2px #a1a1a180;
29
39
  --will-box-shadow-light: 0px 2px 12px 2px #bcb9b980;
30
40
 
31
41
  /* Breakpoints */
32
-
33
42
  --will-lg: 1140px;
34
43
  --will-md: 960px;
35
44
  --will-sm: 600px;
@@ -3,6 +3,7 @@ import { CSSProperties } from 'react'
3
3
  export type Palette = {
4
4
  primary: string
5
5
  secondary: string
6
+ error: string
6
7
  }
7
8
 
8
9
  export type ThemeProps = {
@@ -12,12 +13,14 @@ export type ThemeProps = {
12
13
  export interface CustomPaletteTypes extends CSSProperties {
13
14
  '--will-primary'?: string
14
15
  '--will-secondary'?: string
16
+ '--will-error'?: string
15
17
  }
16
18
 
17
19
  export default function useTheme({ palette }: ThemeProps) {
18
20
  const themePalette: CustomPaletteTypes = {
19
21
  '--will-primary': palette?.primary,
20
22
  '--will-secondary': palette?.secondary,
23
+ '--will-error': palette?.error,
21
24
  }
22
25
 
23
26
  return themePalette
@@ -1,40 +0,0 @@
1
- import React from 'react'
2
-
3
- export const SpinnerSVG = ({
4
- fill,
5
- size,
6
- }: {
7
- fill?: string
8
- size?: number
9
- }) => (
10
- <svg
11
- xmlns="http://www.w3.org/2000/svg"
12
- width={`${size || 25}`}
13
- height={`${size || 25}`}
14
- viewBox="0 0 24 24"
15
- >
16
- <style>
17
- {`
18
- .spinner_z9k8 {
19
- transform-origin: center;
20
- animation: spinner_StKS .75s infinite linear;
21
- }
22
- @keyframes spinner_StKS {
23
- 100% {
24
- transform: rotate(360deg);
25
- }
26
- }
27
- `}
28
- </style>
29
- <path
30
- d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
31
- opacity=".25"
32
- fill={fill}
33
- />
34
- <path
35
- d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"
36
- className="spinner_z9k8"
37
- fill={fill}
38
- />
39
- </svg>
40
- )
@@ -1,3 +0,0 @@
1
- export { parseDates } from './parseDates'
2
- export { parseDate } from './parseDate'
3
- export { nightsCount } from './nightsCount'