mirror of
https://github.com/Elvanos/fantasia-archive.git
synced 2024-05-29 16:40:52 +12:00
added tag support and further improved search
This commit is contained in:
parent
7d2597e3c8
commit
d9a68cf0cf
15
changelog.md
15
changelog.md
|
@ -10,16 +10,21 @@
|
||||||
|
|
||||||
- A new logo added to the app (better visibility of the logo in small scales and icons)
|
- A new logo added to the app (better visibility of the logo in small scales and icons)
|
||||||
- Massive overhaul of the search engine used by the Quick opening existing document and single/multi relationship fields (now supports tags, categories, document types, inteligent filtering and inteligent sorting via importance of the found values)
|
- Massive overhaul of the search engine used by the Quick opening existing document and single/multi relationship fields (now supports tags, categories, document types, inteligent filtering and inteligent sorting via importance of the found values)
|
||||||
|
- Added color support to single/multi relationship fields
|
||||||
|
- Added a hierarchical path to Quick opening existing document and single/multi relationship fields
|
||||||
|
- Added filtering to include or exclude documents that are considered categories in the Quick opening existing document dialog
|
||||||
|
- Added automatic opening of hierarchical tree branches upon adding/moving documents under/among them
|
||||||
|
- Added tags support
|
||||||
|
|
||||||
### QoL adjustments
|
### QoL adjustments
|
||||||
|
|
||||||
- Added a hierarchical path to Quick opening existing document and single/multi relationship fields
|
- Slightly modified the scrollbar visuals to be less intrusive
|
||||||
- Added color support to single/multi relationship fields
|
- Added a light golden tint to the background of the app to go easy on user's eyes before farkmode is added
|
||||||
- Added filtering to include or exclude documents that are considered categories in the Quick opening existing document dialog
|
|
||||||
- Improved performance by reducing the amount of time the side-tree re-renders
|
- Improved performance by reducing the amount of time the side-tree re-renders
|
||||||
- Added automatic opening of hierarchical tree branches upon adding/moving documents under/among them
|
- Visually alligned custom order badge for both nodes with and without children
|
||||||
- Alligned custom order sorting for both nodes with and without children
|
- Added dark visuals to the single-select and multi-select fields to align with thge rest of the app
|
||||||
- All popup dialogs have been unified to dark-color mode
|
- All popup dialogs have been unified to dark-color mode
|
||||||
|
- Prettified a dialog popup for confirmation of closing a document with active edits
|
||||||
- Added a small filter over the big white areas to ease-up on the user's eyes before darkmode is added
|
- Added a small filter over the big white areas to ease-up on the user's eyes before darkmode is added
|
||||||
|
|
||||||
## 0.1.2
|
## 0.1.2
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "fantasiaarchive",
|
"name": "fantasiaarchive",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"description": "A database manager for world building",
|
"description": "A database manager for world building",
|
||||||
"productName": "Fantasia archive",
|
"productName": "Fantasia archive",
|
||||||
"author": "Elvanos <elvanos66@gmail.com>",
|
"author": "Elvanos <elvanos66@gmail.com>",
|
||||||
|
|
|
@ -44,6 +44,7 @@ export default class AppHeader extends BaseClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
.appHeaderInner {
|
.appHeaderInner {
|
||||||
|
z-index: 999999;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
@click.stop.prevent.middle="processNodeLabelMiddleClick(prop.node)"
|
@click.stop.prevent.middle="processNodeLabelMiddleClick(prop.node)"
|
||||||
>
|
>
|
||||||
<q-icon
|
<q-icon
|
||||||
:style="`color: ${prop.node.color}; width: 22px !important;`"
|
:style="`color: ${determineNodeColor(prop.node)}; width: 22px !important;`"
|
||||||
:size="(prop.node.icon.includes('fas')? '16px': '21px')"
|
:size="(prop.node.icon.includes('fas')? '16px': '21px')"
|
||||||
:name="prop.node.icon"
|
:name="prop.node.icon"
|
||||||
class="q-mr-sm self-center" />
|
class="q-mr-sm self-center" />
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
<div class="treeButtonGroup">
|
<div class="treeButtonGroup">
|
||||||
<q-btn
|
<q-btn
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
v-if="prop.node.children && prop.node.children.length > 0 && !prop.node.isRoot"
|
v-if="prop.node.children && prop.node.children.length > 0 && !prop.node.isRoot && !prop.node.isTag"
|
||||||
round
|
round
|
||||||
flat
|
flat
|
||||||
dense
|
dense
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn
|
<q-btn
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
v-if="!prop.node.specialLabel || prop.node.isRoot"
|
v-if="(!prop.node.specialLabel && !prop.node.isRoot) || (prop.node.isRoot && !prop.node.isTag)"
|
||||||
round
|
round
|
||||||
flat
|
flat
|
||||||
dense
|
dense
|
||||||
|
@ -134,7 +134,8 @@ import PouchDB from "pouchdb"
|
||||||
import { engageBlueprints, retrieveAllBlueprints } from "src/scripts/databaseManager/blueprintManager"
|
import { engageBlueprints, retrieveAllBlueprints } from "src/scripts/databaseManager/blueprintManager"
|
||||||
// import { cleanDatabases } from "src/scripts/databaseManager/cleaner"
|
// import { cleanDatabases } from "src/scripts/databaseManager/cleaner"
|
||||||
import { I_Blueprint } from "src/interfaces/I_Blueprint"
|
import { I_Blueprint } from "src/interfaces/I_Blueprint"
|
||||||
import { extend } from "quasar"
|
import { extend, colors } from "quasar"
|
||||||
|
import { tagListBuildFromBlueprints } from "src/scripts/utilities/tagListBuilder"
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { }
|
components: { }
|
||||||
|
@ -268,6 +269,8 @@ export default class ObjectTree extends BaseClass {
|
||||||
resetTreeFilter () {
|
resetTreeFilter () {
|
||||||
this.treeFilter = ""
|
this.treeFilter = ""
|
||||||
const treeFilterDOM = this.$refs.treeFilter as unknown as HTMLInputElement
|
const treeFilterDOM = this.$refs.treeFilter as unknown as HTMLInputElement
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
treeFilterDOM.focus()
|
treeFilterDOM.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +361,7 @@ export default class ObjectTree extends BaseClass {
|
||||||
async buildCurrentObjectTree () {
|
async buildCurrentObjectTree () {
|
||||||
const allBlueprings = this.SGET_allBlueprints
|
const allBlueprings = this.SGET_allBlueprints
|
||||||
const treeObject: any[] = []
|
const treeObject: any[] = []
|
||||||
|
let allTreeDocuments: I_ShortenedDocument[] = []
|
||||||
|
|
||||||
// Process all documents, build hieararchy out of the and sort them via name and custom order
|
// Process all documents, build hieararchy out of the and sort them via name and custom order
|
||||||
for (const blueprint of allBlueprings) {
|
for (const blueprint of allBlueprings) {
|
||||||
|
@ -393,6 +397,8 @@ export default class ObjectTree extends BaseClass {
|
||||||
})
|
})
|
||||||
|
|
||||||
const documentCount = allDocumentsRows.length
|
const documentCount = allDocumentsRows.length
|
||||||
|
const listCopy: I_ShortenedDocument[] = extend(true, [], allDocumentsRows)
|
||||||
|
allTreeDocuments = [...allTreeDocuments, ...listCopy]
|
||||||
|
|
||||||
const hierarchicalTreeContent = this.buildTreeHierarchy(allDocumentsRows)
|
const hierarchicalTreeContent = this.buildTreeHierarchy(allDocumentsRows)
|
||||||
|
|
||||||
|
@ -436,6 +442,36 @@ export default class ObjectTree extends BaseClass {
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
const tagObject = {
|
||||||
|
label: `${tag}`,
|
||||||
|
icon: "mdi-tag",
|
||||||
|
_id: `tag-${tag}`,
|
||||||
|
key: `tag-${tag}`,
|
||||||
|
documentCount: tagDocs.length,
|
||||||
|
isRoot: true,
|
||||||
|
isTag: true,
|
||||||
|
children: tagDocs
|
||||||
|
}
|
||||||
|
treeObject.push(tagObject)
|
||||||
|
})
|
||||||
|
|
||||||
// Assign the finished object to the render model
|
// Assign the finished object to the render model
|
||||||
this.hierarchicalTree = treeObject
|
this.hierarchicalTree = treeObject
|
||||||
}
|
}
|
||||||
|
@ -539,10 +575,15 @@ export default class ObjectTree extends BaseClass {
|
||||||
children: []
|
children: []
|
||||||
type: string
|
type: string
|
||||||
isRoot: boolean
|
isRoot: boolean
|
||||||
|
isTag: boolean
|
||||||
specialLabel: string|boolean
|
specialLabel: string|boolean
|
||||||
}) {
|
}) {
|
||||||
this.selectedTreeNode = null
|
this.selectedTreeNode = null
|
||||||
|
|
||||||
|
if (node.isRoot && node.isTag) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!node.specialLabel && !node.isRoot) {
|
if (!node.specialLabel && !node.isRoot) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.openExistingDocumentRoute(node)
|
this.openExistingDocumentRoute(node)
|
||||||
|
@ -582,6 +623,11 @@ export default class ObjectTree extends BaseClass {
|
||||||
const isExpanded = treeDOM.isExpanded(node.key)
|
const isExpanded = treeDOM.isExpanded(node.key)
|
||||||
treeDOM.setExpanded(node.key, !isExpanded)
|
treeDOM.setExpanded(node.key, !isExpanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
determineNodeColor (node: {color: string, isTag: boolean, isRoot: boolean}) {
|
||||||
|
// @ts-ignore
|
||||||
|
return (node?.isTag && node?.isRoot) ? colors.getBrand("primary") : node.color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,17 @@
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label v-html="opt.label" ></q-item-label>
|
<q-item-label v-html="opt.label" ></q-item-label>
|
||||||
<q-item-label caption class="text-cultured" v-html="opt.hierarchicalPath"></q-item-label>
|
<q-item-label caption class="text-cultured" v-html="opt.hierarchicalPath"></q-item-label>
|
||||||
|
<q-item-label caption class="text-cultured" v-if="opt.tags">
|
||||||
|
<q-chip
|
||||||
|
v-for="(input,index) in opt.tags" :key="index"
|
||||||
|
outline
|
||||||
|
style="opacity: 0.8;"
|
||||||
|
size="12px"
|
||||||
|
class="bg-dark text-cultured"
|
||||||
|
v-html="`${input}`"
|
||||||
|
>
|
||||||
|
</q-chip>
|
||||||
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-btn
|
<q-btn
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
@ -127,6 +138,7 @@ export default class ExistingDocumentDialog extends DialogBase {
|
||||||
type: doc.type,
|
type: doc.type,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
hierarchicalPath: this.getDocumentHieararchicalPath(doc, dbDocuments.rows),
|
hierarchicalPath: this.getDocumentHieararchicalPath(doc, dbDocuments.rows),
|
||||||
|
tags: doc.extraFields.find(e => e.id === "tags")?.value,
|
||||||
color: doc.extraFields.find(e => e.id === "documentColor")?.value,
|
color: doc.extraFields.find(e => e.id === "documentColor")?.value,
|
||||||
isCategory: doc.extraFields.find(e => e.id === "categorySwitch")?.value
|
isCategory: doc.extraFields.find(e => e.id === "categorySwitch")?.value
|
||||||
} as unknown as I_ShortenedDocument
|
} as unknown as I_ShortenedDocument
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
ref="ref_newDocument"
|
ref="ref_newDocument"
|
||||||
style="flex-grow: 1;"
|
style="flex-grow: 1;"
|
||||||
dense
|
dense
|
||||||
|
menu-anchor="bottom middle"
|
||||||
|
menu-self="top middle"
|
||||||
class="newDocumentSelect"
|
class="newDocumentSelect"
|
||||||
:options="filteredNewInput"
|
:options="filteredNewInput"
|
||||||
use-input
|
use-input
|
||||||
|
@ -101,7 +103,7 @@ export default class NewDocumentDialog extends DialogBase {
|
||||||
setTimeout( () =>{
|
setTimeout( () =>{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.$refs.ref_newDocument.focus()
|
this.$refs.ref_newDocument.focus()
|
||||||
}, 100)
|
}, 300)
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
v-if="editMode"
|
v-if="editMode"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
dense
|
dense
|
||||||
|
dark
|
||||||
|
menu-anchor="bottom middle"
|
||||||
|
menu-self="top middle"
|
||||||
|
class="multiSelect"
|
||||||
:options="extraInput"
|
:options="extraInput"
|
||||||
use-input
|
use-input
|
||||||
outlined
|
outlined
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
v-if="editMode"
|
v-if="editMode"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
dense
|
dense
|
||||||
|
dark
|
||||||
|
menu-anchor="bottom middle"
|
||||||
|
menu-self="top middle"
|
||||||
|
class="singleSelect"
|
||||||
:options="extraInput"
|
:options="extraInput"
|
||||||
use-input
|
use-input
|
||||||
outlined
|
outlined
|
||||||
|
|
127
src/components/fields/Field_Tags.vue
Normal file
127
src/components/fields/Field_Tags.vue
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="flex justify-start items-center text-weight-bolder q-mb-sm q-mt-md">
|
||||||
|
<q-icon v-if="inputIcon" :name="inputIcon" :size="inputIcon.includes('fas')? '15px': '20px'" class="q-mr-md"/>
|
||||||
|
{{inputDataBluePrint.name}}
|
||||||
|
<q-icon v-if="toolTip" name="mdi-help-circle" size="16px" class="q-ml-md">
|
||||||
|
<q-tooltip :delay="500">
|
||||||
|
<span v-html="toolTip"/>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="!editMode"
|
||||||
|
>
|
||||||
|
<q-chip
|
||||||
|
v-for="(input,index) in localInput" :key="index"
|
||||||
|
color="primary" text-color="white" class="text-bold">
|
||||||
|
{{input}}
|
||||||
|
</q-chip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-select
|
||||||
|
v-if="editMode"
|
||||||
|
style="width: 100%;"
|
||||||
|
dense
|
||||||
|
dark
|
||||||
|
menu-anchor="bottom middle"
|
||||||
|
menu-self="top middle"
|
||||||
|
class="tagSelect"
|
||||||
|
:options="allTags"
|
||||||
|
use-input
|
||||||
|
outlined
|
||||||
|
use-chips
|
||||||
|
@filter="filterFn"
|
||||||
|
input-debounce="0"
|
||||||
|
new-value-mode="add"
|
||||||
|
multiple
|
||||||
|
v-model="localInput"
|
||||||
|
@input="signalInput"
|
||||||
|
@keydown="signalInput"
|
||||||
|
>
|
||||||
|
</q-select>
|
||||||
|
|
||||||
|
<div class="separatorWrapper">
|
||||||
|
<q-separator color="grey q-mt-lg" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Emit, Prop, Watch } from "vue-property-decorator"
|
||||||
|
|
||||||
|
import BaseClass from "src/BaseClass"
|
||||||
|
import { tagListBuildFromBlueprints } from "src/scripts/utilities/tagListBuilder"
|
||||||
|
import { I_ExtraFields } from "src/interfaces/I_Blueprint"
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: { }
|
||||||
|
})
|
||||||
|
export default class Field_Tags extends BaseClass {
|
||||||
|
@Prop({ default: [] }) readonly inputDataBluePrint!: I_ExtraFields
|
||||||
|
|
||||||
|
@Prop({
|
||||||
|
default: () => {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}) readonly inputDataValue!: []
|
||||||
|
|
||||||
|
@Prop() readonly isNew!: boolean
|
||||||
|
|
||||||
|
@Prop() readonly editMode!: boolean
|
||||||
|
|
||||||
|
changedInput = false
|
||||||
|
localInput = []
|
||||||
|
|
||||||
|
@Watch("inputDataValue", { deep: true, immediate: true })
|
||||||
|
reactToInputChanges () {
|
||||||
|
this.localInput = (this.inputDataValue) ? this.inputDataValue : []
|
||||||
|
}
|
||||||
|
|
||||||
|
@Watch("inputDataBluePrint", { deep: true, immediate: true })
|
||||||
|
reactToBlueprintChanges () {
|
||||||
|
this.buildTagList().catch(e => console.log(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
get inputIcon () {
|
||||||
|
return this.inputDataBluePrint?.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
get toolTip () {
|
||||||
|
return this.inputDataBluePrint?.tooltip
|
||||||
|
}
|
||||||
|
|
||||||
|
allTags: string[] = []
|
||||||
|
|
||||||
|
filterFn (val: string, update: (fn: any) => void) {
|
||||||
|
if (val === "") {
|
||||||
|
update(() => {
|
||||||
|
if (this.inputDataBluePrint?.predefinedSelectValues) {
|
||||||
|
this.allTags = this.inputDataBluePrint.predefinedSelectValues
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
update(() => {
|
||||||
|
if (this.inputDataBluePrint?.predefinedSelectValues) {
|
||||||
|
const needle = val.toLowerCase()
|
||||||
|
this.allTags = this.inputDataBluePrint.predefinedSelectValues.filter(v => v.toLowerCase().indexOf(needle) > -1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async buildTagList () {
|
||||||
|
this.allTags = await tagListBuildFromBlueprints(this.SGET_allBlueprints)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Emit()
|
||||||
|
signalInput () {
|
||||||
|
this.changedInput = true
|
||||||
|
return this.localInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -66,6 +66,9 @@ body {
|
||||||
left: calc(100% - 30px);
|
left: calc(100% - 30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tagSelect,
|
||||||
|
.singleSelect,
|
||||||
|
.multiSelect,
|
||||||
.singleRelashionshipSelect,
|
.singleRelashionshipSelect,
|
||||||
.multiRelashionshipSelect,
|
.multiRelashionshipSelect,
|
||||||
.existingDocumentSelect,
|
.existingDocumentSelect,
|
||||||
|
@ -89,7 +92,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.q-position-engine {
|
.q-position-engine {
|
||||||
overflow-y: scroll !important;
|
overflow-y: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-underline {
|
.text-underline {
|
||||||
|
|
|
@ -19,7 +19,8 @@ export interface I_ExtraFields {
|
||||||
"singleToManyRelationship" |
|
"singleToManyRelationship" |
|
||||||
"manyToSingleRelationship" |
|
"manyToSingleRelationship" |
|
||||||
"manyToManyRelationship" |
|
"manyToManyRelationship" |
|
||||||
"break"
|
"break" |
|
||||||
|
"tags"
|
||||||
|
|
||||||
predefinedListExtras?: {
|
predefinedListExtras?: {
|
||||||
affix?: string
|
affix?: string
|
||||||
|
|
|
@ -29,6 +29,7 @@ export interface I_ShortenedDocument{
|
||||||
children: I_ShortenedDocument[]
|
children: I_ShortenedDocument[]
|
||||||
extraFields: I_ExtraDocumentFields[]
|
extraFields: I_ExtraDocumentFields[]
|
||||||
color?: string
|
color?: string
|
||||||
|
tags?: string[]
|
||||||
|
|
||||||
activeTypeSearch?: boolean
|
activeTypeSearch?: boolean
|
||||||
filteredOut?: boolean
|
filteredOut?: boolean
|
||||||
|
|
|
@ -171,6 +171,16 @@
|
||||||
@signal-input="reactToFieldUpdate($event, field)"
|
@signal-input="reactToFieldUpdate($event, field)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Field_Tags
|
||||||
|
class="inputWrapper"
|
||||||
|
v-if="field.type === 'tags' && fieldLimiter(field.id)"
|
||||||
|
:inputDataBluePrint="field"
|
||||||
|
:inputDataValue="retrieveFieldValue(currentData, field.id)"
|
||||||
|
:isNew="currentData.isNew"
|
||||||
|
:editMode="editMode"
|
||||||
|
@signal-input="reactToFieldUpdate($event, field)"
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -202,7 +212,7 @@ import Field_MultiSelect from "src/components/fields/Field_MultiSelect.vue"
|
||||||
import Field_SingleRelationship from "src/components/fields/Field_SingleRelationship.vue"
|
import Field_SingleRelationship from "src/components/fields/Field_SingleRelationship.vue"
|
||||||
import Field_MultiRelationship from "src/components/fields/Field_MultiRelationship.vue"
|
import Field_MultiRelationship from "src/components/fields/Field_MultiRelationship.vue"
|
||||||
import Field_Wysiwyg from "src/components/fields/Field_Wysiwyg.vue"
|
import Field_Wysiwyg from "src/components/fields/Field_Wysiwyg.vue"
|
||||||
import console from "console"
|
import Field_Tags from "src/components/fields/Field_Tags.vue"
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
|
@ -216,7 +226,8 @@ import console from "console"
|
||||||
Field_MultiSelect,
|
Field_MultiSelect,
|
||||||
Field_SingleRelationship,
|
Field_SingleRelationship,
|
||||||
Field_MultiRelationship,
|
Field_MultiRelationship,
|
||||||
Field_Wysiwyg
|
Field_Wysiwyg,
|
||||||
|
Field_Tags
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -397,6 +408,18 @@ export default class PageDocumentDisplay extends BaseClass {
|
||||||
const dataPass = { doc: dataCopy, treeAction: false }
|
const dataPass = { doc: dataCopy, treeAction: false }
|
||||||
this.SSET_updateOpenedDocument(dataPass)
|
this.SSET_updateOpenedDocument(dataPass)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIELD - Tags
|
||||||
|
if (field.type === "tags") {
|
||||||
|
this.currentData.hasEdits = true
|
||||||
|
const indexToUpdate = this.currentData.extraFields.findIndex(s => s.id === field.id)
|
||||||
|
this.currentData.extraFields[indexToUpdate].value = inputData
|
||||||
|
|
||||||
|
const dataCopy: I_OpenedDocument = extend(true, {}, this.currentData)
|
||||||
|
|
||||||
|
const dataPass = { doc: dataCopy, treeAction: false }
|
||||||
|
this.SSET_updateOpenedDocument(dataPass)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleEditMode () {
|
toggleEditMode () {
|
||||||
|
|
|
@ -62,6 +62,16 @@ export const chaptersBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "content",
|
id: "content",
|
||||||
name: "Chapter content",
|
name: "Chapter content",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const charactersBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const currenciesBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const eventsBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const itemsBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const languagesBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const locationsBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -62,6 +62,16 @@ export const loreNotesBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "notes",
|
id: "notes",
|
||||||
name: "Note list",
|
name: "Note list",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const magicBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const mythsBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const politicalGroupsBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const racesBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const religionsBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -68,6 +68,16 @@ export const techBlueprint: I_Blueprint = {
|
||||||
`,
|
`,
|
||||||
sizing: 2
|
sizing: 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
name: "Tags",
|
||||||
|
type: "tags",
|
||||||
|
icon: "mdi-tag",
|
||||||
|
tooltip:
|
||||||
|
`tags
|
||||||
|
`,
|
||||||
|
sizing: 12
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "otherNames",
|
id: "otherNames",
|
||||||
name: "Other names & Epithets",
|
name: "Other names & Epithets",
|
||||||
|
|
|
@ -40,20 +40,20 @@ export const advancedDocumentFilter = (inputString: string, documentList: I_Shor
|
||||||
const searchWordList = inputString.toLowerCase().split(" ")
|
const searchWordList = inputString.toLowerCase().split(" ")
|
||||||
|
|
||||||
let categorySeach = false as unknown as string
|
let categorySeach = false as unknown as string
|
||||||
let tagSeach = false as unknown as string
|
let tagSearch = false as unknown as string
|
||||||
let typeSeach = false as unknown as string
|
let typeSeach = false as unknown as string
|
||||||
|
|
||||||
const categorySeachIndex = searchWordList.findIndex(w => w.charAt(0) === ">")
|
const categorySeachIndex = searchWordList.findIndex(w => w.charAt(0) === ">")
|
||||||
const tagSeachIndex = searchWordList.findIndex(w => w.charAt(0) === "#")
|
const tagSearchIndex = searchWordList.findIndex(w => w.charAt(0) === "#")
|
||||||
const typeSeachIndex = searchWordList.findIndex(w => w.charAt(0) === "$")
|
const typeSeachIndex = searchWordList.findIndex(w => w.charAt(0) === "$")
|
||||||
|
|
||||||
if (categorySeachIndex >= 0) {
|
if (categorySeachIndex >= 0) {
|
||||||
categorySeach = searchWordList[categorySeachIndex].substring(1)
|
categorySeach = searchWordList[categorySeachIndex].substring(1)
|
||||||
searchWordList[categorySeachIndex] = ""
|
searchWordList[categorySeachIndex] = ""
|
||||||
}
|
}
|
||||||
if (tagSeachIndex >= 0) {
|
if (tagSearchIndex >= 0) {
|
||||||
tagSeach = searchWordList[tagSeachIndex].substring(1)
|
tagSearch = searchWordList[tagSearchIndex].substring(1)
|
||||||
searchWordList[tagSeachIndex] = ""
|
searchWordList[tagSearchIndex] = ""
|
||||||
}
|
}
|
||||||
if (typeSeachIndex >= 0) {
|
if (typeSeachIndex >= 0) {
|
||||||
typeSeach = searchWordList[typeSeachIndex].substring(1)
|
typeSeach = searchWordList[typeSeachIndex].substring(1)
|
||||||
|
@ -67,7 +67,7 @@ export const advancedDocumentFilter = (inputString: string, documentList: I_Shor
|
||||||
/****************************************************************/
|
/****************************************************************/
|
||||||
|
|
||||||
if (categorySeach) {
|
if (categorySeach) {
|
||||||
documentList = documentList.filter((doc, index) => {
|
documentList = documentList.filter(doc => {
|
||||||
let stringPath = doc.hierarchicalPath as unknown as string
|
let stringPath = doc.hierarchicalPath as unknown as string
|
||||||
stringPath = stringPath.toLowerCase()
|
stringPath = stringPath.toLowerCase()
|
||||||
stringPath = stringPath.replace(/>/gi, "")
|
stringPath = stringPath.replace(/>/gi, "")
|
||||||
|
@ -75,12 +75,36 @@ export const advancedDocumentFilter = (inputString: string, documentList: I_Shor
|
||||||
stringPath = stringPath.replace(/ /gi, "-")
|
stringPath = stringPath.replace(/ /gi, "-")
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
doc.hierarchicalPath = `<span class="text-primary">${doc.hierarchicalPath}</span>`
|
doc.hierarchicalPath = `<span class="text-primary text-bold">${doc.hierarchicalPath}</span>`
|
||||||
|
|
||||||
return (stringPath.includes(categorySeach))
|
return (stringPath.includes(categorySeach))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************/
|
||||||
|
// Tag filter
|
||||||
|
/****************************************************************/
|
||||||
|
if (tagSearch) {
|
||||||
|
documentList = documentList.filter(doc => {
|
||||||
|
let matchFound = false
|
||||||
|
|
||||||
|
if (doc.tags) {
|
||||||
|
doc.tags.forEach((tag, index) => {
|
||||||
|
const ogTag = tag
|
||||||
|
tag = tag.toLowerCase()
|
||||||
|
tag = tag.replace(/>/gi, "")
|
||||||
|
tag = tag.replace(/ {2}/gi, "-")
|
||||||
|
tag = tag.replace(/ /gi, "-")
|
||||||
|
if (tag.includes(tagSearch)) {
|
||||||
|
// @ts-ignore
|
||||||
|
doc.tags[index] = `<span class="text-primary text-bold">${ogTag}</span>`
|
||||||
|
}
|
||||||
|
matchFound = (tag.includes(tagSearch) || matchFound)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return matchFound
|
||||||
|
})
|
||||||
|
}
|
||||||
/****************************************************************/
|
/****************************************************************/
|
||||||
// Type filter
|
// Type filter
|
||||||
/****************************************************************/
|
/****************************************************************/
|
||||||
|
@ -148,13 +172,21 @@ export const advancedDocumentFilter = (inputString: string, documentList: I_Shor
|
||||||
// Color the word if they were found
|
// Color the word if they were found
|
||||||
documentWordListColoring = documentWordListColoring.map(docWord => {
|
documentWordListColoring = documentWordListColoring.map(docWord => {
|
||||||
if (foundWordList.includes(docWord.toLowerCase())) {
|
if (foundWordList.includes(docWord.toLowerCase())) {
|
||||||
return `<span class="text-primary text-underline">${docWord}</span>`
|
return `<span class="text-primary text-bold">${docWord}</span>`
|
||||||
}
|
}
|
||||||
return docWord
|
return docWord
|
||||||
})
|
})
|
||||||
doc.label = documentWordListColoring.join(" ")
|
doc.label = documentWordListColoring.join(" ")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Cover case of exact match
|
||||||
|
documentList.map(doc => {
|
||||||
|
if (doc.exactMatch) {
|
||||||
|
doc.label = `<span class="text-primary text-bold">${doc.label}</span>`
|
||||||
|
}
|
||||||
|
return doc
|
||||||
|
})
|
||||||
|
|
||||||
/****************************************************************/
|
/****************************************************************/
|
||||||
// Sorting
|
// Sorting
|
||||||
/****************************************************************/
|
/****************************************************************/
|
||||||
|
|
32
src/scripts/utilities/tagListBuilder.ts
Normal file
32
src/scripts/utilities/tagListBuilder.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { I_Blueprint } from "./../../interfaces/I_Blueprint"
|
||||||
|
import PouchDB from "pouchdb"
|
||||||
|
import { I_ShortenedDocument } from "src/interfaces/I_OpenedDocument"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a tag list of all know database documents
|
||||||
|
*/
|
||||||
|
export const tagListBuildFromBlueprints = async (blueprintList: I_Blueprint[]) => {
|
||||||
|
let allTags: string[] = []
|
||||||
|
for (const blueprint of blueprintList) {
|
||||||
|
const CurrentObjectDB = new PouchDB(blueprint._id)
|
||||||
|
|
||||||
|
const dbDocuments = await CurrentObjectDB.allDocs({ include_docs: true })
|
||||||
|
const docsTagsArray = dbDocuments.rows.map(singleDocument => {
|
||||||
|
const doc = singleDocument.doc as unknown as I_ShortenedDocument
|
||||||
|
const tags: string[] = doc.extraFields.find(e => e.id === "tags")?.value
|
||||||
|
|
||||||
|
return (tags) || []
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
allTags = [...allTags, ...docsTagsArray] as unknown as string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
allTags = allTags.flat()
|
||||||
|
|
||||||
|
return [...new Set([
|
||||||
|
...allTags
|
||||||
|
])].sort((a, b) => a.localeCompare(b))
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"suppressImplicitAnyIndexErrors": true,
|
"suppressImplicitAnyIndexErrors": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src"
|
||||||
|
|
Loading…
Reference in a new issue