@citizenplane/pimp 18.8.0 → 18.8.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citizenplane/pimp",
3
- "version": "18.8.0",
3
+ "version": "18.8.2",
4
4
  "scripts": {
5
5
  "dev": "storybook dev -p 8081",
6
6
  "build-storybook": "storybook build --output-dir ./docs",
@@ -2,4 +2,5 @@
2
2
  --cp-easing-elastic: cubic-bezier(0.34, 1.66, 0.64, 1);
3
3
  --cp-easing-elastic-soft: cubic-bezier(0.34, 1.2, 0.64, 1);
4
4
  --cp-easing-ease-in-out-quart: cubic-bezier(0.76, 0, 0.24, 1);
5
+ --cp-easing-ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
5
6
  }
@@ -0,0 +1,34 @@
1
+ /* Transitions classes that are globally used */
2
+
3
+ .scale-elastic-enter-active,
4
+ .scale-elastic-leave-active {
5
+ transition:
6
+ scale 200ms var(--cp-easing-elastic),
7
+ opacity 200ms ease;
8
+ }
9
+
10
+ .scale-elastic-enter-from,
11
+ .scale-elastic-leave-to {
12
+ opacity: 0;
13
+ scale: 0.9;
14
+ }
15
+
16
+ .drawer-enter-active,
17
+ .drawer-leave-active {
18
+ transition: transform 500ms var(--cp-easing-ease-out-quart);
19
+ }
20
+
21
+ .drawer-enter-from,
22
+ .drawer-leave-to {
23
+ transform: translate3d(0, 100%, 0);
24
+ }
25
+
26
+ .fade-enter-active,
27
+ .fade-leave-active {
28
+ transition: opacity 300ms ease;
29
+ }
30
+
31
+ .fade-enter-from,
32
+ .fade-leave-to {
33
+ opacity: 0;
34
+ }
@@ -4,4 +4,5 @@
4
4
  @import "./css/shadows.css";
5
5
  @import "./css/typography.css";
6
6
  @import "./css/easings.css";
7
+ @import "./css/transitions.css";
7
8
  @import "./css/tokens.css";
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <div class="cpMenu">
3
+ <div v-if="isDrawer" class="cpMenu__drawerOverlay" :class="drawerOverlayDynamicClass" />
3
4
  <div
4
5
  ref="trigger"
5
6
  :aria-controls="menuId"
@@ -62,25 +63,28 @@
62
63
  import PrimevueDrawer from 'primevue/drawer'
63
64
  import type { MenuItem } from 'primevue/menuitem'
64
65
  import PrimevuePopover from 'primevue/popover'
65
- import { computed, nextTick, onUnmounted, ref, useId } from 'vue'
66
+ import { computed, nextTick, ref, useId } from 'vue'
67
+
68
+ import { useIsBreakpoint } from '@/composables/useIsBreakpoint'
66
69
 
67
70
  import CpButton from '@/components/CpButton.vue'
68
71
  import CpIcon from '@/components/CpIcon.vue'
69
72
  import CpMenuList from '@/components/CpMenuList.vue'
70
73
 
74
+ import { Breakpoints } from '@/constants/layout'
71
75
  import { getKeyboardFocusableElements } from '@/helpers/dom'
72
76
 
73
77
  export interface Props {
74
78
  class?: string
75
79
  forcePopover?: boolean
76
- fullHeightDrawer?: boolean
80
+ isFullHeightDrawer?: boolean
77
81
  items?: MenuItem[]
78
82
  keepOpenOnClick?: boolean
79
83
  }
80
84
 
81
85
  const props = withDefaults(defineProps<Props>(), {
82
86
  class: undefined,
83
- fullHeightDrawer: false,
87
+ isFullHeightDrawer: false,
84
88
  items: undefined,
85
89
  keepOpenOnClick: false,
86
90
  })
@@ -90,37 +94,33 @@ const emits = defineEmits<{
90
94
  show: []
91
95
  }>()
92
96
 
93
- const MOBILE_BREAKPOINT_PX = 640
94
-
95
97
  const trigger = ref<HTMLElement | null>(null)
96
98
  const popover = ref<InstanceType<typeof PrimevuePopover>>()
97
99
  const closeButton = ref<InstanceType<typeof CpButton> | null>(null)
98
100
  const isOpen = ref(false)
99
101
  const menuId = useId()
100
102
 
101
- const mediaQuery = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT_PX}px)`)
102
- const isMobileViewport = ref(mediaQuery.matches)
103
- const onMediaChange = (event: MediaQueryListEvent) => {
104
- isMobileViewport.value = event.matches
105
- }
106
- mediaQuery.addEventListener('change', onMediaChange)
107
- onUnmounted(() => mediaQuery.removeEventListener('change', onMediaChange))
103
+ const isMobileViewport = useIsBreakpoint(Breakpoints.TABLET)
108
104
 
109
105
  const isDrawer = computed(() => isMobileViewport.value && !props.forcePopover)
110
106
  const hasItems = computed(() => props.items?.length)
111
107
  const popupType = computed(() => (hasItems.value ? 'menu' : 'dialog'))
112
108
 
113
109
  const popoverPt = {
114
- root: { class: ['cpMenu__overlay', props.class] },
115
- content: { class: 'cpMenu__overlayContent' },
110
+ root: { class: ['cpMenu__menu', props.class] },
111
+ content: { class: 'cpMenu__menuContent' },
116
112
  transition: { name: 'scale-elastic', duration: 100 },
117
113
  }
118
114
 
115
+ const drawerOverlayDynamicClass = computed(() => ({
116
+ 'cpMenu__drawerOverlay--isOpen': isOpen.value,
117
+ }))
118
+
119
119
  const drawerPt = {
120
- root: { class: ['cpMenu__drawer', { 'cpMenu__drawer--fullHeight': props.fullHeightDrawer }] },
120
+ root: { class: ['cpMenu__drawer', { 'cpMenu__drawer--isFullHeight': props.isFullHeightDrawer }] },
121
121
  content: { class: 'cpMenu__drawerContent' },
122
- mask: { class: 'cpMenu__drawerMask' },
123
122
  header: { class: 'cpMenu__drawerHeader' },
123
+ transition: { name: 'drawer', duration: 300 },
124
124
  }
125
125
 
126
126
  const show = (event: Event) => {
@@ -171,7 +171,7 @@ defineExpose({ show, hide, toggle })
171
171
  cursor: pointer;
172
172
  }
173
173
 
174
- &__overlay {
174
+ &__menu {
175
175
  position: absolute;
176
176
  min-width: calc(var(--cp-dimensions-1) * 62.5);
177
177
  border-radius: var(--cp-radius-md);
@@ -182,18 +182,25 @@ defineExpose({ show, hide, toggle })
182
182
  will-change: opacity, transform;
183
183
  }
184
184
 
185
- &__overlayContent {
185
+ &__menuContent {
186
186
  padding: var(--cp-spacing-sm) 0;
187
187
  }
188
188
 
189
- &__drawerMask {
189
+ &__drawerOverlay {
190
190
  position: fixed;
191
- z-index: 2;
191
+ z-index: 0;
192
192
  display: flex;
193
193
  align-items: flex-end;
194
194
  justify-content: center;
195
195
  background-color: var(--cp-background-overlay);
196
196
  inset: 0;
197
+ opacity: 0;
198
+ pointer-events: none;
199
+ transition: opacity 250ms ease;
200
+
201
+ &--isOpen {
202
+ opacity: 1;
203
+ }
197
204
  }
198
205
 
199
206
  &__drawer {
@@ -208,7 +215,7 @@ defineExpose({ show, hide, toggle })
208
215
  box-shadow: var(--cp-shadows-overlay);
209
216
  }
210
217
 
211
- &__drawer--fullHeight {
218
+ &__drawer--isFullHeight {
212
219
  height: 100%;
213
220
  max-height: 100%;
214
221
  border-radius: 0;
@@ -230,29 +237,4 @@ defineExpose({ show, hide, toggle })
230
237
  padding: var(--cp-spacing-md) var(--cp-spacing-xl);
231
238
  }
232
239
  }
233
-
234
- .scale-elastic-enter-active,
235
- .scale-elastic-leave-active {
236
- transition:
237
- scale 200ms var(--cp-easing-elastic),
238
- opacity 200ms ease;
239
- }
240
-
241
- .scale-elastic-enter-from,
242
- .scale-elastic-leave-to {
243
- opacity: 0;
244
- scale: 0.9;
245
- }
246
-
247
- .p-drawer-enter-active,
248
- .p-drawer-leave-active {
249
- transition:
250
- transform 300ms ease,
251
- opacity 250ms ease;
252
- }
253
-
254
- .p-drawer-enter-from,
255
- .p-drawer-leave-to {
256
- transform: translateY(100%);
257
- }
258
240
  </style>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
3
+ <path d="M16 11.9999C16 9.02546 18.1644 6.55652 21.0042 6.08217C21.5489 5.99118 22 6.44763 22 6.99992C22 7.5522 21.5453 7.98709 21.0101 8.12334C19.2798 8.56386 18 10.1324 18 11.9999C18 13.8674 19.2798 15.436 21.0101 15.8765C21.5453 16.0127 22 16.4476 22 16.9999C22 17.5522 21.5489 18.0087 21.0042 17.9177C18.1644 17.4433 16 14.9744 16 11.9999ZM11.5859 12.9999L9.29297 15.2929C8.90244 15.6834 8.90244 16.3164 9.29297 16.7069C9.68349 17.0975 10.3165 17.0975 10.707 16.7069L14.6967 12.7173C14.8838 12.5356 15 12.2813 15 11.9999C15 11.7185 14.8838 11.4642 14.6967 11.2825L10.707 7.29288C10.3165 6.90236 9.68349 6.90236 9.29297 7.29288C8.90244 7.68341 8.90244 8.31642 9.29297 8.70695L11.5859 10.9999H2C1.44772 10.9999 1 11.4476 1 11.9999C1 12.5522 1.44772 12.9999 2 12.9999H11.5859Z"/>
4
+ </svg>
5
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
3
+ <path d="M2 6.99992C2 6.44763 2.45108 5.99118 2.99581 6.08217C5.83559 6.55653 8 9.02546 8 11.9999C8 14.9744 5.83559 17.4433 2.99581 17.9177C2.45107 18.0087 2 17.5522 2 16.9999C2 16.4476 2.45467 16.0127 2.98988 15.8765C4.72024 15.436 6 13.8674 6 11.9999C6 10.1324 4.72024 8.56386 2.98988 8.12334C2.45467 7.98709 2 7.5522 2 6.99992ZM19.5859 13H10C9.44771 13 9 12.5523 9 12C9 11.4478 9.44771 11 10 11H19.5859L17.293 8.70707C16.9024 8.31655 16.9024 7.68353 17.293 7.29301C17.6835 6.90248 18.3165 6.90248 18.707 7.29301L22.707 11.293C23.0976 11.6835 23.0976 12.3165 22.707 12.7071L18.707 16.7071C18.3165 17.0976 17.6835 17.0976 17.293 16.7071C16.9024 16.3165 16.9024 15.6835 17.293 15.293L19.5859 13Z"/>
4
+ </svg>
5
+ </template>
@@ -0,0 +1,16 @@
1
+ import { useWindowSize } from '@vueuse/core'
2
+ import { computed, onMounted, ref } from 'vue'
3
+
4
+ import { Breakpoints } from '@/constants/layout'
5
+
6
+ export const useIsBreakpoint = (breakpoint: Breakpoints = Breakpoints.MOBILE) => {
7
+ const isMounted = ref(false)
8
+ onMounted(() => (isMounted.value = true))
9
+
10
+ const { width } = useWindowSize()
11
+
12
+ return computed(() => {
13
+ if (!isMounted.value) return false
14
+ return width.value < breakpoint
15
+ })
16
+ }
@@ -38,6 +38,7 @@ import IconCorrosive from '@/components/icons/IconCorrosive.vue'
38
38
  import IconDashedCircle from '@/components/icons/IconDashedCircle.vue'
39
39
  import IconDedicated from '@/components/icons/IconDedicated.vue'
40
40
  import IconDeparture from '@/components/icons/IconDeparture.vue'
41
+ import IconDestination from '@/components/icons/IconDestination.vue'
41
42
  import IconDeviceForbidden from '@/components/icons/IconDeviceForbidden.vue'
42
43
  import IconDirectFlightAlt from '@/components/icons/IconDirectFlightAlt.vue'
43
44
  import IconDistribution from '@/components/icons/IconDistribution.vue'
@@ -92,6 +93,7 @@ import IconNoSeat from '@/components/icons/IconNoSeat.vue'
92
93
  import IconNotion from '@/components/icons/IconNotion.vue'
93
94
  import IconOffline from '@/components/icons/IconOffline.vue'
94
95
  import IconOneWay from '@/components/icons/IconOneWay.vue'
96
+ import IconOrigin from '@/components/icons/IconOrigin.vue'
95
97
  import IconOta from '@/components/icons/IconOta.vue'
96
98
  import IconOtaFilled from '@/components/icons/IconOtaFilled.vue'
97
99
  import IconPaid from '@/components/icons/IconPaid.vue'
@@ -173,6 +175,7 @@ export const CustomCpIcons = {
173
175
  dedicated: IconDedicated,
174
176
  departure: IconDeparture,
175
177
  'direct-flight-alt': IconDirectFlightAlt,
178
+ destination: IconDestination,
176
179
  'device-forbidden': IconDeviceForbidden,
177
180
  distribution: IconDistribution,
178
181
  'distribution-closed': IconDistributionClosed,
@@ -226,6 +229,7 @@ export const CustomCpIcons = {
226
229
  notion: IconNotion,
227
230
  offline: IconOffline,
228
231
  'one-way': IconOneWay,
232
+ origin: IconOrigin,
229
233
  ota: IconOta,
230
234
  'ota-filled': IconOtaFilled,
231
235
  paid: IconPaid,
@@ -29,7 +29,7 @@ const meta = {
29
29
  description: 'Disable the bottom-drawer behavior on mobile and keep the popover anchored to the trigger.',
30
30
  table: { defaultValue: { summary: 'false' } },
31
31
  },
32
- fullHeightDrawer: {
32
+ isFullHeightDrawer: {
33
33
  control: 'boolean',
34
34
  description: 'When enabled, the mobile drawer takes the full available height.',
35
35
  table: { defaultValue: { summary: 'false' } },
@@ -132,7 +132,10 @@ export const ForcePopoverOnMobile: Story = {
132
132
  */
133
133
  export const FullHeightDrawer: Story = {
134
134
  args: {
135
- fullHeightDrawer: true,
135
+ isFullHeightDrawer: true,
136
+ },
137
+ globals: {
138
+ viewport: { value: 'mobile1', isRotated: false },
136
139
  },
137
140
  render: (args) => ({
138
141
  components: { CpButton, CpMenu },
@@ -150,7 +153,7 @@ export const FullHeightDrawer: Story = {
150
153
  template: `
151
154
  <CpMenu v-bind="args" :items="items">
152
155
  <template #trigger>
153
- <CpButton>Open full-height drawer</CpButton>
156
+ <CpButton size="xs" appearance="secondary">Open drawer</CpButton>
154
157
  </template>
155
158
  </CpMenu>
156
159
  `,