@citizenplane/pimp 18.20.0 → 18.21.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/CpAncillaryFlightSelection.vue.d.ts +27 -0
- package/dist/components/CpAncillaryFlightSelection.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/constants/CpAncillaryFlightSelectionTypes.d.ts +8 -0
- package/dist/constants/CpAncillaryFlightSelectionTypes.d.ts.map +1 -0
- package/dist/pimp.es.js +1639 -1583
- package/dist/pimp.umd.js +50 -50
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/CpAncillaryFlightSelection.vue +192 -0
- package/src/components/index.ts +4 -0
- package/src/constants/CpAncillaryFlightSelectionTypes.ts +7 -0
- package/src/stories/CpAncillaryFlightSelection.stories.ts +135 -0
package/package.json
CHANGED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="cpAncillaryFlightSelection">
|
|
3
|
+
<div class="cpAncillaryFlightSelection__toggle">
|
|
4
|
+
<cp-switch v-model="applyToAllFlights" color="accent" :label="label" reverse-label />
|
|
5
|
+
</div>
|
|
6
|
+
<cp-transition-expand>
|
|
7
|
+
<div v-if="!applyToAllFlights">
|
|
8
|
+
<div class="cpAncillaryFlightSelection__tabsWrapper">
|
|
9
|
+
<div class="cpAncillaryFlightSelection__tabs" role="tablist">
|
|
10
|
+
<button
|
|
11
|
+
v-for="(flight, index) in flights"
|
|
12
|
+
:key="flight.id"
|
|
13
|
+
:aria-selected="isActiveTab(flight.id)"
|
|
14
|
+
class="cpAncillaryFlightSelection__tab"
|
|
15
|
+
:class="getTabDynamicClass(flight.id)"
|
|
16
|
+
role="tab"
|
|
17
|
+
type="button"
|
|
18
|
+
@click="handleSelectFlight(flight)"
|
|
19
|
+
>
|
|
20
|
+
<span class="cpAncillaryFlightSelection__tabLabel">{{ getTabLabel(index) }}</span>
|
|
21
|
+
<span class="cpAncillaryFlightSelection__tabRoute">
|
|
22
|
+
<span
|
|
23
|
+
class="cpAncillaryFlightSelection__tabRouteText cpAncillaryFlightSelection__tabRouteText--city"
|
|
24
|
+
>{{ flight.originCity }}</span
|
|
25
|
+
>
|
|
26
|
+
<span
|
|
27
|
+
class="cpAncillaryFlightSelection__tabRouteText cpAncillaryFlightSelection__tabRouteText--iata"
|
|
28
|
+
>{{ flight.originIata }}</span
|
|
29
|
+
>
|
|
30
|
+
<cp-icon class="cpAncillaryFlightSelection__tabIcon" size="16" type="arrow-right" />
|
|
31
|
+
<span
|
|
32
|
+
class="cpAncillaryFlightSelection__tabRouteText cpAncillaryFlightSelection__tabRouteText--city"
|
|
33
|
+
>{{ flight.destinationCity }}</span
|
|
34
|
+
>
|
|
35
|
+
<span
|
|
36
|
+
class="cpAncillaryFlightSelection__tabRouteText cpAncillaryFlightSelection__tabRouteText--iata"
|
|
37
|
+
>{{ flight.destinationIata }}</span
|
|
38
|
+
>
|
|
39
|
+
</span>
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</cp-transition-expand>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
import type { CpAncillaryFlightSelectionFlight } from '@/constants/CpAncillaryFlightSelectionTypes'
|
|
50
|
+
|
|
51
|
+
interface Props {
|
|
52
|
+
flights: CpAncillaryFlightSelectionFlight[]
|
|
53
|
+
label?: string
|
|
54
|
+
outboundLabel?: string
|
|
55
|
+
returnLabel?: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
59
|
+
label: 'Apply to all flights',
|
|
60
|
+
outboundLabel: 'Outbound',
|
|
61
|
+
returnLabel: 'Return',
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const applyToAllFlights = defineModel<boolean>('applyToAllFlights', { default: false })
|
|
65
|
+
const activeFlightId = defineModel<string | undefined>('activeFlightId')
|
|
66
|
+
|
|
67
|
+
const isActiveTab = (id: string) => id === activeFlightId.value
|
|
68
|
+
|
|
69
|
+
const getTabDynamicClass = (id: string): string =>
|
|
70
|
+
isActiveTab(id) ? 'cpAncillaryFlightSelection__tab--isActive' : ''
|
|
71
|
+
|
|
72
|
+
const getTabLabel = (index: number): string => (index === 0 ? props.outboundLabel : props.returnLabel)
|
|
73
|
+
|
|
74
|
+
const handleSelectFlight = (flight: CpAncillaryFlightSelectionFlight) => {
|
|
75
|
+
activeFlightId.value = flight.id
|
|
76
|
+
}
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<style lang="scss">
|
|
80
|
+
.cpAncillaryFlightSelection {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
padding: var(--cp-spacing-sm);
|
|
85
|
+
border-radius: var(--cp-radius-xl);
|
|
86
|
+
background: var(--cp-background-tertiary);
|
|
87
|
+
container-type: inline-size;
|
|
88
|
+
gap: var(--cp-spacing-sm);
|
|
89
|
+
|
|
90
|
+
&__toggle {
|
|
91
|
+
padding: var(--cp-spacing-lg) var(--cp-spacing-xl) var(--cp-spacing-lg) var(--cp-spacing-lg);
|
|
92
|
+
border-radius: var(--cp-radius-lg);
|
|
93
|
+
background: var(--cp-background-primary);
|
|
94
|
+
|
|
95
|
+
.cpSwitch__label {
|
|
96
|
+
color: var(--cp-text-secondary);
|
|
97
|
+
font-size: var(--cp-text-size-md);
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
line-height: var(--cp-text-md-line-height);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&__tabsWrapper {
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
padding: var(--cp-spacing-lg);
|
|
107
|
+
gap: var(--cp-spacing-md);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
&__tabs {
|
|
111
|
+
display: flex;
|
|
112
|
+
width: 100%;
|
|
113
|
+
align-items: center;
|
|
114
|
+
padding: var(--cp-spacing-sm);
|
|
115
|
+
border-radius: var(--cp-radius-md);
|
|
116
|
+
background: var(--cp-background-quaternary);
|
|
117
|
+
gap: var(--cp-spacing-sm);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&__tab {
|
|
121
|
+
@extend %u-focus-outline;
|
|
122
|
+
|
|
123
|
+
display: flex;
|
|
124
|
+
flex: 1;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
align-items: center;
|
|
127
|
+
padding: var(--cp-spacing-lg) var(--cp-spacing-xl);
|
|
128
|
+
border-radius: var(--cp-radius-sm);
|
|
129
|
+
gap: var(--cp-spacing-sm-md);
|
|
130
|
+
|
|
131
|
+
&--isActive {
|
|
132
|
+
background: var(--cp-background-primary);
|
|
133
|
+
box-shadow: var(--cp-shadows-xs);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
&__tabLabel {
|
|
138
|
+
color: var(--cp-text-tertiary);
|
|
139
|
+
font-size: var(--cp-text-size-xs);
|
|
140
|
+
font-weight: 600;
|
|
141
|
+
letter-spacing: 1px;
|
|
142
|
+
line-height: var(--cp-text-xs-line-height);
|
|
143
|
+
padding-inline: var(--cp-spacing-xs);
|
|
144
|
+
text-transform: uppercase;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
&__tabRoute {
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
gap: var(--cp-spacing-sm);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
&__tabRouteText {
|
|
154
|
+
color: var(--cp-text-secondary);
|
|
155
|
+
font-size: var(--cp-text-size-md);
|
|
156
|
+
font-weight: 500;
|
|
157
|
+
line-height: var(--cp-text-md-line-height);
|
|
158
|
+
|
|
159
|
+
&--iata {
|
|
160
|
+
display: none;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&__tabIcon {
|
|
165
|
+
color: var(--cp-text-tertiary);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
&__tab--isActive &__tabLabel {
|
|
169
|
+
color: var(--cp-text-primary);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
&__tab--isActive &__tabRouteText {
|
|
173
|
+
color: var(--cp-text-accent-primary);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
&__tab--isActive &__tabIcon {
|
|
177
|
+
color: var(--cp-foreground-accent-secondary);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@container (width < 30rem) {
|
|
181
|
+
.cpAncillaryFlightSelection__tabRouteText {
|
|
182
|
+
&--city {
|
|
183
|
+
display: none;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
&--iata {
|
|
187
|
+
display: inline;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
</style>
|
package/src/components/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import CpAccordionGroup from './CpAccordionGroup.vue'
|
|
|
21
21
|
import CpAirlineLogo from './CpAirlineLogo.vue'
|
|
22
22
|
import CpAlert from './CpAlert.vue'
|
|
23
23
|
import CpAncillaryCategory from './CpAncillaryCategory.vue'
|
|
24
|
+
import CpAncillaryFlightSelection from './CpAncillaryFlightSelection.vue'
|
|
24
25
|
import CpAncillaryItem from './CpAncillaryItem.vue'
|
|
25
26
|
import CpBadge from './CpBadge.vue'
|
|
26
27
|
import CpBottomSheet from './CpBottomSheet.vue'
|
|
@@ -120,6 +121,7 @@ const Components = {
|
|
|
120
121
|
CpCalendar,
|
|
121
122
|
CpAlert,
|
|
122
123
|
CpAncillaryCategory,
|
|
124
|
+
CpAncillaryFlightSelection,
|
|
123
125
|
CpAncillaryItem,
|
|
124
126
|
CpLoader,
|
|
125
127
|
CpInput,
|
|
@@ -195,6 +197,7 @@ const Pimp: Plugin = {
|
|
|
195
197
|
},
|
|
196
198
|
}
|
|
197
199
|
|
|
200
|
+
export type { CpAncillaryFlightSelectionFlight } from '@/constants/CpAncillaryFlightSelectionTypes'
|
|
198
201
|
export type { CpStepperStep } from '@/constants/CpStepperTypes'
|
|
199
202
|
export type {
|
|
200
203
|
CpSeatMapConfig,
|
|
@@ -238,6 +241,7 @@ export {
|
|
|
238
241
|
CpAirlineLogo,
|
|
239
242
|
CpAlert,
|
|
240
243
|
CpAncillaryCategory,
|
|
244
|
+
CpAncillaryFlightSelection,
|
|
241
245
|
CpAncillaryItem,
|
|
242
246
|
CpBadge,
|
|
243
247
|
CpBottomSheet,
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { CpAncillaryFlightSelectionFlight } from '@/constants/CpAncillaryFlightSelectionTypes'
|
|
5
|
+
|
|
6
|
+
import CpAncillaryFlightSelection from '@/components/CpAncillaryFlightSelection.vue'
|
|
7
|
+
|
|
8
|
+
import { docLabelStyle } from '@/stories/documentationStyles'
|
|
9
|
+
|
|
10
|
+
const sampleFlights: CpAncillaryFlightSelectionFlight[] = [
|
|
11
|
+
{
|
|
12
|
+
id: 'outbound',
|
|
13
|
+
originCity: 'Paris',
|
|
14
|
+
originIata: 'CDG',
|
|
15
|
+
destinationCity: 'New York',
|
|
16
|
+
destinationIata: 'JFK',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'inbound',
|
|
20
|
+
originCity: 'New York',
|
|
21
|
+
originIata: 'JFK',
|
|
22
|
+
destinationCity: 'Paris',
|
|
23
|
+
destinationIata: 'CDG',
|
|
24
|
+
},
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
const meta = {
|
|
28
|
+
title: 'Business/CpAncillaryFlightSelection',
|
|
29
|
+
component: CpAncillaryFlightSelection,
|
|
30
|
+
argTypes: {
|
|
31
|
+
label: {
|
|
32
|
+
control: 'text',
|
|
33
|
+
description: 'Label of the "apply to all flights" switch',
|
|
34
|
+
},
|
|
35
|
+
outboundLabel: {
|
|
36
|
+
control: 'text',
|
|
37
|
+
description: 'Label of the first flight tab',
|
|
38
|
+
},
|
|
39
|
+
returnLabel: {
|
|
40
|
+
control: 'text',
|
|
41
|
+
description: 'Label of the second flight tab',
|
|
42
|
+
},
|
|
43
|
+
flights: {
|
|
44
|
+
control: 'object',
|
|
45
|
+
description: 'Flights shown as tabs when the switch is off',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
} satisfies Meta<typeof CpAncillaryFlightSelection>
|
|
49
|
+
|
|
50
|
+
export default meta
|
|
51
|
+
type Story = StoryObj<typeof meta>
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Switch off, tab strip visible, first flight selected. The models are wired
|
|
55
|
+
* with refs to demonstrate the round trip.
|
|
56
|
+
*/
|
|
57
|
+
export const Default: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
flights: sampleFlights,
|
|
60
|
+
label: 'Apply to all flights',
|
|
61
|
+
outboundLabel: 'Outbound',
|
|
62
|
+
returnLabel: 'Return',
|
|
63
|
+
},
|
|
64
|
+
render: (args) => ({
|
|
65
|
+
components: { CpAncillaryFlightSelection },
|
|
66
|
+
setup() {
|
|
67
|
+
const applyToAllFlights = ref(false)
|
|
68
|
+
const activeFlightId = ref(sampleFlights[0].id)
|
|
69
|
+
return { args, applyToAllFlights, activeFlightId }
|
|
70
|
+
},
|
|
71
|
+
template: `
|
|
72
|
+
<div style="padding: 20px; width: 40rem;">
|
|
73
|
+
<CpAncillaryFlightSelection
|
|
74
|
+
v-bind="args"
|
|
75
|
+
v-model:apply-to-all-flights="applyToAllFlights"
|
|
76
|
+
v-model:active-flight-id="activeFlightId"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
`,
|
|
80
|
+
}),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Switch on: the tab strip collapses away.
|
|
85
|
+
*/
|
|
86
|
+
export const ApplyToAllFlights: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
...Default.args,
|
|
89
|
+
},
|
|
90
|
+
render: (args) => ({
|
|
91
|
+
components: { CpAncillaryFlightSelection },
|
|
92
|
+
setup() {
|
|
93
|
+
const applyToAllFlights = ref(true)
|
|
94
|
+
const activeFlightId = ref(sampleFlights[0].id)
|
|
95
|
+
return { args, applyToAllFlights, activeFlightId }
|
|
96
|
+
},
|
|
97
|
+
template: `
|
|
98
|
+
<div style="padding: 20px; width: 40rem;">
|
|
99
|
+
<CpAncillaryFlightSelection
|
|
100
|
+
v-bind="args"
|
|
101
|
+
v-model:apply-to-all-flights="applyToAllFlights"
|
|
102
|
+
v-model:active-flight-id="activeFlightId"
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
`,
|
|
106
|
+
}),
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* At a 358px container width the tabs swap city names for IATA codes.
|
|
111
|
+
*/
|
|
112
|
+
export const Mobile: Story = {
|
|
113
|
+
args: {
|
|
114
|
+
...Default.args,
|
|
115
|
+
},
|
|
116
|
+
parameters: { controls: { disable: true } },
|
|
117
|
+
render: () => ({
|
|
118
|
+
components: { CpAncillaryFlightSelection },
|
|
119
|
+
setup() {
|
|
120
|
+
const applyToAllFlights = ref(false)
|
|
121
|
+
const activeFlightId = ref(sampleFlights[0].id)
|
|
122
|
+
return { sampleFlights, applyToAllFlights, activeFlightId, docLabelStyle }
|
|
123
|
+
},
|
|
124
|
+
template: `
|
|
125
|
+
<div style="display: flex; flex-direction: column; gap: 10px; width: 358px;">
|
|
126
|
+
<span :style="docLabelStyle">358px</span>
|
|
127
|
+
<CpAncillaryFlightSelection
|
|
128
|
+
:flights="sampleFlights"
|
|
129
|
+
v-model:apply-to-all-flights="applyToAllFlights"
|
|
130
|
+
v-model:active-flight-id="activeFlightId"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
`,
|
|
134
|
+
}),
|
|
135
|
+
}
|