add new node components
This commit is contained in:
@@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<article
|
||||||
|
class="node-view"
|
||||||
|
:class="['node-view-' + mode, 'node-view-' + type, 'node-view-' + nodeVariant]"
|
||||||
|
>
|
||||||
|
<div v-if="!loading" class="node-view-wrapper">
|
||||||
|
<component
|
||||||
|
:is="'node-view-header-' + type"
|
||||||
|
v-bind="{ node, mode }"
|
||||||
|
class="node-view-header"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<node-view-body v-bind="{ content: node.content, type: nodeType, mode }" />
|
||||||
|
|
||||||
|
<node-view-footer
|
||||||
|
v-bind="{ node, mode, type: nodeType }"
|
||||||
|
class="node-view-footer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<b-overlay
|
||||||
|
:show="loading"
|
||||||
|
:spinner-variant="nodeVariant === 'depart' ? 'dark' : 'light'"
|
||||||
|
no-wrap
|
||||||
|
/>
|
||||||
|
|
||||||
|
<slot name="bottom" />
|
||||||
|
</article>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
NodeViewHeaderRef,
|
||||||
|
NodeViewHeaderProd,
|
||||||
|
NodeViewBody,
|
||||||
|
NodeViewFooter
|
||||||
|
} from '@/components/nodes'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NodeView',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
NodeViewHeaderRef,
|
||||||
|
NodeViewHeaderProd,
|
||||||
|
NodeViewFooter,
|
||||||
|
NodeViewBody
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
node: { type: Object, default: undefined },
|
||||||
|
variant: { type: String, default: 'dark' },
|
||||||
|
type: { type: String, default: 'ref' },
|
||||||
|
mode: { type: String, default: 'view' }
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
loading () {
|
||||||
|
const dataLevel = this.mode === 'view' ? 2 : 1
|
||||||
|
return this.node === undefined || this.node.dataLevel < dataLevel
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeVariant () {
|
||||||
|
return this.node !== undefined ? this.node.variant : this.variant
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeType () {
|
||||||
|
return this.node !== undefined ? this.node.type : this.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.node-view {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ╭─╴╭─┐┌─╮┌─╮
|
||||||
|
// │ ├─┤├┬╯│ │
|
||||||
|
// ╰─╴╵ ╵╵ ╰└─╯
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
width: 560px;
|
||||||
|
height: 330px;
|
||||||
|
max-width: 560px;
|
||||||
|
max-height: 330px;
|
||||||
|
box-shadow: .5rem .5rem 1rem rgba($black, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-card &-header {
|
||||||
|
padding: $node-card-spacer-sm-y $node-card-spacer-sm-x 0;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: $node-card-spacer-y $node-card-spacer-x 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-card &-footer {
|
||||||
|
padding: $node-card-spacer-y $node-card-spacer-x $node-card-spacer-y * 2;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: $node-card-spacer-sm-y $node-card-spacer-sm-x $node-card-spacer-sm-y * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ╷ ╷╶┬╴┌─╴╷╷╷
|
||||||
|
// │╭╯ │ ├─╴│││
|
||||||
|
// ╰╯ ╶┴╴╰─╴╰╯╯
|
||||||
|
|
||||||
|
&-view {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.node-view-wrapper {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-view &-header {
|
||||||
|
padding: $node-view-spacer-sm-y $node-view-spacer-sm-x 0;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: $node-view-spacer-y $node-view-spacer-x 0;
|
||||||
|
position: sticky;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-view &-footer {
|
||||||
|
padding: $node-view-spacer-sm-y $node-view-spacer-sm-x;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: $node-view-spacer-y $node-view-spacer-x;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors {
|
||||||
|
&-#{$color} {
|
||||||
|
background-color: lighten($value, 3.25%);
|
||||||
|
|
||||||
|
.node-view-header,
|
||||||
|
.node-view-footer {
|
||||||
|
background-color: lighten($value, 3.25%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="node-view-body"
|
||||||
|
:class="['node-view-body-' + mode, 'node-view-body-' + type]"
|
||||||
|
>
|
||||||
|
<slot name="default">
|
||||||
|
<div class="node-view-body-wrapper" v-html="content" />
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'NodeViewBody',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
content: { type: String, default: null },
|
||||||
|
type: { type: String, required: true },
|
||||||
|
mode: { type: String, required: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.node-view-body {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
font: {
|
||||||
|
family: $font-family-text;
|
||||||
|
line-height: inherit;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-view-view & {
|
||||||
|
padding: 0 $node-view-spacer-sm-x;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
&-ref {
|
||||||
|
font-size: 2.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-prod {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: 0 $node-view-spacer-x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-view-card & {
|
||||||
|
padding: 0 $node-card-spacer-sm-x;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
padding: 0 $node-card-spacer-x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-card &-wrapper {
|
||||||
|
@include line-clamp(3, 1.5em);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="node-view-child-list nav-list" :class="{ 'smartphone': smartphone }">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="child in children" :key="child.id"
|
||||||
|
class="node-view-child-list-item"
|
||||||
|
>
|
||||||
|
<dot-button
|
||||||
|
@click="onOpen(child.id)"
|
||||||
|
:variant="child.variant"
|
||||||
|
:active="activeNodes.includes(child.id)"
|
||||||
|
>
|
||||||
|
{{ $t('variants.' + child.variant) }}
|
||||||
|
</dot-button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NodeViewChildList',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
children: { type: Array, required: true },
|
||||||
|
parentId: { type: Number, required: true },
|
||||||
|
smartphone: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['activeNodes']),
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onOpen (childId) {
|
||||||
|
this.$store.dispatch('OPEN_NODE', [this.parentId, childId])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.node-view-child-list {
|
||||||
|
&-item {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.smartphone {
|
||||||
|
padding: $node-view-spacer-sm-y $node-view-spacer-sm-x;
|
||||||
|
background-color: $white;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="node-view-footer" :class="'node-view-footer-' + mode"
|
||||||
|
>
|
||||||
|
<div class="d-flex w-100">
|
||||||
|
<div class="tags">
|
||||||
|
<b-badge
|
||||||
|
v-for="tag in node.tags" :key="tag.id"
|
||||||
|
variant="dark" pill
|
||||||
|
>
|
||||||
|
{{ tag.name }}
|
||||||
|
</b-badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="mode === 'view' && node.siblings">
|
||||||
|
<b-button :id="'siblings-' + node.id">
|
||||||
|
{{ $t('siblings') }}
|
||||||
|
</b-button>
|
||||||
|
|
||||||
|
<b-popover
|
||||||
|
:target="'siblings-' + node.id" triggers="hover" placement="top"
|
||||||
|
>
|
||||||
|
<div v-for="sibling in node.siblings" :key="sibling.id">
|
||||||
|
<h6>
|
||||||
|
<a @click.prevent="onOpen(sibling.id)" href="#">
|
||||||
|
<div class="">
|
||||||
|
{{ toCommaList(sibling.authors) }},
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
{{ sibling.title }},
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
{{ sibling.edition ? sibling.edition.name : sibling.edition }}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</b-popover>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="mode === 'card'">
|
||||||
|
<b-button @click="onOpen(node.id)" variant="outline-dark">
|
||||||
|
{{ $t('text.read') }}
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { toCommaList } from '@/helpers/common'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NodeViewFooter',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
node: { type: Object, required: true },
|
||||||
|
type: { type: String, required: true },
|
||||||
|
mode: { type: String, required: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
authors () {
|
||||||
|
const authors = this.node.authors
|
||||||
|
if (!authors) return 'Pas d\'auteur⋅rices'
|
||||||
|
return authors.map(({ name }) => name).join(', ')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toCommaList,
|
||||||
|
|
||||||
|
onOpen (nodeId) {
|
||||||
|
this.$store.dispatch('OPEN_NODE', [nodeId])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.node-view-footer {
|
||||||
|
margin-top: auto;
|
||||||
|
font-family: $font-family-base;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="node-view-header-prod" :class="'node-view-header-' + mode"
|
||||||
|
>
|
||||||
|
<div class="d-flex w-100">
|
||||||
|
<h4 class="mr-auto">
|
||||||
|
<div class="node-view-header-type">
|
||||||
|
{{ $t('variants.' + node.variant) }} {{ mode === 'card' ? $t('from') : '' }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="mode === 'card'" class="node-view-header-parent">
|
||||||
|
<!-- FIXME display parent title and authors -->
|
||||||
|
{{ parent }}
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<button-close v-if="mode === 'view'" @click="onClose()" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { trim, toCommaList } from '@/helpers/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NodeViewHeaderProd',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
node: { type: Object, required: true },
|
||||||
|
mode: { type: String, required: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
parent () {
|
||||||
|
if (!this.node || !this.node.parents) return
|
||||||
|
return this.node.parents.find(parent => parent.variant === 'depart')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
trim,
|
||||||
|
toCommaList,
|
||||||
|
|
||||||
|
onClose () {
|
||||||
|
this.$store.dispatch('CLOSE_NODE', this.node.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.node-view-header {
|
||||||
|
&-prod {
|
||||||
|
font: {
|
||||||
|
family: $font-family-base;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors {
|
||||||
|
.node-view-#{$color} & {
|
||||||
|
color: darken($value, 32%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-parent {
|
||||||
|
font-family: $font-family-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
h4 {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-view {
|
||||||
|
h4 {
|
||||||
|
font-size: 1rem;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 1.4525rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="node-view-header-ref" :class="'node-view-header-' + mode"
|
||||||
|
>
|
||||||
|
<div class="d-flex w-100">
|
||||||
|
<h4 class="mr-auto">
|
||||||
|
<strong class="d-block">
|
||||||
|
{{ toCommaList(node.authors) }},
|
||||||
|
</strong>
|
||||||
|
<span v-if="node.preTitle" v-html="node.preTitle + ','" />
|
||||||
|
<em v-html="(trim(node.title) || 'pas de titre ital') + ','" />
|
||||||
|
<div class="edition">
|
||||||
|
{{ node.edition.map(ed => ed.name).join(' ') }}
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<node-view-child-list v-if="mode === 'view'" :children="node.children" :parent-id="node.id" />
|
||||||
|
<button-close v-if="mode === 'view'" @click="onClose()" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { trim, toCommaList } from '@/helpers/common'
|
||||||
|
import { NodeViewChildList } from '@/components/nodes'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NodeViewHeaderRef',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
NodeViewChildList
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
node: { type: Object, required: true },
|
||||||
|
mode: { type: String, required: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
trim,
|
||||||
|
toCommaList,
|
||||||
|
|
||||||
|
onClose () {
|
||||||
|
this.$store.dispatch('CLOSE_NODE', this.node.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.node-view-header {
|
||||||
|
&-ref {
|
||||||
|
font: {
|
||||||
|
family: $font-family-text;
|
||||||
|
size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: $font-weight-base;
|
||||||
|
font-size: 32px;
|
||||||
|
|
||||||
|
.authors {
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ╭─╴╭─┐┌─╮┌─╮
|
||||||
|
// │ ├─┤├┬╯│ │
|
||||||
|
// ╰─╴╵ ╵╵ ╰└─╯
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
|
||||||
|
.edition {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 2rem;
|
||||||
|
|
||||||
|
.edition {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ╷ ╷╶┬╴┌─╴╷╷╷
|
||||||
|
// │╭╯ │ ├─╴│││
|
||||||
|
// ╰╯ ╶┴╴╰─╴╰╯╯
|
||||||
|
|
||||||
|
&-view {
|
||||||
|
h4 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
|
||||||
|
.edition {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
font-size: 2.625rem;
|
||||||
|
|
||||||
|
.edition {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-view-child-list {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export { default as NodeViewChildList } from './NodeViewChildList'
|
||||||
|
export { default as NodeViewHeaderRef } from './NodeViewHeaderRef'
|
||||||
|
export { default as NodeViewHeaderProd } from './NodeViewHeaderProd'
|
||||||
|
export { default as NodeViewBody } from './NodeViewBody'
|
||||||
|
export { default as NodeViewFooter } from './NodeViewFooter'
|
||||||
|
export { default as NodeView } from './NodeView'
|
||||||
Reference in New Issue
Block a user