@citizenplane/pimp 18.17.0 → 18.19.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.
Files changed (69) hide show
  1. package/dist/components/BaseInputLabel.vue.d.ts +1 -1
  2. package/dist/components/BaseInputLabel.vue.d.ts.map +1 -1
  3. package/dist/components/CpAncillaryCategory.vue.d.ts +42 -0
  4. package/dist/components/CpAncillaryCategory.vue.d.ts.map +1 -0
  5. package/dist/components/CpAncillaryItem.vue.d.ts +30 -0
  6. package/dist/components/CpAncillaryItem.vue.d.ts.map +1 -0
  7. package/dist/components/CpBottomSheet.vue.d.ts +1 -1
  8. package/dist/components/CpCheckbox.vue.d.ts +2 -2
  9. package/dist/components/CpCheckbox.vue.d.ts.map +1 -1
  10. package/dist/components/CpDialog.vue.d.ts +1 -1
  11. package/dist/components/CpDialog.vue.d.ts.map +1 -1
  12. package/dist/components/CpMenu.vue.d.ts +1 -1
  13. package/dist/components/CpMenu.vue.d.ts.map +1 -1
  14. package/dist/components/CpSelect.vue.d.ts +1 -1
  15. package/dist/components/CpSelect.vue.d.ts.map +1 -1
  16. package/dist/components/CpSwitch.vue.d.ts +4 -4
  17. package/dist/components/CpSwitch.vue.d.ts.map +1 -1
  18. package/dist/components/CpTelInput.vue.d.ts +2 -2
  19. package/dist/components/CpTelInput.vue.d.ts.map +1 -1
  20. package/dist/components/CpTooltip.vue.d.ts +1 -1
  21. package/dist/components/CpTooltip.vue.d.ts.map +1 -1
  22. package/dist/components/index.d.ts +5 -1
  23. package/dist/components/index.d.ts.map +1 -1
  24. package/dist/constants/CpAncillaryCategoryTypes.d.ts +12 -0
  25. package/dist/constants/CpAncillaryCategoryTypes.d.ts.map +1 -0
  26. package/dist/constants/CpAncillaryItemTypes.d.ts +31 -0
  27. package/dist/constants/CpAncillaryItemTypes.d.ts.map +1 -0
  28. package/dist/pimp.es.js +1980 -1781
  29. package/dist/pimp.umd.js +48 -48
  30. package/dist/style.css +1 -1
  31. package/package.json +1 -1
  32. package/src/assets/images/ancillaries/airport-transfer.png +0 -0
  33. package/src/assets/images/ancillaries/bicycle.png +0 -0
  34. package/src/assets/images/ancillaries/cabin-bag.png +0 -0
  35. package/src/assets/images/ancillaries/checked-bag-size-1.png +0 -0
  36. package/src/assets/images/ancillaries/checked-bag-size-2.png +0 -0
  37. package/src/assets/images/ancillaries/checked-bag-size-3.png +0 -0
  38. package/src/assets/images/ancillaries/checked-bag.png +0 -0
  39. package/src/assets/images/ancillaries/connectivity.png +0 -0
  40. package/src/assets/images/ancillaries/diving-set.png +0 -0
  41. package/src/assets/images/ancillaries/extra-weight.png +0 -0
  42. package/src/assets/images/ancillaries/fast-track.png +0 -0
  43. package/src/assets/images/ancillaries/firearm.png +0 -0
  44. package/src/assets/images/ancillaries/fishing-rod.png +0 -0
  45. package/src/assets/images/ancillaries/golf.png +0 -0
  46. package/src/assets/images/ancillaries/hand-bag.png +0 -0
  47. package/src/assets/images/ancillaries/kite-surf.png +0 -0
  48. package/src/assets/images/ancillaries/lounge-access.png +0 -0
  49. package/src/assets/images/ancillaries/meet-and-greet.png +0 -0
  50. package/src/assets/images/ancillaries/music.png +0 -0
  51. package/src/assets/images/ancillaries/oversized.png +0 -0
  52. package/src/assets/images/ancillaries/pet-in-cabin.png +0 -0
  53. package/src/assets/images/ancillaries/pet-in-hold.png +0 -0
  54. package/src/assets/images/ancillaries/skis.png +0 -0
  55. package/src/assets/images/ancillaries/special-meal.png +0 -0
  56. package/src/assets/images/ancillaries/standard-meal.png +0 -0
  57. package/src/assets/images/ancillary-categories/baggages.png +0 -0
  58. package/src/assets/images/ancillary-categories/carbon-offset.png +0 -0
  59. package/src/assets/images/ancillary-categories/insurance.png +0 -0
  60. package/src/assets/images/ancillary-categories/pets.png +0 -0
  61. package/src/assets/images/ancillary-categories/restricted.png +0 -0
  62. package/src/assets/images/ancillary-categories/sport-equipments.png +0 -0
  63. package/src/components/CpAncillaryCategory.vue +311 -0
  64. package/src/components/CpAncillaryItem.vue +275 -0
  65. package/src/components/index.ts +8 -0
  66. package/src/constants/CpAncillaryCategoryTypes.ts +14 -0
  67. package/src/constants/CpAncillaryItemTypes.ts +30 -0
  68. package/src/stories/CpAncillaryCategory.stories.ts +274 -0
  69. package/src/stories/CpAncillaryItem.stories.ts +243 -0
@@ -19,6 +19,8 @@ import CpAccordion from './CpAccordion.vue'
19
19
  import CpAccordionGroup from './CpAccordionGroup.vue'
20
20
  import CpAirlineLogo from './CpAirlineLogo.vue'
21
21
  import CpAlert from './CpAlert.vue'
22
+ import CpAncillaryCategory from './CpAncillaryCategory.vue'
23
+ import CpAncillaryItem from './CpAncillaryItem.vue'
22
24
  import CpBadge from './CpBadge.vue'
23
25
  import CpBottomSheet from './CpBottomSheet.vue'
24
26
  import CpButton from './CpButton.vue'
@@ -114,6 +116,8 @@ const Components = {
114
116
  CpRadioNew,
115
117
  CpCalendar,
116
118
  CpAlert,
119
+ CpAncillaryCategory,
120
+ CpAncillaryItem,
117
121
  CpLoader,
118
122
  CpInput,
119
123
  CpText,
@@ -207,6 +211,8 @@ export type {
207
211
  FlightTerminal,
208
212
  } from '@/constants/tripDetails'
209
213
 
214
+ export { CpAncillaryCategoryTypes, isSwitchCategory } from '@/constants/CpAncillaryCategoryTypes'
215
+ export { CpAncillaryItemTypes } from '@/constants/CpAncillaryItemTypes'
210
216
  export {
211
217
  CpSeatMapTransitionDirections,
212
218
  DEFAULT_CP_SEAT_MAP_CONFIG,
@@ -224,6 +230,8 @@ export {
224
230
  CpAccordionGroup,
225
231
  CpAirlineLogo,
226
232
  CpAlert,
233
+ CpAncillaryCategory,
234
+ CpAncillaryItem,
227
235
  CpBadge,
228
236
  CpBottomSheet,
229
237
  CpButton,
@@ -0,0 +1,14 @@
1
+ export enum CpAncillaryCategoryTypes {
2
+ AIRPORT_SERVICES = 'airportServices',
3
+ BAGGAGES = 'baggages',
4
+ CARBON_OFFSET = 'carbonOffset',
5
+ INSURANCE = 'insurance',
6
+ ON_BOARD = 'onBoard',
7
+ PETS = 'pets',
8
+ RESTRICTED = 'restricted',
9
+ SPORT_EQUIPMENTS = 'sportEquipments',
10
+ }
11
+
12
+ const SWITCH_CATEGORY_TYPES = new Set<CpAncillaryCategoryTypes>([CpAncillaryCategoryTypes.CARBON_OFFSET])
13
+
14
+ export const isSwitchCategory = (type: CpAncillaryCategoryTypes): boolean => SWITCH_CATEGORY_TYPES.has(type)
@@ -0,0 +1,30 @@
1
+ export enum CpAncillaryItemTypes {
2
+ AIRPORT_TRANSFER = 'airportTransfer',
3
+ BICYCLE = 'bicycle',
4
+ CABIN_BAG = 'cabinBag',
5
+ CANCELLATION_INSURANCE = 'cancellationInsurance',
6
+ CHECKED_BAG = 'checkedBag',
7
+ CHECKED_BAG_SIZE_1 = 'checkedBagSize1',
8
+ CHECKED_BAG_SIZE_2 = 'checkedBagSize2',
9
+ CHECKED_BAG_SIZE_3 = 'checkedBagSize3',
10
+ CONNECTIVITY = 'connectivity',
11
+ DIVING_SET = 'divingSet',
12
+ EXTRA_WEIGHT = 'extraWeight',
13
+ FAST_TRACK = 'fastTrack',
14
+ FIREARM = 'firearm',
15
+ FISHING_ROD = 'fishingRod',
16
+ GOLF = 'golf',
17
+ HAND_BAG = 'handBag',
18
+ KITE_SURF = 'kiteSurf',
19
+ LOUNGE_ACCESS = 'loungeAccess',
20
+ MEET_AND_GREET = 'meetAndGreet',
21
+ MUSIC = 'music',
22
+ NAME_CHANGE = 'nameChange',
23
+ OTHER = 'other',
24
+ OVERSIZED = 'oversized',
25
+ PET_IN_CABIN = 'petInCabin',
26
+ PET_IN_HOLD = 'petInHold',
27
+ SKIS = 'skis',
28
+ SPECIAL_MEAL = 'specialMeal',
29
+ STANDARD_MEAL = 'standardMeal',
30
+ }
@@ -0,0 +1,274 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import { onMounted, ref } from 'vue'
3
+
4
+ import CpAncillaryCategory from '@/components/CpAncillaryCategory.vue'
5
+ import CpAncillaryItem from '@/components/CpAncillaryItem.vue'
6
+
7
+ import { CpAncillaryCategoryTypes } from '@/constants/CpAncillaryCategoryTypes'
8
+ import { docLabelStyle } from '@/stories/documentationStyles'
9
+
10
+ const cardGridStyle = 'display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;'
11
+ const galleryListStyle = 'display: flex; flex-direction: column; gap: 24px; width: 100%;'
12
+ const galleryRowStyle = 'display: flex; flex-direction: column; gap: 10px; width: 100%;'
13
+
14
+ const meta = {
15
+ title: 'Business/CpAncillaryCategory',
16
+ component: CpAncillaryCategory,
17
+ decorators: [
18
+ (story) => ({
19
+ components: { story },
20
+ setup() {
21
+ onMounted(() => {
22
+ document.getElementById('storybook-root')?.style.setProperty('width', '100%')
23
+ })
24
+ },
25
+ template: '<story />',
26
+ }),
27
+ ],
28
+ argTypes: {
29
+ currency: {
30
+ control: 'text',
31
+ description: 'ISO currency code used to format the from price',
32
+ },
33
+ description: {
34
+ control: 'text',
35
+ description: 'Description text for the category',
36
+ },
37
+ fromPrice: {
38
+ control: 'number',
39
+ description: 'From price in minor units (cents), shown when isExpandable is set',
40
+ },
41
+ fromPriceLabel: {
42
+ control: 'text',
43
+ description: 'Caption shown before the from price amount',
44
+ },
45
+ isDefaultClosed: {
46
+ control: 'boolean',
47
+ description: 'Whether an expandable category starts closed',
48
+ },
49
+ isExpandable: {
50
+ control: 'boolean',
51
+ description: 'Whether the header gets a from-price + chevron toggle collapsing the body',
52
+ },
53
+ locale: {
54
+ control: 'text',
55
+ description: 'Locale used to format the from price, defaults to the browser locale',
56
+ },
57
+ title: {
58
+ control: 'text',
59
+ description: 'Title of the category',
60
+ },
61
+ type: {
62
+ control: 'select',
63
+ options: Object.values(CpAncillaryCategoryTypes),
64
+ description: 'Picks the visual and, for switch categories, the trailing control',
65
+ },
66
+ },
67
+ } satisfies Meta<typeof CpAncillaryCategory>
68
+
69
+ export default meta
70
+ type Story = StoryObj<typeof meta>
71
+
72
+ /**
73
+ * Expandable category with a from-price. Use the controls to experiment with
74
+ * each prop in isolation.
75
+ */
76
+ export const Default: Story = {
77
+ args: {
78
+ title: 'Baggages',
79
+ description: 'Check in your bags for the flight',
80
+ type: CpAncillaryCategoryTypes.BAGGAGES,
81
+ isExpandable: true,
82
+ fromPrice: 2990,
83
+ fromPriceLabel: 'From',
84
+ currency: 'EUR',
85
+ },
86
+ render: (args) => ({
87
+ components: { CpAncillaryCategory, CpAncillaryItem },
88
+ setup() {
89
+ const quantities = ref({ checkedBag: 0, cabinBag: 0, handBag: 0 })
90
+ return { args, cardGridStyle, quantities }
91
+ },
92
+ template: `
93
+ <div style="padding: 20px; min-width: 15rem;">
94
+ <CpAncillaryCategory v-bind="args">
95
+ <div :style="cardGridStyle">
96
+ <CpAncillaryItem
97
+ v-model:quantity="quantities.checkedBag"
98
+ title="Checked bag"
99
+ description="23kg included"
100
+ type="checkedBag"
101
+ :max-quantity="3"
102
+ max-quantity-label="max. 3"
103
+ :price="2990"
104
+ currency="EUR"
105
+ />
106
+ <CpAncillaryItem
107
+ v-model:quantity="quantities.cabinBag"
108
+ title="Cabin bag"
109
+ description="10kg included"
110
+ type="cabinBag"
111
+ :max-quantity="1"
112
+ max-quantity-label="max. 1"
113
+ :price="1990"
114
+ currency="EUR"
115
+ />
116
+ <CpAncillaryItem
117
+ v-model:quantity="quantities.handBag"
118
+ title="Hand bag"
119
+ description="Personal item"
120
+ type="handBag"
121
+ :max-quantity="1"
122
+ max-quantity-label="max. 1"
123
+ :price="990"
124
+ currency="EUR"
125
+ />
126
+ </div>
127
+ </CpAncillaryCategory>
128
+ </div>
129
+ `,
130
+ }),
131
+ }
132
+
133
+ /* -------------------------------------------------------------------------- */
134
+ /* States */
135
+ /* -------------------------------------------------------------------------- */
136
+
137
+ /**
138
+ * isDefaultClosed starts the expandable category collapsed.
139
+ */
140
+ export const Collapsed: Story = {
141
+ args: {
142
+ ...Default.args,
143
+ isDefaultClosed: true,
144
+ },
145
+ render: (args) => ({
146
+ components: { CpAncillaryCategory, CpAncillaryItem },
147
+ setup() {
148
+ const quantity = ref(0)
149
+ return { args, cardGridStyle, quantity }
150
+ },
151
+ template: `
152
+ <div style="padding: 20px; min-width: 15rem;">
153
+ <CpAncillaryCategory v-bind="args">
154
+ <div :style="cardGridStyle">
155
+ <CpAncillaryItem
156
+ v-model:quantity="quantity"
157
+ title="Checked bag"
158
+ description="23kg included"
159
+ type="checkedBag"
160
+ :max-quantity="3"
161
+ max-quantity-label="max. 3"
162
+ :price="2990"
163
+ currency="EUR"
164
+ />
165
+ </div>
166
+ </CpAncillaryCategory>
167
+ </div>
168
+ `,
169
+ }),
170
+ }
171
+
172
+ /**
173
+ * isExpandable false: no from-price, no chevron, the body is always rendered.
174
+ */
175
+ export const NotExpandable: Story = {
176
+ args: {
177
+ title: 'Insurance',
178
+ description: 'Protect your trip against the unexpected',
179
+ type: CpAncillaryCategoryTypes.INSURANCE,
180
+ isExpandable: false,
181
+ },
182
+ render: (args) => ({
183
+ components: { CpAncillaryCategory, CpAncillaryItem },
184
+ setup() {
185
+ const quantity = ref(0)
186
+ return { args, cardGridStyle, quantity }
187
+ },
188
+ template: `
189
+ <div style="padding: 20px; min-width: 15rem;">
190
+ <CpAncillaryCategory v-bind="args">
191
+ <div :style="cardGridStyle">
192
+ <CpAncillaryItem
193
+ v-model:quantity="quantity"
194
+ title="Cancellation insurance"
195
+ description="Refund protection for your trip"
196
+ type="cancellationInsurance"
197
+ :max-quantity="1"
198
+ max-quantity-label="max. 1"
199
+ :price="1490"
200
+ currency="EUR"
201
+ />
202
+ </div>
203
+ </CpAncillaryCategory>
204
+ </div>
205
+ `,
206
+ }),
207
+ }
208
+
209
+ /**
210
+ * Switch categories (carbonOffset today) show a formatted price and a switch
211
+ * instead of a chevron, and are never expandable.
212
+ */
213
+ export const Switch: Story = {
214
+ args: {
215
+ title: 'Carbon offset',
216
+ description: 'Compensate the CO2 emissions of your flight',
217
+ type: CpAncillaryCategoryTypes.CARBON_OFFSET,
218
+ fromPrice: 490,
219
+ currency: 'EUR',
220
+ },
221
+ render: (args) => ({
222
+ components: { CpAncillaryCategory },
223
+ setup() {
224
+ const checked = ref(false)
225
+ return { args, checked }
226
+ },
227
+ template: `
228
+ <div style="padding: 20px; min-width: 15rem;">
229
+ <CpAncillaryCategory v-bind="args" v-model:checked="checked" />
230
+ </div>
231
+ `,
232
+ }),
233
+ }
234
+
235
+ /* -------------------------------------------------------------------------- */
236
+ /* Gallery */
237
+ /* -------------------------------------------------------------------------- */
238
+
239
+ /**
240
+ * Every enum value's header: each real visual, and the fallback icon for the
241
+ * two art-less types.
242
+ */
243
+ export const Gallery: Story = {
244
+ args: {
245
+ ...Default.args,
246
+ },
247
+ parameters: { controls: { disable: true } },
248
+ render: () => ({
249
+ components: { CpAncillaryCategory },
250
+ setup() {
251
+ const types = Object.values(CpAncillaryCategoryTypes)
252
+ const checkedStates = ref(Object.fromEntries(types.map((type) => [type, false])))
253
+ return { types, checkedStates, galleryListStyle, galleryRowStyle, docLabelStyle }
254
+ },
255
+ template: `
256
+ <div :style="galleryListStyle">
257
+ <div v-for="type in types" :key="type" :style="galleryRowStyle">
258
+ <span :style="docLabelStyle">{{ type }}</span>
259
+ <CpAncillaryCategory
260
+ v-model:checked="checkedStates[type]"
261
+ :title="type"
262
+ description="Sample description"
263
+ :type="type"
264
+ is-expandable
265
+ :from-price="1000"
266
+ currency="EUR"
267
+ >
268
+ <span :style="docLabelStyle">Body content</span>
269
+ </CpAncillaryCategory>
270
+ </div>
271
+ </div>
272
+ `,
273
+ }),
274
+ }
@@ -0,0 +1,243 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import { ref } from 'vue'
3
+
4
+ import CpAncillaryItem from '@/components/CpAncillaryItem.vue'
5
+
6
+ import { CpAncillaryItemTypes } from '@/constants/CpAncillaryItemTypes'
7
+ import { docCellStyle, docLabelStyle, docRowWrapStyle } from '@/stories/documentationStyles'
8
+
9
+ const galleryCellStyle = `${docCellStyle} width: 260px;`
10
+
11
+ const meta = {
12
+ title: 'Business/CpAncillaryItem',
13
+ component: CpAncillaryItem,
14
+ argTypes: {
15
+ currency: {
16
+ control: 'text',
17
+ description: 'ISO currency code used to format price',
18
+ },
19
+ description: {
20
+ control: 'text',
21
+ description: 'Description text for the ancillary',
22
+ },
23
+ locale: {
24
+ control: 'text',
25
+ description: 'Locale used to format price, defaults to the browser locale',
26
+ },
27
+ maxQuantity: {
28
+ control: 'number',
29
+ description: 'Maximum selectable quantity, null for uncapped',
30
+ },
31
+ maxQuantityLabel: {
32
+ control: 'text',
33
+ description: 'Caption shown next to the counter when maxQuantity is set',
34
+ },
35
+ price: {
36
+ control: 'number',
37
+ description: 'Price in minor units (cents), formatted with currency',
38
+ },
39
+ subtitle: {
40
+ control: 'text',
41
+ description: 'Optional second line rendered under the description',
42
+ },
43
+ title: {
44
+ control: 'text',
45
+ description: 'Title of the ancillary',
46
+ },
47
+ type: {
48
+ control: 'select',
49
+ options: Object.values(CpAncillaryItemTypes),
50
+ description: 'Picks the visual for the ancillary',
51
+ },
52
+ vertical: {
53
+ control: 'boolean',
54
+ description: 'Whether to use the column layout instead of the row layout',
55
+ },
56
+ },
57
+ } satisfies Meta<typeof CpAncillaryItem>
58
+
59
+ export default meta
60
+ type Story = StoryObj<typeof meta>
61
+
62
+ /**
63
+ * Default ancillary card. Use the controls to experiment with each prop in isolation.
64
+ */
65
+ export const Default: Story = {
66
+ args: {
67
+ title: 'Checked bag',
68
+ description: '23kg included',
69
+ type: CpAncillaryItemTypes.CHECKED_BAG,
70
+ quantity: 0,
71
+ maxQuantity: 3,
72
+ maxQuantityLabel: 'max. 3',
73
+ price: 2990,
74
+ currency: 'EUR',
75
+ vertical: false,
76
+ },
77
+ render: (args) => ({
78
+ components: { CpAncillaryItem },
79
+ setup() {
80
+ const quantity = ref(0)
81
+ return { args, quantity }
82
+ },
83
+ template: `
84
+ <div style="padding: 20px; min-width: 15rem;">
85
+ <CpAncillaryItem v-bind="args" v-model:quantity="quantity" />
86
+ </div>
87
+ `,
88
+ }),
89
+ }
90
+
91
+ /* -------------------------------------------------------------------------- */
92
+ /* States */
93
+ /* -------------------------------------------------------------------------- */
94
+
95
+ /**
96
+ * quantity > 0 adds the accent ring and swaps the counter background.
97
+ */
98
+ export const Selected: Story = {
99
+ args: {
100
+ ...Default.args,
101
+ quantity: 1,
102
+ },
103
+ render: (args) => ({
104
+ components: { CpAncillaryItem },
105
+ setup() {
106
+ const quantity = ref(1)
107
+ return { args, quantity }
108
+ },
109
+ template: `
110
+ <div style="padding: 20px; min-width: 15rem;">
111
+ <CpAncillaryItem v-bind="args" v-model:quantity="quantity" />
112
+ </div>
113
+ `,
114
+ }),
115
+ }
116
+
117
+ /**
118
+ * No maxQuantity: the counter has no upper bound and the caption is hidden.
119
+ */
120
+ export const Uncapped: Story = {
121
+ args: {
122
+ title: 'Extra weight',
123
+ description: 'Additional 5kg allowance',
124
+ type: CpAncillaryItemTypes.EXTRA_WEIGHT,
125
+ quantity: 0,
126
+ maxQuantity: null,
127
+ price: 990,
128
+ currency: 'EUR',
129
+ vertical: false,
130
+ },
131
+ render: (args) => ({
132
+ components: { CpAncillaryItem },
133
+ setup() {
134
+ const quantity = ref(0)
135
+ return { args, quantity }
136
+ },
137
+ template: `
138
+ <div style="padding: 20px; min-width: 15rem;">
139
+ <CpAncillaryItem v-bind="args" v-model:quantity="quantity" />
140
+ </div>
141
+ `,
142
+ }),
143
+ }
144
+
145
+ /**
146
+ * A type with no bundled visual falls back to the shopping-bag icon.
147
+ */
148
+ export const NoImage: Story = {
149
+ args: {
150
+ title: 'Cancellation insurance',
151
+ description: 'Refund protection for your trip',
152
+ type: CpAncillaryItemTypes.CANCELLATION_INSURANCE,
153
+ quantity: 0,
154
+ maxQuantity: 1,
155
+ maxQuantityLabel: 'max. 1',
156
+ price: 1490,
157
+ currency: 'EUR',
158
+ vertical: false,
159
+ },
160
+ render: (args) => ({
161
+ components: { CpAncillaryItem },
162
+ setup() {
163
+ const quantity = ref(0)
164
+ return { args, quantity }
165
+ },
166
+ template: `
167
+ <div style="padding: 20px; min-width: 15rem;">
168
+ <CpAncillaryItem v-bind="args" v-model:quantity="quantity" />
169
+ </div>
170
+ `,
171
+ }),
172
+ }
173
+
174
+ /**
175
+ * The column layout, used for baggage grids. subtitle renders a second details
176
+ * line under the description.
177
+ */
178
+ export const Vertical: Story = {
179
+ args: {
180
+ title: 'Cabin bag',
181
+ description: '10kg included',
182
+ subtitle: '9kg • 55 × 36 × 25 cm',
183
+ type: CpAncillaryItemTypes.CABIN_BAG,
184
+ quantity: 0,
185
+ maxQuantity: 1,
186
+ maxQuantityLabel: 'max. 1',
187
+ price: 1990,
188
+ currency: 'EUR',
189
+ vertical: true,
190
+ },
191
+ render: (args) => ({
192
+ components: { CpAncillaryItem },
193
+ setup() {
194
+ const quantity = ref(0)
195
+ return { args, quantity }
196
+ },
197
+ template: `
198
+ <div style="padding: 20px; min-width: 15rem;">
199
+ <CpAncillaryItem v-bind="args" v-model:quantity="quantity" />
200
+ </div>
201
+ `,
202
+ }),
203
+ }
204
+
205
+ /* -------------------------------------------------------------------------- */
206
+ /* Gallery */
207
+ /* -------------------------------------------------------------------------- */
208
+
209
+ /**
210
+ * Every enum value rendered at once: each real visual, and the fallback icon
211
+ * for the three art-less types.
212
+ */
213
+ export const Gallery: Story = {
214
+ args: {
215
+ ...Default.args,
216
+ },
217
+ parameters: { controls: { disable: true } },
218
+ render: () => ({
219
+ components: { CpAncillaryItem },
220
+ setup() {
221
+ const types = Object.values(CpAncillaryItemTypes)
222
+ const quantities = ref(Object.fromEntries(types.map((type) => [type, 0])))
223
+ return { types, quantities, galleryCellStyle, docLabelStyle, docRowWrapStyle }
224
+ },
225
+ template: `
226
+ <div :style="docRowWrapStyle">
227
+ <div v-for="type in types" :key="type" :style="galleryCellStyle">
228
+ <span :style="docLabelStyle">{{ type }}</span>
229
+ <CpAncillaryItem
230
+ v-model:quantity="quantities[type]"
231
+ :title="type"
232
+ description="Sample description"
233
+ :type="type"
234
+ :max-quantity="5"
235
+ max-quantity-label="max. 5"
236
+ :price="1000"
237
+ currency="EUR"
238
+ />
239
+ </div>
240
+ </div>
241
+ `,
242
+ }),
243
+ }