@autobusal/routes-order 1.8.0 → 1.9.3

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.
@@ -73,17 +73,46 @@ export const Location = styled.div`
73
73
  display: flex;
74
74
  flex-direction: column;
75
75
  align-items: center;
76
- gap: 5px;
76
+ gap: 3px;
77
77
  text-align: center;
78
78
 
79
79
  & > h2 {
80
80
  margin-bottom: 0;
81
81
  }
82
+ `;
82
83
 
83
- & > span {
84
- font-weight: 700;
85
- color: ${ props => props.theme.font.faded };
86
- }
84
+ /**
85
+ * Edited: Ferjolt Ozuni - Date: 2026-08-01
86
+ *
87
+ * The stop name is supporting detail, not a heading. At the same 16px as
88
+ * body copy it competed with the city above it; a step down lets the eye
89
+ * land on the city first and read the stop only when it needs to.
90
+ */
91
+ export const Stop = styled.span`
92
+ font-size: ${ props => props.theme.size.xs };
93
+ font-weight: 700;
94
+ line-height: 1.3;
95
+ color: ${ props => props.theme.font.faded };
96
+ `;
97
+
98
+ /**
99
+ * Edited: Ferjolt Ozuni - Date: 2026-08-01
100
+ *
101
+ * The country code comes out of the database lowercase ("alb", "grc").
102
+ * An ISO country code is by definition upper case, so it is cased here at
103
+ * render time rather than left to the data - every consumer would otherwise
104
+ * have to remember to do it. Set smaller and muted so it qualifies the city
105
+ * without competing with it.
106
+ */
107
+ export const Iso = styled.span`
108
+ // JSX drops the literal spaces around this element, so the gap on either
109
+ // side of "(ALB)" is this margin's job, not whitespace's
110
+ margin: 0 5px;
111
+ font-size: ${ props => props.theme.size.xs };
112
+ font-weight: 700;
113
+ text-transform: uppercase;
114
+ letter-spacing: .5px;
115
+ color: ${ props => props.theme.font.faded };
87
116
  `;
88
117
 
89
118
  export const Time = styled.div`
@@ -98,13 +127,24 @@ export const Time = styled.div`
98
127
  }
99
128
 
100
129
  & > span {
101
- display: block;
102
- padding: 3px 10px;
103
- font-size: ${ props => props.theme.size.l };
130
+ display: flex;
131
+ align-items: center;
132
+ gap: 5px;
133
+ padding: 3px 12px;
134
+ white-space: nowrap;
135
+ font-size: ${ props => props.theme.size.m };
104
136
  background: ${ props => props.theme.background.neutral };
105
137
  border-radius: 100px;
106
138
  }
107
139
 
140
+ // the hourglass is a hint, not a heading - the big arrow above already
141
+ // owns the visual weight in this column
142
+ & > span > svg {
143
+ flex-shrink: 0;
144
+ font-size: ${ props => props.theme.size.m };
145
+ opacity: .7;
146
+ }
147
+
108
148
  @media (min-width: 640px) {
109
149
  flex-basis: 120px;
110
150
  }
@@ -126,6 +166,13 @@ export const Price = styled.div`
126
166
  }
127
167
  `;
128
168
 
169
+ export const PriceNotice = styled.span`
170
+ margin-top: -6px;
171
+ font-size: ${ props => props.theme.size.xs };
172
+ color: ${ props => props.theme.font.faded };
173
+ text-align: center;
174
+ `;
175
+
129
176
  export const LinkCompany = styled(Link)`
130
177
  display: block;
131
178
  max-width: 165px;
@@ -0,0 +1,40 @@
1
+ import { TFunction } from 'i18next';
2
+ import { Container, Label, Options, Option } from './styles';
3
+ import { SortKey, SORTS } from '../refine';
4
+
5
+ interface Props {
6
+ value: SortKey
7
+ t: TFunction<'common'>
8
+ onChange: (value: SortKey) => void
9
+ }
10
+
11
+ /**
12
+ * Sort control for the results list.
13
+ *
14
+ * Edited: Ferjolt Ozuni - Date: 2026-08-01
15
+ *
16
+ * Rendered as always-visible pills rather than a <select>: the options are
17
+ * few and short, and the point is that somebody who never opens a dropdown
18
+ * still sees that "cheapest" is one click away.
19
+ */
20
+ const Sort = ({ value, t, onChange }: Props): JSX.Element => (
21
+ <Container>
22
+ <Label>{ t('routes_order.step2.sort.title') }</Label>
23
+
24
+ <Options>
25
+ { SORTS.map(sort => (
26
+ <Option
27
+ key={ sort }
28
+ type="button"
29
+ $active={ value === sort }
30
+ aria-pressed={ value === sort }
31
+ onClick={ () => onChange(sort) }
32
+ >
33
+ { t(`routes_order.step2.sort.${ sort }`) }
34
+ </Option>
35
+ )) }
36
+ </Options>
37
+ </Container>
38
+ );
39
+
40
+ export default Sort;
@@ -0,0 +1,42 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ display: flex;
5
+ align-items: center;
6
+ gap: 8px;
7
+ flex-wrap: wrap;
8
+ `;
9
+
10
+ export const Label = styled.span`
11
+ font-size: ${ props => props.theme.size.s };
12
+ font-weight: 700;
13
+ color: ${ props => props.theme.font.faded };
14
+ `;
15
+
16
+ export const Options = styled.div`
17
+ display: flex;
18
+ gap: 6px;
19
+ flex-wrap: wrap;
20
+ `;
21
+
22
+ export const Option = styled.button<{ $active: boolean }>`
23
+ padding: 6px 14px;
24
+ font-size: ${ props => props.theme.size.s };
25
+ font-weight: 700;
26
+ white-space: nowrap;
27
+ border-radius: 100px;
28
+ border: 1px solid ${ props => props.theme.inputs.border };
29
+ background: ${ props => props.theme.background.normal };
30
+ color: ${ props => props.theme.font.normal };
31
+ transition: all 0.2s ease;
32
+
33
+ &:hover {
34
+ border-color: ${ props => props.theme.primary.normal };
35
+ }
36
+
37
+ ${ props => props.$active && css`
38
+ color: ${ props => props.theme.primary.contrast };
39
+ background: ${ props => props.theme.primary.normal };
40
+ border-color: ${ props => props.theme.primary.normal };
41
+ ` }
42
+ `;
@@ -0,0 +1,49 @@
1
+ import { TFunction } from 'i18next';
2
+ import { Container, Result, Total, Range } from './styles';
3
+
4
+ interface Props {
5
+ shown: number
6
+ total: number
7
+ range: { minDisplay: string, maxDisplay: string } | null
8
+ t: TFunction<'common'>
9
+ children?: React.ReactNode
10
+ }
11
+
12
+ /**
13
+ * "8 of 10 trips - from 38.00 EUR to 70.00 EUR".
14
+ *
15
+ * Edited: Ferjolt Ozuni - Date: 2026-08-01
16
+ *
17
+ * Cheap to render and it answers the two questions somebody has before they
18
+ * start reading rows: is there enough choice here, and what will this cost
19
+ * me. The shown/total split also makes it obvious when a filter is hiding
20
+ * results, which is otherwise easy to forget after scrolling.
21
+ */
22
+ const Summary = ({ shown, total, range, t, children }: Props): JSX.Element => (
23
+ <Container className="box">
24
+ <Result>
25
+ { /* Edited: Ferjolt Ozuni - Date: 2026-08-01
26
+ `total`, not `count` - i18next treats a `count` variable as a
27
+ request for plural resolution and would look for _one/_few/_other
28
+ suffixes we'd then owe in all fifteen languages, several of which
29
+ have more plural categories than English. */ }
30
+ <Total>
31
+ { shown === total
32
+ ? t('routes_order.step2.summary.count', { total })
33
+ : t('routes_order.step2.summary.filtered', { shown, total }) }
34
+ </Total>
35
+
36
+ { range !== null && (
37
+ <Range>
38
+ { range.minDisplay === range.maxDisplay
39
+ ? range.minDisplay
40
+ : t('routes_order.step2.summary.range', { min: range.minDisplay, max: range.maxDisplay }) }
41
+ </Range>
42
+ ) }
43
+ </Result>
44
+
45
+ { children }
46
+ </Container>
47
+ );
48
+
49
+ export default Summary;
@@ -0,0 +1,42 @@
1
+ import styled from 'styled-components';
2
+
3
+ /**
4
+ * Edited: Ferjolt Ozuni - Date: 2026-08-01
5
+ *
6
+ * Carries the `box` surface (padding is overridden to something tighter -
7
+ * this is a bar, not a panel) so the count and the sort control sit on the
8
+ * same card background as everything else on the page rather than floating
9
+ * loose on the page background.
10
+ */
11
+ export const Container = styled.div`
12
+ display: flex;
13
+ flex-direction: column;
14
+ gap: 12px;
15
+ margin-bottom: 15px;
16
+
17
+ &.box {
18
+ padding: 12px 15px;
19
+ }
20
+
21
+ @media (min-width: 1024px) {
22
+ flex-direction: row;
23
+ align-items: center;
24
+ justify-content: space-between;
25
+ }
26
+ `;
27
+
28
+ export const Result = styled.div`
29
+ display: flex;
30
+ align-items: baseline;
31
+ gap: 8px;
32
+ flex-wrap: wrap;
33
+ `;
34
+
35
+ export const Total = styled.strong`
36
+ font-size: ${ props => props.theme.size.l };
37
+ `;
38
+
39
+ export const Range = styled.span`
40
+ font-size: ${ props => props.theme.size.s };
41
+ color: ${ props => props.theme.font.faded };
42
+ `;