@citizenplane/pimp 18.23.6 → 18.25.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/CpAncillaryItem.vue.d.ts.map +1 -1
- package/dist/components/CpDynamicMenu.vue.d.ts +51 -0
- package/dist/components/CpDynamicMenu.vue.d.ts.map +1 -0
- package/dist/components/CpIllustration.vue.d.ts +10 -0
- package/dist/components/CpIllustration.vue.d.ts.map +1 -0
- package/dist/components/CpLanguageSwitcher.vue.d.ts +35 -0
- package/dist/components/CpLanguageSwitcher.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +7 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/constants/CpIllustrations.d.ts +55 -0
- package/dist/constants/CpIllustrations.d.ts.map +1 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/pimp.es.js +2096 -1831
- package/dist/pimp.umd.js +45 -45
- package/dist/style.css +1 -1
- package/package.json +2 -1
- package/src/assets/illustrations/accessibility.webp +0 -0
- package/src/assets/illustrations/airplane-seat.webp +0 -0
- package/src/assets/illustrations/airport-experience.png +0 -0
- package/src/assets/illustrations/baggages.webp +0 -0
- package/src/assets/illustrations/boarding-pass-2.webp +0 -0
- package/src/assets/illustrations/boarding-pass.webp +0 -0
- package/src/assets/illustrations/boeing-787-dreamliner.webp +0 -0
- package/src/assets/illustrations/checkout.webp +0 -0
- package/src/assets/illustrations/clock.png +0 -0
- package/src/assets/illustrations/empty-basket.png +0 -0
- package/src/assets/illustrations/empty-result.webp +0 -0
- package/src/assets/illustrations/flip-clock.webp +0 -0
- package/src/assets/illustrations/frequent-flyer.webp +0 -0
- package/src/assets/illustrations/generic.webp +0 -0
- package/src/assets/illustrations/island.webp +0 -0
- package/src/assets/illustrations/miles.webp +0 -0
- package/src/assets/illustrations/name-change.png +0 -0
- package/src/assets/illustrations/passport.webp +0 -0
- package/src/assets/illustrations/payment-declined.png +0 -0
- package/src/assets/illustrations/pets.webp +0 -0
- package/src/assets/illustrations/plane.webp +0 -0
- package/src/assets/illustrations/sign-out.png +0 -0
- package/src/assets/illustrations/sport-equipments.webp +0 -0
- package/src/components/CpAncillaryCategory.vue +7 -7
- package/src/components/CpAncillaryItem.vue +25 -26
- package/src/components/CpDynamicMenu.vue +226 -0
- package/src/components/CpIllustration.vue +44 -0
- package/src/components/CpLanguageSwitcher.vue +151 -0
- package/src/components/index.ts +12 -0
- package/src/constants/CpIllustrations.ts +107 -0
- package/src/constants/index.ts +2 -0
- package/src/stories/CpDynamicMenu.stories.ts +279 -0
- package/src/stories/CpIllustration.stories.ts +129 -0
- package/src/stories/CpLanguageSwitcher.stories.ts +168 -0
- package/src/assets/images/ancillaries/checked-bag-size-2.png +0 -0
- package/src/assets/images/ancillary-categories/baggages.png +0 -0
- package/src/assets/images/ancillary-categories/name-change.png +0 -0
- package/src/assets/images/ancillary-categories/pets.png +0 -0
- package/src/assets/images/ancillary-categories/sport-equipments.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/airport-transfer.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/bicycle.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/cabin-bag.png +0 -0
- /package/src/assets/{images/ancillary-categories → illustrations}/carbon-offset.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/checked-bag-size-1.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/checked-bag-size-3.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/checked-bag.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/connectivity.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/diving-set.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/extra-weight.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/fast-track.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/firearm.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/fishing-rod.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/golf.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/hand-bag.png +0 -0
- /package/src/assets/{images/ancillary-categories → illustrations}/insurance.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/kite-surf.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/lounge-access.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/meet-and-greet.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/music.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/oversized.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/pet-in-cabin.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/pet-in-hold.png +0 -0
- /package/src/assets/{images/ancillary-categories → illustrations}/restricted.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/skis.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/special-meal.png +0 -0
- /package/src/assets/{images/ancillaries → illustrations}/standard-meal.png +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@citizenplane/pimp",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.25.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "storybook dev -p 8081",
|
|
6
6
|
"build-storybook": "storybook build --output-dir ./docs",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"@vitest/coverage-v8": "4.1.9",
|
|
76
76
|
"@vue/test-utils": "2.4.11",
|
|
77
77
|
"commitlint-config-gitmoji": "2.3.1",
|
|
78
|
+
"flag-icons": "7.5.0",
|
|
78
79
|
"happy-dom": "20.14.5",
|
|
79
80
|
"husky": "9.1.7",
|
|
80
81
|
"lint-staged": "17.2.0",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -65,13 +65,13 @@ import CpIcon from '@/components/CpIcon.vue'
|
|
|
65
65
|
import CpSwitch from '@/components/CpSwitch.vue'
|
|
66
66
|
import CpTransitionExpand from '@/components/CpTransitionExpand.vue'
|
|
67
67
|
|
|
68
|
-
import BaggagesVisual from '@/assets/
|
|
69
|
-
import CarbonOffsetVisual from '@/assets/
|
|
70
|
-
import InsuranceVisual from '@/assets/
|
|
71
|
-
import NameChangeVisual from '@/assets/
|
|
72
|
-
import PetsVisual from '@/assets/
|
|
73
|
-
import RestrictedVisual from '@/assets/
|
|
74
|
-
import SportEquipmentsVisual from '@/assets/
|
|
68
|
+
import BaggagesVisual from '@/assets/illustrations/baggages.webp'
|
|
69
|
+
import CarbonOffsetVisual from '@/assets/illustrations/carbon-offset.png'
|
|
70
|
+
import InsuranceVisual from '@/assets/illustrations/insurance.png'
|
|
71
|
+
import NameChangeVisual from '@/assets/illustrations/name-change.png'
|
|
72
|
+
import PetsVisual from '@/assets/illustrations/pets.webp'
|
|
73
|
+
import RestrictedVisual from '@/assets/illustrations/restricted.png'
|
|
74
|
+
import SportEquipmentsVisual from '@/assets/illustrations/sport-equipments.webp'
|
|
75
75
|
import { CpAncillaryCategoryTypes, isSwitchCategory } from '@/constants/CpAncillaryCategoryTypes'
|
|
76
76
|
|
|
77
77
|
interface Props {
|
|
@@ -51,31 +51,30 @@ import CpButton from '@/components/CpButton.vue'
|
|
|
51
51
|
import CpIcon from '@/components/CpIcon.vue'
|
|
52
52
|
import CpQuantityCounter from '@/components/CpQuantityCounter.vue'
|
|
53
53
|
|
|
54
|
-
import AirportTransferImage from '@/assets/
|
|
55
|
-
import BicycleImage from '@/assets/
|
|
56
|
-
import CabinBagImage from '@/assets/
|
|
57
|
-
import CheckedBagImage from '@/assets/
|
|
58
|
-
import CheckedBagSize1Image from '@/assets/
|
|
59
|
-
import
|
|
60
|
-
import
|
|
61
|
-
import
|
|
62
|
-
import
|
|
63
|
-
import
|
|
64
|
-
import
|
|
65
|
-
import
|
|
66
|
-
import
|
|
67
|
-
import
|
|
68
|
-
import
|
|
69
|
-
import
|
|
70
|
-
import
|
|
71
|
-
import
|
|
72
|
-
import
|
|
73
|
-
import
|
|
74
|
-
import
|
|
75
|
-
import
|
|
76
|
-
import
|
|
77
|
-
import
|
|
78
|
-
import StandardMealImage from '@/assets/images/ancillaries/standard-meal.png'
|
|
54
|
+
import AirportTransferImage from '@/assets/illustrations/airport-transfer.png'
|
|
55
|
+
import BicycleImage from '@/assets/illustrations/bicycle.png'
|
|
56
|
+
import CabinBagImage from '@/assets/illustrations/cabin-bag.png'
|
|
57
|
+
import CheckedBagImage from '@/assets/illustrations/checked-bag.png'
|
|
58
|
+
import CheckedBagSize1Image from '@/assets/illustrations/checked-bag-size-1.png'
|
|
59
|
+
import CheckedBagSize3Image from '@/assets/illustrations/checked-bag-size-3.png'
|
|
60
|
+
import ConnectivityImage from '@/assets/illustrations/connectivity.png'
|
|
61
|
+
import DivingSetImage from '@/assets/illustrations/diving-set.png'
|
|
62
|
+
import ExtraWeightImage from '@/assets/illustrations/extra-weight.png'
|
|
63
|
+
import FastTrackImage from '@/assets/illustrations/fast-track.png'
|
|
64
|
+
import FirearmImage from '@/assets/illustrations/firearm.png'
|
|
65
|
+
import FishingRodImage from '@/assets/illustrations/fishing-rod.png'
|
|
66
|
+
import GolfImage from '@/assets/illustrations/golf.png'
|
|
67
|
+
import HandBagImage from '@/assets/illustrations/hand-bag.png'
|
|
68
|
+
import KiteSurfImage from '@/assets/illustrations/kite-surf.png'
|
|
69
|
+
import LoungeAccessImage from '@/assets/illustrations/lounge-access.png'
|
|
70
|
+
import MeetAndGreetImage from '@/assets/illustrations/meet-and-greet.png'
|
|
71
|
+
import MusicImage from '@/assets/illustrations/music.png'
|
|
72
|
+
import OversizedImage from '@/assets/illustrations/oversized.png'
|
|
73
|
+
import PetInCabinImage from '@/assets/illustrations/pet-in-cabin.png'
|
|
74
|
+
import PetInHoldImage from '@/assets/illustrations/pet-in-hold.png'
|
|
75
|
+
import SkisImage from '@/assets/illustrations/skis.png'
|
|
76
|
+
import SpecialMealImage from '@/assets/illustrations/special-meal.png'
|
|
77
|
+
import StandardMealImage from '@/assets/illustrations/standard-meal.png'
|
|
79
78
|
import { CpAncillaryItemTypes } from '@/constants/CpAncillaryItemTypes'
|
|
80
79
|
|
|
81
80
|
interface Props {
|
|
@@ -114,7 +113,7 @@ const ancillaryImages: Partial<Record<CpAncillaryItemTypes, string>> = {
|
|
|
114
113
|
[CpAncillaryItemTypes.CABIN_BAG]: CabinBagImage,
|
|
115
114
|
[CpAncillaryItemTypes.CHECKED_BAG]: CheckedBagImage,
|
|
116
115
|
[CpAncillaryItemTypes.CHECKED_BAG_SIZE_1]: CheckedBagSize1Image,
|
|
117
|
-
[CpAncillaryItemTypes.CHECKED_BAG_SIZE_2]:
|
|
116
|
+
[CpAncillaryItemTypes.CHECKED_BAG_SIZE_2]: CheckedBagImage,
|
|
118
117
|
[CpAncillaryItemTypes.CHECKED_BAG_SIZE_3]: CheckedBagSize3Image,
|
|
119
118
|
[CpAncillaryItemTypes.CONNECTIVITY]: ConnectivityImage,
|
|
120
119
|
[CpAncillaryItemTypes.DIVING_SET]: DivingSetImage,
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="cpDynamicMenu">
|
|
3
|
+
<cp-menu v-if="!isMobile" ref="menuRef" v-bind="menuProps" @hide="onMenuHide" @show="onMenuShow">
|
|
4
|
+
<template #trigger>
|
|
5
|
+
<slot name="trigger" />
|
|
6
|
+
</template>
|
|
7
|
+
<slot />
|
|
8
|
+
</cp-menu>
|
|
9
|
+
<template v-else>
|
|
10
|
+
<div
|
|
11
|
+
ref="triggerRef"
|
|
12
|
+
:aria-controls="menuId"
|
|
13
|
+
:aria-expanded="isSheetVisible"
|
|
14
|
+
:aria-haspopup="popupType"
|
|
15
|
+
class="cpDynamicMenu__trigger"
|
|
16
|
+
@click="toggleSheet"
|
|
17
|
+
>
|
|
18
|
+
<slot name="trigger" />
|
|
19
|
+
</div>
|
|
20
|
+
<cp-bottom-sheet v-model="isSheetVisible" v-bind="bottomSheetProps">
|
|
21
|
+
<template v-if="hasSheetTitle" #header>
|
|
22
|
+
<slot name="sheet-title">
|
|
23
|
+
<cp-heading class="cpDynamicMenu__sheetTitle" heading-level="h3">{{ sheetTitle }}</cp-heading>
|
|
24
|
+
</slot>
|
|
25
|
+
</template>
|
|
26
|
+
<cp-menu-list v-if="hasItems" :id="menuId" :items="items" @async-complete="onItemClick" @click="onItemClick" />
|
|
27
|
+
<slot v-else />
|
|
28
|
+
<template #footer>
|
|
29
|
+
<slot name="footer" />
|
|
30
|
+
</template>
|
|
31
|
+
</cp-bottom-sheet>
|
|
32
|
+
</template>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup lang="ts">
|
|
37
|
+
import type { MenuItem } from 'primevue/menuitem'
|
|
38
|
+
import { computed, ref, useId, useSlots, useTemplateRef, watch } from 'vue'
|
|
39
|
+
|
|
40
|
+
import type { CpMenuPlacement } from '@/components/CpMenu.vue'
|
|
41
|
+
|
|
42
|
+
import { useIsBreakpoint } from '@/composables/useIsBreakpoint'
|
|
43
|
+
|
|
44
|
+
import CpBottomSheet from '@/components/CpBottomSheet.vue'
|
|
45
|
+
import CpHeading from '@/components/CpHeading.vue'
|
|
46
|
+
import CpMenu from '@/components/CpMenu.vue'
|
|
47
|
+
import CpMenuList from '@/components/CpMenuList.vue'
|
|
48
|
+
|
|
49
|
+
import { Breakpoints } from '@/constants/layout'
|
|
50
|
+
import { getKeyboardFocusableElements } from '@/helpers/dom'
|
|
51
|
+
|
|
52
|
+
interface Props {
|
|
53
|
+
animationDuration?: number
|
|
54
|
+
breakpoint?: number
|
|
55
|
+
class?: string
|
|
56
|
+
contentClass?: string
|
|
57
|
+
expandOnContentDrag?: boolean
|
|
58
|
+
halfAndExpand?: boolean
|
|
59
|
+
items?: MenuItem[]
|
|
60
|
+
keepOpenOnClick?: boolean
|
|
61
|
+
placement?: CpMenuPlacement
|
|
62
|
+
preventClose?: boolean
|
|
63
|
+
sheetTitle?: string
|
|
64
|
+
snapPoints?: Array<number | `${number}%`>
|
|
65
|
+
swipeCloseThreshold?: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const props = defineProps<Props>()
|
|
69
|
+
|
|
70
|
+
const emits = defineEmits<{
|
|
71
|
+
hide: []
|
|
72
|
+
show: []
|
|
73
|
+
}>()
|
|
74
|
+
|
|
75
|
+
const slots = useSlots()
|
|
76
|
+
|
|
77
|
+
const isMobile = useIsBreakpoint(props.breakpoint ?? Breakpoints.MOBILE)
|
|
78
|
+
|
|
79
|
+
const menuRef = useTemplateRef('menuRef')
|
|
80
|
+
const triggerRef = useTemplateRef<HTMLElement>('triggerRef')
|
|
81
|
+
|
|
82
|
+
const isMenuOpen = ref(false)
|
|
83
|
+
const isSheetVisible = ref(false)
|
|
84
|
+
const menuId = useId()
|
|
85
|
+
|
|
86
|
+
const hasItems = computed(() => Boolean(props.items?.length))
|
|
87
|
+
const hasSheetTitle = computed(() => Boolean(props.sheetTitle || slots['sheet-title']))
|
|
88
|
+
|
|
89
|
+
const popupType = computed(() => {
|
|
90
|
+
if (hasItems.value) return 'menu'
|
|
91
|
+
return 'dialog'
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const menuProps = computed(() => ({
|
|
95
|
+
class: props.class,
|
|
96
|
+
forcePopover: true,
|
|
97
|
+
items: props.items,
|
|
98
|
+
keepOpenOnClick: props.keepOpenOnClick,
|
|
99
|
+
placement: props.placement,
|
|
100
|
+
}))
|
|
101
|
+
|
|
102
|
+
const sheetContentClass = computed(() => {
|
|
103
|
+
if (!hasItems.value) return props.contentClass
|
|
104
|
+
if (!props.contentClass) return 'cpDynamicMenu__menuListContent'
|
|
105
|
+
return `cpDynamicMenu__menuListContent ${props.contentClass}`
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const sheetBaseClass = computed(() => {
|
|
109
|
+
if (hasSheetTitle.value) return 'cpDynamicMenu__sheet'
|
|
110
|
+
return 'cpDynamicMenu__sheet cpDynamicMenu__sheet--hasNoTitle'
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const sheetClass = computed(() => {
|
|
114
|
+
if (!props.class) return sheetBaseClass.value
|
|
115
|
+
return `${sheetBaseClass.value} ${props.class}`
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const bottomSheetProps = computed(() => ({
|
|
119
|
+
animationDuration: props.animationDuration,
|
|
120
|
+
canBackdropClose: !props.preventClose,
|
|
121
|
+
canSwipeClose: !props.preventClose,
|
|
122
|
+
class: sheetClass.value,
|
|
123
|
+
contentClass: sheetContentClass.value,
|
|
124
|
+
expandOnContentDrag: props.expandOnContentDrag,
|
|
125
|
+
halfAndExpand: props.halfAndExpand,
|
|
126
|
+
preventClose: props.preventClose,
|
|
127
|
+
snapPoints: props.snapPoints,
|
|
128
|
+
swipeCloseThreshold: props.swipeCloseThreshold,
|
|
129
|
+
}))
|
|
130
|
+
|
|
131
|
+
const focusTrigger = () => {
|
|
132
|
+
if (!triggerRef.value) return
|
|
133
|
+
const [firstFocusableElement] = getKeyboardFocusableElements(triggerRef.value) as HTMLElement[]
|
|
134
|
+
firstFocusableElement?.focus()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// CpMenu emits show/hide twice when driven imperatively, so only real state changes are relayed.
|
|
138
|
+
const onMenuShow = () => {
|
|
139
|
+
if (isMenuOpen.value) return
|
|
140
|
+
isMenuOpen.value = true
|
|
141
|
+
emits('show')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const onMenuHide = () => {
|
|
145
|
+
if (!isMenuOpen.value) return
|
|
146
|
+
isMenuOpen.value = false
|
|
147
|
+
emits('hide')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const toggleSheet = () => {
|
|
151
|
+
isSheetVisible.value = !isSheetVisible.value
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const onItemClick = () => {
|
|
155
|
+
if (props.keepOpenOnClick) return
|
|
156
|
+
isSheetVisible.value = false
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const show = (event: Event) => {
|
|
160
|
+
if (isMobile.value) {
|
|
161
|
+
isSheetVisible.value = true
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
menuRef.value?.show(event)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const hide = () => {
|
|
168
|
+
if (isMobile.value) {
|
|
169
|
+
isSheetVisible.value = false
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
menuRef.value?.hide()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const toggle = (event: Event) => {
|
|
176
|
+
if (isMobile.value) {
|
|
177
|
+
toggleSheet()
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
menuRef.value?.toggle(event)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
watch(isSheetVisible, (isVisible) => {
|
|
184
|
+
if (isVisible) {
|
|
185
|
+
emits('show')
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
emits('hide')
|
|
189
|
+
focusTrigger()
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
watch(isMobile, (isMobileViewport) => {
|
|
193
|
+
if (!isMobileViewport) {
|
|
194
|
+
isSheetVisible.value = false
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
if (isMenuOpen.value) menuRef.value?.hide()
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
defineExpose({ show, hide, toggle })
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
<style lang="scss">
|
|
204
|
+
.cpDynamicMenu {
|
|
205
|
+
display: contents;
|
|
206
|
+
|
|
207
|
+
&__trigger {
|
|
208
|
+
display: inline-flex;
|
|
209
|
+
cursor: pointer;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
&__sheetTitle {
|
|
213
|
+
font-size: var(--cp-text-size-2xl);
|
|
214
|
+
font-weight: 700;
|
|
215
|
+
line-height: var(--cp-line-height-2xl);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
&__menuListContent.cpBottomSheet__content {
|
|
219
|
+
padding: var(--cp-spacing-sm) 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&__sheet--hasNoTitle .cpBottomSheet__header {
|
|
223
|
+
padding-bottom: 0;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<img
|
|
3
|
+
alt=""
|
|
4
|
+
aria-hidden="true"
|
|
5
|
+
class="cpIllustration"
|
|
6
|
+
:class="nameClass"
|
|
7
|
+
:height="size"
|
|
8
|
+
:src="source"
|
|
9
|
+
:width="size"
|
|
10
|
+
/>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { computed } from 'vue'
|
|
15
|
+
|
|
16
|
+
import { type CpIllustrationName, CpIllustrations } from '@/constants'
|
|
17
|
+
|
|
18
|
+
export interface Props {
|
|
19
|
+
name: string
|
|
20
|
+
size?: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
+
size: 24,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const resolvedName = computed<CpIllustrationName>(() => {
|
|
28
|
+
const name = props.name as CpIllustrationName
|
|
29
|
+
|
|
30
|
+
if (!CpIllustrations[name]) return 'generic'
|
|
31
|
+
|
|
32
|
+
return name
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const source = computed(() => CpIllustrations[resolvedName.value])
|
|
36
|
+
|
|
37
|
+
const nameClass = computed(() => `cpIllustration--${resolvedName.value}`)
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<style lang="scss">
|
|
41
|
+
.cpIllustration {
|
|
42
|
+
object-fit: contain;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<cp-dynamic-menu
|
|
3
|
+
ref="dynamicMenuRef"
|
|
4
|
+
:breakpoint="breakpoint"
|
|
5
|
+
:class="props.class"
|
|
6
|
+
content-class="cpLanguageSwitcher__content"
|
|
7
|
+
:placement="placement"
|
|
8
|
+
>
|
|
9
|
+
<template #trigger>
|
|
10
|
+
<slot name="trigger">
|
|
11
|
+
<cp-button appearance="tertiary" color="accent" size="sm">
|
|
12
|
+
<template #leading-icon>
|
|
13
|
+
<cp-icon type="globe" />
|
|
14
|
+
</template>
|
|
15
|
+
{{ uppercasedCurrentCode }}
|
|
16
|
+
</cp-button>
|
|
17
|
+
</slot>
|
|
18
|
+
</template>
|
|
19
|
+
<ul class="cpLanguageSwitcher__list">
|
|
20
|
+
<li v-for="lang in langs" :key="lang.code">
|
|
21
|
+
<button
|
|
22
|
+
:aria-current="getAriaCurrent(lang.code)"
|
|
23
|
+
class="cpLanguageSwitcher__lang"
|
|
24
|
+
:class="getLangClass(lang.code)"
|
|
25
|
+
type="button"
|
|
26
|
+
@click="selectLanguage(lang.code)"
|
|
27
|
+
>
|
|
28
|
+
<span class="cpLanguageSwitcher__left">
|
|
29
|
+
<span v-if="lang.flag" class="cpLanguageSwitcher__flag" :class="getFlagClass(lang.flag)" />
|
|
30
|
+
<span class="cpLanguageSwitcher__name">{{ lang.name }}</span>
|
|
31
|
+
</span>
|
|
32
|
+
<cp-icon v-if="isCurrentLanguage(lang.code)" class="cpLanguageSwitcher__check" size="20" type="check" />
|
|
33
|
+
</button>
|
|
34
|
+
</li>
|
|
35
|
+
</ul>
|
|
36
|
+
</cp-dynamic-menu>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import { computed, useTemplateRef } from 'vue'
|
|
41
|
+
|
|
42
|
+
import type { CpMenuPlacement } from '@/components/CpMenu.vue'
|
|
43
|
+
|
|
44
|
+
import CpButton from '@/components/CpButton.vue'
|
|
45
|
+
import CpDynamicMenu from '@/components/CpDynamicMenu.vue'
|
|
46
|
+
import CpIcon from '@/components/CpIcon.vue'
|
|
47
|
+
|
|
48
|
+
export interface LanguageOption {
|
|
49
|
+
code: string
|
|
50
|
+
flag?: string
|
|
51
|
+
name: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface Props {
|
|
55
|
+
breakpoint?: number
|
|
56
|
+
class?: string
|
|
57
|
+
langs: LanguageOption[]
|
|
58
|
+
placement?: CpMenuPlacement
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const props = defineProps<Props>()
|
|
62
|
+
|
|
63
|
+
const currentCode = defineModel<string>()
|
|
64
|
+
|
|
65
|
+
const dynamicMenuRef = useTemplateRef('dynamicMenuRef')
|
|
66
|
+
|
|
67
|
+
const uppercasedCurrentCode = computed(() => {
|
|
68
|
+
if (!currentCode.value) return ''
|
|
69
|
+
return currentCode.value.toUpperCase()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const isCurrentLanguage = (code: string) => code === currentCode.value
|
|
73
|
+
|
|
74
|
+
const getLangClass = (code: string) => ({ 'cpLanguageSwitcher__lang--isSelected': isCurrentLanguage(code) })
|
|
75
|
+
|
|
76
|
+
// flag-icons is a devDependency only: the consumer app loads flag-icons.min.css itself.
|
|
77
|
+
const getFlagClass = (flag: string) => `fi fi-${flag.toLowerCase()}`
|
|
78
|
+
|
|
79
|
+
const getAriaCurrent = (code: string) => {
|
|
80
|
+
if (!isCurrentLanguage(code)) return undefined
|
|
81
|
+
return 'true'
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const selectLanguage = (code: string) => {
|
|
85
|
+
if (!isCurrentLanguage(code)) currentCode.value = code
|
|
86
|
+
dynamicMenuRef.value?.hide()
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<style lang="scss">
|
|
91
|
+
.cpLanguageSwitcher {
|
|
92
|
+
&__content.cpBottomSheet__content {
|
|
93
|
+
padding: var(--cp-spacing-sm) 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&__list {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
padding: 0 var(--cp-spacing-sm);
|
|
100
|
+
margin: 0;
|
|
101
|
+
gap: var(--cp-spacing-sm);
|
|
102
|
+
list-style: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&__lang {
|
|
106
|
+
@extend %u-focus-outline;
|
|
107
|
+
|
|
108
|
+
display: flex;
|
|
109
|
+
width: 100%;
|
|
110
|
+
align-items: center;
|
|
111
|
+
justify-content: space-between;
|
|
112
|
+
padding: var(--cp-spacing-lg) var(--cp-spacing-xl) var(--cp-spacing-lg) var(--cp-spacing-lg);
|
|
113
|
+
border: none;
|
|
114
|
+
border-radius: var(--cp-radius-md);
|
|
115
|
+
background-color: transparent;
|
|
116
|
+
color: var(--cp-text-primary);
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
gap: var(--cp-spacing-md);
|
|
119
|
+
|
|
120
|
+
&:hover {
|
|
121
|
+
background-color: var(--cp-background-primary-hover);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
&--isSelected {
|
|
125
|
+
background-color: var(--cp-background-accent-primary);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&__left {
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
gap: var(--cp-spacing-lg);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
&__flag {
|
|
136
|
+
flex: none;
|
|
137
|
+
border-radius: var(--cp-radius-sm);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
&__name {
|
|
141
|
+
font-size: var(--cp-text-size-md);
|
|
142
|
+
font-weight: 500;
|
|
143
|
+
line-height: var(--cp-line-height-md);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
&__check {
|
|
147
|
+
flex: none;
|
|
148
|
+
color: var(--cp-text-accent-primary);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
</style>
|
package/src/components/index.ts
CHANGED
|
@@ -36,11 +36,14 @@ import CpDate from './CpDate.vue'
|
|
|
36
36
|
import CpDatepicker from './CpDatepicker.vue'
|
|
37
37
|
import CpDialog from './CpDialog.vue'
|
|
38
38
|
import CpDynamicDialog from './CpDynamicDialog.vue'
|
|
39
|
+
import CpDynamicMenu from './CpDynamicMenu.vue'
|
|
39
40
|
import CpFlightTabs from './CpFlightTabs.vue'
|
|
40
41
|
import CpHeading from './CpHeading.vue'
|
|
41
42
|
import CpIcon from './CpIcon.vue'
|
|
43
|
+
import CpIllustration from './CpIllustration.vue'
|
|
42
44
|
import CpInput from './CpInput.vue'
|
|
43
45
|
import CpItemActions from './CpItemActions.vue'
|
|
46
|
+
import CpLanguageSwitcher from './CpLanguageSwitcher.vue'
|
|
44
47
|
import CpLoader from './CpLoader.vue'
|
|
45
48
|
import CpMenu from './CpMenu.vue'
|
|
46
49
|
import CpMenuItem from './CpMenuItem.vue'
|
|
@@ -111,11 +114,13 @@ const Components = {
|
|
|
111
114
|
CpButtonGroup,
|
|
112
115
|
CpDialog,
|
|
113
116
|
CpDynamicDialog,
|
|
117
|
+
CpDynamicMenu,
|
|
114
118
|
CpDate,
|
|
115
119
|
CpContextualMenu,
|
|
116
120
|
CpMenu,
|
|
117
121
|
CpMenuItem,
|
|
118
122
|
CpMenuList,
|
|
123
|
+
CpLanguageSwitcher,
|
|
119
124
|
CpItemActions,
|
|
120
125
|
CpCoreDatepicker,
|
|
121
126
|
CpDatepicker,
|
|
@@ -146,6 +151,7 @@ const Components = {
|
|
|
146
151
|
CpTable,
|
|
147
152
|
CpTableColumnEditor,
|
|
148
153
|
CpIcon,
|
|
154
|
+
CpIllustration,
|
|
149
155
|
CpTelInput,
|
|
150
156
|
CpTooltip,
|
|
151
157
|
CpTextMorph,
|
|
@@ -201,9 +207,11 @@ const Pimp: Plugin = {
|
|
|
201
207
|
},
|
|
202
208
|
}
|
|
203
209
|
|
|
210
|
+
export type { LanguageOption } from '@/components/CpLanguageSwitcher.vue'
|
|
204
211
|
export type { CpAncillaryFlightSelectionFlight } from '@/constants/CpAncillaryFlightSelectionTypes'
|
|
205
212
|
export type { CpBasketCategory, CpBasketGroup, CpBasketRow } from '@/constants/CpBasketTypes'
|
|
206
213
|
export type { CpFlightTabsFlight } from '@/constants/CpFlightTabsTypes'
|
|
214
|
+
export type { CpIllustrationName } from '@/constants/CpIllustrations'
|
|
207
215
|
export type { CpStepperStep } from '@/constants/CpStepperTypes'
|
|
208
216
|
export type {
|
|
209
217
|
CpSeatMapConfig,
|
|
@@ -228,6 +236,7 @@ export type {
|
|
|
228
236
|
|
|
229
237
|
export { CpAncillaryCategoryTypes, isSwitchCategory } from '@/constants/CpAncillaryCategoryTypes'
|
|
230
238
|
export { CpAncillaryItemTypes } from '@/constants/CpAncillaryItemTypes'
|
|
239
|
+
export { CpIllustrationNames, CpIllustrations } from '@/constants/CpIllustrations'
|
|
231
240
|
export {
|
|
232
241
|
CpSeatMapTransitionDirections,
|
|
233
242
|
DEFAULT_CP_SEAT_MAP_CONFIG,
|
|
@@ -262,11 +271,14 @@ export {
|
|
|
262
271
|
CpDatepicker,
|
|
263
272
|
CpDialog,
|
|
264
273
|
CpDynamicDialog,
|
|
274
|
+
CpDynamicMenu,
|
|
265
275
|
CpFlightTabs,
|
|
266
276
|
CpHeading,
|
|
267
277
|
CpIcon,
|
|
278
|
+
CpIllustration,
|
|
268
279
|
CpInput,
|
|
269
280
|
CpItemActions,
|
|
281
|
+
CpLanguageSwitcher,
|
|
270
282
|
CpLoader,
|
|
271
283
|
CpMenu,
|
|
272
284
|
CpMenuItem,
|