auto open tree when an item is active

This commit is contained in:
2020-05-20 11:19:43 +02:00
parent 0072f766e8
commit f2c8e8af05
9 changed files with 189 additions and 82 deletions
+45 -2
View File
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using zero.Core.Database.Indexes;
using zero.Core.Entities;
using zero.Core.Extensions;
using zero.Core.Options;
@@ -27,10 +28,11 @@ namespace zero.Core.Api
/// <inheritdoc />
public async Task<IList<TreeItem>> GetChildren(string parentId = null)
public async Task<IList<TreeItem>> GetChildren(string parentId = null, string activeId = null)
{
IList<TreeItem> items = new List<TreeItem>();
IReadOnlyCollection<PageType> pageTypes = Options.Pages.GetAllItems();
string[] openIds = new string[0] { };
using (IAsyncDocumentSession session = Backoffice.Raven.OpenAsyncSession())
{
@@ -40,6 +42,20 @@ namespace zero.Core.Api
.WhereIf(x => x.ParentId == parentId, !parentId.IsNullOrEmpty(), x => x.ParentId == null)
.ToListAsync();
if (!activeId.IsNullOrEmpty())
{
Pages_ByHierarchy.Result result = await session.Query<Pages_ByHierarchy.Result, Pages_ByHierarchy>()
.ProjectInto<Pages_ByHierarchy.Result>()
.Include<Pages_ByHierarchy.Result, Page>(x => x.Path.Select(p => p.Id))
.ForApp(Backoffice.AppId)
.FirstOrDefaultAsync(x => x.Id == activeId);
if (result != null)
{
openIds = result.Path.Select(x => x.Id).ToArray(); // .Union(new string[1] { activeId })
}
}
foreach (Page page in pages)
{
PageType pageType = pageTypes.FirstOrDefault(x => x.Alias == page.PageTypeAlias);
@@ -58,6 +74,7 @@ namespace zero.Core.Api
ParentId = page.ParentId,
Sort = page.Sort,
Icon = pageType.Icon,
IsOpen = openIds.Contains(page.Id),
IsInactive = !page.IsActive
});
}
@@ -78,6 +95,27 @@ namespace zero.Core.Api
return items;
}
/// <inheritdoc />
public async Task<IList<Page>> GetHierarchy(string id)
{
using (IAsyncDocumentSession session = Backoffice.Raven.OpenAsyncSession())
{
Pages_ByHierarchy.Result result = await session.Query<Pages_ByHierarchy.Result, Pages_ByHierarchy>()
.ProjectInto<Pages_ByHierarchy.Result>()
.Include<Pages_ByHierarchy.Result, Page>(x => x.Path.Select(p => p.Id))
.ForApp(Backoffice.AppId)
.FirstOrDefaultAsync(x => x.Id == id);
if (result == null)
{
return new List<Page>();
}
return (await session.LoadAsync<Page>(result.Path.Select(x => x.Id))).Select(x => x.Value).ToList();
}
}
}
@@ -86,6 +124,11 @@ namespace zero.Core.Api
/// <summary>
/// Get all children for the current parent page (or root if empty)
/// </summary>
Task<IList<TreeItem>> GetChildren(string parentId = null);
Task<IList<TreeItem>> GetChildren(string parentId = null, string activeId = null);
/// <summary>
/// Get hierarchy for a page
/// </summary>
Task<IList<Page>> GetHierarchy(string id);
}
}
@@ -0,0 +1,53 @@
using Raven.Client.Documents.Indexes;
using System;
using System.Collections.Generic;
using System.Linq;
using zero.Core.Entities;
namespace zero.Core.Database.Indexes
{
public class Pages_ByHierarchy : AbstractIndexCreationTask<Page, Pages_ByHierarchy.Result>
{
public class Result : IZeroIdEntity, IAppAwareEntity, IZeroDbConventions
{
public string Id { get; set; }
public string AppId { get; set; }
public string Name { get; set; }
public List<PathResult> Path { get; set; } = new List<PathResult>();
}
public class PathResult
{
public string Id { get; set; }
public string Name { get; set; }
}
public Pages_ByHierarchy()
{
Map = items => items.Select(item => new Result
{
Id = item.Id,
Name = item.Name,
AppId = item.AppId,
Path = Recurse(item, x => LoadDocument<Page>(x.ParentId))
.Where(x => x != null && x.Id != null && x.Id != item.Id)
.Reverse()
.Select(current => new PathResult()
{
Id = current.Id,
Name = current.Name
})
.ToList()
});
StoreAllFields(FieldStorage.Yes);
//Index(x => x.ChannelId, FieldIndexing.Exact);
}
}
}
-46
View File
@@ -1,46 +0,0 @@
[
{
"id": "10",
"parentId": "1",
"sort": 0,
"name": "Products",
"icon": "fth-folder",
"hasChildren": false
},
{
"id": "11",
"parentId": "1",
"sort": 1,
"name": "Checkout",
"icon": "fth-folder",
"hasChildren": false
},
{
"id": "12",
"parentId": "1",
"sort": 2,
"name": "About us",
"icon": "fth-folder",
"modifier": {
"name": "Redirect",
"icon": "fth-arrow-up-right"
},
"hasChildren": false
},
{
"id": "13",
"parentId": "1",
"sort": 3,
"name": "Support",
"icon": "fth-folder",
"hasChildren": true
},
{
"id": "14",
"parentId": "1",
"sort": 4,
"name": "News",
"icon": "fth-folder",
"hasChildren": false
}
]
-20
View File
@@ -1,20 +0,0 @@
[
{
"id": "1",
"parentId": null,
"sort": 0,
"name": "Home",
"description": "Entry point on the website",
"icon": "fth-home",
"hasChildren": true
},
{
"id": "recyclebin",
"parentId": null,
"sort": 1000,
"name": "@page.recyclebin.name",
"description": "@page.recyclebin.description",
"icon": "fth-trash",
"hasChildren": false
}
]
+38 -5
View File
@@ -11,15 +11,22 @@
<i v-if="item.modifier" :title="item.modifier.name" class="ui-tree-item-modifier" :class="item.modifier.icon"></i>
{{item.name | localize}}
</router-link>
<ui-dot-button class="ui-tree-item-actions" />
<ui-dot-button class="ui-tree-item-actions" v-if="configuration.onActionsRequested" />
</div>
<ui-tree v-if="item.hasChildren && item.isOpen" :get="get" :parent="item.id" :depth="depth + 1" />
<ui-tree v-if="item.hasChildren && item.isOpen" :get="get" :parent="item.id" :depth="depth + 1" :active="active" :config="config" />
</template>
</div>
</template>
<script>
import { each as _each, extend as _extend, debounce as _debounce, isArray as _isArray } from 'underscore';
const defaultConfig = {
// return actions for an item
onActionsRequested: null
};
export default {
name: 'uiTree',
@@ -28,6 +35,10 @@
type: Number,
default: 0
},
active: {
type: String,
default: null
},
parent: {
type: String,
default: null
@@ -35,27 +46,49 @@
get: {
type: Function,
required: true
},
config: {
type: Object,
default: () =>
{
return defaultConfig;
}
}
},
watch: {
active(val)
{
console.info('active: ' + val);
}
},
data: () => ({
items: [],
status: 'none'
status: 'none',
actions: [],
configuration: {}
}),
created ()
mounted()
{
this.initialize();
this.load(this.parent);
},
methods: {
initialize()
{
this.configuration = _extend(defaultConfig, this.config);
},
// loads children of the given parent id or on root if empty
load(parent)
{
this.setStatus('loading', this.items);
let promise = this.get(parent);
let promise = this.get(parent, this.active);
promise.then(response =>
{
+2 -2
View File
@@ -3,11 +3,11 @@
<ui-header-bar :title="'Page ' + $route.params.id" :on-back="onBack">
<ui-dropdown>
<template v-slot:button>
<ui-button type="light" label="Actions" caret="down" />
<ui-button type="white" label="Actions" caret="down" />
</template>
<ui-dropdown-list v-model="actions" :action="actionSelected" />
</ui-dropdown>
<ui-button type="light" label="Preview" icon="fth-eye" />
<ui-button type="white" label="Preview" icon="fth-eye" />
<ui-button label="Save" />
</ui-header-bar>
</div>
+46 -3
View File
@@ -4,7 +4,7 @@
<ui-header-bar title="Pages" :back-button="false">
<ui-dot-button />
</ui-header-bar>
<ui-tree :get="getItems" />
<ui-tree :get="getItems" :config="treeConfig" :active="id" />
<div class="page-container-tree-resizable ui-resizable"></div>
</div>
@@ -39,11 +39,18 @@
save: 'page-tree',
handle: '.ui-resizable'
},
actions: []
actions: [],
treeConfig: {
}
}),
computed: {
id()
{
return this.$route.params.id;
},
isOverview()
{
return !this.$route.params.id && this.$route.name !== 'recyclebin';
@@ -77,6 +84,42 @@
name: 'history'
}
});
this.treeConfig.onActionsRequested = item =>
{
let actions = [];
actions.push({
name: 'Create',
icon: 'fth-plus'
});
actions.push({
name: 'Move',
icon: 'fth-corner-down-right'
});
actions.push({
name: 'Copy',
icon: 'fth-copy',
disabled: true
});
actions.push({
name: 'Sort',
icon: 'fth-arrow-down'
});
actions.push({
type: 'separator'
});
actions.push({
name: 'Delete',
icon: 'fth-x',
action(item, dropdown)
{
dropdown.hide();
}
});
return actions;
};
},
@@ -91,7 +134,7 @@
return Promise.resolve(this.cache[key]);
}
return PageTreeApi.getChildren(parent).then(response =>
return PageTreeApi.getChildren(parent, this.id).then(response =>
{
response.forEach(item =>
{
+3 -2
View File
@@ -3,11 +3,12 @@ import Axios from 'axios';
export default {
// get all pages with a certain parent (can be empty)
getChildren(parent)
getChildren(parent, active)
{
return Axios.get('pageTree/getChildren', {
params: {
parent: parent
parent: parent,
active: active
}
}).then(res => Promise.resolve(res.data));
}
+2 -2
View File
@@ -15,9 +15,9 @@ namespace zero.Web.Controllers
}
public async Task<IActionResult> GetChildren(string parent = null)
public async Task<IActionResult> GetChildren(string parent = null, string active = null)
{
return AsJson(await Api.GetChildren(parent));
return AsJson(await Api.GetChildren(parent, active));
}
}
}