@citizenplane/pimp 9.0.1 → 9.1.1
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 +3671 -3671
- package/dist/pimp.umd.js +18 -18
- package/package.json +1 -1
- package/src/components/CpContextualMenu.vue +1 -1
- package/src/components/CpMenuItem.vue +16 -8
- package/src/components/CpTable.vue +45 -32
- package/src/stories/CpContextualMenu.stories.ts +7 -2
- package/src/stories/CpTable.stories.ts +52 -47
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<context-menu ref="menu" :model="items" :pt="passThroughConfig">
|
|
3
3
|
<template #item="{ item, props }">
|
|
4
|
-
<cp-menu-item v-bind="{ ...item, ...props.action }" />
|
|
4
|
+
<cp-menu-item v-bind="{ ...item, ...props.action }" @on-async-command-complete="hide" />
|
|
5
5
|
</template>
|
|
6
6
|
</context-menu>
|
|
7
7
|
</template>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
class="cpMenuItem__button"
|
|
6
6
|
:class="dynamicClass"
|
|
7
7
|
:disabled="disabled"
|
|
8
|
-
@click
|
|
8
|
+
@click="handleItemClick"
|
|
9
9
|
>
|
|
10
10
|
<transition name="fade" :duration="100" mode="out-in">
|
|
11
11
|
<span v-if="isLoading" class="cpMenuItem__loaderWrapper">
|
|
@@ -28,6 +28,7 @@ import { computed } from 'vue'
|
|
|
28
28
|
import type { MenuItem } from 'primevue/menuitem'
|
|
29
29
|
|
|
30
30
|
interface Props {
|
|
31
|
+
isAsync?: boolean
|
|
31
32
|
isCritical?: boolean
|
|
32
33
|
isDisabled?: boolean
|
|
33
34
|
isLoading?: boolean
|
|
@@ -44,9 +45,10 @@ const props = withDefaults(defineProps<Props & Omit<MenuItem, 'class' | 'disable
|
|
|
44
45
|
isLoading: false,
|
|
45
46
|
isDisabled: false,
|
|
46
47
|
isCritical: false,
|
|
48
|
+
isAsync: false,
|
|
47
49
|
})
|
|
48
50
|
|
|
49
|
-
const emit = defineEmits(['onItemClick'])
|
|
51
|
+
const emit = defineEmits(['onItemClick', 'onAsyncCommandComplete'])
|
|
50
52
|
|
|
51
53
|
const dynamicClass = computed(() => ({
|
|
52
54
|
'cpMenuItem__button--reverseLabel': props.reverseLabel,
|
|
@@ -55,12 +57,18 @@ const dynamicClass = computed(() => ({
|
|
|
55
57
|
|
|
56
58
|
const disabled = computed(() => props.isLoading || props.isDisabled)
|
|
57
59
|
|
|
58
|
-
const handleItemClick = (event: Event) => {
|
|
59
|
-
if (props.command)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
const handleItemClick = async (event: Event) => {
|
|
61
|
+
if (!props.command) return
|
|
62
|
+
|
|
63
|
+
if (props.isAsync) {
|
|
64
|
+
// Stop the event from bubbling up to prevent menu auto close
|
|
65
|
+
event.stopPropagation()
|
|
66
|
+
|
|
67
|
+
await props.command({ originalEvent: event, item: props })
|
|
68
|
+
|
|
69
|
+
emit('onAsyncCommandComplete')
|
|
70
|
+
} else {
|
|
71
|
+
props.command({ originalEvent: event, item: props })
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
emit('onItemClick')
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
:class="getRowClasses(rowData, rowIndex)"
|
|
38
38
|
:tabindex="getTabindex(rowData)"
|
|
39
39
|
@click="handleRowClick(rowData, rowIndex)"
|
|
40
|
-
@
|
|
40
|
+
@contextmenu.prevent="handleContextMenu({ rowData, rowIndex }, $event)"
|
|
41
41
|
@keydown.enter="handleRowClick(rowData, rowIndex)"
|
|
42
42
|
>
|
|
43
43
|
<slot name="row" :row="rowData">
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
type="button"
|
|
66
66
|
class="cpTable__action"
|
|
67
67
|
:class="getQuickOptionClasses(option)"
|
|
68
|
-
:disabled="option.
|
|
69
|
-
@click.stop="option.action(
|
|
68
|
+
:disabled="option.isDisabled"
|
|
69
|
+
@click.stop="($event) => option.action(rowData, $event)"
|
|
70
70
|
>
|
|
71
71
|
<cp-icon :type="option.icon" size="16" />
|
|
72
72
|
</button>
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
<button
|
|
75
75
|
type="button"
|
|
76
76
|
class="cpTable__defaultAction"
|
|
77
|
-
@click.stop="
|
|
77
|
+
@click.stop="handleContextMenu({ rowData, rowIndex }, $event)"
|
|
78
78
|
>
|
|
79
79
|
<cp-icon type="more-vertical" size="16" />
|
|
80
80
|
</button>
|
|
@@ -108,12 +108,19 @@
|
|
|
108
108
|
</div>
|
|
109
109
|
</div>
|
|
110
110
|
<div class="cpTable__overlay" />
|
|
111
|
+
<cp-contextual-menu
|
|
112
|
+
v-if="hasRowOptions"
|
|
113
|
+
ref="contextualMenu"
|
|
114
|
+
:items="contextualMenuItems"
|
|
115
|
+
@hide="resetCurrentRowData"
|
|
116
|
+
/>
|
|
111
117
|
</div>
|
|
112
118
|
</template>
|
|
113
119
|
|
|
114
120
|
<script setup lang="ts">
|
|
115
121
|
import { ref, computed } from 'vue'
|
|
116
122
|
|
|
123
|
+
import CpContextualMenu from '@/components/CpContextualMenu.vue'
|
|
117
124
|
import CpTableEmptyState from '@/components/CpTableEmptyState.vue'
|
|
118
125
|
|
|
119
126
|
import { camelize, decamelize } from '@/helpers/string'
|
|
@@ -123,7 +130,6 @@ import { randomString } from '@/helpers'
|
|
|
123
130
|
|
|
124
131
|
interface Emits {
|
|
125
132
|
(e: 'onRowClick', data: Record<string, unknown>): void
|
|
126
|
-
(e: 'onRowRightClick', payload: { data: Record<string, unknown>; event: Event }): void
|
|
127
133
|
(e: 'onNextClick'): void
|
|
128
134
|
(e: 'onPreviousClick'): void
|
|
129
135
|
}
|
|
@@ -148,11 +154,13 @@ interface Pagination {
|
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
interface RowOptions {
|
|
151
|
-
action: (
|
|
152
|
-
disabled?: boolean
|
|
157
|
+
action: (rowData: Record<string, unknown>, $event: MouseEvent) => void
|
|
153
158
|
icon: string
|
|
154
159
|
id: string
|
|
160
|
+
isAsync?: boolean
|
|
155
161
|
isCritical?: boolean
|
|
162
|
+
isDisabled?: boolean
|
|
163
|
+
isLoading?: boolean
|
|
156
164
|
label: string
|
|
157
165
|
}
|
|
158
166
|
|
|
@@ -195,25 +203,18 @@ const LoaderColor = '#5341F9'
|
|
|
195
203
|
const uniqueId = ref(randomString())
|
|
196
204
|
const pageNumber = ref(0)
|
|
197
205
|
const cpTableContainer = ref<HTMLElement | null>(null)
|
|
206
|
+
const contextualMenu = ref<InstanceType<typeof CpContextualMenu>>()
|
|
198
207
|
|
|
199
208
|
const hasQuickOptions = computed(() => !!quickOptions.value.length)
|
|
209
|
+
const hasRowOptions = computed(() => props.enableRowOptions && props.rowOptions.length)
|
|
200
210
|
const hasMoreQuickActionsThanLimit = computed(() => props.rowOptions.length >= props.quickOptionsLimit)
|
|
201
211
|
|
|
202
|
-
const
|
|
203
|
-
if (!props.enableRowOptions || !props.rowOptions.length) {
|
|
204
|
-
return {
|
|
205
|
-
id: 'default',
|
|
206
|
-
label: 'More',
|
|
207
|
-
icon: 'more-vertical',
|
|
208
|
-
action: () => {},
|
|
209
|
-
}
|
|
210
|
-
}
|
|
212
|
+
const defaultRowOption = computed<RowOptions>(() => {
|
|
211
213
|
return {
|
|
212
214
|
id: 'more',
|
|
213
215
|
label: 'More',
|
|
214
216
|
icon: 'more-vertical',
|
|
215
|
-
action: (
|
|
216
|
-
handleRowRightClick({ rowData, rowIndex }, $event),
|
|
217
|
+
action: (rowData: Record<string, unknown>, $event: MouseEvent) => showContextualMenu($event),
|
|
217
218
|
}
|
|
218
219
|
})
|
|
219
220
|
|
|
@@ -222,10 +223,18 @@ const quickOptions = computed(() => {
|
|
|
222
223
|
|
|
223
224
|
if (hasMoreQuickActionsThanLimit.value) {
|
|
224
225
|
const slicedOptions = props.rowOptions.slice(0, props.quickOptionsLimit)
|
|
225
|
-
return [...slicedOptions,
|
|
226
|
+
return [...slicedOptions, defaultRowOption.value]
|
|
226
227
|
}
|
|
227
228
|
|
|
228
|
-
return [...props.rowOptions,
|
|
229
|
+
return [...props.rowOptions, defaultRowOption.value]
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
const currentRowData = ref<Record<string, unknown>>({})
|
|
233
|
+
const contextualMenuItems = computed(() => {
|
|
234
|
+
return props.rowOptions.map((option) => ({
|
|
235
|
+
...option,
|
|
236
|
+
command: ({ originalEvent }: { originalEvent: MouseEvent }) => option.action(currentRowData.value, originalEvent),
|
|
237
|
+
}))
|
|
229
238
|
})
|
|
230
239
|
|
|
231
240
|
const containerDOMElement = computed(() => cpTableContainer.value)
|
|
@@ -363,7 +372,21 @@ const paginationResultsDetails = computed(() => {
|
|
|
363
372
|
return `${formattedNumberOfResults} ${pluralizedCount}`
|
|
364
373
|
})
|
|
365
374
|
|
|
366
|
-
const
|
|
375
|
+
const handleContextMenu = (
|
|
376
|
+
{ rowData, rowIndex }: { rowData: Record<string, unknown>; rowIndex: number },
|
|
377
|
+
event: MouseEvent,
|
|
378
|
+
) => {
|
|
379
|
+
if (!hasRowOptions.value || isFullWidthRow(rowData)) return
|
|
380
|
+
currentRowData.value = getRowPayload(rowIndex)
|
|
381
|
+
|
|
382
|
+
showContextualMenu(event)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const showContextualMenu = (event: MouseEvent) => contextualMenu.value?.show(event)
|
|
386
|
+
const hideContextualMenu = () => contextualMenu.value?.hide()
|
|
387
|
+
const resetCurrentRowData = () => (currentRowData.value = {})
|
|
388
|
+
|
|
389
|
+
const getQuickOptionTooltip = (option: RowOptions) => (!option.isDisabled ? option.label : '')
|
|
367
390
|
|
|
368
391
|
const getQuickOptionClasses = (option: RowOptions) => {
|
|
369
392
|
return { 'cpTable__action--isCritical': option.isCritical }
|
|
@@ -378,16 +401,6 @@ const handleRowClick = (rowData: Record<string, unknown>, rowIndex: number) => {
|
|
|
378
401
|
emit('onRowClick', data)
|
|
379
402
|
}
|
|
380
403
|
|
|
381
|
-
const handleRowRightClick = (
|
|
382
|
-
{ rowData, rowIndex }: { rowData: Record<string, unknown>; rowIndex: number },
|
|
383
|
-
event: Event,
|
|
384
|
-
) => {
|
|
385
|
-
if (isFullWidthRow(rowData)) return
|
|
386
|
-
|
|
387
|
-
const data = getRowPayload(rowIndex)
|
|
388
|
-
emit('onRowRightClick', { data, event })
|
|
389
|
-
}
|
|
390
|
-
|
|
391
404
|
const handleNavigationClick = (isNext = true) => {
|
|
392
405
|
resetScrollPosition()
|
|
393
406
|
|
|
@@ -504,7 +517,7 @@ const areRowOptionsEnabled = (rowData: Record<string, unknown>) => props.enableR
|
|
|
504
517
|
|
|
505
518
|
const resetPagination = () => (pageNumber.value = 0)
|
|
506
519
|
|
|
507
|
-
defineExpose({ resetPagination })
|
|
520
|
+
defineExpose({ hideContextualMenu, resetPagination })
|
|
508
521
|
</script>
|
|
509
522
|
|
|
510
523
|
<style lang="scss">
|
|
@@ -45,11 +45,16 @@ export const Default: Story = {
|
|
|
45
45
|
label: 'Download',
|
|
46
46
|
icon: 'download',
|
|
47
47
|
isLoading: isLoading.value,
|
|
48
|
-
|
|
48
|
+
isAsync: true,
|
|
49
|
+
command: async () => {
|
|
49
50
|
isLoading.value = true
|
|
50
|
-
|
|
51
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
52
|
+
isLoading.value = false
|
|
51
53
|
},
|
|
52
54
|
},
|
|
55
|
+
{
|
|
56
|
+
separator: true,
|
|
57
|
+
},
|
|
53
58
|
{
|
|
54
59
|
label: 'Delete',
|
|
55
60
|
icon: 'trash-2',
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ref, computed } from 'vue'
|
|
2
|
+
|
|
1
3
|
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
4
|
|
|
3
5
|
import CpTable from '@/components/CpTable.vue'
|
|
@@ -108,17 +110,6 @@ export const Default: Story = {
|
|
|
108
110
|
}),
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
export const WithPagination: Story = {
|
|
112
|
-
args: {
|
|
113
|
-
...Default.args,
|
|
114
|
-
pagination: {
|
|
115
|
-
enabled: true,
|
|
116
|
-
limit: 3,
|
|
117
|
-
format: PAGINATION_FORMATS.PAGES,
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
}
|
|
121
|
-
|
|
122
113
|
export const ClickableRows: Story = {
|
|
123
114
|
args: {
|
|
124
115
|
...Default.args,
|
|
@@ -126,24 +117,28 @@ export const ClickableRows: Story = {
|
|
|
126
117
|
},
|
|
127
118
|
}
|
|
128
119
|
|
|
129
|
-
export const
|
|
120
|
+
export const Loading: Story = {
|
|
130
121
|
args: {
|
|
131
122
|
...Default.args,
|
|
132
|
-
|
|
123
|
+
isLoading: true,
|
|
133
124
|
},
|
|
134
125
|
}
|
|
135
126
|
|
|
136
|
-
export const
|
|
127
|
+
export const Empty: Story = {
|
|
137
128
|
args: {
|
|
138
129
|
...Default.args,
|
|
139
|
-
|
|
130
|
+
data: [],
|
|
140
131
|
},
|
|
141
132
|
}
|
|
142
133
|
|
|
143
|
-
export const
|
|
134
|
+
export const WithPagination: Story = {
|
|
144
135
|
args: {
|
|
145
136
|
...Default.args,
|
|
146
|
-
|
|
137
|
+
pagination: {
|
|
138
|
+
enabled: true,
|
|
139
|
+
limit: 3,
|
|
140
|
+
format: PAGINATION_FORMATS.PAGES,
|
|
141
|
+
},
|
|
147
142
|
},
|
|
148
143
|
}
|
|
149
144
|
|
|
@@ -191,42 +186,52 @@ export const WithCustomRowOptions: Story = {
|
|
|
191
186
|
args: {
|
|
192
187
|
...Default.args,
|
|
193
188
|
enableRowOptions: true,
|
|
194
|
-
rowOptions: [
|
|
195
|
-
{
|
|
196
|
-
id: 'see',
|
|
197
|
-
label: 'see',
|
|
198
|
-
icon: 'eye',
|
|
199
|
-
action: () => console.log('See'),
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
id: 'edit',
|
|
203
|
-
label: 'edit',
|
|
204
|
-
icon: 'edit-2',
|
|
205
|
-
action: () => console.log('Edit'),
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
id: 'disable',
|
|
209
|
-
label: 'disable',
|
|
210
|
-
icon: 'history',
|
|
211
|
-
disabled: true,
|
|
212
|
-
action: () => console.log('History'),
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
id: 'delete',
|
|
216
|
-
label: 'delete',
|
|
217
|
-
icon: 'trash',
|
|
218
|
-
isCritical: true,
|
|
219
|
-
action: () => console.log('Delete'),
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
189
|
},
|
|
223
190
|
render: (args) => ({
|
|
224
191
|
components: { CpTable },
|
|
225
192
|
setup() {
|
|
226
|
-
|
|
193
|
+
const isEditLoading = ref(false)
|
|
194
|
+
|
|
195
|
+
const rowOptions = computed(() => [
|
|
196
|
+
{
|
|
197
|
+
id: 'see',
|
|
198
|
+
label: 'See',
|
|
199
|
+
icon: 'eye',
|
|
200
|
+
action: () => console.log('See'),
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 'edit',
|
|
204
|
+
label: 'Edit',
|
|
205
|
+
icon: 'edit-2',
|
|
206
|
+
isAsync: true,
|
|
207
|
+
isLoading: isEditLoading.value,
|
|
208
|
+
action: async (payload) => {
|
|
209
|
+
isEditLoading.value = true
|
|
210
|
+
console.log('Edit', payload)
|
|
211
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
212
|
+
isEditLoading.value = false
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
id: 'disable',
|
|
217
|
+
label: 'Disable',
|
|
218
|
+
icon: 'history',
|
|
219
|
+
isDisabled: true,
|
|
220
|
+
action: () => console.log('History'),
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: 'delete',
|
|
224
|
+
label: 'Delete',
|
|
225
|
+
icon: 'trash',
|
|
226
|
+
isCritical: true,
|
|
227
|
+
action: () => console.log('Delete'),
|
|
228
|
+
},
|
|
229
|
+
])
|
|
230
|
+
|
|
231
|
+
return { args, isEditLoading, rowOptions }
|
|
227
232
|
},
|
|
228
233
|
template: `
|
|
229
|
-
<CpTable v-bind="args">
|
|
234
|
+
<CpTable v-bind="args" :row-options="rowOptions">
|
|
230
235
|
<template #status="{ cell }">
|
|
231
236
|
<span :style="{
|
|
232
237
|
padding: '4px 8px',
|