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