@citizenplane/pimp 18.10.3 → 18.11.0
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/dist/components/CpSeatMap.vue.d.ts +29 -0
- package/dist/components/CpSeatMap.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapEmergencyExitDialog.vue.d.ts +21 -0
- package/dist/components/CpSeatMapEmergencyExitDialog.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapGrid.vue.d.ts +17 -0
- package/dist/components/CpSeatMapGrid.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapPlane.vue.d.ts +24 -0
- package/dist/components/CpSeatMapPlane.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapRow.vue.d.ts +22 -0
- package/dist/components/CpSeatMapRow.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapRowFacility.vue.d.ts +16 -0
- package/dist/components/CpSeatMapRowFacility.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapSeat.vue.d.ts +21 -0
- package/dist/components/CpSeatMapSeat.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapSeatWrapper.vue.d.ts +23 -0
- package/dist/components/CpSeatMapSeatWrapper.vue.d.ts.map +1 -0
- package/dist/components/CpSeatMapZone.vue.d.ts +16 -0
- package/dist/components/CpSeatMapZone.vue.d.ts.map +1 -0
- package/dist/components/icons/IconPlaneNose.vue.d.ts +3 -0
- package/dist/components/icons/IconPlaneNose.vue.d.ts.map +1 -0
- package/dist/components/icons/IconPlaneWing.vue.d.ts +3 -0
- package/dist/components/icons/IconPlaneWing.vue.d.ts.map +1 -0
- package/dist/components/icons/IconSparkle.vue.d.ts +3 -0
- package/dist/components/icons/IconSparkle.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +14 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/constants/seatMap/CpSeatMapConfig.d.ts +22 -0
- package/dist/constants/seatMap/CpSeatMapConfig.d.ts.map +1 -0
- package/dist/constants/seatMap/CpSeatMapSeatSelection.d.ts +12 -0
- package/dist/constants/seatMap/CpSeatMapSeatSelection.d.ts.map +1 -0
- package/dist/constants/seatMap/CpSeatMapTransitionDirections.d.ts +5 -0
- package/dist/constants/seatMap/CpSeatMapTransitionDirections.d.ts.map +1 -0
- package/dist/constants/seatMap/IataTravelerTypes.d.ts +7 -0
- package/dist/constants/seatMap/IataTravelerTypes.d.ts.map +1 -0
- package/dist/constants/seatMap/Seat.d.ts +18 -0
- package/dist/constants/seatMap/Seat.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatAncillary.d.ts +9 -0
- package/dist/constants/seatMap/SeatAncillary.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatCharacteristic.d.ts +7 -0
- package/dist/constants/seatMap/SeatCharacteristic.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatCharacteristicCodes.d.ts +10 -0
- package/dist/constants/seatMap/SeatCharacteristicCodes.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatMap.d.ts +10 -0
- package/dist/constants/seatMap/SeatMap.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatMapZone.d.ts +6 -0
- package/dist/constants/seatMap/SeatMapZone.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatRow.d.ts +15 -0
- package/dist/constants/seatMap/SeatRow.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatRowFacilities.d.ts +10 -0
- package/dist/constants/seatMap/SeatRowFacilities.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatRowFacilityFlags.d.ts +8 -0
- package/dist/constants/seatMap/SeatRowFacilityFlags.d.ts.map +1 -0
- package/dist/constants/seatMap/SeatStatuses.d.ts +8 -0
- package/dist/constants/seatMap/SeatStatuses.d.ts.map +1 -0
- package/dist/constants/seatMap/index.d.ts +16 -0
- package/dist/constants/seatMap/index.d.ts.map +1 -0
- package/dist/helpers/seatMap.d.ts +28 -0
- package/dist/helpers/seatMap.d.ts.map +1 -0
- package/dist/pimp.es.js +4263 -3756
- package/dist/pimp.umd.js +40 -40
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/CpSeatMap.vue +226 -0
- package/src/components/CpSeatMapEmergencyExitDialog.vue +87 -0
- package/src/components/CpSeatMapGrid.vue +41 -0
- package/src/components/CpSeatMapPlane.vue +221 -0
- package/src/components/CpSeatMapRow.vue +183 -0
- package/src/components/CpSeatMapRowFacility.vue +88 -0
- package/src/components/CpSeatMapSeat.vue +138 -0
- package/src/components/CpSeatMapSeatWrapper.vue +189 -0
- package/src/components/CpSeatMapZone.vue +60 -0
- package/src/components/icons/IconPlaneNose.vue +39 -0
- package/src/components/icons/IconPlaneWing.vue +11 -0
- package/src/components/icons/IconSparkle.vue +8 -0
- package/src/components/index.ts +51 -0
- package/src/constants/seatMap/CpSeatMapConfig.ts +47 -0
- package/src/constants/seatMap/CpSeatMapSeatSelection.ts +12 -0
- package/src/constants/seatMap/CpSeatMapTransitionDirections.ts +4 -0
- package/src/constants/seatMap/IataTravelerTypes.ts +6 -0
- package/src/constants/seatMap/Seat.ts +18 -0
- package/src/constants/seatMap/SeatAncillary.ts +8 -0
- package/src/constants/seatMap/SeatCharacteristic.ts +7 -0
- package/src/constants/seatMap/SeatCharacteristicCodes.ts +9 -0
- package/src/constants/seatMap/SeatMap.ts +10 -0
- package/src/constants/seatMap/SeatMapZone.ts +5 -0
- package/src/constants/seatMap/SeatRow.ts +15 -0
- package/src/constants/seatMap/SeatRowFacilities.ts +10 -0
- package/src/constants/seatMap/SeatRowFacilityFlags.ts +7 -0
- package/src/constants/seatMap/SeatStatuses.ts +7 -0
- package/src/constants/seatMap/index.ts +16 -0
- package/src/helpers/seatMap.ts +76 -0
- package/src/stories/CpSeatMap.stories.ts +357 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { action } from 'storybook/actions'
|
|
3
|
+
import { computed, ref, watch } from 'vue'
|
|
4
|
+
|
|
5
|
+
import type { CpSeatMapSeatSelection } from '@/constants/seatMap/CpSeatMapSeatSelection'
|
|
6
|
+
import type { Seat } from '@/constants/seatMap/Seat'
|
|
7
|
+
import type { SeatMap } from '@/constants/seatMap/SeatMap'
|
|
8
|
+
import type { SeatMapZone } from '@/constants/seatMap/SeatMapZone'
|
|
9
|
+
import type { SeatRow } from '@/constants/seatMap/SeatRow'
|
|
10
|
+
import type { SeatRowFacilityFlags } from '@/constants/seatMap/SeatRowFacilityFlags'
|
|
11
|
+
|
|
12
|
+
import CpSeatMap from '@/components/CpSeatMap.vue'
|
|
13
|
+
import CpSelect from '@/components/CpSelect.vue'
|
|
14
|
+
|
|
15
|
+
import { IataTravelerTypes } from '@/constants/seatMap/IataTravelerTypes'
|
|
16
|
+
import { SeatStatuses } from '@/constants/seatMap/SeatStatuses'
|
|
17
|
+
import { displaySeat } from '@/helpers/seatMap'
|
|
18
|
+
|
|
19
|
+
const meta = {
|
|
20
|
+
title: 'Business/CpSeatMap',
|
|
21
|
+
component: CpSeatMap,
|
|
22
|
+
parameters: {
|
|
23
|
+
layout: 'padded',
|
|
24
|
+
},
|
|
25
|
+
// The plane wings sit 180px outside the cabin, so the story needs side room.
|
|
26
|
+
decorators: [
|
|
27
|
+
() => ({
|
|
28
|
+
template:
|
|
29
|
+
'<div style="margin: 0 auto; padding: 40px; border-radius: 8px; background-color: #fff;"><story/></div>',
|
|
30
|
+
}),
|
|
31
|
+
],
|
|
32
|
+
} satisfies Meta<typeof CpSeatMap>
|
|
33
|
+
|
|
34
|
+
export default meta
|
|
35
|
+
|
|
36
|
+
type Story = StoryObj<typeof meta>
|
|
37
|
+
|
|
38
|
+
const EXTRA_LEGROOM = {
|
|
39
|
+
amount: 12,
|
|
40
|
+
color: '#f5a623',
|
|
41
|
+
data_id: 1,
|
|
42
|
+
eligible_traveler_ids: [],
|
|
43
|
+
formatted_amount: '€12',
|
|
44
|
+
label: 'Extra legroom',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let seatId = 0
|
|
48
|
+
|
|
49
|
+
const makeSeat = (column: string, status: SeatStatuses, isExitRow: boolean): Seat => {
|
|
50
|
+
seatId += 1
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
characteristics: [],
|
|
54
|
+
column,
|
|
55
|
+
eligible_ancillaries: [],
|
|
56
|
+
has_specific_message: false,
|
|
57
|
+
id: seatId,
|
|
58
|
+
is_exit_row: isExitRow,
|
|
59
|
+
occupied_traveler_id: status === SeatStatuses.OCCUPIED ? seatId : 0,
|
|
60
|
+
status,
|
|
61
|
+
type: 'seat',
|
|
62
|
+
...(status === SeatStatuses.PAYABLE ? { selected_ancillary: EXTRA_LEGROOM } : {}),
|
|
63
|
+
...(status === SeatStatuses.BLOCKED ? { blocking_code: 'CREW' } : {}),
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface CabinFacilities {
|
|
68
|
+
bottom?: SeatRowFacilityFlags
|
|
69
|
+
top?: SeatRowFacilityFlags
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface CabinInput {
|
|
73
|
+
columns: string[]
|
|
74
|
+
exitRows?: number[]
|
|
75
|
+
/** Row number -> galleys and lavatories sitting above or below that row. */
|
|
76
|
+
facilities?: Record<number, CabinFacilities>
|
|
77
|
+
id: string
|
|
78
|
+
/** `null` marks an aisle. */
|
|
79
|
+
layout: (string | null)[]
|
|
80
|
+
/** Share of seats already taken, scattered deterministically. */
|
|
81
|
+
occupancy?: number
|
|
82
|
+
rowNumbers: number[]
|
|
83
|
+
/** Row number -> column -> status, overriding everything else. */
|
|
84
|
+
statuses?: Record<number, Record<string, SeatStatuses>>
|
|
85
|
+
title: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Deterministic scatter so a story always renders the same cabin — Math.random
|
|
90
|
+
* would reshuffle the map on every reload and make screenshots useless.
|
|
91
|
+
*/
|
|
92
|
+
const seatHash = (row: number, column: string) => (row * 31 + column.charCodeAt(0) * 17) % 100
|
|
93
|
+
|
|
94
|
+
const resolveStatus = (row: number, column: string, isExit: boolean, occupancy: number) => {
|
|
95
|
+
// Exit rows sell as extra legroom.
|
|
96
|
+
if (isExit) return SeatStatuses.PAYABLE
|
|
97
|
+
|
|
98
|
+
const hash = seatHash(row, column)
|
|
99
|
+
if (hash < occupancy * 100) return SeatStatuses.OCCUPIED
|
|
100
|
+
if (hash > 97) return SeatStatuses.BLOCKED
|
|
101
|
+
|
|
102
|
+
return SeatStatuses.FREE
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const makeCabinRows = ({
|
|
106
|
+
columns,
|
|
107
|
+
id,
|
|
108
|
+
rowNumbers,
|
|
109
|
+
exitRows = [],
|
|
110
|
+
facilities = {},
|
|
111
|
+
occupancy = 0,
|
|
112
|
+
statuses = {},
|
|
113
|
+
}: CabinInput): SeatRow[] => {
|
|
114
|
+
return rowNumbers.map((number) => {
|
|
115
|
+
const isExit = exitRows.includes(number)
|
|
116
|
+
const rowStatuses = statuses[number] ?? {}
|
|
117
|
+
const rowFacilities = facilities[number] ?? {}
|
|
118
|
+
|
|
119
|
+
const seats = columns.reduce<Record<string, Seat>>((accumulator, column) => {
|
|
120
|
+
const status = rowStatuses[column] ?? resolveStatus(number, column, isExit, occupancy)
|
|
121
|
+
accumulator[column] = makeSeat(column, status, isExit)
|
|
122
|
+
return accumulator
|
|
123
|
+
}, {})
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
has_exit_back: false,
|
|
127
|
+
has_exit_front: false,
|
|
128
|
+
has_exit_left: isExit,
|
|
129
|
+
has_exit_right: isExit,
|
|
130
|
+
is_exit: isExit,
|
|
131
|
+
number,
|
|
132
|
+
seats,
|
|
133
|
+
zone: id,
|
|
134
|
+
...(rowFacilities.top ? { top: rowFacilities.top } : {}),
|
|
135
|
+
...(rowFacilities.bottom ? { bottom: rowFacilities.bottom } : {}),
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const makeSeatMap = (aircraft: string, cabins: CabinInput[]): SeatMap => {
|
|
141
|
+
const zones = cabins.reduce<Record<string, SeatMapZone>>((accumulator, cabin) => {
|
|
142
|
+
accumulator[cabin.id] = { layout: cabin.layout, prbds: [], title: cabin.title }
|
|
143
|
+
return accumulator
|
|
144
|
+
}, {})
|
|
145
|
+
|
|
146
|
+
const rows = cabins.flatMap(makeCabinRows)
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
aircraft,
|
|
150
|
+
ref_id_segment: `segment-${aircraft.toLowerCase()}`,
|
|
151
|
+
exit_row_warning_child_max_age: 12,
|
|
152
|
+
zones,
|
|
153
|
+
rows,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const rowRange = (from: number, to: number) => Array.from({ length: to - from + 1 }, (_, index) => from + index)
|
|
158
|
+
|
|
159
|
+
/** Seats the stories hand back as selected, kept free so they read as assignable. */
|
|
160
|
+
const FREE_FOR_SELECTION = { B: SeatStatuses.FREE, C: SeatStatuses.FREE }
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* ATR 72-600: 72 seats in a single 2+2 cabin, forward galley, rear lavatory,
|
|
164
|
+
* overwing exits at row 12.
|
|
165
|
+
*/
|
|
166
|
+
const atr72 = makeSeatMap('ATR 72', [
|
|
167
|
+
{
|
|
168
|
+
id: 'cabin',
|
|
169
|
+
title: 'Cabin',
|
|
170
|
+
layout: ['A', 'B', null, 'C', 'D'],
|
|
171
|
+
columns: ['A', 'B', 'C', 'D'],
|
|
172
|
+
rowNumbers: rowRange(1, 18),
|
|
173
|
+
exitRows: [12],
|
|
174
|
+
occupancy: 0.34,
|
|
175
|
+
statuses: { 7: FREE_FOR_SELECTION, 8: FREE_FOR_SELECTION },
|
|
176
|
+
facilities: {
|
|
177
|
+
1: { top: { galley_left: true } },
|
|
178
|
+
18: { bottom: { lavatory_right: true } },
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
])
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* A320: 2+2 business over three rows, then 3+3 economy to row 32. Forward galley
|
|
185
|
+
* and lavatory between the cabins, an overwing exit row, two lavatories aft.
|
|
186
|
+
*/
|
|
187
|
+
const a320 = makeSeatMap('A320', [
|
|
188
|
+
{
|
|
189
|
+
id: 'business',
|
|
190
|
+
title: 'Business',
|
|
191
|
+
layout: ['A', 'B', null, 'C', 'D'],
|
|
192
|
+
columns: ['A', 'B', 'C', 'D'],
|
|
193
|
+
rowNumbers: rowRange(1, 3),
|
|
194
|
+
occupancy: 0.4,
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 'economy',
|
|
198
|
+
title: 'Economy',
|
|
199
|
+
layout: ['A', 'B', 'C', null, 'D', 'E', 'F'],
|
|
200
|
+
columns: ['A', 'B', 'C', 'D', 'E', 'F'],
|
|
201
|
+
rowNumbers: rowRange(7, 32),
|
|
202
|
+
exitRows: [11],
|
|
203
|
+
occupancy: 0.38,
|
|
204
|
+
statuses: { 7: FREE_FOR_SELECTION, 8: FREE_FOR_SELECTION },
|
|
205
|
+
facilities: {
|
|
206
|
+
7: { top: { galley_left: true, lavatory_right: true } },
|
|
207
|
+
32: { bottom: { lavatory_left: true, lavatory_right: true } },
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
])
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* B777-300ER: 2+2+2 business to row 8, then 3+4+3 economy from row 20 to 48.
|
|
214
|
+
* Ten seats across trips the component's large-plane treatment, which narrows
|
|
215
|
+
* seats and aisles. Mid-cabin lavatories sit ahead of the second exit row.
|
|
216
|
+
* No `I` column — aviation skips it to avoid reading it as a 1.
|
|
217
|
+
*/
|
|
218
|
+
const b777 = makeSeatMap('B777', [
|
|
219
|
+
{
|
|
220
|
+
id: 'business',
|
|
221
|
+
title: 'Business',
|
|
222
|
+
layout: ['A', 'B', null, 'C', 'D', null, 'E', 'F'],
|
|
223
|
+
columns: ['A', 'B', 'C', 'D', 'E', 'F'],
|
|
224
|
+
rowNumbers: rowRange(1, 8),
|
|
225
|
+
occupancy: 0.45,
|
|
226
|
+
statuses: { 7: FREE_FOR_SELECTION, 8: FREE_FOR_SELECTION },
|
|
227
|
+
facilities: {
|
|
228
|
+
1: { top: { galley_left: true, galley_right: true } },
|
|
229
|
+
8: { bottom: { lavatory_left: true, lavatory_right: true } },
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'economy',
|
|
234
|
+
title: 'Economy',
|
|
235
|
+
layout: ['A', 'B', 'C', null, 'D', 'E', 'F', 'G', null, 'H', 'J', 'K'],
|
|
236
|
+
columns: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K'],
|
|
237
|
+
rowNumbers: rowRange(20, 48),
|
|
238
|
+
exitRows: [20, 35],
|
|
239
|
+
occupancy: 0.36,
|
|
240
|
+
facilities: {
|
|
241
|
+
34: { bottom: { lavatory_left: true, lavatory_right: true } },
|
|
242
|
+
48: { bottom: { galley_left: true, lavatory_right: true } },
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
])
|
|
246
|
+
|
|
247
|
+
const AIRCRAFT: Record<string, SeatMap> = {
|
|
248
|
+
'atr-72': atr72,
|
|
249
|
+
a320,
|
|
250
|
+
b777,
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const AIRCRAFT_OPTIONS = [
|
|
254
|
+
{ label: 'ATR 72 — 2+2', value: 'atr-72' },
|
|
255
|
+
{ label: 'A320 — 3+3', value: 'a320' },
|
|
256
|
+
{ label: 'B777 — 3+4+3', value: 'b777' },
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
const selection = (
|
|
260
|
+
row: number,
|
|
261
|
+
column: string,
|
|
262
|
+
value: CpSeatMapSeatSelection,
|
|
263
|
+
): Record<string, CpSeatMapSeatSelection> => ({
|
|
264
|
+
[displaySeat({ row, column })]: value,
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Every story picks its aircraft, so one map can be compared against another.
|
|
269
|
+
*
|
|
270
|
+
* `isInteractive` wires the click back into `selections`, which is what a consumer
|
|
271
|
+
* has to do: the map only reports the seat, it never remembers it.
|
|
272
|
+
*/
|
|
273
|
+
const withAircraftPicker =
|
|
274
|
+
(isInteractive = false) =>
|
|
275
|
+
(args: NonNullable<Story['args']>) => ({
|
|
276
|
+
components: { CpSeatMap, CpSelect },
|
|
277
|
+
setup() {
|
|
278
|
+
const aircraft = ref('a320')
|
|
279
|
+
const seatMap = computed(() => AIRCRAFT[aircraft.value] as SeatMap)
|
|
280
|
+
|
|
281
|
+
const ownSelections = ref<Record<string, CpSeatMapSeatSelection>>({})
|
|
282
|
+
const selections = computed(() => (isInteractive ? ownSelections.value : args.selections))
|
|
283
|
+
|
|
284
|
+
// A seat key from one aircraft rarely exists on the next one.
|
|
285
|
+
watch(aircraft, () => {
|
|
286
|
+
ownSelections.value = {}
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
const handleSelect = (seat: Seat, rowNumber: number) => {
|
|
290
|
+
action('select')(seat, rowNumber)
|
|
291
|
+
if (!isInteractive) return
|
|
292
|
+
|
|
293
|
+
const key = displaySeat({ row: rowNumber, column: seat.column })
|
|
294
|
+
// Clicking the seat again frees it; clicking another moves the traveler over.
|
|
295
|
+
ownSelections.value = key in ownSelections.value ? {} : { [key]: { initials: 'RN' } }
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return { args, aircraft, seatMap, selections, handleSelect, aircraftOptions: AIRCRAFT_OPTIONS }
|
|
299
|
+
},
|
|
300
|
+
// The plane nose bleeds upwards out of the cabin, so the picker is lifted above it.
|
|
301
|
+
template: `
|
|
302
|
+
<div style="display: flex; flex-direction: column; gap: 24px; width:fit-content; margin: 0 auto;">
|
|
303
|
+
<div style="position: absolute; top: 24px; left: 24px; z-index: 1; max-width: 280px; background-color: #fff;">
|
|
304
|
+
<cp-select v-model="aircraft" hide-default-value label="Aircraft" :options="aircraftOptions" />
|
|
305
|
+
</div>
|
|
306
|
+
<cp-seat-map v-bind="args" :seat-map="seatMap" :selections="selections" @select="handleSelect" />
|
|
307
|
+
</div>
|
|
308
|
+
`,
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Nothing assigned yet, and the story does the seating itself: click a free seat
|
|
313
|
+
* to take it, click it again to give it up, click another to move over. Occupied,
|
|
314
|
+
* blocked and paid-for seats read from the map.
|
|
315
|
+
*/
|
|
316
|
+
export const Default: Story = {
|
|
317
|
+
args: {
|
|
318
|
+
seatMap: a320,
|
|
319
|
+
},
|
|
320
|
+
render: withAircraftPicker(true),
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* The consumer has seated two travelers and hands their initials back through
|
|
325
|
+
* `selections`, keyed by seat (`7B`).
|
|
326
|
+
*/
|
|
327
|
+
export const WithSelection: Story = {
|
|
328
|
+
args: {
|
|
329
|
+
seatMap: a320,
|
|
330
|
+
selections: {
|
|
331
|
+
...selection(7, 'B', { initials: 'JD' }),
|
|
332
|
+
...selection(8, 'C', { initials: 'AM' }),
|
|
333
|
+
},
|
|
334
|
+
onSelect: action('select'),
|
|
335
|
+
},
|
|
336
|
+
render: withAircraftPicker(),
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* One of the seated travelers carries a lap infant: `hasLinkedTraveler` shows the
|
|
341
|
+
* badge and `linkedTravelerType` picks its icon.
|
|
342
|
+
*/
|
|
343
|
+
export const WithLinkedInfant: Story = {
|
|
344
|
+
args: {
|
|
345
|
+
seatMap: a320,
|
|
346
|
+
selections: {
|
|
347
|
+
...selection(7, 'B', {
|
|
348
|
+
initials: 'JD',
|
|
349
|
+
hasLinkedTraveler: true,
|
|
350
|
+
linkedTravelerType: IataTravelerTypes.INFANT,
|
|
351
|
+
}),
|
|
352
|
+
...selection(8, 'C', { initials: 'AM' }),
|
|
353
|
+
},
|
|
354
|
+
onSelect: action('select'),
|
|
355
|
+
},
|
|
356
|
+
render: withAircraftPicker(),
|
|
357
|
+
}
|