update library opening strategy

This commit is contained in:
axolotle
2021-06-10 18:26:14 +02:00
parent ddff9313cb
commit ecc5253f38
9 changed files with 169 additions and 160 deletions
+4
View File
@@ -79,4 +79,8 @@ export default {
opacity: .25;
}
}
#app > header {
border-bottom: $line;
}
</style>
+2 -2
View File
@@ -53,8 +53,8 @@ $grid-breakpoints: (
xs: 0,
sm: 576px,
md: 1024px,
// lg: 992px,
// xl: 1200px
lg: 1200px,
xl: 1440px,
);
$layout-bp: md;
+1 -2
View File
@@ -10,8 +10,7 @@ import store from './store'
Vue.use(BootstrapVue, {
BButton: { pill: true },
BCardHeader: { headerBgVariant: 'white' },
BCardFooter: { footerBgVariant: 'white' }
BOverlay: { blur: null, spinnerType: 'grow', opacity: 0 }
})
Vue.use(Meta)
Vue.use(Vue2TouchEvents)
+2 -1
View File
@@ -41,5 +41,6 @@
"siblings": "Textes rebonds",
"text": {
"read": "Voir le texte"
}
},
"from": "à partir de:"
}
+46 -120
View File
@@ -1,101 +1,55 @@
<template>
<div class="library">
<!-- BACKGROUND (mode) -->
<component :is="mode" @open="openText" />
<component :is="'library-' + mode" @open-node="openNode" />
<!-- FOREGROUND (texts) -->
<div v-if="parents.length" class="main-texts-container">
<section
v-for="(parent, i) in parents" :key="parent.id"
class="main-text-container text-break"
:style="`--n: ${i};`"
:class="{ 'text-blur': i !== parents.length - 1 }"
>
<text-card
:id="parent.id"
:children="parent.children"
class="text-card-main"
@open="openText" @close="closeText"
@click.native="onMainTextClick(i)"
/>
<section class="sub-texts-container">
<text-card
v-for="(id, j) in parent.children" :key="id"
:id="id"
class="text-card-sub text-break"
:style="`--n: ${j};`"
@close="closeText(parent.id, $event)"
@click.native="onSubTextClick(i, j)"
/>
</section>
</section>
</div>
<!-- FOREGROUND (nodes) -->
<node-book
v-if="nodebook.length"
:nodes="nodebook"
class="library-node-book no-events-container"
@open-node="openNode"
@close-node="closeNode"
/>
</div>
</template>
<script>
import { CardList, CardMap, TreeMap } from './library'
import TextCard from '@/components/text/TextCard'
import { mapGetters } from 'vuex'
import {
LibraryList,
LibraryMap,
LibraryTree
} from './library'
import { NodeBook } from '@/components/layouts'
export default {
name: 'Library',
components: {
CardList,
CardMap,
TreeMap,
TextCard
LibraryList,
LibraryMap,
LibraryTree,
NodeBook
},
props: {
mode: { type: String, required: true },
texts: { type: Array, required: true }
query: { type: Object, required: true }
},
computed: {
parents () {
return this.texts.map(text => {
const [id, ...children] = text
return { id, children }
})
...mapGetters(['mode', 'nodebook'])
},
watch: {
query (query) {
this.$store.dispatch('UPDATE_NODEBOOK', query)
}
},
methods: {
openText (id, childId) {
const parent = this.parents.find(p => p.id === id)
if (parent && (childId === undefined || parent.children.includes(childId))) return
if (!parent) {
this.parents.push({ id, children: childId ? [childId] : [] })
} else if (childId) {
parent.children.push(childId)
}
this.updateQuery(this.parents)
},
closeText (id, childId) {
const parent = this.parents.find(p => p.id === id)
if (!childId) {
this.updateQuery(this.parents.filter(p => p !== parent))
} else {
parent.children = parent.children.filter(childId_ => childId_ !== childId)
this.updateQuery(this.parents)
}
},
updateQuery (parents) {
// Update the route's query (will not reload the page) and let vue determine what changed.
this.$router.push({
query: {
mode: this.mode,
texts: parents.map(parent => [parent.id, ...parent.children])
}
})
},
onMainTextClick (mainTextIndex) {
if (mainTextIndex === this.texts.length - 1) return
this.parents.push(this.parents.splice(mainTextIndex, 1)[0])
@@ -118,7 +72,19 @@ export default {
if (needUpdate) {
this.updateQuery(this.parents)
}
},
openNode (parentId, childId) {
this.$store.dispatch('OPEN_NODE', [parentId, childId])
},
closeNode (parentId, childId) {
this.$store.dispatch('CLOSE_NODE', [parentId, childId])
}
},
created () {
this.$store.dispatch('UPDATE_NODEBOOK', this.query)
}
}
</script>
@@ -126,53 +92,13 @@ export default {
<style lang="scss" scoped>
.library {
height: 100%;
width: 100%;
overflow: hidden;
}
.main-texts-container {
position: relative;
top: -100%;
height: 100%;
pointer-events: none;
}
.main-text-container {
border-top: 2px solid $black;
display: flex;
width: 100%;
> * {
flex-basis: 50%;
&-node-book {
position: relative;
margin-bottom: -100%;
// compensate header border
height: calc(100% + 2px);
top: calc(-100% - 2px);
}
}
.sub-texts-container {
position: relative;
height: 100%;
}
.text-card {
pointer-events: auto;
width: 100%;
}
.text-break {
position: absolute;
height: calc(100% - (var(--n) * #{$text-card-header-height}));
top: calc(var(--n) * #{$text-card-header-height});
z-index: var(--n);
}
.text-blur::before {
content: '';
pointer-events: none;
position: absolute;
z-index: 1;
display: block;
width: 100%;
height: 100%;
background-color: rgba($white, .5);
}
</style>
+19 -20
View File
@@ -6,14 +6,14 @@
>
<b-form-radio-group
id="mode-select"
v-model="currentMode" :options="modes"
v-model="activeMode" :options="modes"
name="mode-select" :aria-describedby="ariaDescribedby"
buttons button-variant="outline-dark"
/>
</b-form-group>
<b-form-group
v-if="currentMode !== 'tree-map'"
v-if="mode !== 'tree'"
:label="$t('options.filters.title')"
>
<b-form-group
@@ -22,8 +22,7 @@
>
<multiple-tags-select
id="tags-select" :button-text="$t('options.filters.choices.tags')"
v-model="selectedTags" :options="tags"
:disabled="true"
v-model="selectedTags" :options="tagsOptions"
/>
</b-form-group>
@@ -80,20 +79,13 @@ export default {
MultipleTagsSelect
},
props: {
show: { type: Boolean, required: true },
mode: { type: String, required: true },
tags: { type: Array, default: () => ([]) }
},
data () {
return {
modes: [
{ text: this.$t('options.display.choices.tree-map'), value: 'tree-map' },
{ text: this.$t('options.display.choices.card-map'), value: 'card-map' },
{ text: this.$t('options.display.choices.card-list'), value: 'card-list' }
{ text: this.$t('options.display.choices.tree-map'), value: 'tree' },
{ text: this.$t('options.display.choices.card-map'), value: 'map' },
{ text: this.$t('options.display.choices.card-list'), value: 'list' }
],
currentMode: this.mode,
selectedTags: [],
strangeness: 0,
textDepartId: undefined,
@@ -102,7 +94,11 @@ export default {
},
computed: {
...mapGetters(['nodesDepartsOptions']),
...mapGetters(['mode', 'nodebook', 'nodesDepartsOptions', 'tagsOptions']),
show () {
return this.nodebook.length === 0
},
nodeDepartId: {
get () {
@@ -111,12 +107,15 @@ export default {
set (value) {
this.$store.dispatch('SET_NODE_DEPART_ID', value)
}
}
},
},
watch: {
currentMode (mode) {
this.$router.push({ query: { mode } })
activeMode: {
get () {
return this.mode
},
set (value) {
this.$store.dispatch('UPDATE_QUERY_MODE', value)
}
}
}
}
+1 -13
View File
@@ -28,19 +28,7 @@ export default [
options: () => import(/* webpackChunkName: "library" */ '@/pages/library/LibraryOptions')
},
props: {
default: ({ query }) => {
let { mode = 'tree-map', texts = [] } = query
if (typeof texts === 'string') texts = [texts]
// In case of a reload or direct link, vue-router doesn't turn the query string into an array.
if (texts && texts.length && typeof texts[0] === 'string') {
texts = texts.map(text => text.split(',').map(id => parseInt(id)))
}
return { mode, texts }
},
options: ({ query }) => ({
show: !('texts' in query && query.texts.length),
mode: query.mode || 'tree-map'
})
default: ({ query }) => ({ query })
}
},
+83 -2
View File
@@ -1,10 +1,12 @@
import Vue from 'vue'
import router from '@/router'
import api from '@/api'
import {
AllTags
} from '@/api/queries'
import {
parseNodesQueryParams,
getUniqueNodesIds,
getRelatedNodesIds,
buildTree
@@ -16,10 +18,38 @@ export default {
tags: undefined,
strangeness: [0, 1, 2, 3, 4],
nodeDepartId: undefined,
trees: {}
trees: {},
mode: undefined,
nodebook: undefined
},
mutations: {
'SET_MODE' (state, mode) {
state.mode = mode
},
'SET_NODEBOOK' (state, nodes) {
state.nodebook = nodes
},
'ADD_NODEBOOK_NODE' (state, [stack, parentId, childId]) {
console.log('ADD_NODEBOOK_NODE', stack, parentId, childId)
if (stack === undefined) {
stack = childId === undefined ? [parentId] : [parentId, childId]
state.nodebook.push(stack)
} else {
stack.push(childId)
}
},
'REMOVE_NODEBOOK_NODE' (state, [stack, nodeId]) {
if (stack[0] === nodeId) {
state.nodebook.splice(state.nodebook.indexOf(stack), 1)
} else {
stack.splice(stack.indexOf(nodeId), 1)
}
},
'SET_TAGS' (state, tags) {
state.tags = tags
},
@@ -34,9 +64,30 @@ export default {
},
actions: {
async 'UPDATE_NODEBOOK' ({ state, commit, dispatch }, query) {
const { mode = 'tree', nodebook } = parseNodesQueryParams(query)
commit('SET_MODE', mode)
await dispatch('GET_NODES', { ids: [].concat(...nodebook), dataLevel: 'full' })
commit('SET_NODEBOOK', nodebook)
},
'UPDATE_QUERY_NODES' ({ state }, from) {
const query = {
mode: state.mode,
nodes: [...state.nodebook.map(ids => ids.join(','))]
}
router.push({ query })
},
'UPDATE_QUERY_MODE' (store, mode) {
if (router.currentRoute.query.mode === mode) return
router.push({ query: { mode } })
},
async 'INIT_LIBRARY' ({ state, commit, dispatch, rootState }) {
const departIds = await dispatch('GET_ALL_NODES_IDS', 'depart')
await dispatch('GET_NODES', { ids: departIds, dataLevel: 'initial' })
dispatch('GET_ALL_TAGS')
return departIds
},
@@ -78,12 +129,42 @@ export default {
}
return getters.nodeTree
},
async 'OPEN_NODE' ({ state, commit, dispatch }, [parentId, childId]) {
const stack = state.nodebook.find(stack => stack[0] === parentId)
if (stack && (childId === undefined || stack.includes(childId))) return
commit('ADD_NODEBOOK_NODE', [stack, parentId, childId])
dispatch('UPDATE_QUERY_NODES')
},
'CLOSE_NODE' ({ state, commit, dispatch }, [parentId, childId]) {
const stack = state.nodebook.find(stack => stack.includes(parentId) && (childId ? stack.includes(childId) : true))
commit('REMOVE_NODEBOOK_NODE', [stack, childId || parentId])
dispatch('UPDATE_QUERY_NODES')
}
},
getters: {
mode: (state) => state.mode,
nodebook: (state, getters, rootState) => {
if (!state.nodebook) return []
return state.nodebook.map(([parentId, ...childrenIds]) => {
return {
parent: rootState.nodes[parentId],
children: childrenIds.map(id => rootState.nodes[id])
}
})
},
activeNodes: (state) => {
if (!state.nodebook) return []
return state.nodebook.reduce((acc, ids) => [...acc, ...ids], [])
},
tagsOptions: state => {
if (state.tags === undefined) return
if (state.tags === undefined) return []
return state.tags.map(tag => ({ value: tag.id, text: tag.name }))
},
+11
View File
@@ -29,6 +29,17 @@ export const VARIANT_IDS = Object.fromEntries(
)
export function parseNodesQueryParams ({ mode, nodes = [] }) {
let nodebook = typeof nodes === 'string' ? [nodes] : nodes
nodebook = nodebook.map(ids => {
return typeof ids === 'string'
? ids.split(',').map(id => parseInt(id))
: [...ids]
})
return { mode, nodebook }
}
export function getUniqueNodesIds (tree) {
function extractId (ids, node) {
ids.add(node.id)