@mrfylke/contact-form 0.1.10 → 0.1.21
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/README.md +393 -2
- package/lib/ContactFormRoot.d.ts +11 -1
- package/lib/ContactFormRoot.d.ts.map +1 -1
- package/lib/ContactFormRoot.js +2 -2
- package/lib/commoneEvents.d.ts +1 -1
- package/lib/commoneEvents.d.ts.map +1 -1
- package/lib/components/form/date-selector.d.ts.map +1 -1
- package/lib/components/form/date-selector.js +5 -4
- package/lib/components/form/departure-time-field.d.ts +16 -0
- package/lib/components/form/departure-time-field.d.ts.map +1 -0
- package/lib/components/form/departure-time-field.js +28 -0
- package/lib/components/form/description-modal.d.ts.map +1 -1
- package/lib/components/form/description-modal.js +2 -1
- package/lib/components/form/file-input.d.ts.map +1 -1
- package/lib/components/form/file-input.js +2 -1
- package/lib/components/form/index.d.ts +1 -0
- package/lib/components/form/index.d.ts.map +1 -1
- package/lib/components/form/index.js +3 -1
- package/lib/components/form/input.d.ts.map +1 -1
- package/lib/components/form/input.js +2 -1
- package/lib/components/form/searchable-select/utils.js +6 -1
- package/lib/components/index.d.ts +1 -1
- package/lib/components/index.d.ts.map +1 -1
- package/lib/components/index.js +2 -1
- package/lib/contact.module.css +9 -0
- package/lib/error-content/error-content.d.ts.map +1 -1
- package/lib/error-content/error-content.js +2 -1
- package/lib/group-travel/index.d.ts.map +1 -1
- package/lib/group-travel/index.js +3 -1
- package/lib/index.d.ts +16 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +41 -1
- package/lib/journey-info/index.d.ts.map +1 -1
- package/lib/journey-info/index.js +14 -8
- package/lib/journey-info/journeyInfoStateMachine.d.ts +8 -8
- package/lib/layouts/contact-page-layout.d.ts +2 -2
- package/lib/layouts/contact-page-layout.d.ts.map +1 -1
- package/lib/layouts/contact-page-layout.js +29 -20
- package/lib/layouts/container.module.css +1 -1
- package/lib/layouts/layout.module.css +23 -2
- package/lib/lines/use-departures.d.ts +12 -0
- package/lib/lines/use-departures.d.ts.map +1 -0
- package/lib/lines/use-departures.js +35 -0
- package/lib/lines/use-lines.d.ts +5 -6
- package/lib/lines/use-lines.d.ts.map +1 -1
- package/lib/lines/use-lines.js +75 -42
- package/lib/lost-property/LostPropertyForm.d.ts +10 -0
- package/lib/lost-property/LostPropertyForm.d.ts.map +1 -0
- package/lib/lost-property/LostPropertyForm.js +65 -0
- package/lib/lost-property/LostPropertyMachine.d.ts +116 -0
- package/lib/lost-property/LostPropertyMachine.d.ts.map +1 -0
- package/lib/lost-property/LostPropertyMachine.js +187 -0
- package/lib/lost-property/index.d.ts.map +1 -1
- package/lib/lost-property/index.js +27 -3
- package/lib/means-of-transport/forms/delayForm.d.ts.map +1 -1
- package/lib/means-of-transport/forms/delayForm.js +26 -18
- package/lib/means-of-transport/forms/driverForm.d.ts.map +1 -1
- package/lib/means-of-transport/forms/driverForm.js +26 -18
- package/lib/means-of-transport/forms/injuryForm.d.ts.map +1 -1
- package/lib/means-of-transport/forms/injuryForm.js +26 -18
- package/lib/means-of-transport/forms/serviceOfferingForm.d.ts.map +1 -1
- package/lib/means-of-transport/forms/serviceOfferingForm.js +10 -10
- package/lib/means-of-transport/forms/stopForm.d.ts.map +1 -1
- package/lib/means-of-transport/forms/stopForm.js +13 -12
- package/lib/means-of-transport/forms/transportationForm.d.ts.map +1 -1
- package/lib/means-of-transport/forms/transportationForm.js +26 -18
- package/lib/means-of-transport/index.d.ts.map +1 -1
- package/lib/means-of-transport/index.js +13 -4
- package/lib/means-of-transport/means-of-transport-form-machine.d.ts +10 -8
- package/lib/means-of-transport/means-of-transport-form-machine.d.ts.map +1 -1
- package/lib/means-of-transport/means-of-transport-form-machine.js +6 -1
- package/lib/modules/config/types.d.ts +56 -1
- package/lib/modules/config/types.d.ts.map +1 -1
- package/lib/refund/events.d.ts +1 -1
- package/lib/refund/events.d.ts.map +1 -1
- package/lib/refund/forms/refund-and-travel-guarantee/index.d.ts.map +1 -1
- package/lib/refund/forms/refund-and-travel-guarantee/index.js +7 -5
- package/lib/refund/forms/refund-and-travel-guarantee/refundCarForm.d.ts.map +1 -1
- package/lib/refund/forms/refund-and-travel-guarantee/refundCarForm.js +39 -29
- package/lib/refund/forms/refund-and-travel-guarantee/refundTaxiForm.d.ts.map +1 -1
- package/lib/refund/forms/refund-and-travel-guarantee/refundTaxiForm.js +37 -28
- package/lib/refund/forms/refund-ticket/appTicketRefund.d.ts.map +1 -1
- package/lib/refund/forms/refund-ticket/appTicketRefund.js +46 -25
- package/lib/refund/forms/refund-ticket/index.d.ts.map +1 -1
- package/lib/refund/forms/refund-ticket/index.js +9 -6
- package/lib/refund/forms/refund-ticket/otherTicketRefund.d.ts.map +1 -1
- package/lib/refund/forms/refund-ticket/otherTicketRefund.js +56 -30
- package/lib/refund/forms/residualValueOnTravelCard/index.d.ts.map +1 -1
- package/lib/refund/forms/residualValueOnTravelCard/index.js +5 -3
- package/lib/refund/index.d.ts.map +1 -1
- package/lib/refund/index.js +39 -6
- package/lib/refund/refundFormMachine.d.ts +37 -13
- package/lib/refund/refundFormMachine.d.ts.map +1 -1
- package/lib/refund/refundFormMachine.js +107 -11
- package/lib/server/journey-planner/index.d.ts.map +1 -1
- package/lib/server/journey-planner/index.js +15 -6
- package/lib/server/journey-planner/validators.d.ts +2 -0
- package/lib/server/journey-planner/validators.d.ts.map +1 -1
- package/lib/server/journey-planner/validators.js +1 -0
- package/lib/success-content/success-content.d.ts.map +1 -1
- package/lib/success-content/success-content.js +2 -1
- package/lib/test-utils/setup.d.ts +2 -0
- package/lib/test-utils/setup.d.ts.map +1 -0
- package/lib/test-utils/setup.js +3 -0
- package/lib/ticket-control/events.d.ts +1 -1
- package/lib/ticket-control/events.d.ts.map +1 -1
- package/lib/ticket-control/forms/feeComplaintForm.d.ts.map +1 -1
- package/lib/ticket-control/forms/feeComplaintForm.js +39 -36
- package/lib/ticket-control/forms/feedbackForm.d.ts.map +1 -1
- package/lib/ticket-control/forms/feedbackForm.js +26 -14
- package/lib/ticket-control/forms/postponePayment.d.ts.map +1 -1
- package/lib/ticket-control/forms/postponePayment.js +8 -7
- package/lib/ticket-control/index.d.ts.map +1 -1
- package/lib/ticket-control/index.js +23 -8
- package/lib/ticket-control/ticket-control-form-machine.d.ts +19 -11
- package/lib/ticket-control/ticket-control-form-machine.d.ts.map +1 -1
- package/lib/ticket-control/ticket-control-form-machine.js +25 -5
- package/lib/ticketing/forms/app/appAccountForm.d.ts.map +1 -1
- package/lib/ticketing/forms/app/appAccountForm.js +9 -8
- package/lib/ticketing/forms/app/appTicketingForm.d.ts.map +1 -1
- package/lib/ticketing/forms/app/appTicketingForm.js +13 -12
- package/lib/ticketing/forms/app/appTravelSuggestionForm.d.ts.map +1 -1
- package/lib/ticketing/forms/app/appTravelSuggestionForm.js +6 -5
- package/lib/ticketing/forms/app/index.d.ts.map +1 -1
- package/lib/ticketing/forms/app/index.js +2 -1
- package/lib/ticketing/forms/priceAndTicketTypesForm.d.ts.map +1 -1
- package/lib/ticketing/forms/priceAndTicketTypesForm.js +6 -5
- package/lib/ticketing/forms/travel-card/index.d.ts.map +1 -1
- package/lib/ticketing/forms/travel-card/index.js +2 -1
- package/lib/ticketing/forms/travel-card/orderTravelCard.d.ts.map +1 -1
- package/lib/ticketing/forms/travel-card/orderTravelCard.js +2 -2
- package/lib/ticketing/forms/travel-card/travelCardQuestionForm.d.ts.map +1 -1
- package/lib/ticketing/forms/travel-card/travelCardQuestionForm.js +9 -8
- package/lib/ticketing/forms/webshop/index.d.ts.map +1 -1
- package/lib/ticketing/forms/webshop/index.js +2 -1
- package/lib/ticketing/forms/webshop/webshopAccountForm.d.ts.map +1 -1
- package/lib/ticketing/forms/webshop/webshopAccountForm.js +9 -8
- package/lib/ticketing/forms/webshop/webshopTicketingForm.d.ts.map +1 -1
- package/lib/ticketing/forms/webshop/webshopTicketingForm.js +12 -11
- package/lib/ticketing/index.d.ts.map +1 -1
- package/lib/ticketing/index.js +21 -6
- package/lib/ticketing/ticketingStateMachine.d.ts +12 -12
- package/lib/translations/index.d.ts +1 -1
- package/lib/translations/index.d.ts.map +1 -1
- package/lib/translations/index.js +2 -1
- package/lib/translations/merge-overrides.d.ts.map +1 -1
- package/lib/translations/merge-overrides.js +8 -1
- package/lib/translations/pages/contact.d.ts +61 -0
- package/lib/translations/pages/contact.d.ts.map +1 -1
- package/lib/translations/pages/contact.js +64 -2
- package/lib/translations/translations-context.d.ts +2 -0
- package/lib/translations/translations-context.d.ts.map +1 -1
- package/lib/translations/translations-context.js +39 -0
- package/lib/types/submit-payloads.d.ts +40 -11
- package/lib/types/submit-payloads.d.ts.map +1 -1
- package/lib/types/submit-payloads.js +1 -1
- package/lib/types.d.ts +1 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/url-form-type.d.ts +6 -0
- package/lib/url-form-type.d.ts.map +1 -0
- package/lib/url-form-type.js +21 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +10 -0
- package/lib/validation/validationRules.d.ts.map +1 -1
- package/lib/validation/validationRules.js +31 -0
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -8,9 +8,400 @@ A configurable contact form package for public transportation organizations.
|
|
|
8
8
|
npm install @mrfylke/contact-form
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### Peer dependencies
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npm install react react-dom next @atb-as/theme @internationalized/date react-aria-components
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
### 1. Create your configuration
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { ContactFormConfig } from '@mrfylke/contact-form';
|
|
23
|
+
|
|
24
|
+
export const contactFormConfig: ContactFormConfig = {
|
|
25
|
+
theme: {
|
|
26
|
+
light: { /* colors, spacer, border */ },
|
|
27
|
+
dark: { /* same structure */ },
|
|
28
|
+
},
|
|
29
|
+
icons: {
|
|
30
|
+
'transportation/BusFill': MyBusIcon,
|
|
31
|
+
// ... see Icon set section below
|
|
32
|
+
},
|
|
33
|
+
api: {
|
|
34
|
+
baseUrl: '/api',
|
|
35
|
+
endpoints: { submitForm: '/contact/submit' },
|
|
36
|
+
onSubmit,
|
|
37
|
+
},
|
|
38
|
+
org: {
|
|
39
|
+
name: 'Your Company',
|
|
40
|
+
supportEmail: 'support@yourcompany.com',
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Wrap your app
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { ContactFormRoot } from '@mrfylke/contact-form';
|
|
49
|
+
import { contactFormConfig } from './config/contact-form-config';
|
|
50
|
+
|
|
51
|
+
export function RootLayout({ children }) {
|
|
52
|
+
const acceptLanguage = (await headers()).get('accept-language');
|
|
53
|
+
return (
|
|
54
|
+
<ContactFormRoot config={contactFormConfig} acceptLanguageHeader={acceptLanguage ?? undefined}>
|
|
55
|
+
{children}
|
|
56
|
+
</ContactFormRoot>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Render form pages
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { RefundContent, ContactPageLayout } from '@mrfylke/contact-form';
|
|
65
|
+
|
|
66
|
+
export default function ContactPage() {
|
|
67
|
+
return (
|
|
68
|
+
<ContactPageLayout title="Contact Us">
|
|
69
|
+
<RefundContent />
|
|
70
|
+
</ContactPageLayout>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## API configuration
|
|
76
|
+
|
|
77
|
+
The package is decoupled from your backend. You supply an `onSubmit` callback; the package defines the payload shape.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import type { FormSchemaName } from '@mrfylke/contact-form';
|
|
81
|
+
|
|
82
|
+
const onSubmit = async (
|
|
83
|
+
schemaName: FormSchemaName,
|
|
84
|
+
state: Record<string, unknown>,
|
|
85
|
+
): Promise<Response> => {
|
|
86
|
+
return fetch('/api/contact/submit', {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
body: JSON.stringify({ ...state, formType: schemaName }),
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`schemaName` is one of: `'ticketControl'` · `'refund'` · `'meansOfTransport'` · `'ticketing'` · `'journeyInfo'`
|
|
95
|
+
|
|
96
|
+
### Routing submissions to different endpoints
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const onSubmit = async (schemaName: FormSchemaName, state) => {
|
|
100
|
+
const endpointMap: Record<FormSchemaName, string> = {
|
|
101
|
+
ticketControl: '/api/contact/ticket-control',
|
|
102
|
+
refund: '/api/contact/refund',
|
|
103
|
+
meansOfTransport: '/api/contact/means-of-transport',
|
|
104
|
+
ticketing: '/api/contact/ticketing',
|
|
105
|
+
journeyInfo: '/api/contact/journey-info',
|
|
106
|
+
};
|
|
107
|
+
return fetch(endpointMap[schemaName], {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: { 'Content-Type': 'application/json' },
|
|
110
|
+
body: JSON.stringify(state),
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Payload types
|
|
116
|
+
|
|
117
|
+
Import these in your API handlers to type-check incoming data:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import type {
|
|
121
|
+
TicketControlSubmitPayload,
|
|
122
|
+
RefundSubmitPayload,
|
|
123
|
+
MeansOfTransportSubmitPayload,
|
|
124
|
+
TicketingSubmitPayload,
|
|
125
|
+
JourneyInfoSubmitPayload,
|
|
126
|
+
} from '@mrfylke/contact-form';
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Attachments are base64-encoded strings within the payload.
|
|
130
|
+
|
|
131
|
+
### Lines endpoint
|
|
132
|
+
|
|
133
|
+
The package includes a ready-made route handler for fetching transit lines from Entur.
|
|
134
|
+
|
|
135
|
+
**Option 1 – ready-made handler (recommended)**
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// app/api/contact/lines/route.ts
|
|
139
|
+
import { createLinesRouteHandler } from '@mrfylke/contact-form/server';
|
|
140
|
+
|
|
141
|
+
export const GET = createLinesRouteHandler();
|
|
142
|
+
// Or with explicit authority:
|
|
143
|
+
// export const GET = createLinesRouteHandler({ getAuthorityId: () => 'ATB:Authority:2' });
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Set `ENTUR_AUTHORITY_ID` in your `.env` and point `api.endpoints.lines` at the mounted path.
|
|
147
|
+
|
|
148
|
+
**Option 2 – call `getLines()` directly**
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { getLines } from '@mrfylke/contact-form/server';
|
|
152
|
+
|
|
153
|
+
export async function GET() {
|
|
154
|
+
const lines = await getLines(); // reads ENTUR_AUTHORITY_ID from env
|
|
155
|
+
return Response.json(lines);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Server exports:** `createLinesRouteHandler` · `getLines` · `getEnturAuthorityId` · `EnturAuthority`
|
|
160
|
+
|
|
161
|
+
## `ContactFormRoot` props
|
|
162
|
+
|
|
163
|
+
| Prop | Type | Description |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `config` | `ContactFormConfig` | Required. Theme, icons, API, org, features. |
|
|
166
|
+
| `acceptLanguageHeader` | `string` | Raw `Accept-Language` header from the server. |
|
|
167
|
+
| `initialLanguage` | `Language` | Client-side initial language when no header is available. |
|
|
168
|
+
| `forceTheme` | `'light' \| 'dark'` | Forces a theme variant; disables the dark-mode toggle. |
|
|
169
|
+
| `translationsOverride` | `ContactFormTranslationsOverride` | Merges additional translation overrides on top of `config.translations`. |
|
|
170
|
+
|
|
171
|
+
## Container and layout
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import { ContactFormRoot, ContactFormContainer, ContactPageLayout } from '@mrfylke/contact-form';
|
|
175
|
+
|
|
176
|
+
<ContactFormRoot config={contactFormConfig}>
|
|
177
|
+
<ContactFormContainer header={<MyHeader />} footer={<MyFooter />}>
|
|
178
|
+
<ContactPageLayout title="Contact" backLink={backLink}>
|
|
179
|
+
{content}
|
|
180
|
+
</ContactPageLayout>
|
|
181
|
+
</ContactFormContainer>
|
|
182
|
+
</ContactFormRoot>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Header and footer are your app's responsibility. Their components can use `useContactFormConfig()`, `useTheme()`, and `useTranslation()` from the package.
|
|
186
|
+
|
|
187
|
+
### Layout config
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
layout: {
|
|
191
|
+
basePath: '/contact',
|
|
192
|
+
backLinkDefault: { href: '/', label: PageText.Contact.contactPageLayout.homeLink },
|
|
193
|
+
successPath: '/contact/thanks', // default: basePath + '/success'
|
|
194
|
+
errorPath: '/contact/oops', // default: basePath + '/error'
|
|
195
|
+
successContent: <CustomThanksMessage />,
|
|
196
|
+
errorContent: <CustomErrorMessage />,
|
|
197
|
+
footerUrls: {
|
|
198
|
+
privacy: 'https://example.com/privacy',
|
|
199
|
+
accessibility: 'https://example.com/accessibility',
|
|
200
|
+
help: '/contact',
|
|
201
|
+
tickets: 'https://example.com/tickets',
|
|
202
|
+
homePage: { href: 'https://example.com', name: 'example.com' },
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Render `SuccessContent`/`ErrorContent` (or your overrides) without `ContactPageLayout` — they are standalone result pages.
|
|
208
|
+
|
|
209
|
+
## Deep-linking
|
|
210
|
+
|
|
211
|
+
Form type selections are reflected in the URL as a `?type=` query parameter. This allows direct links to specific form states.
|
|
212
|
+
|
|
213
|
+
**Reading on load:** on mount each form section reads `?type=` and selects the matching radio if it is in the enabled set.
|
|
214
|
+
|
|
215
|
+
**Writing on change:** selecting a radio updates `?type=` via `replaceState` (no page reload).
|
|
216
|
+
|
|
217
|
+
**Bypassing agreement checkboxes:** for sections with an initial agreement step (refund), append `&agreed=true` to pre-tick the checkbox and jump straight to the form.
|
|
218
|
+
|
|
219
|
+
**Available values per section:**
|
|
220
|
+
|
|
221
|
+
| Section | `?type=` values |
|
|
222
|
+
|---|---|
|
|
223
|
+
| Means of transport | `driver` · `transportation` · `delay` · `stop` · `serviceOffering` · `injury` |
|
|
224
|
+
| Ticket control | `feeComplaint` · `feedback` · `postponePayment` |
|
|
225
|
+
| Refund | `refundOfTicket` · `refundAndTravelGuarantee` · `residualValueOnTravelCard` |
|
|
226
|
+
| Ticketing | `priceAndTicketTypes` · `app` · `webshop` · `travelCard` |
|
|
227
|
+
|
|
228
|
+
Examples:
|
|
229
|
+
- `/contact/refund?type=refundAndTravelGuarantee&agreed=true` — opens travel guarantee form with agreement pre-accepted
|
|
230
|
+
- `/contact/ticket-control?type=feeComplaint` — opens fee complaint form directly
|
|
231
|
+
|
|
232
|
+
Only values present in `formSchemaOverrides.enabledFormTypes` / `enabledFormCategories` are accepted; invalid values are ignored.
|
|
233
|
+
|
|
234
|
+
## Translations
|
|
235
|
+
|
|
236
|
+
Translations are required — the package ships no built-in copy. The key structure follows **planner-web** as source of truth.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import {
|
|
240
|
+
PageText,
|
|
241
|
+
ComponentText,
|
|
242
|
+
CommonText,
|
|
243
|
+
ServerText,
|
|
244
|
+
} from '@mrfylke/contact-form/translations';
|
|
245
|
+
|
|
246
|
+
const contactFormConfig: ContactFormConfig = {
|
|
247
|
+
translations: { pages: PageText, components: ComponentText, common: CommonText, server: ServerText },
|
|
248
|
+
};
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
To override specific keys, merge with the defaults:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
const customTranslations = {
|
|
255
|
+
pages: {
|
|
256
|
+
Contact: {
|
|
257
|
+
contactPageLayout: {
|
|
258
|
+
title: { no: 'Kontakt oss', en: 'Contact us', nn: 'Kontakt oss' },
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
components: ComponentText,
|
|
263
|
+
common: CommonText,
|
|
264
|
+
server: ServerText,
|
|
265
|
+
};
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Theme
|
|
269
|
+
|
|
270
|
+
### Using `@atb-as/theme` (recommended)
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { createThemesFor, ThemeVariant } from '@atb-as/theme';
|
|
274
|
+
import { adaptAtbTheme } from '@mrfylke/contact-form/config';
|
|
275
|
+
|
|
276
|
+
const theme = adaptAtbTheme(createThemesFor(ThemeVariant.FRAM));
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Manual theme
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
type Theme = {
|
|
283
|
+
light: ThemeColors;
|
|
284
|
+
dark: ThemeColors;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
type ThemeColors = {
|
|
288
|
+
color: {
|
|
289
|
+
transport: TransportColors;
|
|
290
|
+
status: StatusColors;
|
|
291
|
+
background: BackgroundColors;
|
|
292
|
+
interactive: { primary: InteractiveColor; secondary: InteractiveColor; destructive: InteractiveColor };
|
|
293
|
+
};
|
|
294
|
+
spacer: { small: string; medium: string; large: string; xLarge: string };
|
|
295
|
+
border: { radius: { small: string; regular: string; circle: string }; width: { slim: string; medium: string } };
|
|
296
|
+
};
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Use `themeOverrides` for small tweaks without rebuilding the full theme:
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
themeOverrides: { light: { spacer: { medium: '1.25rem' } } }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Icon set
|
|
306
|
+
|
|
307
|
+
Provide a React component for each key. Each component accepts `size`, `className`, `role`, `alt`, and `style` props.
|
|
308
|
+
|
|
309
|
+
**Transportation:** `transportation/BusFill` · `transportation/TrainFill` · `transportation/TramFill` · `transportation/WalkFill` · `transportation/BicycleFill` · `transportation/PlaneFill` · `transportation/BoatFill` · `transportation/FerryFill` · `transportation/MetroFill` · `transportation/UnknownFill`
|
|
310
|
+
|
|
311
|
+
**Actions:** `actions/Close` · `actions/Clear` · `actions/Add`
|
|
312
|
+
|
|
313
|
+
**Status:** `status/CheckmarkFill` · `status/ErrorFill`
|
|
314
|
+
|
|
315
|
+
## Overriding standard behavior
|
|
316
|
+
|
|
317
|
+
| Feature | Default | Config key |
|
|
318
|
+
|---|---|---|
|
|
319
|
+
| Pages/tabs | All 7 tabs, all internal | `pagesOverrides` |
|
|
320
|
+
| Ferry transport mode | Included | `features.includeFerry` |
|
|
321
|
+
| Form schemas | All types/categories per page | `formSchemaOverrides` |
|
|
322
|
+
| Header links | Single optional home link | `layout.headerLinksOverride` |
|
|
323
|
+
| Footer links | Standard sections | `layout.footerLinksOverride` |
|
|
324
|
+
| Translations | No defaults — must be provided | `translations` |
|
|
325
|
+
| Theme | Must be provided | `theme` + `themeOverrides` |
|
|
326
|
+
|
|
327
|
+
### Pages
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
pagesOverrides: {
|
|
331
|
+
hidePageIds: ['lost-property'],
|
|
332
|
+
externalUrlByPageId: { refund: 'https://example.com/refund-form' },
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Page IDs: `'ticket-control'` · `'refund'` · `'means-of-transport'` · `'ticketing'` · `'lost-property'` · `'group-travel'` · `'journey-info'`
|
|
337
|
+
|
|
338
|
+
### Ferry flag
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
features: { includeFerry: false } // removes ferry from transport mode dropdowns and line queries
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Form schema overrides
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
formSchemaOverrides: {
|
|
348
|
+
ticketControl: { enabledFormTypes: ['feeComplaint', 'feedback'] },
|
|
349
|
+
meansOfTransport: { enabledFormTypes: ['driver', 'delay', 'transportation'] },
|
|
350
|
+
refund: { enabledFormCategories: ['refundOfTicket', 'refundAndTravelGuarantee'] },
|
|
351
|
+
ticketing: { enabledFormCategories: ['app', 'webshop'] },
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Can also be loaded from a JSON/YAML file:
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
import schemaOverrides from './config/contact-form-schema.json';
|
|
359
|
+
// ...
|
|
360
|
+
formSchemaOverrides: schemaOverrides,
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Header links override
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
layout: {
|
|
367
|
+
headerLinksOverride: [
|
|
368
|
+
{ href: 'https://example.com', label: { no: 'Hjem', en: 'Home', nn: 'Heim' } },
|
|
369
|
+
],
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Empty array removes all header links.
|
|
374
|
+
|
|
375
|
+
### Footer links override
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
layout: {
|
|
379
|
+
footerLinksOverride: [
|
|
380
|
+
{ href: 'https://example.com/tickets', label: { no: 'Billetter', en: 'Tickets', nn: 'Billettar' }, section: 'general' },
|
|
381
|
+
{ href: 'https://example.com/privacy', label: { no: 'Personvern', en: 'Privacy', nn: 'Personvern' }, section: 'bottom' },
|
|
382
|
+
],
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Links with `section: 'bottom'` replace the standard privacy/accessibility row.
|
|
387
|
+
|
|
388
|
+
## Styling
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import '@mrfylke/contact-form/lib/styles.css';
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Examples
|
|
395
|
+
|
|
396
|
+
See the `apps/next-demo` directory for a complete implementation, including commented examples of all override options in `apps/next-demo/src/config/contact-form-config.tsx`.
|
|
397
|
+
|
|
398
|
+
## Troubleshooting
|
|
399
|
+
|
|
400
|
+
**`useContactFormConfig must be used within ContactFormConfigProvider`** — wrap your app with `<ContactFormRoot>`.
|
|
401
|
+
|
|
402
|
+
**Icons not showing** — verify all required icon keys are provided and check the browser console for warnings.
|
|
403
|
+
|
|
404
|
+
**Theme not applying** — ensure `<ContactFormRoot>` wraps everything and your theme object matches the expected structure.
|
|
14
405
|
|
|
15
406
|
## License
|
|
16
407
|
|
package/lib/ContactFormRoot.d.ts
CHANGED
|
@@ -14,6 +14,16 @@ export type ContactFormRootProps = {
|
|
|
14
14
|
* Will be converted to a basic Accept-Language style string internally.
|
|
15
15
|
*/
|
|
16
16
|
initialLanguage?: Language;
|
|
17
|
+
/**
|
|
18
|
+
* Optional explicit default language, e.g. restored from localStorage/cookie.
|
|
19
|
+
* Takes precedence over browser/server detection when provided.
|
|
20
|
+
*/
|
|
21
|
+
defaultLanguage?: Language;
|
|
22
|
+
/**
|
|
23
|
+
* Called whenever the user manually selects a language.
|
|
24
|
+
* Use this to persist the selection (e.g. localStorage, cookie).
|
|
25
|
+
*/
|
|
26
|
+
onLanguageChange?: (lang: Language) => void;
|
|
17
27
|
/**
|
|
18
28
|
* Optional: force a specific theme mode (light or dark).
|
|
19
29
|
* When set, dark mode toggle is effectively disabled and the chosen
|
|
@@ -33,5 +43,5 @@ export type ContactFormRootProps = {
|
|
|
33
43
|
* providers together. Renders children inside the provider stack so
|
|
34
44
|
* consumers can pass their routes/layout as children.
|
|
35
45
|
*/
|
|
36
|
-
export declare function ContactFormRoot({ config, acceptLanguageHeader, initialLanguage, forceTheme, translationsOverride, children, }: ContactFormRootProps): import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
export declare function ContactFormRoot({ config, acceptLanguageHeader, initialLanguage, defaultLanguage, onLanguageChange, forceTheme, translationsOverride, children, }: ContactFormRootProps): import("react/jsx-runtime").JSX.Element;
|
|
37
47
|
//# sourceMappingURL=ContactFormRoot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContactFormRoot.d.ts","sourceRoot":"","sources":["../src/ContactFormRoot.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9C,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAEL,KAAK,+BAA+B,EACpC,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAQxB,MAAM,MAAM,oBAAoB,GAAG;IACjC,mEAAmE;IACnE,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,eAAe,CAAC,EAAE,QAAQ,CAAC;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,+BAA+B,CAAC;IACvD,sDAAsD;IACtD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,oBAAoB,EACpB,QAAQ,GACT,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ContactFormRoot.d.ts","sourceRoot":"","sources":["../src/ContactFormRoot.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9C,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAEL,KAAK,+BAA+B,EACpC,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAQxB,MAAM,MAAM,oBAAoB,GAAG;IACjC,mEAAmE;IACnE,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,eAAe,CAAC,EAAE,QAAQ,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,QAAQ,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,+BAA+B,CAAC;IACvD,sDAAsD;IACtD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,QAAQ,GACT,EAAE,oBAAoB,2CAyBtB"}
|
package/lib/ContactFormRoot.js
CHANGED
|
@@ -15,13 +15,13 @@ function ThemeSync({ children }) {
|
|
|
15
15
|
* providers together. Renders children inside the provider stack so
|
|
16
16
|
* consumers can pass their routes/layout as children.
|
|
17
17
|
*/
|
|
18
|
-
function ContactFormRoot({ config, acceptLanguageHeader, initialLanguage, forceTheme, translationsOverride, children, }) {
|
|
18
|
+
function ContactFormRoot({ config, acceptLanguageHeader, initialLanguage, defaultLanguage, onLanguageChange, forceTheme, translationsOverride, children, }) {
|
|
19
19
|
const header = acceptLanguageHeader ?? deriveHeaderFromInitialLanguage(initialLanguage);
|
|
20
20
|
// Merge config translations (required) with optional root-level override (override takes precedence)
|
|
21
21
|
const mergedTranslations = translationsOverride
|
|
22
22
|
? deepMerge(config.translations, translationsOverride)
|
|
23
23
|
: config.translations;
|
|
24
|
-
return ((0, jsx_runtime_1.jsx)(config_1.ContactFormConfigProvider, { config: config, children: (0, jsx_runtime_1.jsx)(theme_1.ThemeProvider, { forceTheme: forceTheme, children: (0, jsx_runtime_1.jsx)(ThemeSync, { children: (0, jsx_runtime_1.jsx)(translations_1.AppLanguageProvider, { serverAcceptLanguage: header ?? 'no,en;q=0.9', overrides: mergedTranslations, children: children }) }) }) }));
|
|
24
|
+
return ((0, jsx_runtime_1.jsx)(config_1.ContactFormConfigProvider, { config: config, children: (0, jsx_runtime_1.jsx)(theme_1.ThemeProvider, { forceTheme: forceTheme, children: (0, jsx_runtime_1.jsx)(ThemeSync, { children: (0, jsx_runtime_1.jsx)(translations_1.AppLanguageProvider, { serverAcceptLanguage: header ?? 'no,en;q=0.9', overrides: mergedTranslations, defaultLanguage: defaultLanguage, onLanguageChange: onLanguageChange, children: children }) }) }) }));
|
|
25
25
|
}
|
|
26
26
|
function deriveHeaderFromInitialLanguage(language) {
|
|
27
27
|
if (!language) {
|
package/lib/commoneEvents.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare const commonEvents: {
|
|
|
4
4
|
type: "VALIDATE";
|
|
5
5
|
} | {
|
|
6
6
|
type: "ON_INPUT_CHANGE";
|
|
7
|
-
inputName: "address" | "attachments" | "bankAccountNumber" | "city" | "date" | "email" | "firstName" | "feedback" | "IBAN" | "lastName" | "phoneNumber" | "postalCode" | "SWIFT" | "toStop" | "fromStop" | "plannedDepartureTime";
|
|
7
|
+
inputName: "address" | "attachments" | "bankAccountNumber" | "city" | "date" | "email" | "firstName" | "feedback" | "IBAN" | "lastName" | "phoneNumber" | "postalCode" | "SWIFT" | "toStop" | "fromStop" | "plannedDepartureTime" | "tripId";
|
|
8
8
|
value: any;
|
|
9
9
|
} | {
|
|
10
10
|
type: "ON_TRANSPORTMODE_CHANGE";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commoneEvents.d.ts","sourceRoot":"","sources":["../src/commoneEvents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;AAEzB,eAAO,MAAM,YAAY,EACrB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IACE,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EACL,SAAS,GACT,aAAa,GACb,mBAAmB,GACnB,MAAM,GACN,MAAM,GACN,OAAO,GACP,WAAW,GACX,UAAU,GACV,MAAM,GACN,UAAU,GACV,aAAa,GACb,YAAY,GACZ,OAAO,GACP,QAAQ,GACR,UAAU,GACV,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"commoneEvents.d.ts","sourceRoot":"","sources":["../src/commoneEvents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;AAEzB,eAAO,MAAM,YAAY,EACrB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IACE,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EACL,SAAS,GACT,aAAa,GACb,mBAAmB,GACnB,MAAM,GACN,MAAM,GACN,OAAO,GACP,WAAW,GACX,UAAU,GACV,MAAM,GACN,UAAU,GACV,aAAa,GACb,YAAY,GACZ,OAAO,GACP,QAAQ,GACR,UAAU,GACV,sBAAsB,GACtB,QAAQ,CAAC;IACb,KAAK,EAAE,GAAG,CAAC;CACZ,GACD;IACE,IAAI,EAAE,yBAAyB,CAAC;IAChC,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,IAAI,GAAG,SAAS,CAAC;CACzB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-selector.d.ts","sourceRoot":"","sources":["../../../src/components/form/date-selector.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,oBAAoB,CAAC;AAItE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,EAAE,EACF,KAAK,EACL,KAAK,EACL,UAAU,EACV,YAAY,EACZ,QAAQ,GACT,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"date-selector.d.ts","sourceRoot":"","sources":["../../../src/components/form/date-selector.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,oBAAoB,CAAC;AAItE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,EAAE,EACF,KAAK,EACL,KAAK,EACL,UAAU,EACV,YAAY,EACZ,QAAQ,GACT,EAAE,iBAAiB,2CAwEnB"}
|
|
@@ -14,8 +14,9 @@ const icon_1 = require("../icon");
|
|
|
14
14
|
const error_message_1 = require("../error-message");
|
|
15
15
|
function DateSelector({ id, label, value, isRequired, errorMessage, onChange, }) {
|
|
16
16
|
const { t } = (0, translations_1.useTranslation)();
|
|
17
|
-
const zonedDateTime = value
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const zonedDateTime = value ? (0, date_1.fromDate)(new Date(value), 'Europe/Oslo') : null;
|
|
18
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: form_module_css_1.default.dateSelectorContainer, children: [(0, jsx_runtime_1.jsxs)(react_aria_components_1.DatePicker, { id: `date_selector__${id}`, granularity: "day", value: zonedDateTime, onChange: (e) => e && onChange(e.toString().slice(0, 10)), onOpenChange: (isOpen) => {
|
|
19
|
+
if (isOpen && !value)
|
|
20
|
+
onChange((0, date_1.today)('Europe/Oslo').toString());
|
|
21
|
+
}, className: form_module_css_1.default.dateSelector, shouldForceLeadingZeros: true, "aria-label": label, "aria-labelledby": `label-${id}`, children: [(0, jsx_runtime_1.jsx)(form_component_label_1.default, { label: label, htmlFor: `date-selector-${id}`, isRequired: isRequired }), (0, jsx_runtime_1.jsxs)(react_aria_components_1.Group, { className: form_module_css_1.default.calendarSelectorGroup, children: [(0, jsx_runtime_1.jsx)(react_aria_components_1.DateInput, { className: form_module_css_1.default.dateSelectorInput, children: (segment) => ((0, jsx_runtime_1.jsx)(react_aria_components_1.DateSegment, { segment: segment, className: form_module_css_1.default.dateSelectorSegment })) }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Button, { className: form_module_css_1.default.calendarButton, children: (0, jsx_runtime_1.jsx)(icon_1.MonoIcon, { icon: "time/Date" }) })] }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Popover, { className: form_module_css_1.default.calendarDialog, placement: "bottom right", children: (0, jsx_runtime_1.jsx)(react_aria_components_1.Dialog, { children: (0, jsx_runtime_1.jsxs)(react_aria_components_1.Calendar, { children: [(0, jsx_runtime_1.jsxs)("header", { className: form_module_css_1.default.calendarDialog__header, children: [(0, jsx_runtime_1.jsx)(react_aria_components_1.Button, { slot: "previous", className: form_module_css_1.default.calendarDialog__headerButtons, children: (0, jsx_runtime_1.jsx)(icon_1.MonoIcon, { icon: "navigation/ArrowLeft" }) }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Heading, { className: form_module_css_1.default.calendarDialog__title }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Button, { slot: "next", className: form_module_css_1.default.calendarDialog__headerButtons, children: (0, jsx_runtime_1.jsx)(icon_1.MonoIcon, { icon: "navigation/ArrowRight" }) })] }), (0, jsx_runtime_1.jsx)(react_aria_components_1.CalendarGrid, { className: form_module_css_1.default.calendarGrid, children: (date) => ((0, jsx_runtime_1.jsx)(react_aria_components_1.CalendarCell, { date: date, className: form_module_css_1.default.calendarGrid__cell })) })] }) }) })] }), (0, jsx_runtime_1.jsx)("input", { id: `date_selector__${id}`, type: "hidden", value: value ?? '' }), errorMessage && (0, jsx_runtime_1.jsx)(error_message_1.ErrorMessage, { message: t(errorMessage) })] }));
|
|
21
22
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TranslatedString } from '../../translations';
|
|
2
|
+
type DepartureTimeFieldProps = {
|
|
3
|
+
id?: string;
|
|
4
|
+
date?: string;
|
|
5
|
+
lineId?: string;
|
|
6
|
+
fromStopId?: string;
|
|
7
|
+
toStopId?: string;
|
|
8
|
+
selectedTripId?: string;
|
|
9
|
+
plannedDepartureTime?: string;
|
|
10
|
+
onChange: (departureTime?: string, tripId?: string) => void;
|
|
11
|
+
isRequired?: boolean;
|
|
12
|
+
errorMessage?: TranslatedString;
|
|
13
|
+
};
|
|
14
|
+
export declare function DepartureTimeField({ id, date, lineId, fromStopId, toStopId, selectedTripId, plannedDepartureTime, onChange, isRequired, errorMessage, }: DepartureTimeFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=departure-time-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"departure-time-field.d.ts","sourceRoot":"","sources":["../../../src/components/form/departure-time-field.tsx"],"names":[],"mappings":"AAMA,OAAO,EACL,gBAAgB,EAGjB,MAAM,oBAAoB,CAAC;AAI5B,KAAK,uBAAuB,GAAG;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,EAA2B,EAC3B,IAAI,EACJ,MAAM,EACN,UAAU,EACV,QAAQ,EACR,cAAc,EACd,oBAAoB,EACpB,QAAQ,EACR,UAAU,EACV,YAAY,GACb,EAAE,uBAAuB,2CAoDzB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DepartureTimeField = DepartureTimeField;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const use_departures_1 = require("../../lines/use-departures");
|
|
9
|
+
const translations_1 = require("../../translations");
|
|
10
|
+
const select_1 = __importDefault(require("./select"));
|
|
11
|
+
const time_selector_1 = __importDefault(require("./time-selector"));
|
|
12
|
+
function DepartureTimeField({ id = 'plannedDepartureTime', date, lineId, fromStopId, toStopId, selectedTripId, plannedDepartureTime, onChange, isRequired, errorMessage, }) {
|
|
13
|
+
const PageText = (0, translations_1.usePageText)();
|
|
14
|
+
const { t } = (0, translations_1.useTranslation)();
|
|
15
|
+
const hasDeparturesEndpoint = (0, use_departures_1.useDeparturesEndpointConfigured)();
|
|
16
|
+
const { departures, isLoading } = (0, use_departures_1.useDepartures)(date, lineId, fromStopId, toStopId);
|
|
17
|
+
const label = t(PageText.Contact.input.plannedDepartureTime.label);
|
|
18
|
+
const errorStr = errorMessage ? t(errorMessage) : undefined;
|
|
19
|
+
if (!hasDeparturesEndpoint) {
|
|
20
|
+
return ((0, jsx_runtime_1.jsx)(time_selector_1.default, { id: id, label: label, value: plannedDepartureTime, isRequired: isRequired, errorMessage: errorMessage, onChange: (time) => onChange(time, undefined) }));
|
|
21
|
+
}
|
|
22
|
+
const selectedDeparture = departures.find((d) => selectedTripId
|
|
23
|
+
? d.tripId === selectedTripId
|
|
24
|
+
: d.departureTime === plannedDepartureTime);
|
|
25
|
+
return ((0, jsx_runtime_1.jsx)(select_1.default, { id: id, label: label, value: selectedDeparture, options: departures, valueToId: (d) => d.tripId, valueToText: (d) => (0, use_departures_1.formatDepartureTime)(d.departureTime), onChange: (d) => onChange(d?.departureTime, d?.tripId), isRequired: isRequired, disabled: !lineId || !fromStopId || !toStopId || !date, placeholder: isLoading
|
|
26
|
+
? t(PageText.Contact.input.plannedDepartureTime.loading)
|
|
27
|
+
: t(PageText.Contact.input.plannedDepartureTime.optionLabel), error: errorStr }));
|
|
28
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"description-modal.d.ts","sourceRoot":"","sources":["../../../src/components/form/description-modal.tsx"],"names":[],"mappings":"AASA,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE;QACZ,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC;IACF,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAI,mDAKvB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"description-modal.d.ts","sourceRoot":"","sources":["../../../src/components/form/description-modal.tsx"],"names":[],"mappings":"AASA,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE;QACZ,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC;IACF,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAI,mDAKvB,qBAAqB,mDA2FvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -13,6 +13,7 @@ const typography_1 = require("../typography");
|
|
|
13
13
|
const button_1 = require("../button");
|
|
14
14
|
const icon_1 = require("../icon");
|
|
15
15
|
const DescriptionModal = ({ title, modalContent, isModalOpen, closeModal, }) => {
|
|
16
|
+
const PageText = (0, translations_1.usePageText)();
|
|
16
17
|
const { t } = (0, translations_1.useTranslation)();
|
|
17
18
|
const modalRef = (0, react_1.useRef)(null);
|
|
18
19
|
const { description, instruction, bulletPoints } = modalContent;
|
|
@@ -37,7 +38,7 @@ const DescriptionModal = ({ title, modalContent, isModalOpen, closeModal, }) =>
|
|
|
37
38
|
if (!isModalOpen)
|
|
38
39
|
return null;
|
|
39
40
|
return ((0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: form_module_css_1.default.modal_wrapper, initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { ease: 'easeInOut', duration: 0.15 }, children: (0, jsx_runtime_1.jsx)(focus_1.FocusScope, { contain: true, restoreFocus: true, autoFocus: true, children: (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { ref: modalRef, initial: { opacity: 0, y: -30 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 0 }, transition: { ease: 'backOut', delay: 0.1, duration: 0.15 }, children: (0, jsx_runtime_1.jsxs)("dialog", { "aria-modal": "true", "aria-labelledby": "title", tabIndex: 0, className: form_module_css_1.default.modal_content, children: [(0, jsx_runtime_1.jsxs)("div", { className: form_module_css_1.default.modal_header, children: [(0, jsx_runtime_1.jsx)(typography_1.Typo.h2, { id: "title", textType: "body__m", autoFocus: true, children: title }), (0, jsx_runtime_1.jsx)(button_1.Button, { className: form_module_css_1.default.iconButton, onClick: closeModal, icon: { right: (0, jsx_runtime_1.jsx)(icon_1.MonoIcon, { icon: 'actions/Close' }) }, buttonProps: {
|
|
40
|
-
'aria-label': t(
|
|
41
|
+
'aria-label': t(PageText.Contact.components.modal.close()),
|
|
41
42
|
} })] }), description && (0, jsx_runtime_1.jsx)(typography_1.Typo.p, { textType: "body__m", children: description }), bulletPoints && ((0, jsx_runtime_1.jsx)("ul", { className: form_module_css_1.default.modal__rules_list, children: bulletPoints.map((desc, index) => ((0, jsx_runtime_1.jsx)("li", { children: (0, jsx_runtime_1.jsx)(typography_1.Typo.p, { textType: "body__m", children: desc }, index) }, index))) })), instruction && ((0, jsx_runtime_1.jsx)(typography_1.Typo.p, { textType: "body__m__strong", children: instruction }))] }) }) }) }));
|
|
42
43
|
};
|
|
43
44
|
exports.default = DescriptionModal;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-input.d.ts","sourceRoot":"","sources":["../../../src/components/form/file-input.tsx"],"names":[],"mappings":"AAUA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;CACpC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC;AAI3D,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,EAAE,EACF,QAAQ,EACR,SAAS,EACT,KAAK,EACL,UAAkB,EAClB,IAAI,EACJ,YAAY,GACb,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"file-input.d.ts","sourceRoot":"","sources":["../../../src/components/form/file-input.tsx"],"names":[],"mappings":"AAUA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;CACpC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC;AAI3D,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,EAAE,EACF,QAAQ,EACR,SAAS,EACT,KAAK,EACL,UAAkB,EAClB,IAAI,EACJ,YAAY,GACb,EAAE,cAAc,2CAkHhB"}
|
|
@@ -16,6 +16,7 @@ const translations_1 = require("../../translations");
|
|
|
16
16
|
const theme_1 = require("../../modules/theme");
|
|
17
17
|
const MAX_ALLOWED_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
18
18
|
function FileInput({ id, onChange, iconLabel, label, isRequired = false, name, errorMessage, }) {
|
|
19
|
+
const PageText = (0, translations_1.usePageText)();
|
|
19
20
|
const { t } = (0, translations_1.useTranslation)();
|
|
20
21
|
const { theme } = (0, theme_1.useTheme)();
|
|
21
22
|
const { background } = theme.color;
|
|
@@ -47,7 +48,7 @@ function FileInput({ id, onChange, iconLabel, label, isRequired = false, name, e
|
|
|
47
48
|
const filteredFiles = uploadedFiles.filter((file) => {
|
|
48
49
|
const isTooLarge = file.size > MAX_ALLOWED_FILE_SIZE;
|
|
49
50
|
if (isTooLarge) {
|
|
50
|
-
alert(t(
|
|
51
|
+
alert(t(PageText.Contact.components.fileinput.errorMessages.tooLarge(file.name)));
|
|
51
52
|
}
|
|
52
53
|
return !isTooLarge;
|
|
53
54
|
});
|
|
@@ -7,5 +7,6 @@ export { default as Select } from './select';
|
|
|
7
7
|
export { default as Textarea } from './textarea';
|
|
8
8
|
export { default as DateSelector } from './date-selector';
|
|
9
9
|
export { default as TimeSelector } from './time-selector';
|
|
10
|
+
export { DepartureTimeField } from './departure-time-field';
|
|
10
11
|
export { SearchableSelect, getLineOptions, getStopOptions, } from './searchable-select';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/form/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/form/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getStopOptions = exports.getLineOptions = exports.SearchableSelect = exports.TimeSelector = exports.DateSelector = exports.Textarea = exports.Select = exports.Radio = exports.Input = exports.Fieldset = exports.FileInput = exports.FieldWrapperWithError = void 0;
|
|
6
|
+
exports.getStopOptions = exports.getLineOptions = exports.SearchableSelect = exports.DepartureTimeField = exports.TimeSelector = exports.DateSelector = exports.Textarea = exports.Select = exports.Radio = exports.Input = exports.Fieldset = exports.FileInput = exports.FieldWrapperWithError = void 0;
|
|
7
7
|
var field_wrapper_with_error_1 = require("./field-wrapper-with-error");
|
|
8
8
|
Object.defineProperty(exports, "FieldWrapperWithError", { enumerable: true, get: function () { return __importDefault(field_wrapper_with_error_1).default; } });
|
|
9
9
|
var file_input_1 = require("./file-input");
|
|
@@ -22,6 +22,8 @@ var date_selector_1 = require("./date-selector");
|
|
|
22
22
|
Object.defineProperty(exports, "DateSelector", { enumerable: true, get: function () { return __importDefault(date_selector_1).default; } });
|
|
23
23
|
var time_selector_1 = require("./time-selector");
|
|
24
24
|
Object.defineProperty(exports, "TimeSelector", { enumerable: true, get: function () { return __importDefault(time_selector_1).default; } });
|
|
25
|
+
var departure_time_field_1 = require("./departure-time-field");
|
|
26
|
+
Object.defineProperty(exports, "DepartureTimeField", { enumerable: true, get: function () { return departure_time_field_1.DepartureTimeField; } });
|
|
25
27
|
var searchable_select_1 = require("./searchable-select");
|
|
26
28
|
Object.defineProperty(exports, "SearchableSelect", { enumerable: true, get: function () { return searchable_select_1.SearchableSelect; } });
|
|
27
29
|
Object.defineProperty(exports, "getLineOptions", { enumerable: true, get: function () { return searchable_select_1.getLineOptions; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/form/input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,WAAW,EAAY,MAAM,OAAO,CAAC;AAGrD,OAAO,
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/form/input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,WAAW,EAAY,MAAM,OAAO,CAAC;AAGrD,OAAO,EACL,gBAAgB,EAGjB,MAAM,oBAAoB,CAAC;AAM5B,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC;IACF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;CAC3D,GAAG,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAEzC,eAAO,MAAM,KAAK,GAAI,qHAanB,UAAU,4CAmEZ,CAAC;AAEF,eAAe,KAAK,CAAC"}
|