fix: allow adding/removing/editing tab in FieldLayoutEditor
This commit is contained in:
parent
38565a14f6
commit
13f68dc81a
@ -1,11 +1,83 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="flex flex-col gap-2">
|
||||||
<Draggable :list="sections" item-key="label" class="flex flex-col gap-5.5">
|
<div
|
||||||
|
class="flex justify-between items-center gap-1 text-base bg-surface-gray-2 rounded py-2 px-2.5"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<Draggable
|
||||||
|
v-if="tabs.length && !tabs[tabIndex].no_tabs"
|
||||||
|
:list="tabs"
|
||||||
|
item-key="label"
|
||||||
|
class="flex items-center gap-1"
|
||||||
|
@end="(e) => (tabIndex = e.newIndex)"
|
||||||
|
>
|
||||||
|
<template #item="{ element: tab, index: i }">
|
||||||
|
<div
|
||||||
|
class="cursor-pointer"
|
||||||
|
:class="[
|
||||||
|
tabIndex == i
|
||||||
|
? 'text-ink-gray-9 border border-outline-gray-modals bg-surface-white rounded shadow-sm'
|
||||||
|
: 'text-ink-gray-5',
|
||||||
|
tab.editingLabel ? 'p-1' : 'px-2 py-1',
|
||||||
|
]"
|
||||||
|
@click="tabIndex = i"
|
||||||
|
>
|
||||||
|
<div @dblclick="() => (tab.editingLabel = true)">
|
||||||
|
<div v-if="!tab.editingLabel" class="flex items-center gap-2">
|
||||||
|
{{ __(tab.label) || __('Untitled') }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex gap-1 items-center">
|
||||||
|
<Input
|
||||||
|
v-model="tab.label"
|
||||||
|
@keydown.enter="tab.editingLabel = false"
|
||||||
|
@blur="tab.editingLabel = false"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
v-if="tab.editingLabel"
|
||||||
|
icon="check"
|
||||||
|
variant="ghost"
|
||||||
|
@click="tab.editingLabel = false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Draggable>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
class="!h-6.5 !text-ink-gray-5 hover:!text-ink-gray-9"
|
||||||
|
@click="addTab"
|
||||||
|
:label="__('Add Tab')"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FeatherIcon name="plus" class="h-4" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Dropdown
|
||||||
|
v-if="tabs.length && !tabs[tabIndex].no_tabs"
|
||||||
|
:options="getTabOptions(tabs[tabIndex])"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<Button variant="ghost">
|
||||||
|
<FeatherIcon name="more-horizontal" class="h-4" />
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
<div v-show="tabIndex == i" v-for="(tab, i) in tabs" :key="tab.label">
|
||||||
|
<Draggable
|
||||||
|
:list="tab.sections"
|
||||||
|
item-key="label"
|
||||||
|
class="flex flex-col gap-5.5"
|
||||||
|
>
|
||||||
<template #item="{ element: section }">
|
<template #item="{ element: section }">
|
||||||
<div class="flex flex-col gap-1.5 p-2.5 bg-surface-gray-2 rounded">
|
<div class="flex flex-col gap-1.5 p-2.5 bg-surface-gray-2 rounded">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div
|
<div
|
||||||
class="flex h-7 max-w-fit cursor-pointer items-center gap-2 text-base font-medium leading-4 text-ink-gray-9"
|
class="flex h-7 max-w-fit cursor-pointer items-center gap-2 text-base font-medium leading-4 text-ink-gray-9"
|
||||||
|
@dblclick="() => (section.editingLabel = true)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="!section.editingLabel"
|
v-if="!section.editingLabel"
|
||||||
@ -34,7 +106,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Dropdown :options="getOptions(section)">
|
<Dropdown :options="getSectionOptions(section)">
|
||||||
<template #default>
|
<template #default>
|
||||||
<Button variant="ghost">
|
<Button variant="ghost">
|
||||||
<FeatherIcon name="more-horizontal" class="h-4" />
|
<FeatherIcon name="more-horizontal" class="h-4" />
|
||||||
@ -109,7 +181,7 @@
|
|||||||
variant="subtle"
|
variant="subtle"
|
||||||
:label="__('Add Section')"
|
:label="__('Add Section')"
|
||||||
@click="
|
@click="
|
||||||
sections.push({
|
tabs[tabIndex].sections.push({
|
||||||
label: __('New Section'),
|
label: __('New Section'),
|
||||||
opened: true,
|
opened: true,
|
||||||
fields: [],
|
fields: [],
|
||||||
@ -122,19 +194,22 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
|
import Autocomplete from '@/components/frappe-ui/Autocomplete.vue'
|
||||||
import DragVerticalIcon from '@/components/Icons/DragVerticalIcon.vue'
|
import DragVerticalIcon from '@/components/Icons/DragVerticalIcon.vue'
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
import { Dropdown, createResource } from 'frappe-ui'
|
import { Dropdown, createResource } from 'frappe-ui'
|
||||||
import { computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
sections: Object,
|
tabs: Object,
|
||||||
doctype: String,
|
doctype: String,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const tabIndex = ref(0)
|
||||||
|
|
||||||
const restrictedFieldTypes = [
|
const restrictedFieldTypes = [
|
||||||
'Table',
|
'Table',
|
||||||
'Geolocation',
|
'Geolocation',
|
||||||
@ -159,12 +234,43 @@ const fields = createResource({
|
|||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function addTab() {
|
||||||
|
if (props.tabs.length == 1 && props.tabs[0].no_tabs) {
|
||||||
|
delete props.tabs[0].no_tabs
|
||||||
|
return
|
||||||
|
}
|
||||||
|
props.tabs.push({ label: __('New Tab'), sections: [] })
|
||||||
|
tabIndex.value = props.tabs.length ? props.tabs.length - 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
function addField(section, field) {
|
function addField(section, field) {
|
||||||
if (!field) return
|
if (!field) return
|
||||||
section.fields.push(field)
|
section.fields.push(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOptions(section) {
|
function getTabOptions(tab) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Edit',
|
||||||
|
icon: 'edit',
|
||||||
|
onClick: () => (tab.editingLabel = true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Remove tab',
|
||||||
|
icon: 'trash-2',
|
||||||
|
onClick: () => {
|
||||||
|
if (props.tabs.length == 1) {
|
||||||
|
props.tabs[0].no_tabs = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
props.tabs.splice(tabIndex.value, 1)
|
||||||
|
tabIndex.value = tabIndex.value ? tabIndex.value - 1 : 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSectionOptions(section) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: 'Edit',
|
label: 'Edit',
|
||||||
@ -178,35 +284,68 @@ function getOptions(section) {
|
|||||||
onClick: () => (section.collapsible = !section.collapsible),
|
onClick: () => (section.collapsible = !section.collapsible),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: section.hideLabel ? 'Show Label' : 'Hide Label',
|
label: section.hideLabel ? 'Show label' : 'Hide label',
|
||||||
icon: section.hideLabel ? 'eye' : 'eye-off',
|
icon: section.hideLabel ? 'eye' : 'eye-off',
|
||||||
onClick: () => (section.hideLabel = !section.hideLabel),
|
onClick: () => (section.hideLabel = !section.hideLabel),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: section.hideBorder ? 'Show Border' : 'Hide Border',
|
label: section.hideBorder ? 'Show border' : 'Hide border',
|
||||||
icon: 'minus',
|
icon: 'minus',
|
||||||
onClick: () => (section.hideBorder = !section.hideBorder),
|
onClick: () => (section.hideBorder = !section.hideBorder),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Add Column',
|
label: 'Add column',
|
||||||
icon: 'columns',
|
icon: 'columns',
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
(section.columns = section.columns ? section.columns + 1 : 4),
|
(section.columns = section.columns ? section.columns + 1 : 4),
|
||||||
condition: () => !section.columns || section.columns < 4,
|
condition: () => !section.columns || section.columns < 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Remove Column',
|
label: 'Remove column',
|
||||||
icon: 'columns',
|
icon: 'columns',
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
(section.columns = section.columns ? section.columns - 1 : 2),
|
(section.columns = section.columns ? section.columns - 1 : 2),
|
||||||
condition: () => !section.columns || section.columns > 1,
|
condition: () => !section.columns || section.columns > 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Remove Section',
|
label: 'Remove section',
|
||||||
icon: 'trash-2',
|
icon: 'trash-2',
|
||||||
onClick: () => props.sections.splice(props.sections.indexOf(section), 1),
|
onClick: () => {
|
||||||
|
let currentTab = props.tabs[tabIndex.value]
|
||||||
|
currentTab.sections.splice(currentTab.sections.indexOf(section), 1)
|
||||||
|
},
|
||||||
condition: () => section.editable !== false,
|
condition: () => section.editable !== false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Move to previous tab',
|
||||||
|
icon: 'trash-2',
|
||||||
|
onClick: () => {
|
||||||
|
let previousTab = props.tabs[tabIndex.value - 1]
|
||||||
|
previousTab.sections.push(section)
|
||||||
|
props.tabs[tabIndex.value].sections.splice(
|
||||||
|
props.tabs[tabIndex.value].sections.indexOf(section),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
tabIndex.value -= 1
|
||||||
|
},
|
||||||
|
condition: () =>
|
||||||
|
section.editable !== false && props.tabs[tabIndex.value - 1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Move to next tab',
|
||||||
|
icon: 'trash-2',
|
||||||
|
onClick: () => {
|
||||||
|
let nextTab = props.tabs[tabIndex.value + 1]
|
||||||
|
nextTab.sections.push(section)
|
||||||
|
props.tabs[tabIndex.value].sections.splice(
|
||||||
|
props.tabs[tabIndex.value].sections.indexOf(section),
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
tabIndex.value += 1
|
||||||
|
},
|
||||||
|
condition: () =>
|
||||||
|
section.editable !== false && props.tabs[tabIndex.value + 1],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
<div v-if="tabs?.data">
|
<div v-if="tabs?.data">
|
||||||
<FieldLayoutEditor
|
<FieldLayoutEditor
|
||||||
v-if="!preview"
|
v-if="!preview"
|
||||||
:sections="tabs.data"
|
:tabs="tabs.data"
|
||||||
:doctype="_doctype"
|
:doctype="_doctype"
|
||||||
/>
|
/>
|
||||||
<FieldLayout v-else :tabs="tabs.data" :data="{}" :modal="true" />
|
<FieldLayout v-else :tabs="tabs.data" :data="{}" :modal="true" />
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
<div v-if="tabs?.data">
|
<div v-if="tabs?.data">
|
||||||
<FieldLayoutEditor
|
<FieldLayoutEditor
|
||||||
v-if="!preview"
|
v-if="!preview"
|
||||||
:sections="tabs.data"
|
:tabs="tabs.data"
|
||||||
:doctype="_doctype"
|
:doctype="_doctype"
|
||||||
/>
|
/>
|
||||||
<FieldLayout v-else :tabs="tabs.data" :data="{}" />
|
<FieldLayout v-else :tabs="tabs.data" :data="{}" />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user