Files
mixtape/zero.Backoffice.UI/app/modules/pages/_pages.vue
T
2021-12-20 00:39:39 +01:00

384 lines
9.3 KiB
Vue

<template>
<div class="page-container">
<div ref="scrollable" class="app-tree" v-resizable="resizable">
<ui-tree ref="tree" :get="getItems" :config="treeConfig" :active="id" header="Pages" @setactive="onActiveSet">
<template v-slot:actions="props">
<template v-if="!props.item || props.id !== 'recyclebin'">
<ui-dropdown-button label="@ui.create" icon="fth-plus" @click="create(props.item)" />
<ui-dropdown-button v-if="props.item" label="@ui.move.title" icon="fth-corner-down-right" @click="move(props.item)" />
<ui-dropdown-button v-if="props.item" label="@ui.copy.title" icon="fth-copy" @click="copy(props.item)" />
<ui-dropdown-button label="@ui.sort.title" icon="fth-arrow-down" @click="sort(props.item)" />
<ui-dropdown-separator v-if="props.item" />
<ui-dropdown-button v-if="props.item" label="@ui.delete" icon="fth-trash" @click="remove(props.item)" />
</template>
</template>
</ui-tree>
<div class="app-tree-resizable ui-resizable"></div>
</div>
<router-view v-if="!isOverview"></router-view>
<div v-if="isOverview" class="page-overview">
<button type="button" @click="action.action(action)" v-for="action in actions" :key="action.alias" class="page-overview-action">
<div class="page-overview-action-icon">
<ui-icon :symbol="action.icon" :size="24" />
</div>
<p class="page-overview-action-text">
<strong v-localize="'@page.overview.actions.' + action.alias"></strong>
<br>
<span v-localize="{ key: '@page.overview.actions.' + action.alias + '_text', tokens: action.tokens }"></span>
</p>
</button>
</div>
</div>
</template>
<script>
import PagesApi from 'zero/api/pages.js'
import Overlay from 'zero/helpers/overlay.js'
import CreateOverlay from './overlays/create.vue'
import SortOverlay from './overlays/sort.vue'
import MoveOverlay from './overlays/move.vue'
import CopyOverlay from './overlays/copy.vue'
import EventHub from 'zero/helpers/eventhub.js'
import Notification from 'zero/helpers/notification.js'
import Strings from 'zero/helpers/strings.js';
export default {
data: () => ({
page: true,
cache: {},
resizable: {
axis: 'x',
min: 260,
max: 520,
save: 'page-tree',
handle: '.ui-resizable'
},
actions: [],
treeConfig: {
}
}),
//watch: {
// '$route'()
// {
// var $ela = document.querySelector('.app-tree .ui-tree-item.is-active');
// var centerOffset = this.$el.offsetHeight * 0.5 - $ela.offsetHeight * 0.5;
// this.$nextTick(() =>
// {
// this.$el.scrollTo({ top: $ela.offsetTop, behavior: 'smooth' });
// });
// console.info({
// elOffsetHeight: this.$el.offsetHeight,
// aOffsetHeight: $ela.offsetHeight,
// aOffsetTop: $ela.offsetTop,
// centerOffset,
// scrollTop: $ela.offsetTop - centerOffset
// });
// }
//},
computed: {
id()
{
return this.$route.params.id;
},
isOverview()
{
return !this.$route.params.id && !this.$route.params.type && this.$route.name !== 'recyclebin';
}
},
mounted()
{
this.buildActions();
EventHub.$off('page.update');
EventHub.$on('page.update', page =>
{
this.cache = [];
this.$refs.tree.refresh();
});
},
methods: {
onActiveSet(val)
{
this.$nextTick(() =>
{
let container = this.$refs.scrollable;
let child = val.$el;
let centerOffset = container.offsetHeight * 0.5 - child.offsetHeight * 0.5;
let threshold = container.offsetHeight * 0.1;
let activeRange = {
from: container.scrollTop + threshold,
to: container.scrollTop + container.offsetHeight - threshold
};
let rect = child.getBoundingClientRect();
let scrollTop = rect.top + container.offsetTop;
let scrollTo = child.offsetTop;
//console.info(child.offsetTop + ' [ ' + activeRange.from + ' - ' + activeRange.to + ' ]', rect);
//if (activeRange.from <= scrollTo && activeRange.to >= scrollTo)
//{
// console.info('in range');
// return;
//}
//console.info('scroll to: ' + scrollTo);
container.scrollTo({ top: scrollTo, behavior: 'smooth' });
});
},
getItems(parent)
{
const key = !parent ? '__root' : parent;
if (this.cache[key])
{
return Promise.resolve(this.cache[key]);
}
return PagesApi.getChildren(parent, this.id).then(response =>
{
response.forEach(item =>
{
item.url = {
name: 'page',
params: { id: item.id }
};
if (item.id === "recyclebin")
{
item.hasActions = false;
item.url = {
name: 'recyclebin'
};
}
});
this.cache[key] = response;
return response;
});
},
// opens dialog for creating a new page
create(parent)
{
Overlay.open({
component: CreateOverlay,
width: 480,
parent: parent
}).then(() =>
{
}, () =>
{
});
},
sort(item)
{
return Overlay.open({
component: SortOverlay,
display: 'editor',
model: item
}).then(value =>
{
EventHub.$emit('page.sort', value);
EventHub.$emit('page.update');
});
},
move(item)
{
return Overlay.open({
component: MoveOverlay,
display: 'editor',
model: item
}).then(value =>
{
EventHub.$emit('page.move', value);
EventHub.$emit('page.update');
});
},
copy(item)
{
console.info(item);
return Overlay.open({
component: CopyOverlay,
display: 'editor',
model: item
}).then(value =>
{
EventHub.$emit('page.update');
this.$router.push({
name: 'page',
params: { id: value.id }
});
});
},
remove(item)
{
Overlay.confirmDelete(item.name, '@deleteoverlay.page_text').then(opts =>
{
opts.state('loading');
PagesApi.delete(item.id).then(response =>
{
if (response.success)
{
opts.state('success');
opts.hide();
EventHub.$emit('page.delete', response.model);
EventHub.$emit('page.update');
Notification.success('@deleteoverlay.success', '@deleteoverlay.page_success_text');
}
else
{
opts.errors(response.errors);
}
});
});
},
buildActions()
{
this.actions = [];
var instance = this;
let lastEditedPageId = localStorage.getItem('zero.last-page.' + zero.appId);
if (lastEditedPageId)
{
PagesApi.getById(lastEditedPageId).then(res =>
{
this.actions.push({
alias: 'continue',
icon: 'fth-corner-down-right',
tokens: {
page: res.entity.name
},
action()
{
instance.$router.push({
name: 'page',
params: { id: res.entity.id }
});
}
});
});
}
this.actions.push({
alias: 'new',
icon: 'fth-plus',
tokens: {
root: 'Home'
},
action()
{
instance.create();
}
});
this.actions.push({
alias: 'history',
icon: 'fth-clock',
action()
{
Notification.error('Not implemented', 'Page editing history has not been implemented yet');
}
});
}
}
}
</script>
<style lang="scss">
.page-container
{
display: grid;
grid-template-columns: auto 1fr;
gap: 2px;
justify-content: stretch;
height: calc(100vh - 70px);
}
.page-overview
{
display: flex;
flex-direction: column;
margin-left: 80px;
padding-top: 115px;
}
.page-overview-action
{
color: var(--color-text);
font-size: var(--font-size);
display: grid;
grid-template-columns: auto 1fr;
gap: 35px;
align-items: center;
}
.page-overview-action + .page-overview-action
{
margin-top: var(--padding);
}
.page-overview-action-icon
{
display: inline-flex;
justify-content: center;
align-items: center;
width: 90px;
height: 90px;
background: var(--color-box);
border-radius: var(--radius);
box-shadow: var(--shadow-short);
}
.page-overview-action-text
{
line-height: 1.3;
color: var(--color-text-dim);
}
.page-overview-action-text strong
{
display: inline-block;
margin-bottom: 8px;
color: var(--color-text);
font-size: var(--font-size-l);
}
</style>