@citizenplane/pimp 8.32.2 → 8.32.5
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/pimp.es.js +6601 -5479
- package/dist/pimp.umd.js +184 -36
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/README.md +1 -1
- package/src/assets/styles/helpers/_keyframes.scss +13 -0
- package/src/components/CpBadge.vue +0 -5
- package/src/components/CpContextualMenu.vue +49 -0
- package/src/components/CpMenuItem.vue +155 -0
- package/src/components/CpMultiselect.vue +30 -18
- package/src/components/index.ts +4 -17
- package/src/stories/CpContextualMenu.stories.ts +68 -0
- package/src/stories/CpMultiselect.stories.ts +3 -3
- package/tsconfig.json +2 -0
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
</template>
|
|
31
31
|
<template #chip="{ value, removeCallback }">
|
|
32
32
|
<slot name="selected-option" :option="value" :remove="removeCallback">
|
|
33
|
-
<cp-badge is-clearable size="sm" @on-clear="removeCallback
|
|
33
|
+
<cp-badge is-clearable size="sm" @on-clear="removeCallback">
|
|
34
34
|
<template #leading-icon>
|
|
35
35
|
<slot name="selected-option-leading-icon" :option="value" />
|
|
36
36
|
</template>
|
|
@@ -73,7 +73,6 @@ import { isEmpty } from '@/helpers/object'
|
|
|
73
73
|
|
|
74
74
|
interface Emits {
|
|
75
75
|
(e: 'search', query: string): void
|
|
76
|
-
(e: 'select', option: Record<string, unknown>): void
|
|
77
76
|
(e: 'clear'): void
|
|
78
77
|
(e: 'update:modelValue', value: Record<string, unknown> | Record<string, unknown>[] | null): void
|
|
79
78
|
}
|
|
@@ -129,8 +128,14 @@ const selectModel = computed({
|
|
|
129
128
|
},
|
|
130
129
|
})
|
|
131
130
|
|
|
132
|
-
const
|
|
133
|
-
|
|
131
|
+
const hasMultipleValues = computed(() => Array.isArray(selectModel.value) && selectModel.value.length > 0)
|
|
132
|
+
|
|
133
|
+
const multiselectDynamicClass = computed(() => {
|
|
134
|
+
return hasMultipleValues.value ? 'cpMultiselect__select--hasMultipleValues' : ''
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const passThroughConfig = computed(() => ({
|
|
138
|
+
root: { class: `cpMultiselect__select ${multiselectDynamicClass.value}` },
|
|
134
139
|
inputmultiple: { class: 'cpMultiselect__tags' },
|
|
135
140
|
dropdown: { class: 'cpMultiselect__toggle' },
|
|
136
141
|
inputchip: { class: 'cpMultiselect__inputWrapper' },
|
|
@@ -139,16 +144,15 @@ const passThroughConfig = {
|
|
|
139
144
|
list: { class: 'cpMultiselect__list' },
|
|
140
145
|
option: { class: 'cpMultiselect__option' },
|
|
141
146
|
loader: { class: 'cpMultiselect__hidden' },
|
|
142
|
-
}
|
|
147
|
+
}))
|
|
143
148
|
|
|
144
149
|
const multiselect = ref<InstanceType<typeof AutoComplete> | null>(null)
|
|
145
150
|
|
|
151
|
+
// @ts-expect-error 'overlayVisible' does not exist on type instance of AutoComplete
|
|
146
152
|
const isDropdownOpen = computed(() => multiselect.value?.overlayVisible)
|
|
147
153
|
|
|
148
154
|
const chevronDynamicClass = computed(() => {
|
|
149
|
-
return {
|
|
150
|
-
'cpMultiselect__dropdownIcon--isRotated': isDropdownOpen.value,
|
|
151
|
-
}
|
|
155
|
+
return { 'cpMultiselect__dropdownIcon--isRotated': isDropdownOpen.value }
|
|
152
156
|
})
|
|
153
157
|
|
|
154
158
|
const displayPrefix = computed(() => {
|
|
@@ -166,8 +170,10 @@ const handleClear = () => (selectModel.value = null)
|
|
|
166
170
|
|
|
167
171
|
const toggleDropdown = () => {
|
|
168
172
|
if (isDropdownOpen.value) {
|
|
173
|
+
// @ts-expect-error 'hide' does not exist on type instance of AutoComplete
|
|
169
174
|
multiselect.value?.hide()
|
|
170
175
|
} else {
|
|
176
|
+
// @ts-expect-error 'show' does not exist on type instance of AutoComplete
|
|
171
177
|
multiselect.value?.show()
|
|
172
178
|
}
|
|
173
179
|
}
|
|
@@ -181,16 +187,22 @@ const handleUpdateModelValue = (value: Record<string, unknown> | null) => {
|
|
|
181
187
|
|
|
182
188
|
const overrideAlignOverlay = () => {
|
|
183
189
|
if (multiselect.value) {
|
|
190
|
+
// @ts-expect-error 'alignOverlay' does not exist on type instance of AutoComplete
|
|
184
191
|
multiselect.value.alignOverlay = alignOverlay
|
|
185
192
|
}
|
|
186
193
|
}
|
|
187
194
|
|
|
188
195
|
const alignOverlay = () => {
|
|
196
|
+
// @ts-expect-error 'el' does not exist on type instance of AutoComplete
|
|
189
197
|
const target = multiselect.value?.$el
|
|
198
|
+
|
|
199
|
+
// @ts-expect-error 'overlay' does not exist on type instance of AutoComplete
|
|
190
200
|
if (!multiselect.value?.overlay || !target) return
|
|
191
201
|
|
|
202
|
+
// @ts-expect-error 'overlay' does not exist on type instance of AutoComplete
|
|
192
203
|
multiselect.value.overlay.style.width = `${getOuterWidth(target)}px`
|
|
193
204
|
|
|
205
|
+
// @ts-expect-error 'overlay' does not exist on type instance of AutoComplete
|
|
194
206
|
absolutePosition(multiselect.value.overlay, target)
|
|
195
207
|
}
|
|
196
208
|
|
|
@@ -208,6 +220,9 @@ onMounted(() => overrideAlignOverlay())
|
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
&__prefix {
|
|
223
|
+
display: flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
flex-shrink: 0;
|
|
211
226
|
order: -1;
|
|
212
227
|
|
|
213
228
|
&:empty {
|
|
@@ -217,14 +232,17 @@ onMounted(() => overrideAlignOverlay())
|
|
|
217
232
|
|
|
218
233
|
&__select {
|
|
219
234
|
display: flex;
|
|
220
|
-
min-height: fn.px-to-rem(46);
|
|
221
235
|
align-items: center;
|
|
222
236
|
justify-content: space-between;
|
|
223
|
-
padding: fn.px-to-rem(
|
|
237
|
+
padding: fn.px-to-rem(10.5);
|
|
224
238
|
border: 1px solid colors.$border-color;
|
|
225
239
|
border-radius: fn.px-to-rem(10);
|
|
226
240
|
gap: sp.$space;
|
|
227
241
|
|
|
242
|
+
&--hasMultipleValues {
|
|
243
|
+
padding: fn.px-to-rem(9.5) fn.px-to-rem(10.5);
|
|
244
|
+
}
|
|
245
|
+
|
|
228
246
|
&:has(input:focus-visible) {
|
|
229
247
|
box-shadow: 0 0 0 fn.px-to-em(3) color.scale(colors.$primary-color, $lightness: 70%);
|
|
230
248
|
}
|
|
@@ -272,21 +290,15 @@ onMounted(() => overrideAlignOverlay())
|
|
|
272
290
|
padding: 0;
|
|
273
291
|
flex: 1;
|
|
274
292
|
font-size: fn.px-to-rem(14);
|
|
275
|
-
line-height: fn.px-to-rem(
|
|
293
|
+
line-height: fn.px-to-rem(22);
|
|
276
294
|
background-color: transparent;
|
|
295
|
+
width: 100%;
|
|
277
296
|
|
|
278
297
|
&:disabled {
|
|
279
298
|
cursor: not-allowed;
|
|
280
299
|
}
|
|
281
300
|
}
|
|
282
301
|
|
|
283
|
-
&__input {
|
|
284
|
-
padding: 0;
|
|
285
|
-
flex: 1;
|
|
286
|
-
font-size: fn.px-to-rem(14);
|
|
287
|
-
line-height: fn.px-to-rem(24);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
302
|
&__dropdownIcon {
|
|
291
303
|
@include mx.square-sizing(16);
|
|
292
304
|
|
package/src/components/index.ts
CHANGED
|
@@ -1,45 +1,33 @@
|
|
|
1
1
|
import { vTooltip } from 'floating-vue'
|
|
2
|
-
// PLUGINS
|
|
3
2
|
import { vMaska } from 'maska/vue'
|
|
4
3
|
import PrimeVue from 'primevue/config'
|
|
5
4
|
import { App } from 'vue'
|
|
6
5
|
import { BindOnceDirective } from 'vue-bind-once'
|
|
7
6
|
|
|
8
|
-
// DIRECTIVES
|
|
9
7
|
import ClickOutside from '../directives/ClickOutside'
|
|
10
8
|
import CpCoreDatepicker from '../libs/CoreDatepicker.vue'
|
|
11
9
|
import CpAirlineLogo from './CpAirlineLogo.vue'
|
|
12
|
-
// Feedback indicators
|
|
13
10
|
import CpAlert from './CpAlert.vue'
|
|
14
|
-
// COMPONENTS
|
|
15
|
-
// Atomic elements
|
|
16
11
|
import CpBadge from './CpBadge.vue'
|
|
17
|
-
// Buttons
|
|
18
12
|
import CpButton from './CpButton.vue'
|
|
19
13
|
import CpCalendar from './CpCalendar.vue'
|
|
20
|
-
// Toggles
|
|
21
14
|
import CpCheckbox from './CpCheckbox.vue'
|
|
22
|
-
|
|
15
|
+
import CpContextualMenu from './CpContextualMenu.vue'
|
|
23
16
|
import CpDate from './CpDate.vue'
|
|
24
17
|
import CpDatepicker from './CpDatepicker.vue'
|
|
25
18
|
import CpDialog from './CpDialog.vue'
|
|
26
19
|
import CpDialogWrapper from './CpDialogWrapper.vue'
|
|
27
|
-
// Typography
|
|
28
20
|
import CpHeading from './CpHeading.vue'
|
|
29
|
-
// Visual
|
|
30
21
|
import CpIcon from './CpIcon.vue'
|
|
31
|
-
// Inputs
|
|
32
22
|
import CpInput from './CpInput.vue'
|
|
33
|
-
// Dropdown menus
|
|
34
23
|
import CpLoader from './CpLoader.vue'
|
|
24
|
+
import CpMenuItem from './CpMenuItem.vue'
|
|
35
25
|
import CpMultiselect from './CpMultiselect.vue'
|
|
36
26
|
import CpPartnerBadge from './CpPartnerBadge.vue'
|
|
37
27
|
import CpRadio from './CpRadio.vue'
|
|
38
|
-
// Selects
|
|
39
28
|
import CpSelect from './CpSelect.vue'
|
|
40
29
|
import CpSelectMenu from './CpSelectMenu.vue'
|
|
41
30
|
import CpSwitch from './CpSwitch.vue'
|
|
42
|
-
// List and Tables
|
|
43
31
|
import CpTable from './CpTable.vue'
|
|
44
32
|
import CpTextarea from './CpTextarea.vue'
|
|
45
33
|
import CpToaster from './CpToaster.vue'
|
|
@@ -52,11 +40,8 @@ import IconGroupBy from './icons/IconGroupBy.vue'
|
|
|
52
40
|
import IconOta from './icons/IconOta.vue'
|
|
53
41
|
import IconSupplier from './icons/IconSupplier.vue'
|
|
54
42
|
import IconThirdParty from './icons/IconThirdParty.vue'
|
|
55
|
-
// Icons
|
|
56
43
|
import IconTooltip from './icons/IconTooltip.vue'
|
|
57
|
-
// Helpers and Utilities
|
|
58
44
|
import TransitionExpand from './TransitionExpand.vue'
|
|
59
|
-
// Methods
|
|
60
45
|
import createToaster from '@/plugins/toaster'
|
|
61
46
|
|
|
62
47
|
const Components = {
|
|
@@ -67,6 +52,8 @@ const Components = {
|
|
|
67
52
|
CpDialogWrapper,
|
|
68
53
|
CpDialog,
|
|
69
54
|
CpDate,
|
|
55
|
+
CpContextualMenu,
|
|
56
|
+
CpMenuItem,
|
|
70
57
|
CpCoreDatepicker,
|
|
71
58
|
CpDatepicker,
|
|
72
59
|
CpCalendar,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
4
|
+
|
|
5
|
+
import CpContextualMenu from '@/components/CpContextualMenu.vue'
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: 'CpContextualMenu',
|
|
9
|
+
component: CpContextualMenu,
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
'A component that displays airline logos using IATA codes. Fetches logos from Kiwi.com CDN and displays them with customizable sizes.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
items: {
|
|
20
|
+
control: 'object',
|
|
21
|
+
description: 'The items to display in the menu',
|
|
22
|
+
table: {
|
|
23
|
+
type: { summary: 'object' },
|
|
24
|
+
defaultValue: { summary: '[]' },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
tags: ['autodocs'],
|
|
29
|
+
} satisfies Meta<typeof CpContextualMenu>
|
|
30
|
+
|
|
31
|
+
export default meta
|
|
32
|
+
type Story = StoryObj<typeof meta>
|
|
33
|
+
|
|
34
|
+
export const Default: Story = {
|
|
35
|
+
render: (args) => ({
|
|
36
|
+
components: { CpContextualMenu },
|
|
37
|
+
setup() {
|
|
38
|
+
const menu = ref<InstanceType<typeof CpContextualMenu>>()
|
|
39
|
+
const showMenu = (event: MouseEvent) => menu.value?.show(event)
|
|
40
|
+
|
|
41
|
+
const isLoading = ref(false)
|
|
42
|
+
|
|
43
|
+
const items = computed(() => [
|
|
44
|
+
{
|
|
45
|
+
label: 'Download',
|
|
46
|
+
icon: 'download',
|
|
47
|
+
isLoading: isLoading.value,
|
|
48
|
+
command: () => {
|
|
49
|
+
isLoading.value = true
|
|
50
|
+
setTimeout(() => (isLoading.value = false), 2000)
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: 'Delete',
|
|
55
|
+
icon: 'trash-2',
|
|
56
|
+
isCritical: true,
|
|
57
|
+
command: () => alert('Delete clicked'),
|
|
58
|
+
},
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
return { args, menu, showMenu, isLoading, items }
|
|
62
|
+
},
|
|
63
|
+
template: `
|
|
64
|
+
<p @contextmenu="showMenu">Right click on me to open the menu</p>
|
|
65
|
+
<CpContextualMenu :items="items" ref="menu" />
|
|
66
|
+
`,
|
|
67
|
+
}),
|
|
68
|
+
}
|
|
@@ -112,7 +112,7 @@ export const Single: Story = {
|
|
|
112
112
|
<div style="padding: 20px;">
|
|
113
113
|
<CpMultiselect v-model="selectedSupplier" v-bind="args" :options="dynamicOptions" :is-loading="isLoading" @search="handleSearch">
|
|
114
114
|
<template #prefix>
|
|
115
|
-
<cp-partner-badge type="supplier" size="
|
|
115
|
+
<cp-partner-badge type="supplier" size="sm" />
|
|
116
116
|
</template>
|
|
117
117
|
<template #option="{ option }">
|
|
118
118
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
@@ -191,7 +191,7 @@ export const Multiple: Story = {
|
|
|
191
191
|
<div style="padding: 20px;">
|
|
192
192
|
<CpMultiselect v-model="selectedAirlines" v-bind="args" :options="dynamicOptions" :is-loading="isLoading" @search="handleSearch">
|
|
193
193
|
<template #prefix>
|
|
194
|
-
<cp-partner-badge type="airline" size="
|
|
194
|
+
<cp-partner-badge type="airline" size="sm" />
|
|
195
195
|
</template>
|
|
196
196
|
<template #selected-option-leading-icon="{ option }">
|
|
197
197
|
<cp-airline-logo :iata-code="option.iata_code" size="14" />
|
|
@@ -225,7 +225,7 @@ export const Invalid: Story = {
|
|
|
225
225
|
<div style="padding: 20px;">
|
|
226
226
|
<CpMultiselect v-model="selectedSupplier" v-bind="args">
|
|
227
227
|
<template #prefix>
|
|
228
|
-
<cp-partner-badge type="supplier" size="
|
|
228
|
+
<cp-partner-badge type="supplier" size="sm" />
|
|
229
229
|
</template>
|
|
230
230
|
<template #option="{ option }">
|
|
231
231
|
<div style="display: flex; align-items: center; gap: 8px;">
|