fix: show view action in view selector dropdown itself
This commit is contained in:
parent
d2c2254230
commit
c1882f7280
@ -108,9 +108,9 @@ watch(show, (value) => {
|
||||
duplicateMode.value = false
|
||||
nextTick(() => {
|
||||
_view.value = { ...view.value }
|
||||
if (_view.value.name) {
|
||||
if (_view.value.mode === 'edit') {
|
||||
editMode.value = true
|
||||
} else if (_view.value.label) {
|
||||
} else if (_view.value.mode === 'duplicate') {
|
||||
duplicateMode.value = true
|
||||
}
|
||||
})
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<template #default="{ open }">
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="text-lg font-medium"
|
||||
class="text-lg font-medium text-nowrap"
|
||||
:label="__(viewControls.currentView.label)"
|
||||
>
|
||||
<template #prefix>
|
||||
@ -25,17 +25,62 @@
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
</Dropdown>
|
||||
<Dropdown :options="viewControls.viewActions">
|
||||
<template #default>
|
||||
<Button variant="ghost" icon="more-horizontal" />
|
||||
<template #item="{ item, active }">
|
||||
<button
|
||||
:class="[
|
||||
active ? 'bg-gray-100' : 'text-gray-800',
|
||||
'group flex gap-4 h-7 w-full justify-between items-center rounded px-2 text-base',
|
||||
]"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<FeatherIcon
|
||||
v-if="item.icon && typeof item.icon === 'string'"
|
||||
:name="item.icon"
|
||||
class="mr-2 h-4 w-4 flex-shrink-0 text-gray-700"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<component
|
||||
class="mr-2 h-4 w-4 flex-shrink-0 text-gray-700"
|
||||
v-else-if="item.icon"
|
||||
:is="item.icon"
|
||||
/>
|
||||
<span class="whitespace-nowrap">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="item.name"
|
||||
class="flex flex-row-reverse gap-2 items-center min-w-11"
|
||||
>
|
||||
<Dropdown
|
||||
:class="active ? 'block' : 'hidden'"
|
||||
placement="right-start"
|
||||
:options="viewControls.viewActions(item)"
|
||||
>
|
||||
<template #default="{ togglePopover }">
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="!size-5"
|
||||
icon="more-horizontal"
|
||||
@click.stop="togglePopover()"
|
||||
/>
|
||||
</template>
|
||||
</Dropdown>
|
||||
<FeatherIcon
|
||||
v-if="isCurrentView(item)"
|
||||
name="check"
|
||||
class="size-4 text-gray-700"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import Icon from '@/components/Icon.vue'
|
||||
import { Dropdown } from 'frappe-ui'
|
||||
import Dropdown from '@/components/frappe-ui/Dropdown.vue'
|
||||
|
||||
const props = defineProps({
|
||||
routeName: {
|
||||
@ -45,4 +90,8 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const viewControls = defineModel()
|
||||
|
||||
const isCurrentView = (item) => {
|
||||
return item.name === viewControls.value.currentView.name
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -162,6 +162,7 @@
|
||||
afterUpdate: () => {
|
||||
viewUpdated = false
|
||||
reloadView()
|
||||
list.reload()
|
||||
},
|
||||
}"
|
||||
/>
|
||||
@ -280,14 +281,17 @@ function getViewType() {
|
||||
let viewType = route.params.viewType || 'list'
|
||||
let types = {
|
||||
list: {
|
||||
name: 'list',
|
||||
label: __('List'),
|
||||
icon: markRaw(ListIcon),
|
||||
},
|
||||
group_by: {
|
||||
name: 'group_by',
|
||||
label: __('Group By'),
|
||||
icon: markRaw(GroupByIcon),
|
||||
},
|
||||
kanban: {
|
||||
name: 'kanban',
|
||||
label: __('Kanban'),
|
||||
icon: markRaw(KanbanIcon),
|
||||
},
|
||||
@ -299,6 +303,7 @@ function getViewType() {
|
||||
const currentView = computed(() => {
|
||||
let _view = getView(route.query.view, route.params.viewType, props.doctype)
|
||||
return {
|
||||
name: _view?.name || getViewType().name,
|
||||
label:
|
||||
_view?.label || props.options?.defaultViewName || getViewType().label,
|
||||
icon: _view?.icon || getViewType().icon,
|
||||
@ -471,6 +476,7 @@ let allowedViews = props.options.allowedViews || ['list']
|
||||
|
||||
if (allowedViews.includes('list')) {
|
||||
defaultViews.push({
|
||||
name: 'list',
|
||||
label: __(props.options?.defaultViewName) || __('List'),
|
||||
icon: markRaw(ListIcon),
|
||||
onClick() {
|
||||
@ -481,6 +487,7 @@ if (allowedViews.includes('list')) {
|
||||
}
|
||||
if (allowedViews.includes('kanban')) {
|
||||
defaultViews.push({
|
||||
name: 'kanban',
|
||||
label: __(props.options?.defaultViewName) || __('Kanban'),
|
||||
icon: markRaw(KanbanIcon),
|
||||
onClick() {
|
||||
@ -491,6 +498,7 @@ if (allowedViews.includes('kanban')) {
|
||||
}
|
||||
if (allowedViews.includes('group_by')) {
|
||||
defaultViews.push({
|
||||
name: 'group_by',
|
||||
label: __(props.options?.defaultViewName) || __('Group By'),
|
||||
icon: markRaw(GroupByIcon),
|
||||
onClick() {
|
||||
@ -522,6 +530,7 @@ const viewsDropdownOptions = computed(() => {
|
||||
|
||||
if (list.value?.data?.views) {
|
||||
list.value.data.views.forEach((view) => {
|
||||
view.name = view.name
|
||||
view.label = __(view.label)
|
||||
view.type = view.type || 'list'
|
||||
view.icon = getIcon(view.icon, view.type)
|
||||
@ -544,17 +553,16 @@ const viewsDropdownOptions = computed(() => {
|
||||
)
|
||||
let pinnedViews = list.value.data.views.filter((v) => v.pinned)
|
||||
|
||||
publicViews.length &&
|
||||
_views.push({
|
||||
group: __('Public Views'),
|
||||
items: publicViews,
|
||||
})
|
||||
|
||||
savedViews.length &&
|
||||
_views.push({
|
||||
group: __('Saved Views'),
|
||||
items: savedViews,
|
||||
})
|
||||
publicViews.length &&
|
||||
_views.push({
|
||||
group: __('Public Views'),
|
||||
items: publicViews,
|
||||
})
|
||||
pinnedViews.length &&
|
||||
_views.push({
|
||||
group: __('Pinned Views'),
|
||||
@ -866,7 +874,10 @@ function updatePageLength(value, loadMore = false) {
|
||||
}
|
||||
|
||||
// View Actions
|
||||
const viewActions = computed(() => {
|
||||
const viewActions = (view) => {
|
||||
let isDefault = typeof view.name === 'string'
|
||||
let _view = getView(view.name)
|
||||
|
||||
let actions = [
|
||||
{
|
||||
group: __('Default Views'),
|
||||
@ -875,37 +886,36 @@ const viewActions = computed(() => {
|
||||
{
|
||||
label: __('Duplicate'),
|
||||
icon: () => h(DuplicateIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => duplicateView(),
|
||||
onClick: () => duplicateView(_view),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
if (route.query.view && (!view.value.public || isManager())) {
|
||||
if (!isDefault && (!_view.public || isManager())) {
|
||||
actions[0].items.push({
|
||||
label: __('Edit'),
|
||||
icon: () => h(EditIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => editView(),
|
||||
onClick: () => editView(_view),
|
||||
})
|
||||
|
||||
if (!view.value.public) {
|
||||
if (!_view.public) {
|
||||
actions[0].items.push({
|
||||
label: view.value.pinned ? __('Unpin View') : __('Pin View'),
|
||||
icon: () =>
|
||||
h(view.value.pinned ? UnpinIcon : PinIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => pinView(),
|
||||
label: _view.pinned ? __('Unpin View') : __('Pin View'),
|
||||
icon: () => h(_view.pinned ? UnpinIcon : PinIcon, { class: 'h-4 w-4' }),
|
||||
onClick: () => pinView(_view),
|
||||
})
|
||||
}
|
||||
|
||||
if (isManager()) {
|
||||
actions[0].items.push({
|
||||
label: view.value.public ? __('Make Private') : __('Make Public'),
|
||||
label: _view.public ? __('Make Private') : __('Make Public'),
|
||||
icon: () =>
|
||||
h(FeatherIcon, {
|
||||
name: view.value.public ? 'lock' : 'unlock',
|
||||
name: _view.public ? 'lock' : 'unlock',
|
||||
class: 'h-4 w-4',
|
||||
}),
|
||||
onClick: () => publicView(),
|
||||
onClick: () => publicView(_view),
|
||||
})
|
||||
}
|
||||
|
||||
@ -926,7 +936,7 @@ const viewActions = computed(() => {
|
||||
label: __('Delete'),
|
||||
variant: 'solid',
|
||||
theme: 'red',
|
||||
onClick: (close) => deleteView(close),
|
||||
onClick: (close) => deleteView(_view, close),
|
||||
},
|
||||
],
|
||||
}),
|
||||
@ -935,63 +945,61 @@ const viewActions = computed(() => {
|
||||
})
|
||||
}
|
||||
return actions
|
||||
})
|
||||
}
|
||||
|
||||
const viewModalObj = ref({})
|
||||
|
||||
function createView() {
|
||||
view.value.name = ''
|
||||
view.value.label = ''
|
||||
view.value.icon = ''
|
||||
viewModalObj.value = view.value
|
||||
viewModalObj.value.mode = 'create'
|
||||
showViewModal.value = true
|
||||
}
|
||||
|
||||
function duplicateView() {
|
||||
let label =
|
||||
__(
|
||||
getView(route.query.view, route.params.viewType, props.doctype)?.label,
|
||||
) || getViewType().label
|
||||
view.value.name = ''
|
||||
view.value.label = label + __(' (New)')
|
||||
viewModalObj.value = view.value
|
||||
function duplicateView(v) {
|
||||
v.label = v.label + __(' (New)')
|
||||
viewModalObj.value = v
|
||||
viewModalObj.value.mode = 'duplicate'
|
||||
showViewModal.value = true
|
||||
}
|
||||
|
||||
function editView() {
|
||||
let cView = getView(route.query.view, route.params.viewType, props.doctype)
|
||||
view.value.name = route.query.view
|
||||
view.value.label = __(cView?.label) || getViewType().label
|
||||
view.value.icon = cView?.icon || ''
|
||||
viewModalObj.value = view.value
|
||||
function editView(v) {
|
||||
viewModalObj.value = v
|
||||
viewModalObj.value.mode = 'edit'
|
||||
showViewModal.value = true
|
||||
}
|
||||
|
||||
function publicView() {
|
||||
function publicView(v) {
|
||||
call('crm.fcrm.doctype.crm_view_settings.crm_view_settings.public', {
|
||||
name: route.query.view,
|
||||
value: !view.value.public,
|
||||
name: v.name,
|
||||
value: !v.public,
|
||||
}).then(() => {
|
||||
view.value.public = !view.value.public
|
||||
v.public = !v.public
|
||||
reloadView()
|
||||
list.value.reload()
|
||||
})
|
||||
}
|
||||
|
||||
function pinView() {
|
||||
function pinView(v) {
|
||||
call('crm.fcrm.doctype.crm_view_settings.crm_view_settings.pin', {
|
||||
name: route.query.view,
|
||||
value: !view.value.pinned,
|
||||
name: v.name,
|
||||
value: !v.pinned,
|
||||
}).then(() => {
|
||||
view.value.pinned = !view.value.pinned
|
||||
v.pinned = !v.pinned
|
||||
reloadView()
|
||||
list.value.reload()
|
||||
})
|
||||
}
|
||||
|
||||
function deleteView(close) {
|
||||
function deleteView(v, close) {
|
||||
call('crm.fcrm.doctype.crm_view_settings.crm_view_settings.delete', {
|
||||
name: route.query.view,
|
||||
name: v.name,
|
||||
}).then(() => {
|
||||
router.push({ name: route.name })
|
||||
reloadView()
|
||||
list.value.reload()
|
||||
})
|
||||
close()
|
||||
}
|
||||
@ -1020,6 +1028,7 @@ function saveView() {
|
||||
load_default_columns: view.value.load_default_columns,
|
||||
}
|
||||
viewModalObj.value = view.value
|
||||
viewModalObj.value.mode = 'edit'
|
||||
showViewModal.value = true
|
||||
}
|
||||
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
:show="open"
|
||||
:placement="popoverPlacement"
|
||||
>
|
||||
<template #target>
|
||||
<MenuButton as="div">
|
||||
<slot v-if="$slots.default" v-bind="{ open }" />
|
||||
<template #target="{ togglePopover }">
|
||||
<MenuButton as="template">
|
||||
<slot v-if="$slots.default" v-bind="{ open, togglePopover }" />
|
||||
<Button v-else :active="open" v-bind="button">
|
||||
{{ button ? button?.label || null : 'Options' }}
|
||||
</Button>
|
||||
@ -17,13 +17,18 @@
|
||||
<template #body>
|
||||
<div
|
||||
class="rounded-lg bg-white shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
:class="{
|
||||
'mt-2': ['bottom', 'left', 'right'].includes(placement),
|
||||
'ml-2': placement == 'right-start',
|
||||
}"
|
||||
>
|
||||
<MenuItems
|
||||
class="mt-2 min-w-40 divide-y divide-gray-100"
|
||||
class="min-w-40 divide-y divide-gray-100"
|
||||
:class="{
|
||||
'left-0 origin-top-left': placement == 'left',
|
||||
'right-0 origin-top-right': placement == 'right',
|
||||
'inset-x-0 origin-top': placement == 'center',
|
||||
'mt-0 origin-top-right': placement == 'right-start',
|
||||
}"
|
||||
>
|
||||
<div v-for="group in groups" :key="group.key" class="p-1.5">
|
||||
@ -38,34 +43,36 @@
|
||||
:key="item.label"
|
||||
v-slot="{ active }"
|
||||
>
|
||||
<component
|
||||
v-if="item.component"
|
||||
:is="item.component"
|
||||
:active="active"
|
||||
/>
|
||||
<button
|
||||
v-else
|
||||
:class="[
|
||||
active ? 'bg-gray-100' : 'text-gray-800',
|
||||
'group flex h-7 w-full items-center rounded px-2 text-base',
|
||||
]"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<FeatherIcon
|
||||
v-if="item.icon && typeof item.icon === 'string'"
|
||||
:name="item.icon"
|
||||
class="mr-2 h-4 w-4 flex-shrink-0 text-gray-700"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<slot name="item" v-bind="{ item, active }">
|
||||
<component
|
||||
class="mr-2 h-4 w-4 flex-shrink-0 text-gray-700"
|
||||
v-else-if="item.icon"
|
||||
:is="item.icon"
|
||||
v-if="item.component"
|
||||
:is="item.component"
|
||||
:active="active"
|
||||
/>
|
||||
<span class="whitespace-nowrap">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
:class="[
|
||||
active ? 'bg-gray-100' : 'text-gray-800',
|
||||
'group flex h-7 w-full items-center rounded px-2 text-base',
|
||||
]"
|
||||
@click="item.onClick"
|
||||
>
|
||||
<FeatherIcon
|
||||
v-if="item.icon && typeof item.icon === 'string'"
|
||||
:name="item.icon"
|
||||
class="mr-2 h-4 w-4 flex-shrink-0 text-gray-700"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<component
|
||||
class="mr-2 h-4 w-4 flex-shrink-0 text-gray-700"
|
||||
v-else-if="item.icon"
|
||||
:is="item.icon"
|
||||
/>
|
||||
<span class="whitespace-nowrap">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</button>
|
||||
</slot>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
@ -130,6 +137,7 @@ const popoverPlacement = computed(() => {
|
||||
if (props.placement === 'left') return 'bottom-start'
|
||||
if (props.placement === 'right') return 'bottom-end'
|
||||
if (props.placement === 'center') return 'bottom-center'
|
||||
if (props.placement === 'right-start') return 'right-start'
|
||||
return 'bottom'
|
||||
})
|
||||
|
||||
@ -140,6 +148,7 @@ function normalizeDropdownItem(option) {
|
||||
}
|
||||
|
||||
return {
|
||||
name: option.name,
|
||||
label: option.label,
|
||||
icon: option.icon,
|
||||
group: option.group,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user