2021-01-31 02:43:13 +13:00
|
|
|
<template>
|
|
|
|
|
|
|
|
<span>
|
2021-03-05 12:51:41 +13:00
|
|
|
<q-page-sticky position="top-left" class="treeSearchWrapper">
|
|
|
|
|
2021-03-04 13:27:07 +13:00
|
|
|
<q-input ref="treeFilter" dark filled v-model="treeFilter" label="Filter document tree...">
|
2021-03-05 12:51:41 +13:00
|
|
|
<template v-slot:append>
|
2021-01-31 02:43:13 +13:00
|
|
|
<q-icon name="mdi-text-search" />
|
|
|
|
</template>
|
2021-03-05 12:51:41 +13:00
|
|
|
<template v-slot:prepend>
|
|
|
|
<q-icon v-if="treeFilter !== ''" name="clear" class="cursor-pointer text-secondary" @click="resetTreeFilter" />
|
2021-01-31 02:43:13 +13:00
|
|
|
</template>
|
|
|
|
</q-input>
|
2021-03-05 12:51:41 +13:00
|
|
|
</q-page-sticky>
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-03-08 11:07:40 +13:00
|
|
|
<h6 class="projectTitle text-cultured">
|
|
|
|
<span>
|
|
|
|
{{projectName}}
|
|
|
|
<q-tooltip
|
|
|
|
:delay="1000"
|
|
|
|
>
|
|
|
|
This is your currently opened project's name.
|
|
|
|
</q-tooltip>
|
|
|
|
</span>
|
|
|
|
</h6>
|
|
|
|
|
2021-01-31 02:43:13 +13:00
|
|
|
<q-tree
|
2021-02-09 15:21:48 +13:00
|
|
|
class="objectTree q-pa-sm"
|
2021-02-23 11:30:18 +13:00
|
|
|
:nodes="hierarchicalTree"
|
2021-02-21 03:13:37 +13:00
|
|
|
node-key="key"
|
2021-01-31 02:43:13 +13:00
|
|
|
no-connectors
|
|
|
|
ref="tree"
|
|
|
|
dark
|
2021-03-08 11:07:40 +13:00
|
|
|
:duration="200"
|
2021-01-31 02:43:13 +13:00
|
|
|
:filter="treeFilter"
|
|
|
|
:selected.sync="selectedTreeNode"
|
2021-02-26 14:50:46 +13:00
|
|
|
:expanded.sync="expandedTreeNodes"
|
2021-03-08 11:07:40 +13:00
|
|
|
no-nodes-label="Loading your project..."
|
|
|
|
no-results-label="Nothing matches your request"
|
2021-01-31 02:43:13 +13:00
|
|
|
>
|
|
|
|
<template v-slot:default-header="prop">
|
2021-03-08 11:07:40 +13:00
|
|
|
<div class="row items-center col-grow"
|
|
|
|
@click.stop.prevent="processNodeClick(prop.node)"
|
|
|
|
@click.stop.prevent.middle="processNodeLabelMiddleClick(prop.node)"
|
|
|
|
>
|
2021-02-23 11:30:18 +13:00
|
|
|
<div class="documentLabel"
|
|
|
|
:style="`color: ${prop.node.color}`"
|
|
|
|
>
|
2021-02-26 14:50:46 +13:00
|
|
|
<q-icon
|
2021-03-03 14:10:05 +13:00
|
|
|
:style="`color: ${determineNodeColor(prop.node)}; width: 22px !important;`"
|
2021-02-26 14:50:46 +13:00
|
|
|
:size="(prop.node.icon.includes('fas')? '16px': '21px')"
|
|
|
|
:name="prop.node.icon"
|
|
|
|
class="q-mr-sm self-center" />
|
2021-01-31 14:46:23 +13:00
|
|
|
{{ prop.node.label }}
|
|
|
|
<span
|
2021-02-23 11:30:18 +13:00
|
|
|
class="text-primary text-weight-medium q-ml-xs"
|
2021-01-31 14:46:23 +13:00
|
|
|
v-if="prop.node.isRoot">
|
2021-03-05 12:51:41 +13:00
|
|
|
({{prop.node.allCount}})
|
|
|
|
<q-tooltip
|
|
|
|
:delay="1000"
|
|
|
|
>
|
2021-03-08 11:07:40 +13:00
|
|
|
Document count: <span class="text-bold text-satin-sheen-gold-dark">{{prop.node.documentCount}}</span>
|
2021-03-05 12:51:41 +13:00
|
|
|
<br>
|
2021-03-08 11:07:40 +13:00
|
|
|
Category count: <span class="text-bold text-satin-sheen-gold-dark">{{prop.node.categoryCount}}</span>
|
2021-03-05 12:51:41 +13:00
|
|
|
</q-tooltip>
|
2021-01-31 14:46:23 +13:00
|
|
|
</span>
|
2021-01-31 02:43:13 +13:00
|
|
|
<q-badge
|
2021-02-26 14:50:46 +13:00
|
|
|
class="treeBadge"
|
2021-03-03 08:59:56 +13:00
|
|
|
:class="{'noChilden': prop.node.children.length === 0}"
|
2021-02-26 14:50:46 +13:00
|
|
|
v-if="prop.node.sticker"
|
|
|
|
color="primary"
|
|
|
|
outline
|
|
|
|
floating
|
|
|
|
>
|
|
|
|
{{prop.node.sticker}}
|
|
|
|
<q-tooltip
|
|
|
|
:delay="500"
|
|
|
|
>
|
2021-01-31 02:43:13 +13:00
|
|
|
Order priority of the document
|
|
|
|
</q-tooltip>
|
|
|
|
</q-badge>
|
2021-02-23 11:30:18 +13:00
|
|
|
<div class="treeButtonGroup">
|
|
|
|
<q-btn
|
|
|
|
tabindex="-1"
|
2021-03-03 14:10:05 +13:00
|
|
|
v-if="prop.node.children && prop.node.children.length > 0 && !prop.node.isRoot && !prop.node.isTag"
|
2021-02-23 11:30:18 +13:00
|
|
|
round
|
2021-02-26 14:50:46 +13:00
|
|
|
flat
|
2021-02-23 11:30:18 +13:00
|
|
|
dense
|
2021-02-26 14:50:46 +13:00
|
|
|
color="dark"
|
|
|
|
class="z-1 q-ml-sm treeButton treeButton--edit"
|
|
|
|
icon="mdi-pencil"
|
2021-03-05 12:51:41 +13:00
|
|
|
size="10px"
|
2021-02-26 14:50:46 +13:00
|
|
|
@click.stop.prevent="openExistingDocumentRoute(prop.node)"
|
|
|
|
>
|
|
|
|
<q-tooltip
|
|
|
|
:delay="300"
|
2021-02-23 11:30:18 +13:00
|
|
|
>
|
2021-02-26 14:50:46 +13:00
|
|
|
Open/Edit {{ prop.node.label }}
|
2021-02-23 11:30:18 +13:00
|
|
|
</q-tooltip>
|
|
|
|
</q-btn>
|
|
|
|
<q-btn
|
|
|
|
tabindex="-1"
|
2021-03-03 14:10:05 +13:00
|
|
|
v-if="(!prop.node.specialLabel && !prop.node.isRoot) || (prop.node.isRoot && !prop.node.isTag)"
|
2021-02-23 11:30:18 +13:00
|
|
|
round
|
2021-02-26 14:50:46 +13:00
|
|
|
flat
|
2021-02-23 11:30:18 +13:00
|
|
|
dense
|
2021-02-26 14:50:46 +13:00
|
|
|
color="dark"
|
|
|
|
class="z-1 q-ml-sm treeButton treeButton--add"
|
|
|
|
icon="mdi-plus"
|
2021-03-05 12:51:41 +13:00
|
|
|
size="10px"
|
2021-02-26 14:50:46 +13:00
|
|
|
@click.stop.prevent="processNodeNewDocumentButton(prop.node)"
|
|
|
|
>
|
|
|
|
<q-tooltip
|
|
|
|
:delay="300"
|
|
|
|
>
|
|
|
|
Add a new document belonging under {{ prop.node.label }}
|
2021-02-23 11:30:18 +13:00
|
|
|
</q-tooltip>
|
|
|
|
</q-btn>
|
|
|
|
</div>
|
|
|
|
</div>
|
2021-01-31 02:43:13 +13:00
|
|
|
</div>
|
2021-02-23 11:30:18 +13:00
|
|
|
|
2021-01-31 02:43:13 +13:00
|
|
|
</template>
|
|
|
|
</q-tree>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
<q-list>
|
|
|
|
<q-separator
|
|
|
|
color="white"
|
|
|
|
inset
|
|
|
|
class="q-mt-md"
|
|
|
|
/>
|
|
|
|
<q-item
|
|
|
|
v-ripple
|
|
|
|
clickable
|
|
|
|
class="q-mt-md"
|
|
|
|
>
|
|
|
|
<q-item-section avatar>
|
|
|
|
<q-icon :name="menuAddNewItem.icon" />
|
|
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
|
|
{{ menuAddNewItem.label }}
|
|
|
|
</q-item-section>
|
|
|
|
</q-item>
|
|
|
|
|
|
|
|
</q-list>
|
|
|
|
|
|
|
|
-->
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
2021-02-23 11:30:18 +13:00
|
|
|
import { Component, Watch } from "vue-property-decorator"
|
2021-01-31 02:43:13 +13:00
|
|
|
|
|
|
|
import BaseClass from "src/BaseClass"
|
2021-02-28 06:00:57 +13:00
|
|
|
import { I_OpenedDocument, I_ShortenedDocument } from "src/interfaces/I_OpenedDocument"
|
2021-01-31 02:43:13 +13:00
|
|
|
import PouchDB from "pouchdb"
|
2021-02-26 14:50:46 +13:00
|
|
|
import { engageBlueprints, retrieveAllBlueprints } from "src/scripts/databaseManager/blueprintManager"
|
|
|
|
// import { cleanDatabases } from "src/scripts/databaseManager/cleaner"
|
2021-01-31 02:43:13 +13:00
|
|
|
import { I_Blueprint } from "src/interfaces/I_Blueprint"
|
2021-03-03 14:10:05 +13:00
|
|
|
import { extend, colors } from "quasar"
|
|
|
|
import { tagListBuildFromBlueprints } from "src/scripts/utilities/tagListBuilder"
|
2021-03-08 11:07:40 +13:00
|
|
|
import { retrieveCurrentProjectName } from "src/scripts/projectManagement/projectManagent"
|
2021-01-31 02:43:13 +13:00
|
|
|
|
|
|
|
@Component({
|
|
|
|
components: { }
|
|
|
|
})
|
|
|
|
export default class ObjectTree extends BaseClass {
|
2021-02-23 11:30:18 +13:00
|
|
|
/****************************************************************/
|
|
|
|
// KEYBINDS MANAGEMENT
|
|
|
|
/****************************************************************/
|
|
|
|
@Watch("SGET_getCurrentKeyBindData", { deep: true })
|
|
|
|
processKeyPush () {
|
|
|
|
// Focus left tree search
|
|
|
|
if (this.determineKeyBind("focusHierarchicalTree")) {
|
2021-02-21 01:06:21 +13:00
|
|
|
const treeFilterDOM = this.$refs.treeFilter as unknown as HTMLInputElement
|
|
|
|
treeFilterDOM.focus()
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
// Clear input in the left tree search
|
|
|
|
if (this.determineKeyBind("clearInputHierarchicalTree")) {
|
2021-02-21 01:06:21 +13:00
|
|
|
this.resetTreeFilter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/****************************************************************/
|
|
|
|
// GENERIC FUNCTIONALITY
|
|
|
|
/****************************************************************/
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-03-08 11:07:40 +13:00
|
|
|
projectName = ""
|
|
|
|
|
2021-01-31 02:43:13 +13:00
|
|
|
/**
|
2021-02-23 11:30:18 +13:00
|
|
|
* Load all blueprints and build the tree out of them
|
2021-01-31 02:43:13 +13:00
|
|
|
*/
|
2021-02-23 11:30:18 +13:00
|
|
|
async created () {
|
2021-03-08 11:07:40 +13:00
|
|
|
this.projectName = await retrieveCurrentProjectName()
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
// await cleanDatabases()
|
|
|
|
await this.processBluePrints()
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
// Unfuck the rendering by giving the app some time to load first
|
2021-03-04 13:27:07 +13:00
|
|
|
await this.$nextTick()
|
|
|
|
|
|
|
|
this.buildCurrentObjectTree().catch((e) => {
|
|
|
|
console.log(e)
|
|
|
|
})
|
2021-02-23 11:30:18 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
// BLUEPRINT MANAGEMENT
|
|
|
|
/****************************************************************/
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/**
|
|
|
|
* In case any of the blueprints change, reload the whole tree
|
|
|
|
*/
|
2021-01-31 02:43:13 +13:00
|
|
|
@Watch("SGET_allBlueprints", { deep: true })
|
|
|
|
reactToBluePrintRefresh () {
|
2021-02-23 11:30:18 +13:00
|
|
|
this.buildCurrentObjectTree().catch((e) => {
|
|
|
|
console.log(e)
|
|
|
|
})
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/**
|
|
|
|
* Processes all blueprints and redies the store for population of the app
|
|
|
|
*/
|
|
|
|
async processBluePrints (): Promise<void> {
|
|
|
|
await engageBlueprints()
|
|
|
|
|
|
|
|
const allObjectBlueprints = (await retrieveAllBlueprints()).rows.map((blueprint) => {
|
|
|
|
return blueprint.doc
|
|
|
|
}) as I_Blueprint[]
|
|
|
|
|
|
|
|
this.SSET_allBlueprints(allObjectBlueprints)
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/****************************************************************/
|
|
|
|
// HIERARCHICAL TREE - HELPERS AND MODELS
|
|
|
|
/****************************************************************/
|
|
|
|
|
2021-03-08 11:07:40 +13:00
|
|
|
@Watch("$route", { deep: true })
|
|
|
|
async reactToRouteChange () {
|
|
|
|
// Wait for animations
|
|
|
|
await this.sleep(200)
|
|
|
|
if (this.SGET_allOpenedDocuments.docs.length > 0) {
|
|
|
|
const currentDoc = this.findRequestedOrActiveDocument() as unknown as I_OpenedDocument
|
|
|
|
this.selectedTreeNode = currentDoc._id
|
|
|
|
}
|
|
|
|
else {
|
2021-01-31 02:43:13 +13:00
|
|
|
this.selectedTreeNode = null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
2021-01-31 02:43:13 +13:00
|
|
|
@Watch("SGET_allOpenedDocuments", { deep: true })
|
2021-02-28 06:00:57 +13:00
|
|
|
async reactToDocumentListChange (val: { treeAction: boolean, docs: I_OpenedDocument[]}) {
|
|
|
|
if (val.treeAction) {
|
|
|
|
await this.buildCurrentObjectTree()
|
|
|
|
this.buildTreeExpands(val?.docs)
|
|
|
|
this.lastDocsSnapShot = extend(true, [], val.docs)
|
|
|
|
}
|
|
|
|
else if (val.docs.length !== this.lastDocsSnapShot.length) {
|
|
|
|
this.lastDocsSnapShot = extend(true, [], val.docs)
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
|
2021-02-28 06:00:57 +13:00
|
|
|
lastDocsSnapShot:I_OpenedDocument[] = []
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/**
|
|
|
|
* Generic wrapper for adding of new object types to the tree
|
|
|
|
*/
|
|
|
|
menuAddNewItem = {
|
|
|
|
icon: "mdi-plus",
|
|
|
|
label: "Add new object type"
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains all the data for the render in tree
|
|
|
|
*/
|
|
|
|
hierarchicalTree: {children: I_ShortenedDocument[], icon: string, label: string}[] = []
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A resetter for the currently selected node
|
|
|
|
*/
|
2021-03-08 11:07:40 +13:00
|
|
|
selectedTreeNode = null as null | string
|
2021-02-23 11:30:18 +13:00
|
|
|
|
2021-02-26 14:50:46 +13:00
|
|
|
/**
|
|
|
|
* Holds all currently expanded notes
|
|
|
|
*/
|
|
|
|
expandedTreeNodes = []
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/**
|
|
|
|
* Filter model for the tree
|
|
|
|
*/
|
|
|
|
treeFilter = ""
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resets the tree filter and refocuses the search box
|
|
|
|
*/
|
|
|
|
resetTreeFilter () {
|
|
|
|
this.treeFilter = ""
|
|
|
|
const treeFilterDOM = this.$refs.treeFilter as unknown as HTMLInputElement
|
2021-03-03 14:10:05 +13:00
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2021-02-23 11:30:18 +13:00
|
|
|
treeFilterDOM.focus()
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************/
|
|
|
|
// HIERARCHICAL TREE - CONTENT CONSTRUCTION
|
|
|
|
/****************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sort the whole tree via alphabetical and custom numeric order
|
|
|
|
* @param input Hierartchical tree object to sort
|
|
|
|
*/
|
2021-01-31 02:43:13 +13:00
|
|
|
sortDocuments (input: I_ShortenedDocument[]) {
|
|
|
|
input
|
2021-02-23 11:30:18 +13:00
|
|
|
|
|
|
|
// Sort by name
|
2021-01-31 02:43:13 +13:00
|
|
|
.sort((a, b) => a.label.localeCompare(b.label))
|
2021-02-23 11:30:18 +13:00
|
|
|
|
|
|
|
// Sort by custom order
|
2021-01-31 02:43:13 +13:00
|
|
|
.sort((a, b) => {
|
|
|
|
const order1 = a.extraFields.find(e => e.id === "order")?.value
|
|
|
|
const order2 = b.extraFields.find(e => e.id === "order")?.value
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
if (order1 > order2) {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
if (order1 < order2) {
|
|
|
|
return -1
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
|
|
|
|
return 0
|
|
|
|
})
|
|
|
|
|
2021-02-28 06:00:57 +13:00
|
|
|
// Put the number value on top of the list and alphabetical below them
|
|
|
|
input = [
|
|
|
|
...input.filter(e => e.extraFields.find(e => e.id === "order")?.value),
|
|
|
|
...input.filter(e => !e.extraFields.find(e => e.id === "order")?.value)
|
|
|
|
]
|
|
|
|
|
2021-01-31 02:43:13 +13:00
|
|
|
input.forEach((e, i) => {
|
2021-02-23 11:30:18 +13:00
|
|
|
// Run recursive if the node has any children
|
|
|
|
if (e.children.length > 0) {
|
|
|
|
input[i].children = this.sortDocuments(input[i].children)
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
})
|
|
|
|
|
|
|
|
return input
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/**
|
|
|
|
* Builds proper hiearachy for flat array of documents
|
|
|
|
* @param input Non-hierarchical tree to build the hiearachy out of
|
|
|
|
*/
|
2021-01-31 02:43:13 +13:00
|
|
|
buildTreeHierarchy (input: I_ShortenedDocument[]) {
|
|
|
|
const map: number[] = []
|
|
|
|
let node
|
|
|
|
const roots = []
|
|
|
|
let i
|
|
|
|
|
|
|
|
for (i = 0; i < input.length; i += 1) {
|
2021-02-23 11:30:18 +13:00
|
|
|
// Initialize the map
|
|
|
|
map[input[i]._id] = i
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < input.length; i += 1) {
|
|
|
|
node = input[i]
|
|
|
|
if (node.parentDoc !== false) {
|
2021-02-23 11:30:18 +13:00
|
|
|
// If there are any dangling branches check that map[node.parentDoc] exists
|
2021-01-31 02:43:13 +13:00
|
|
|
if (input[map[node.parentDoc]]) {
|
|
|
|
input[map[node.parentDoc]].children.push(node)
|
2021-02-23 11:30:18 +13:00
|
|
|
}
|
|
|
|
else {
|
2021-01-31 02:43:13 +13:00
|
|
|
roots.push(node)
|
|
|
|
}
|
2021-02-23 11:30:18 +13:00
|
|
|
}
|
|
|
|
else {
|
2021-01-31 02:43:13 +13:00
|
|
|
roots.push(node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const sortedRoots = this.sortDocuments(roots)
|
|
|
|
|
|
|
|
return sortedRoots
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
/**
|
|
|
|
* Builds a brand new sparkling hearchy tree out of available data
|
|
|
|
*/
|
2021-01-31 02:43:13 +13:00
|
|
|
async buildCurrentObjectTree () {
|
|
|
|
const allBlueprings = this.SGET_allBlueprints
|
|
|
|
const treeObject: any[] = []
|
2021-03-03 14:10:05 +13:00
|
|
|
let allTreeDocuments: I_ShortenedDocument[] = []
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
// Process all documents, build hieararchy out of the and sort them via name and custom order
|
2021-01-31 02:43:13 +13:00
|
|
|
for (const blueprint of allBlueprings) {
|
|
|
|
const CurrentObjectDB = new PouchDB(blueprint._id)
|
|
|
|
|
|
|
|
const allDocuments = await CurrentObjectDB.allDocs({ include_docs: true })
|
|
|
|
|
|
|
|
const allDocumentsRows = allDocuments.rows
|
|
|
|
.map((singleDocument) => {
|
|
|
|
const doc = singleDocument.doc as unknown as I_ShortenedDocument
|
|
|
|
|
2021-02-09 15:21:48 +13:00
|
|
|
const parentDocID = doc.extraFields.find(e => e.id === "parentDoc")?.value.value as unknown as {_id: string}
|
2021-02-21 01:06:21 +13:00
|
|
|
const color = doc.extraFields.find(e => e.id === "documentColor")?.value as unknown as string
|
|
|
|
const isCategory = doc.extraFields.find(e => e.id === "categorySwitch")?.value as unknown as string
|
2021-01-31 02:43:13 +13:00
|
|
|
|
|
|
|
return {
|
|
|
|
label: doc.extraFields.find(e => e.id === "name")?.value,
|
2021-02-21 01:06:21 +13:00
|
|
|
icon: (isCategory) ? "fas fa-folder-open" : doc.icon,
|
2021-03-05 12:51:41 +13:00
|
|
|
isCategory: !!(isCategory),
|
2021-01-31 02:43:13 +13:00
|
|
|
sticker: doc.extraFields.find(e => e.id === "order")?.value,
|
|
|
|
parentDoc: (parentDocID) ? parentDocID._id : false,
|
2021-02-23 11:30:18 +13:00
|
|
|
handler: this.openExistingDocumentRoute,
|
2021-01-31 02:43:13 +13:00
|
|
|
expandable: true,
|
2021-02-21 01:06:21 +13:00
|
|
|
color: color,
|
2021-01-31 02:43:13 +13:00
|
|
|
type: doc.type,
|
|
|
|
children: [],
|
|
|
|
hasEdits: false,
|
|
|
|
isNew: false,
|
|
|
|
url: doc.url,
|
|
|
|
extraFields: (doc?.extraFields) || [],
|
2021-02-21 03:13:37 +13:00
|
|
|
_id: singleDocument.id,
|
|
|
|
key: singleDocument.id
|
2021-01-31 02:43:13 +13:00
|
|
|
} as I_ShortenedDocument
|
|
|
|
})
|
|
|
|
|
2021-03-05 12:51:41 +13:00
|
|
|
const documentCount = allDocumentsRows.filter(e => !e.isCategory).length
|
|
|
|
const categoryCount = allDocumentsRows.filter(e => e.isCategory).length
|
|
|
|
const allCount = allDocumentsRows.length
|
|
|
|
|
2021-03-03 14:10:05 +13:00
|
|
|
const listCopy: I_ShortenedDocument[] = extend(true, [], allDocumentsRows)
|
|
|
|
allTreeDocuments = [...allTreeDocuments, ...listCopy]
|
2021-01-31 14:46:23 +13:00
|
|
|
|
2021-01-31 02:43:13 +13:00
|
|
|
const hierarchicalTreeContent = this.buildTreeHierarchy(allDocumentsRows)
|
|
|
|
|
|
|
|
const treeRow = {
|
|
|
|
label: blueprint.namePlural,
|
|
|
|
icon: blueprint.icon,
|
2021-01-31 14:46:23 +13:00
|
|
|
order: blueprint.order,
|
2021-01-31 02:43:13 +13:00
|
|
|
_id: blueprint._id,
|
2021-02-21 03:13:37 +13:00
|
|
|
key: blueprint._id,
|
2021-02-23 11:30:18 +13:00
|
|
|
handler: this.addNewObjectRoute,
|
2021-01-31 02:43:13 +13:00
|
|
|
specialLabel: blueprint.nameSingular.toLowerCase(),
|
2021-01-31 14:46:23 +13:00
|
|
|
isRoot: true,
|
2021-03-05 12:51:41 +13:00
|
|
|
allCount: allCount,
|
2021-01-31 14:46:23 +13:00
|
|
|
documentCount: documentCount,
|
2021-03-05 12:51:41 +13:00
|
|
|
categoryCount: categoryCount,
|
2021-01-31 02:43:13 +13:00
|
|
|
children: [
|
|
|
|
...hierarchicalTreeContent,
|
|
|
|
{
|
|
|
|
label: `Add new ${blueprint.nameSingular.toLowerCase()}`,
|
|
|
|
icon: "mdi-plus",
|
2021-02-23 11:30:18 +13:00
|
|
|
handler: this.addNewObjectRoute,
|
|
|
|
children: false,
|
|
|
|
key: `${blueprint._id}_add`,
|
|
|
|
_id: blueprint._id,
|
|
|
|
specialLabel: blueprint.nameSingular.toLowerCase()
|
|
|
|
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
treeObject.push(treeRow)
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
// Sort the top level of the blueprints
|
2021-01-31 14:46:23 +13:00
|
|
|
treeObject.sort((a, b) => {
|
|
|
|
if (a.order < b.order) {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a.order > b.order) {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
})
|
|
|
|
|
2021-03-03 14:10:05 +13:00
|
|
|
const tagList = await tagListBuildFromBlueprints(this.SGET_allBlueprints)
|
|
|
|
|
|
|
|
tagList.forEach((tag: string) => {
|
|
|
|
const tagDocs = allTreeDocuments
|
|
|
|
.filter(doc => {
|
|
|
|
const docTags = doc.extraFields.find(e => e.id === "tags")?.value as unknown as string[]
|
|
|
|
return (docTags && docTags.includes(tag))
|
|
|
|
})
|
|
|
|
.map((doc:I_ShortenedDocument) => {
|
|
|
|
// @ts-ignore
|
|
|
|
doc.key = `${tag}${doc._id}`
|
|
|
|
// @ts-ignore
|
|
|
|
doc.isTag = true
|
|
|
|
return doc
|
|
|
|
})
|
|
|
|
.sort((a, b) => a.label.localeCompare(b.label))
|
|
|
|
|
2021-03-05 12:51:41 +13:00
|
|
|
const documentCount = tagDocs.filter(e => !e.isCategory).length
|
|
|
|
const categoryCount = tagDocs.filter(e => e.isCategory).length
|
|
|
|
const allCount = tagDocs.length
|
|
|
|
|
2021-03-03 14:10:05 +13:00
|
|
|
const tagObject = {
|
|
|
|
label: `${tag}`,
|
|
|
|
icon: "mdi-tag",
|
|
|
|
_id: `tag-${tag}`,
|
|
|
|
key: `tag-${tag}`,
|
2021-03-05 12:51:41 +13:00
|
|
|
allCount: allCount,
|
|
|
|
documentCount: documentCount,
|
|
|
|
categoryCount: categoryCount,
|
2021-03-03 14:10:05 +13:00
|
|
|
isRoot: true,
|
|
|
|
isTag: true,
|
|
|
|
children: tagDocs
|
|
|
|
}
|
|
|
|
treeObject.push(tagObject)
|
|
|
|
})
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
// Assign the finished object to the render model
|
|
|
|
this.hierarchicalTree = treeObject
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
processNodeNewDocumentButton (node: {
|
|
|
|
key: string
|
|
|
|
_id: string
|
|
|
|
children: []
|
|
|
|
type: string
|
|
|
|
isRoot: boolean
|
|
|
|
specialLabel: string|boolean
|
|
|
|
}) {
|
|
|
|
// If this is top level blueprint
|
|
|
|
if (node.isRoot) {
|
|
|
|
// @ts-ignore
|
|
|
|
this.addNewObjectRoute(node)
|
|
|
|
}
|
|
|
|
// If this is a custom document
|
|
|
|
else {
|
|
|
|
const routeObject = {
|
|
|
|
_id: node.type,
|
|
|
|
parent: node._id
|
|
|
|
}
|
|
|
|
// @ts-ignore
|
|
|
|
this.addNewObjectRoute(routeObject)
|
|
|
|
}
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-02-28 06:00:57 +13:00
|
|
|
buildTreeExpands (newDocs: I_OpenedDocument[]) {
|
|
|
|
const expandIDs: string[] = []
|
|
|
|
|
|
|
|
// Check for parent changes
|
|
|
|
newDocs.forEach(s => {
|
|
|
|
const oldParentDoc = this.lastDocsSnapShot.find(doc => doc._id === s._id)
|
|
|
|
// Fizzle if the parent doesn't exist in the old version
|
|
|
|
if (!oldParentDoc) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
const oldParentDocField = this.retrieveFieldValue(oldParentDoc, "parentDoc")
|
|
|
|
// @ts-ignore
|
|
|
|
const oldParentDocID = (oldParentDocField?.value) ? oldParentDocField.value.value : ""
|
|
|
|
|
|
|
|
const newParentDocField = this.retrieveFieldValue(s, "parentDoc")
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
const newParentDocID = (newParentDocField?.value) ? newParentDocField.value.value : ""
|
|
|
|
if ((newParentDocID !== oldParentDocID) || (newParentDocID && oldParentDoc.isNew)) {
|
|
|
|
expandIDs.push(newParentDocID)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// Process top level documents
|
|
|
|
newDocs.forEach(s => {
|
|
|
|
const newParentDocField = this.retrieveFieldValue(s, "parentDoc")
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
const newParentDocID = (newParentDocField?.value) ? newParentDocField.value.value : false
|
|
|
|
|
|
|
|
if (!newParentDocID) {
|
|
|
|
expandIDs.push(s.type)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-03-03 08:59:56 +13:00
|
|
|
expandIDs.forEach(s => {
|
2021-02-28 06:00:57 +13:00
|
|
|
this.recursivelyExpandNode(s)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
recursivelyExpandNode (nodeID: string) {
|
|
|
|
const treeDOM = this.$refs.tree as unknown as {
|
|
|
|
setExpanded: (key:string, state: boolean)=> void
|
|
|
|
getNodeByKey: (key:string)=> void
|
|
|
|
}
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
this.expandedTreeNodes = [...new Set([
|
|
|
|
...this.expandedTreeNodes,
|
|
|
|
nodeID
|
|
|
|
])]
|
|
|
|
|
|
|
|
const currentTreeNode = (treeDOM.getNodeByKey(nodeID)) as unknown as {parentDoc: string, type: string}
|
|
|
|
|
|
|
|
// Dig into the upper hierarchy
|
|
|
|
if (currentTreeNode?.parentDoc) {
|
|
|
|
this.recursivelyExpandNode(currentTreeNode.parentDoc)
|
|
|
|
}
|
|
|
|
// If we are at the top of the tree, expand the top category
|
|
|
|
else if (currentTreeNode?.type) {
|
|
|
|
// @ts-ignore
|
|
|
|
this.expandedTreeNodes = [...new Set([
|
|
|
|
...this.expandedTreeNodes,
|
|
|
|
currentTreeNode.type
|
|
|
|
])]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
processNodeLabelMiddleClick (node: {
|
|
|
|
key: string
|
|
|
|
_id: string
|
|
|
|
children: []
|
|
|
|
type: string
|
|
|
|
isRoot: boolean
|
2021-03-03 14:10:05 +13:00
|
|
|
isTag: boolean
|
2021-02-23 11:30:18 +13:00
|
|
|
specialLabel: string|boolean
|
|
|
|
}) {
|
2021-03-03 14:10:05 +13:00
|
|
|
if (node.isRoot && node.isTag) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
if (!node.specialLabel && !node.isRoot) {
|
|
|
|
// @ts-ignore
|
|
|
|
this.openExistingDocumentRoute(node)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.addNewObjectRoute(node)
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
processNodeClick (node: {
|
|
|
|
key: string
|
|
|
|
children: []
|
|
|
|
specialLabel: string|boolean
|
|
|
|
}) {
|
|
|
|
// If this is a category or has children
|
|
|
|
if (node.children.length > 0) {
|
|
|
|
this.expandeCollapseNode(node)
|
|
|
|
}
|
|
|
|
// If this lacks a "special label" - AKA anything that isn't the "Add new XY" node
|
|
|
|
else if (!node.specialLabel) {
|
|
|
|
// @ts-ignore
|
|
|
|
this.openExistingDocumentRoute(node)
|
|
|
|
}
|
|
|
|
// If this lacks a "special label" - AKA if this is the "Add new XY" node
|
|
|
|
else {
|
|
|
|
// @ts-ignore
|
|
|
|
this.addNewObjectRoute(node)
|
|
|
|
}
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
expandeCollapseNode (node: {key: string}) {
|
|
|
|
const treeDOM = this.$refs.tree as unknown as {
|
|
|
|
setExpanded: (key:string, state: boolean)=> void,
|
|
|
|
isExpanded: (key:string)=> boolean
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
const isExpanded = treeDOM.isExpanded(node.key)
|
|
|
|
treeDOM.setExpanded(node.key, !isExpanded)
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
2021-03-03 14:10:05 +13:00
|
|
|
|
|
|
|
determineNodeColor (node: {color: string, isTag: boolean, isRoot: boolean}) {
|
|
|
|
// @ts-ignore
|
|
|
|
return (node?.isTag && node?.isRoot) ? colors.getBrand("primary") : node.color
|
|
|
|
}
|
2021-01-31 02:43:13 +13:00
|
|
|
}
|
|
|
|
</script>
|
2021-02-09 15:21:48 +13:00
|
|
|
|
|
|
|
<style lang="scss">
|
2021-03-05 12:51:41 +13:00
|
|
|
|
2021-03-08 11:07:40 +13:00
|
|
|
.projectTitle {
|
|
|
|
margin: 0 0 -5px 0;
|
|
|
|
padding: 65px 10px 0;
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:21:48 +13:00
|
|
|
.objectTree {
|
2021-03-08 11:07:40 +13:00
|
|
|
> .q-tree__node {
|
|
|
|
padding-left: 0 !important;
|
|
|
|
}
|
|
|
|
|
|
|
|
.q-tree__children {
|
|
|
|
padding-left: 5px;
|
|
|
|
}
|
2021-03-05 12:51:41 +13:00
|
|
|
|
2021-02-09 15:21:48 +13:00
|
|
|
.q-tree__arrow {
|
2021-02-23 11:30:18 +13:00
|
|
|
margin-right: 0;
|
2021-02-26 14:50:46 +13:00
|
|
|
padding: 4px 4px 4px 0;
|
2021-03-08 11:07:40 +13:00
|
|
|
position: absolute;
|
|
|
|
pointer-events: none;
|
2021-02-23 11:30:18 +13:00
|
|
|
}
|
|
|
|
|
2021-03-05 12:51:41 +13:00
|
|
|
.q-tree__node {
|
|
|
|
padding: 0 0 0 22px;
|
|
|
|
}
|
|
|
|
|
2021-02-23 11:30:18 +13:00
|
|
|
.q-tree__node-header {
|
|
|
|
padding: 0;
|
2021-03-08 11:07:40 +13:00
|
|
|
|
|
|
|
&:focus {
|
|
|
|
> .q-focus-helper {
|
|
|
|
opacity: 0 !important;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
> .q-focus-helper {
|
|
|
|
opacity: 0.15 !important;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&.q-tree__node--selected {
|
|
|
|
> .q-focus-helper {
|
|
|
|
opacity: 0.22 !important;
|
|
|
|
}
|
|
|
|
}
|
2021-02-09 15:21:48 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
.documentLabel {
|
2021-02-23 11:30:18 +13:00
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
2021-03-08 11:07:40 +13:00
|
|
|
padding: 4px 4px 4px 25px;
|
|
|
|
align-items: center;
|
2021-02-23 11:30:18 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
.treeButtonGroup {
|
|
|
|
flex-grow: 0;
|
|
|
|
flex-shrink: 0;
|
|
|
|
display: flex;
|
|
|
|
height: fit-content;
|
|
|
|
margin-left: auto;
|
|
|
|
align-self: center;
|
2021-02-09 15:21:48 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 14:50:46 +13:00
|
|
|
.treeBadge {
|
|
|
|
left: inherit;
|
|
|
|
right: calc(100% + 3px);
|
|
|
|
padding: 3px 2px;
|
|
|
|
border: none;
|
|
|
|
background: rgba($primary, 0.15);
|
|
|
|
top: 50%;
|
|
|
|
transform: translateY(-50%);
|
|
|
|
min-width: 24px;
|
|
|
|
justify-content: center;
|
2021-03-03 08:59:56 +13:00
|
|
|
|
|
|
|
&.noChilden {
|
2021-03-08 11:07:40 +13:00
|
|
|
right: calc(100% + 3px);
|
2021-03-03 08:59:56 +13:00
|
|
|
}
|
2021-02-26 14:50:46 +13:00
|
|
|
}
|
2021-02-23 11:30:18 +13:00
|
|
|
|
2021-03-05 12:51:41 +13:00
|
|
|
.treeSearchWrapper {
|
|
|
|
top: -40px;
|
|
|
|
left: -375px;
|
|
|
|
width: 375px;
|
|
|
|
z-index: 555;
|
|
|
|
background-color: $dark;
|
|
|
|
|
|
|
|
> div {
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
label {
|
|
|
|
background-color: $dark;
|
|
|
|
|
|
|
|
&.q-field--focused {
|
|
|
|
width: 100vw;
|
|
|
|
max-width: inherit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 14:50:46 +13:00
|
|
|
.treeButton {
|
2021-02-23 11:30:18 +13:00
|
|
|
&--add {
|
|
|
|
.q-icon {
|
|
|
|
font-size: 20px;
|
2021-02-26 14:50:46 +13:00
|
|
|
color: $primary;
|
2021-02-23 11:30:18 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&--edit {
|
|
|
|
.q-icon {
|
|
|
|
font-size: 14px;
|
2021-02-26 14:50:46 +13:00
|
|
|
color: #fff;
|
2021-02-23 11:30:18 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-09 15:21:48 +13:00
|
|
|
</style>
|