@autobusal/common 0.0.17 → 0.0.19
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/Autocomplete/Autocomplete.tsx +105 -0
- package/Autocomplete/Suggestions.tsx +24 -0
- package/Autocomplete/styles.ts +38 -0
- package/Autocomplete/types.ts +4 -0
- package/Button/styles.ts +1 -0
- package/Calendar/Browse/Browse.tsx +65 -0
- package/Calendar/Browse/styles.ts +34 -0
- package/Calendar/Calendar.tsx +56 -0
- package/Calendar/Days.tsx +101 -0
- package/Calendar/Header/Header.tsx +30 -0
- package/Calendar/Header/styles.ts +15 -0
- package/Calendar/Picker.tsx +45 -0
- package/Calendar/styles.ts +69 -0
- package/Calendar/types.ts +28 -0
- package/Calendar/utilities/browse.tsx +53 -0
- package/Calendar/utilities/settings.ts +86 -0
- package/Pagination/styles.ts +1 -0
- package/Passengers/Passengers.tsx +74 -0
- package/Passengers/Person.tsx +48 -0
- package/Passengers/Persons.tsx +41 -0
- package/Passengers/styles.ts +70 -0
- package/Passengers/utilities/getText.ts +21 -0
- package/RouteItem/styles.ts +1 -0
- package/index.ts +8 -0
- package/package.json +3 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useState, useRef, ChangeEvent, KeyboardEvent } from 'react';
|
|
2
|
+
import { UseFormRegister, UseFormSetValue } from 'react-hook-form';
|
|
3
|
+
import { TFunction } from 'i18next';
|
|
4
|
+
import { useOutside } from '@autobusal/hooks';
|
|
5
|
+
import { validate } from '@autobusal/utilities';
|
|
6
|
+
import Suggestions from './Suggestions';
|
|
7
|
+
import { Container } from './styles';
|
|
8
|
+
import { AutocompleteData } from './types';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
name: 'from' | 'to'
|
|
12
|
+
defaultValue: string
|
|
13
|
+
data: AutocompleteData[]
|
|
14
|
+
validation: string
|
|
15
|
+
t: TFunction<'general'>
|
|
16
|
+
refs: UseFormRegister<any>
|
|
17
|
+
onUpdate: UseFormSetValue<any>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Autocomplete = ({ name, defaultValue, data, validation, t, refs, onUpdate }: Props): JSX.Element => {
|
|
21
|
+
const found = data.filter(item => {
|
|
22
|
+
return item.value === defaultValue;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const [ show, setShow ] = useState<boolean>(false);
|
|
26
|
+
const [ q, setQ ] = useState<string>(found.length > 0 ? found[0].name : '');
|
|
27
|
+
const [ selected, setSelected ] = useState<number>(-1);
|
|
28
|
+
const [ value, setValue ] = useState<string>(defaultValue);
|
|
29
|
+
|
|
30
|
+
const ref = useRef(null);
|
|
31
|
+
|
|
32
|
+
useOutside(ref, () => setShow(false));
|
|
33
|
+
|
|
34
|
+
const options = data.filter(item => {
|
|
35
|
+
return item.name.toLowerCase().indexOf(q.toLowerCase()) > -1;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const onChange = (event: ChangeEvent<HTMLInputElement>): void => {
|
|
39
|
+
setQ(event.target.value);
|
|
40
|
+
|
|
41
|
+
setShow(true);
|
|
42
|
+
setSelected(-1);
|
|
43
|
+
|
|
44
|
+
setValue('');
|
|
45
|
+
onUpdate(name, '');
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>): void => {
|
|
49
|
+
if (event.code === 'Escape') {
|
|
50
|
+
setShow(false);
|
|
51
|
+
} else if (event.code === 'ArrowUp') {
|
|
52
|
+
let value = selected - 1;
|
|
53
|
+
|
|
54
|
+
if (value < 0) {
|
|
55
|
+
value = 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setSelected(value);
|
|
59
|
+
} else if (event.code === 'ArrowDown') {
|
|
60
|
+
let value = selected + 1;
|
|
61
|
+
|
|
62
|
+
if (value > options.length - 1) {
|
|
63
|
+
value = options.length - 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setSelected(value);
|
|
67
|
+
} else if (event.code === 'Enter') {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
onSelected(selected);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const onSelected = (index: number): void => {
|
|
74
|
+
const selected = options[index];
|
|
75
|
+
|
|
76
|
+
if (selected !== undefined) {
|
|
77
|
+
setShow(false);
|
|
78
|
+
setSelected(-1);
|
|
79
|
+
|
|
80
|
+
setQ(selected.name);
|
|
81
|
+
|
|
82
|
+
setValue(selected.value);
|
|
83
|
+
onUpdate(name, selected.value);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Container ref={ ref }>
|
|
89
|
+
<input
|
|
90
|
+
type="text"
|
|
91
|
+
name={ `${ name }_slug` }
|
|
92
|
+
autoComplete="off"
|
|
93
|
+
value={ q }
|
|
94
|
+
onChange={ (event) => onChange(event) }
|
|
95
|
+
onKeyDown={ (event) => onKeyDown(event) }
|
|
96
|
+
/>
|
|
97
|
+
|
|
98
|
+
{ (show && q.length > 1 && options.length > 0) && <Suggestions options={ options } selected={ selected } onClick={ onSelected } /> }
|
|
99
|
+
|
|
100
|
+
<input type="hidden" defaultValue={ value } { ...refs(name, validate(validation, t)) } />
|
|
101
|
+
</Container>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default Autocomplete;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ContainerSuggestions, ButtonSuggestion } from './styles';
|
|
2
|
+
import { AutocompleteData } from './types';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
options: AutocompleteData[]
|
|
6
|
+
selected: number
|
|
7
|
+
onClick: (index: number) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Suggestions = ({ options, selected, onClick }: Props): JSX.Element => {
|
|
11
|
+
const items = options.map((item, index) => (
|
|
12
|
+
<ButtonSuggestion key={ index } type="button" $selected={ selected === index } onClick={ () => onClick(index) }>
|
|
13
|
+
{ item.name }
|
|
14
|
+
</ButtonSuggestion>
|
|
15
|
+
));
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<ContainerSuggestions>
|
|
19
|
+
{ items }
|
|
20
|
+
</ContainerSuggestions>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default Suggestions;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Container = styled.div`
|
|
4
|
+
position: relative;
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
export const ContainerSuggestions = styled.div`
|
|
8
|
+
position: absolute;
|
|
9
|
+
top: 44px;
|
|
10
|
+
left: 0;
|
|
11
|
+
z-index: 100;
|
|
12
|
+
width: 250px;
|
|
13
|
+
max-height: 200px;
|
|
14
|
+
overflow-y: auto;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
gap: 6px;
|
|
18
|
+
padding: 6px;
|
|
19
|
+
background: ${ props => props.theme.foreground.normal };
|
|
20
|
+
box-shadow: ${ props => props.theme.boxShadow };
|
|
21
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const ButtonSuggestion = styled.button<{ $selected: boolean }>`
|
|
25
|
+
padding: 6px 12px;
|
|
26
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
27
|
+
transition: all 0.3s ease;
|
|
28
|
+
|
|
29
|
+
&:hover, &.picked {
|
|
30
|
+
color: ${ props => props.theme.primary.contrast };
|
|
31
|
+
background-color: ${ props => props.theme.primary.normal };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
${ props => props.$selected && css`
|
|
35
|
+
color: ${ props => props.theme.primary.contrast };
|
|
36
|
+
background-color: ${ props => props.theme.primary.normal };
|
|
37
|
+
` }
|
|
38
|
+
`;
|
package/Button/styles.ts
CHANGED
|
@@ -22,6 +22,7 @@ export const ButtonStyled = styled.button<ButtonType>`
|
|
|
22
22
|
justify-content: center;
|
|
23
23
|
padding: 0 20px;
|
|
24
24
|
font-weight: 700;
|
|
25
|
+
color: ${ props => props.theme.primary.contrast };
|
|
25
26
|
background: ${ props => props.theme.primary.normal };
|
|
26
27
|
border: 1px solid ${ props => props.theme.primary.normal };
|
|
27
28
|
border-radius: 100px;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ChangeEvent } from 'react';
|
|
2
|
+
import { TFunction } from 'i18next';
|
|
3
|
+
import { AiOutlineArrowLeft, AiOutlineArrowRight } from 'react-icons/ai';
|
|
4
|
+
import { createDate } from '@autobusal/utilities';
|
|
5
|
+
import { getMonths, getYears } from '../utilities/browse';
|
|
6
|
+
import { Container, ButtonBrowse, Choose, Select } from './styles';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
type: 'picker' | 'dob' | 'date'
|
|
10
|
+
value: string
|
|
11
|
+
t: TFunction<'general'>
|
|
12
|
+
onChange: (date: Date) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Browse = ({ type, value, t, onChange }: Props): JSX.Element => {
|
|
16
|
+
const date = createDate(value);
|
|
17
|
+
|
|
18
|
+
const onPrevious = (): void => {
|
|
19
|
+
date.setMonth(date.getMonth() - 1);
|
|
20
|
+
|
|
21
|
+
onChange(date);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const onNext = (): void => {
|
|
25
|
+
date.setMonth(date.getMonth() + 1);
|
|
26
|
+
|
|
27
|
+
onChange(date);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const onMonth = (event: ChangeEvent<HTMLSelectElement>): void => {
|
|
31
|
+
date.setMonth(Number(event.target.value));
|
|
32
|
+
|
|
33
|
+
onChange(date);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const onYear = (event: ChangeEvent<HTMLSelectElement>): void => {
|
|
37
|
+
date.setFullYear(Number(event.target.value));
|
|
38
|
+
|
|
39
|
+
onChange(date);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Container>
|
|
44
|
+
<ButtonBrowse type="button" onClick={onPrevious}>
|
|
45
|
+
<AiOutlineArrowLeft />
|
|
46
|
+
</ButtonBrowse>
|
|
47
|
+
|
|
48
|
+
<Choose>
|
|
49
|
+
<Select value={ date.getMonth() } onChange={ onMonth }>
|
|
50
|
+
{ getMonths(t) }
|
|
51
|
+
</Select>
|
|
52
|
+
|
|
53
|
+
<Select $type="year" value={ date.getFullYear() } onChange={ onYear }>
|
|
54
|
+
{ getYears(type, date.getFullYear()) }
|
|
55
|
+
</Select>
|
|
56
|
+
</Choose>
|
|
57
|
+
|
|
58
|
+
<ButtonBrowse type="button" onClick={onNext}>
|
|
59
|
+
<AiOutlineArrowRight />
|
|
60
|
+
</ButtonBrowse>
|
|
61
|
+
</Container>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default Browse;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Container = styled.div`
|
|
4
|
+
display: flex;
|
|
5
|
+
gap: 5px;
|
|
6
|
+
padding-bottom: 5px;
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
export const ButtonBrowse = styled.button`
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
align-items: center;
|
|
13
|
+
width: 26px;
|
|
14
|
+
height: 26px;
|
|
15
|
+
font-size: ${ props => props.theme.size.s };
|
|
16
|
+
background-color: ${ props => props.theme.background.neutral };
|
|
17
|
+
border-radius: 100px;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
export const Choose = styled.div`
|
|
21
|
+
flex: 1;
|
|
22
|
+
display: flex;
|
|
23
|
+
gap: 5px;
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export const Select = styled.select<{$type?: 'year'}>`
|
|
27
|
+
height: 26px;
|
|
28
|
+
${props => props.$type === 'year' && css`
|
|
29
|
+
flex-basis: 90px;
|
|
30
|
+
`}
|
|
31
|
+
text-align: center;
|
|
32
|
+
background-color: ${ props => props.theme.background.neutral };
|
|
33
|
+
border: none;
|
|
34
|
+
`;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useState, useRef } from 'react';
|
|
2
|
+
import { UseFormRegister, UseFormSetValue } from 'react-hook-form';
|
|
3
|
+
import { TFunction } from 'i18next';
|
|
4
|
+
import { useOutside } from '@autobusal/hooks';
|
|
5
|
+
import { validate, prepareDate } from '@autobusal/utilities';
|
|
6
|
+
import Picker from './Picker';
|
|
7
|
+
import { Container } from './styles';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
type: 'picker' | 'dob' | 'date'
|
|
11
|
+
name: string
|
|
12
|
+
defaultValue?: string
|
|
13
|
+
t: TFunction<'general'>
|
|
14
|
+
validation?: string
|
|
15
|
+
refs: UseFormRegister<any>
|
|
16
|
+
onUpdate: UseFormSetValue<any>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Calendar = ({ type, name, defaultValue, t, validation, refs, onUpdate }: Props): JSX.Element => {
|
|
20
|
+
const [ show, setShow ] = useState<boolean>(false);
|
|
21
|
+
const [ prepared, setPrepared ] = useState<string>(defaultValue ?? prepareDate(new Date()));
|
|
22
|
+
const [ selected, setSelected ] = useState<string>(prepared);
|
|
23
|
+
|
|
24
|
+
const ref = useRef(null);
|
|
25
|
+
|
|
26
|
+
useOutside(ref, () => setShow(false));
|
|
27
|
+
|
|
28
|
+
const onClick = (prepared: string): void => {
|
|
29
|
+
setShow(false);
|
|
30
|
+
|
|
31
|
+
setSelected(prepared);
|
|
32
|
+
setPrepared(prepared);
|
|
33
|
+
|
|
34
|
+
onUpdate(name, prepared);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Container ref={ ref }>
|
|
39
|
+
<input type="text" autoComplete="off" readOnly={ true } value={ prepared } onClick={ () => setShow(true) } />
|
|
40
|
+
|
|
41
|
+
{ show && (
|
|
42
|
+
<Picker
|
|
43
|
+
type={ type }
|
|
44
|
+
value={ prepared }
|
|
45
|
+
selected={ selected }
|
|
46
|
+
t={ t }
|
|
47
|
+
onClick={ onClick }
|
|
48
|
+
/>
|
|
49
|
+
) }
|
|
50
|
+
|
|
51
|
+
<input type="hidden" defaultValue={ prepared } { ...refs(name, validate(validation ?? '', t)) } />
|
|
52
|
+
</Container>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default Calendar;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { createDate, prepareDate } from '@autobusal/utilities';
|
|
3
|
+
import { getCalendar, getLimits, compareDates } from './utilities/settings';
|
|
4
|
+
import { ContainerDays, Day } from './styles';
|
|
5
|
+
import { CalendarData } from './types';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
type: 'picker' | 'dob' | 'date'
|
|
9
|
+
value: string
|
|
10
|
+
selected: string
|
|
11
|
+
onSelected: (prepared: string, disabled: boolean) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Days = ({ type, value, selected, onSelected }: Props): JSX.Element => {
|
|
15
|
+
const [ calendar, setCalendar ] = useState<CalendarData>(getCalendar(value));
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setCalendar(getCalendar(value));
|
|
19
|
+
}, [value]);
|
|
20
|
+
|
|
21
|
+
const today = prepareDate(new Date());
|
|
22
|
+
|
|
23
|
+
const limits = getLimits(type);
|
|
24
|
+
|
|
25
|
+
const rows: JSX.Element[] = [];
|
|
26
|
+
|
|
27
|
+
let currentDay: number = 1;
|
|
28
|
+
|
|
29
|
+
// prepare the previous date
|
|
30
|
+
const previousDate: Date = createDate(value);
|
|
31
|
+
previousDate.setMonth(previousDate.getMonth() - 1);
|
|
32
|
+
|
|
33
|
+
// setup the current date
|
|
34
|
+
const currentDate: Date = createDate(value);
|
|
35
|
+
|
|
36
|
+
// prepare the next date
|
|
37
|
+
const nextDate: Date = createDate(value);
|
|
38
|
+
nextDate.setMonth(nextDate.getMonth() + 1);
|
|
39
|
+
|
|
40
|
+
for (let i = 1; i <= calendar.weeks; i++) {
|
|
41
|
+
for (let j = 1; j <= 7; j++) {
|
|
42
|
+
const key = i * 10 + j;
|
|
43
|
+
|
|
44
|
+
if (i == 1 && j < calendar.begins) {
|
|
45
|
+
previousDate.setDate(calendar.previous);
|
|
46
|
+
|
|
47
|
+
const disabled = compareDates(previousDate, limits);
|
|
48
|
+
|
|
49
|
+
rows.push(
|
|
50
|
+
<Day key={ key } $type="other" $disabled={ disabled }>
|
|
51
|
+
<span>{ calendar.previous }</span>
|
|
52
|
+
</Day>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
calendar.previous++;
|
|
56
|
+
} else if (currentDay > calendar.month) {
|
|
57
|
+
nextDate.setDate(calendar.next);
|
|
58
|
+
|
|
59
|
+
const disabled = compareDates(nextDate, limits);
|
|
60
|
+
|
|
61
|
+
rows.push(
|
|
62
|
+
<Day key={ key } $type="other" $disabled={ disabled }>
|
|
63
|
+
<span>{ calendar.next }</span>
|
|
64
|
+
</Day>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
calendar.next++;
|
|
68
|
+
} else {
|
|
69
|
+
currentDate.setDate(currentDay);
|
|
70
|
+
|
|
71
|
+
// we create a new date to use for the compare check
|
|
72
|
+
const ourDate = new Date(currentDate);
|
|
73
|
+
|
|
74
|
+
const disabled = compareDates(ourDate, limits);
|
|
75
|
+
|
|
76
|
+
const currentValue = prepareDate(ourDate);
|
|
77
|
+
|
|
78
|
+
rows.push(
|
|
79
|
+
<Day key={ key }
|
|
80
|
+
$type={ today === currentValue ? 'today' : undefined }
|
|
81
|
+
$disabled={ disabled }
|
|
82
|
+
$selected={ selected === currentValue }
|
|
83
|
+
onClick={ () => onSelected(currentValue, disabled) }
|
|
84
|
+
>
|
|
85
|
+
<span>{ currentDay }</span>
|
|
86
|
+
</Day>
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
currentDay++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<ContainerDays>
|
|
96
|
+
{ rows }
|
|
97
|
+
</ContainerDays>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default Days;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { Container, HeaderDay } from './styles';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
t: TFunction<'common'>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const Header = ({ t }: Props): JSX.Element => {
|
|
9
|
+
const days = [
|
|
10
|
+
t('data.days.mon', { ns: 'common' }),
|
|
11
|
+
t('data.days.tue', { ns: 'common' }),
|
|
12
|
+
t('data.days.wed', { ns: 'common' }),
|
|
13
|
+
t('data.days.thu', { ns: 'common' }),
|
|
14
|
+
t('data.days.fri', { ns: 'common' }),
|
|
15
|
+
t('data.days.sat', { ns: 'common' }),
|
|
16
|
+
t('data.days.sun', { ns: 'common' })
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const items = days.map((item, index) => (
|
|
20
|
+
<HeaderDay key={ index }>{ item }</HeaderDay>
|
|
21
|
+
));
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Container>
|
|
25
|
+
{ items }
|
|
26
|
+
</Container>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default Header;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { Day } from '../styles';
|
|
3
|
+
|
|
4
|
+
export const Container = styled.div`
|
|
5
|
+
display: flex;
|
|
6
|
+
`;
|
|
7
|
+
|
|
8
|
+
export const HeaderDay = styled(Day)`
|
|
9
|
+
justify-content: center;
|
|
10
|
+
font-size: ${ props => props.theme.size.xs };
|
|
11
|
+
color: ${ props => props.theme.font.faded };
|
|
12
|
+
font-weight: 700;
|
|
13
|
+
text-transform: uppercase;
|
|
14
|
+
cursor: default;
|
|
15
|
+
`;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { TFunction } from 'i18next';
|
|
3
|
+
import { prepareDate } from '@autobusal/utilities';
|
|
4
|
+
import Browse from './Browse/Browse';
|
|
5
|
+
import Header from './Header/Header';
|
|
6
|
+
import Days from './Days';
|
|
7
|
+
import { ContainerPicker } from './styles';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
type: 'picker' | 'dob' | 'date'
|
|
11
|
+
value: string
|
|
12
|
+
selected: string
|
|
13
|
+
t: TFunction<'general'>
|
|
14
|
+
onClick: (prepared: string) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Picker = ({ type, value, selected, t, onClick }: Props): (JSX.Element | null) => {
|
|
18
|
+
const [ prepared, setPrepared ] = useState<string>(value);
|
|
19
|
+
|
|
20
|
+
const onSelected = (prepared: string, disabled: boolean): void => {
|
|
21
|
+
if (disabled) {
|
|
22
|
+
return alert(t('general:errors.invalid.date'));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onClick(prepared);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const onChange = (newDate: Date): void => {
|
|
29
|
+
setPrepared(
|
|
30
|
+
prepareDate(newDate)
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<ContainerPicker>
|
|
36
|
+
<Browse type={ type } value={ prepared } t={ t } onChange={ onChange } />
|
|
37
|
+
|
|
38
|
+
<Header t={ t } />
|
|
39
|
+
|
|
40
|
+
<Days type={ type } value={ prepared } selected={ selected } onSelected={ onSelected } />
|
|
41
|
+
</ContainerPicker>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default Picker;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Container = styled.div`
|
|
4
|
+
position: relative;
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
export const ContainerPicker = styled.div`
|
|
8
|
+
position: absolute;
|
|
9
|
+
top: 44px;
|
|
10
|
+
left: 0;
|
|
11
|
+
z-index: 100;
|
|
12
|
+
width: 276px;
|
|
13
|
+
padding: 5px;
|
|
14
|
+
background-color: ${ props => props.theme.foreground.normal };
|
|
15
|
+
box-shadow: ${ props => props.theme.boxShadow };
|
|
16
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
export const ContainerDays = styled.div`
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-wrap: wrap;
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const Day = styled.div<{
|
|
25
|
+
$type?: ('today' | 'other')
|
|
26
|
+
$disabled?: boolean
|
|
27
|
+
$selected?: boolean
|
|
28
|
+
}>`
|
|
29
|
+
display: flex;
|
|
30
|
+
width: calc(100% / 7);
|
|
31
|
+
padding: 2px;
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
|
|
34
|
+
& > span {
|
|
35
|
+
width: 26px;
|
|
36
|
+
height: 26px;
|
|
37
|
+
margin: 0 auto;
|
|
38
|
+
font-size: ${ props => props.theme.size.xs };
|
|
39
|
+
line-height: 26px;
|
|
40
|
+
text-align: center;
|
|
41
|
+
border-radius: 100px;
|
|
42
|
+
transition: all 0.3s ease;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
${ props => props.$type === 'other' && css`
|
|
46
|
+
color: ${ props => props.theme.font.faded };
|
|
47
|
+
` }
|
|
48
|
+
|
|
49
|
+
${ props => props.$type === 'today' && css`
|
|
50
|
+
font-weight: 700;
|
|
51
|
+
color: ${ props => props.theme.primary.normal };
|
|
52
|
+
` }
|
|
53
|
+
|
|
54
|
+
${ props => props.$disabled === true && css`
|
|
55
|
+
opacity: .3;
|
|
56
|
+
` }
|
|
57
|
+
|
|
58
|
+
${ props => props.$selected && css`
|
|
59
|
+
& > span {
|
|
60
|
+
font-weight: 700;
|
|
61
|
+
color: ${ props => props.theme.primary.contrast };
|
|
62
|
+
background-color: ${ props => props.theme.primary.normal };
|
|
63
|
+
}
|
|
64
|
+
` }
|
|
65
|
+
|
|
66
|
+
&:hover > span {
|
|
67
|
+
background-color: ${ props => props.theme.background.neutral };
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface CalendarData {
|
|
2
|
+
begins: number
|
|
3
|
+
month: number
|
|
4
|
+
previous: number
|
|
5
|
+
next: number
|
|
6
|
+
weeks: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DateLimit {
|
|
10
|
+
year: number
|
|
11
|
+
month: number
|
|
12
|
+
day: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DatesLimit {
|
|
16
|
+
min: DateLimit
|
|
17
|
+
max: DateLimit
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ReturnLimit {
|
|
21
|
+
min: number
|
|
22
|
+
max: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CalendarLimit {
|
|
26
|
+
year: ReturnLimit
|
|
27
|
+
time: ReturnLimit
|
|
28
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { getLimits } from './settings';
|
|
3
|
+
|
|
4
|
+
export const getMonths = (t: TFunction<'general'>): JSX.Element[] => {
|
|
5
|
+
const months = [
|
|
6
|
+
t('data.months.january', { ns: 'common' }),
|
|
7
|
+
t('data.months.february', { ns: 'common' }),
|
|
8
|
+
t('data.months.march', { ns: 'common' }),
|
|
9
|
+
t('data.months.april', { ns: 'common' }),
|
|
10
|
+
t('data.months.may', { ns: 'common' }),
|
|
11
|
+
t('data.months.june', { ns: 'common' }),
|
|
12
|
+
t('data.months.july', { ns: 'common' }),
|
|
13
|
+
t('data.months.august', { ns: 'common' }),
|
|
14
|
+
t('data.months.september', { ns: 'common' }),
|
|
15
|
+
t('data.months.october', { ns: 'common' }),
|
|
16
|
+
t('data.months.november', { ns: 'common' }),
|
|
17
|
+
t('data.months.december', { ns: 'common' })
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const options: JSX.Element[] = [];
|
|
21
|
+
|
|
22
|
+
months.forEach((month, index) => (
|
|
23
|
+
options.push(<option key={ index } value={ index }>{ month }</option>)
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
return options;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const getYears = (type: ('picker' | 'dob' | 'date'), currentYear: number): JSX.Element[] => {
|
|
30
|
+
const limits = getLimits(type);
|
|
31
|
+
|
|
32
|
+
const current = new Date().getFullYear();
|
|
33
|
+
const start = current - limits.year.min;
|
|
34
|
+
const end = current + limits.year.max;
|
|
35
|
+
|
|
36
|
+
const options: JSX.Element[] = [];
|
|
37
|
+
|
|
38
|
+
// add dummy year if browsing
|
|
39
|
+
if (currentYear < start) {
|
|
40
|
+
options.push(<option key={ currentYear } value={ currentYear }>{ currentYear }</option>);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (let i = start; i <= end; i++) {
|
|
44
|
+
options.push(<option key={ i } value={ i }>{ i }</option>);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// add dummy year if browsing
|
|
48
|
+
if (currentYear > end) {
|
|
49
|
+
options.push(<option key={ currentYear } value={ currentYear }>{ currentYear }</option>);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return options;
|
|
53
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createDate } from '@autobusal/utilities';
|
|
2
|
+
import { CalendarLimit, DatesLimit, CalendarData } from '../types';
|
|
3
|
+
|
|
4
|
+
export const getLimits = (type: ('picker' | 'dob' | 'date')): CalendarLimit => {
|
|
5
|
+
const today = new Date();
|
|
6
|
+
|
|
7
|
+
today.setHours(0, 0, 0, 0);
|
|
8
|
+
|
|
9
|
+
const limits: DatesLimit = {
|
|
10
|
+
min: {
|
|
11
|
+
year: 0,
|
|
12
|
+
month: 0,
|
|
13
|
+
day: 1
|
|
14
|
+
},
|
|
15
|
+
max: {
|
|
16
|
+
year: 0,
|
|
17
|
+
month: 11,
|
|
18
|
+
day: 31
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (type === 'dob') {
|
|
23
|
+
limits.min.year = 100;
|
|
24
|
+
|
|
25
|
+
limits.max.month = today.getMonth();
|
|
26
|
+
limits.max.day = today.getDate();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (type === 'picker') {
|
|
30
|
+
limits.min.month = today.getMonth();
|
|
31
|
+
limits.min.day = today.getDate();
|
|
32
|
+
|
|
33
|
+
limits.max.year = 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (type == 'date') {
|
|
37
|
+
limits.min.year = 5;
|
|
38
|
+
limits.max.year = 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
year: {
|
|
43
|
+
min: limits.min.year,
|
|
44
|
+
max: limits.max.year
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
time: {
|
|
48
|
+
min: new Date(today.getFullYear() - limits.min.year, limits.min.month, limits.min.day, 0, 0, 0, 0).getTime(),
|
|
49
|
+
max: new Date(today.getFullYear() + limits.max.year, limits.max.month, limits.max.day, 0, 0, 0, 0).getTime()
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const getCalendar = (value: string): CalendarData => {
|
|
55
|
+
const prepared = createDate(value);
|
|
56
|
+
|
|
57
|
+
// when the month begins (when in the week)
|
|
58
|
+
let begins = new Date(prepared.getFullYear(), prepared.getMonth(), 1).getDay();
|
|
59
|
+
begins = begins === 0 ? 7 : begins; // move Sunday to be the 7th day
|
|
60
|
+
|
|
61
|
+
// get the previous month
|
|
62
|
+
const previous = new Date(prepared.getFullYear(), (prepared.getMonth() - 1), 0).getDate(); // the amount of days in the previous month
|
|
63
|
+
|
|
64
|
+
const calendar: CalendarData = {
|
|
65
|
+
begins: begins,
|
|
66
|
+
month: new Date(prepared.getFullYear(), (prepared.getMonth() + 1), 0).getDate(), // the amount of days in a month
|
|
67
|
+
previous: previous - begins + 2,
|
|
68
|
+
next: 1,
|
|
69
|
+
weeks: 5
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// const total = calendar.first_day + calendar.month_days - 1;
|
|
73
|
+
const total = calendar.begins + calendar.month - 1;
|
|
74
|
+
|
|
75
|
+
if (total > 35) {
|
|
76
|
+
calendar.weeks = 6;
|
|
77
|
+
} else if (total <= 28) {
|
|
78
|
+
calendar.weeks = 4;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return calendar;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const compareDates = (date: Date, limits: CalendarLimit): boolean => (
|
|
85
|
+
date.getTime() < limits.time.min || date.getTime() > limits.time.max
|
|
86
|
+
);
|
package/Pagination/styles.ts
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { TFunction } from 'i18next';
|
|
3
|
+
import { UseFormRegister, UseFormSetValue } from 'react-hook-form';
|
|
4
|
+
import { useOutside } from '@autobusal/hooks';
|
|
5
|
+
import getText from './utilities/getText';
|
|
6
|
+
import Persons from './Persons';
|
|
7
|
+
import { Container } from './styles';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
defaultAdults: number
|
|
11
|
+
defaultChildren: number
|
|
12
|
+
defaultBabies: number
|
|
13
|
+
t: TFunction<'common'>
|
|
14
|
+
refs: UseFormRegister<any>
|
|
15
|
+
onUpdate: UseFormSetValue<any>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Passengers = ({ defaultAdults, defaultChildren, defaultBabies, t, refs, onUpdate }: Props): JSX.Element => {
|
|
19
|
+
const [ show, setShow ] = useState<boolean>(false);
|
|
20
|
+
const [ adults, setAdults ] = useState<number>(defaultAdults);
|
|
21
|
+
const [ children, setChildren ] = useState<number>(defaultChildren);
|
|
22
|
+
const [ babies, setBabies ] = useState<number>(defaultBabies);
|
|
23
|
+
const [ display, setDisplay ] = useState(
|
|
24
|
+
getText(adults, children, babies, t)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const ref = useRef(null);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const text = getText(adults, children, babies, t);
|
|
31
|
+
setDisplay(text);
|
|
32
|
+
}, [adults, children, babies]);
|
|
33
|
+
|
|
34
|
+
useOutside(ref, () => setShow(false));
|
|
35
|
+
|
|
36
|
+
const onChange = (name: ('adults' | 'children' | 'babies'), amount: number): void => {
|
|
37
|
+
switch (name) {
|
|
38
|
+
case 'adults':
|
|
39
|
+
setAdults(amount);
|
|
40
|
+
break;
|
|
41
|
+
|
|
42
|
+
case 'children':
|
|
43
|
+
setChildren(amount);
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case 'babies':
|
|
47
|
+
setBabies(amount);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onUpdate(name, amount);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Container ref={ ref }>
|
|
56
|
+
<input type="text" value={ display } readOnly={ true } onClick={ () => setShow(!show) } />
|
|
57
|
+
|
|
58
|
+
{ show &&
|
|
59
|
+
<Persons
|
|
60
|
+
adults={ adults }
|
|
61
|
+
children={ children }
|
|
62
|
+
babies={ babies }
|
|
63
|
+
t={ t }
|
|
64
|
+
onChange={ onChange }
|
|
65
|
+
/> }
|
|
66
|
+
|
|
67
|
+
<input type="hidden" defaultValue={ adults } { ...refs('adults') } />
|
|
68
|
+
<input type="hidden" defaultValue={ children } { ...refs('children') } />
|
|
69
|
+
<input type="hidden" defaultValue={ babies } { ...refs('babies') } />
|
|
70
|
+
</Container>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default Passengers;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { FaMinus, FaPlus } from 'react-icons/fa';
|
|
3
|
+
import { Choose, About, Options, Title, Description, Number, ButtonAmount } from './styles';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
name: 'adults' | 'children' | 'babies'
|
|
7
|
+
title: string
|
|
8
|
+
description: string
|
|
9
|
+
amount: number
|
|
10
|
+
onChange: (name: ('adults' | 'babies' | 'children'), amount: number) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Person = ({ name, title, description, amount, onChange }: Props): JSX.Element => {
|
|
14
|
+
const [ number, setNumber ] = useState<number>(amount);
|
|
15
|
+
|
|
16
|
+
const onClick = (type: ('minus' | 'plus')): void => {
|
|
17
|
+
let value = type === 'minus' ? (number - 1) : (number + 1);
|
|
18
|
+
|
|
19
|
+
if (value < 0) {
|
|
20
|
+
value = 0;
|
|
21
|
+
} else if (value > 6) {
|
|
22
|
+
value = 6;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setNumber(value);
|
|
26
|
+
|
|
27
|
+
onChange(name, value);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Choose>
|
|
32
|
+
<About>
|
|
33
|
+
<Title>{ title }</Title>
|
|
34
|
+
<Description>{ description }</Description>
|
|
35
|
+
</About>
|
|
36
|
+
|
|
37
|
+
<Options>
|
|
38
|
+
<ButtonAmount type="button" onClick={ () => onClick('minus') }><FaMinus /></ButtonAmount>
|
|
39
|
+
|
|
40
|
+
<Number>{ number }</Number>
|
|
41
|
+
|
|
42
|
+
<ButtonAmount type="button" onClick={ () => onClick('plus') }><FaPlus /></ButtonAmount>
|
|
43
|
+
</Options>
|
|
44
|
+
</Choose>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default Person;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import Person from './Person';
|
|
3
|
+
import { Passengers } from './styles';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
adults: number
|
|
7
|
+
children: number
|
|
8
|
+
babies: number
|
|
9
|
+
t: TFunction<'common'>
|
|
10
|
+
onChange: (name: ('adults' | 'children' | 'babies'), amount: number) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Persons = ({ adults, children, babies, t, onChange }: Props): JSX.Element => (
|
|
14
|
+
<Passengers>
|
|
15
|
+
<Person
|
|
16
|
+
name="adults"
|
|
17
|
+
title={ t('data.persons.adult.plural', { ns: 'common' }) }
|
|
18
|
+
description={ t('data.persons.adult.description', { ns: 'common' }) }
|
|
19
|
+
amount={ adults }
|
|
20
|
+
onChange={ onChange }
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
<Person
|
|
24
|
+
name="children"
|
|
25
|
+
title={ t('data.persons.child.plural', { ns: 'common' }) }
|
|
26
|
+
description={ t('data.persons.child.description', { ns: 'common' }) }
|
|
27
|
+
amount={ children }
|
|
28
|
+
onChange={ onChange }
|
|
29
|
+
/>
|
|
30
|
+
|
|
31
|
+
<Person
|
|
32
|
+
name="babies"
|
|
33
|
+
title={ t('data.persons.baby.plural', { ns: 'common' }) }
|
|
34
|
+
description={ t('data.persons.baby.description', { ns: 'common' }) }
|
|
35
|
+
amount={ babies }
|
|
36
|
+
onChange={ onChange }
|
|
37
|
+
/>
|
|
38
|
+
</Passengers>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export default Persons;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Container = styled.div`
|
|
4
|
+
position: relative;
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
export const Passengers = styled.div`
|
|
8
|
+
position: absolute;
|
|
9
|
+
top: 44px;
|
|
10
|
+
left: 0;
|
|
11
|
+
z-index: 100;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: 15px;
|
|
15
|
+
width: 275px;
|
|
16
|
+
padding: 15px;
|
|
17
|
+
background-color: ${ props => props.theme.foreground.normal };
|
|
18
|
+
box-shadow: ${ props => props.theme.boxShadow };
|
|
19
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
20
|
+
|
|
21
|
+
@media (min-width: 640px) {
|
|
22
|
+
width: 300px;
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export const Choose = styled.div`
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
export const About = styled.div`
|
|
32
|
+
line-height: 1.2;
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
export const Title = styled.strong`
|
|
36
|
+
display: block;
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
export const Description = styled.span`
|
|
40
|
+
font-size: ${ props => props.theme.size.s };
|
|
41
|
+
color: ${ props => props.theme.font.faded };
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
export const Options = styled.div`
|
|
45
|
+
margin-left: auto;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 4px;
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const NumberAndButtons = css`
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
width: 36px;
|
|
56
|
+
height: 36px;
|
|
57
|
+
background: ${ props => props.theme.background.neutral };
|
|
58
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
export const Number = styled.span`
|
|
62
|
+
${ NumberAndButtons }
|
|
63
|
+
width: 50px;
|
|
64
|
+
font-weight: 700;
|
|
65
|
+
font-size: ${ props => props.theme.size.l };
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
export const ButtonAmount = styled.button`
|
|
69
|
+
${ NumberAndButtons }
|
|
70
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
|
|
3
|
+
const getText = (adults: number, children: number, babies: number, t: TFunction<'common'>): string => {
|
|
4
|
+
let final = [];
|
|
5
|
+
|
|
6
|
+
if (adults > 0) {
|
|
7
|
+
final.push(`${ t('data.persons.adult.plural', { ns: 'common' }) }: ${ adults }`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (children > 0) {
|
|
11
|
+
final.push(`${ t('data.persons.child.plural', { ns: 'common' }) }: ${ children }`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (babies > 0) {
|
|
15
|
+
final.push(`${ t('data.persons.baby.plural', { ns: 'common' }) }: ${ babies }`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return final.join(', ');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default getText;
|
package/RouteItem/styles.ts
CHANGED
|
@@ -162,6 +162,7 @@ export const LinkOrder = styled(Link)<{ $type: 'normal' | 'small' }>`
|
|
|
162
162
|
transition: all 0.3s ease;
|
|
163
163
|
|
|
164
164
|
${ props => props.$type === 'normal' && css`
|
|
165
|
+
color: ${ props => props.theme.primary.contrast };
|
|
165
166
|
background-color: ${ props => props.theme.primary.normal };
|
|
166
167
|
` }
|
|
167
168
|
|
package/index.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
|
+
import Autocomplete from './Autocomplete/Autocomplete';
|
|
1
2
|
import BackTo from './BackTo';
|
|
3
|
+
import Calendar from './Calendar/Calendar';
|
|
2
4
|
import CookieNotification from './CookieNotification/CookieNotification';
|
|
3
5
|
import ChangeLanguage from './ChangeLanguage/ChangeLanguage';
|
|
4
6
|
import { Button } from './Button/Button';
|
|
5
7
|
import { General, Paragraph } from './Loading/Loading';
|
|
6
8
|
import Modal from './Modal/Modal';
|
|
7
9
|
import Meta from './Meta';
|
|
10
|
+
import Pagination from './Pagination/Pagination';
|
|
11
|
+
import Passengers from './Passengers/Passengers';
|
|
8
12
|
import RouteItem from './RouteItem/RouteItem';
|
|
9
13
|
|
|
10
14
|
export {
|
|
15
|
+
Autocomplete,
|
|
11
16
|
BackTo,
|
|
12
17
|
Button,
|
|
18
|
+
Calendar,
|
|
13
19
|
CookieNotification,
|
|
14
20
|
ChangeLanguage,
|
|
15
21
|
General,
|
|
16
22
|
Meta,
|
|
17
23
|
Modal,
|
|
24
|
+
Pagination,
|
|
18
25
|
Paragraph,
|
|
26
|
+
Passengers,
|
|
19
27
|
RouteItem
|
|
20
28
|
};
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autobusal/common",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@autobusal/hooks": "^0.0.3",
|
|
8
8
|
"@autobusal/providers": "^1.0.16",
|
|
9
|
+
"@autobusal/utilities": "^0.0.3",
|
|
9
10
|
"i18next": "^23.7.18",
|
|
10
11
|
"react": "^18.2.0",
|
|
11
12
|
"react-helmet": "^6.1.0",
|
|
13
|
+
"react-hook-form": "^7.49.3",
|
|
12
14
|
"react-icons": "^5.0.1",
|
|
13
15
|
"react-loading-skeleton": "^3.3.1",
|
|
14
16
|
"react-router-dom": "^6.21.3",
|