refactor BaseCard to use slots and props

This commit is contained in:
Alina Beck 2020-02-11 16:10:45 +01:00
parent 9c3378807c
commit 37959d8167
10 changed files with 199 additions and 112 deletions

View File

@ -8,14 +8,19 @@
>
<template slot-scope="{ errors }">
<base-card>
<section :class="['card-image', form.blurImage && '--blur-image']">
<img v-if="contribution" class="preview-image" :src="contribution.image | proxyApiUrl" />
<template v-slot:heroImage>
<img
v-if="contribution"
:src="contribution.image | proxyApiUrl"
:class="['image', form.blurImage && '--blur-image']"
/>
<teaser-image
:contribution="contribution"
:class="[form.blurImage && '--blur-image']"
@addTeaserImage="addTeaserImage"
@addImageAspectRatio="addImageAspectRatio"
/>
</section>
</template>
<div v-if="form.teaserImage || form.image" class="blur-toggle">
<label for="blur-img">{{ $t('contribution.inappropriatePicture') }}</label>
<input type="checkbox" id="blur-img" v-model="form.blurImage" />
@ -259,45 +264,35 @@ export default {
</script>
<style lang="scss">
.contribution-form {
> .base-card {
display: flex;
flex-direction: column;
.contribution-form > .base-card {
display: flex;
flex-direction: column;
> .card-image {
position: relative;
overflow: hidden;
margin-bottom: $space-xx-small;
&.--blur-image img {
filter: blur(22px);
}
}
> .ds-form-item {
margin: 0;
}
> .ds-chip {
align-self: flex-end;
margin: $space-xx-small 0 $space-base;
cursor: default;
}
> .select-field {
align-self: flex-end;
}
> .buttons {
align-self: flex-end;
margin-top: $space-base;
}
> .hero-image {
position: relative;
}
.preview-image {
width: 100%;
max-height: 2000px;
object-fit: contain;
.image.--blur-image {
filter: blur(22px);
}
> .ds-form-item {
margin: 0;
}
> .ds-chip {
align-self: flex-end;
margin: $space-xx-small 0 $space-base;
cursor: default;
}
> .select-field {
align-self: flex-end;
}
> .buttons {
align-self: flex-end;
margin-top: $space-base;
}
.blur-toggle {

View File

@ -1,6 +1,6 @@
<template>
<base-card class="delete-data">
<h2 class="card-heading">
<h2 class="title">
<base-icon name="warning" />
{{ $t('settings.deleteUserAccount.name') }}
</h2>
@ -103,7 +103,7 @@ export default {
display: flex;
flex-direction: column;
> .card-heading > .base-icon {
> .title > .base-icon {
color: $color-danger;
}

View File

@ -9,8 +9,8 @@
:to="{ name: 'post-id-slug', params, ...hashParam }"
@click.native="$emit('read')"
>
<base-card class="--wide-content">
<h2 class="card-heading">{{ from.title || from.post.title }}</h2>
<base-card wideContent>
<h2 class="title">{{ from.title || from.post.title }}</h2>
<p>
<strong v-if="isComment" class="comment">{{ $t(`notifications.comment`) }}:</strong>
{{ from.contentExcerpt | removeHtml }}

View File

@ -44,7 +44,7 @@ export const post = {
__typename: 'Post',
}
storiesOf('Post Card', module)
storiesOf('PostTeaser', module)
.addDecorator(withA11y)
.addDecorator(helpers.layout)
.add('without image', () => ({
@ -82,11 +82,7 @@ storiesOf('Post Card', module)
data: () => ({
post: {
...post,
pinnedBy: {
id: '4711',
name: 'Ad Min',
role: 'admin',
},
pinned: true,
},
}),
template: `

View File

@ -7,17 +7,17 @@
:lang="post.language"
:class="{
'disabled-content': post.disabled,
'--highlight': isPinned,
'--blur-image': post.imageBlurred,
}"
:highlight="isPinned"
>
<div v-if="post.image" class="card-image">
<template v-if="post.image" v-slot:heroImage>
<img :src="post.image | proxyApiUrl" class="image" />
</div>
</template>
<client-only>
<user-teaser :user="post.author" :date-time="post.createdAt" />
</client-only>
<h2 class="card-heading hyphenate-text">{{ post.title }}</h2>
<h2 class="title hyphenate-text">{{ post.title }}</h2>
<!-- TODO: replace editor content with tiptap render view -->
<!-- eslint-disable vue/no-v-html -->
<div class="content hyphenate-text" v-html="excerpt" />
@ -87,7 +87,7 @@ export default {
mounted() {
const width = this.$el.offsetWidth
const height = Math.min(width / this.post.imageAspectRatio, 2000)
const imageElement = this.$el.querySelector('.card-image > .image')
const imageElement = this.$el.querySelector('.hero-image')
if (imageElement) {
imageElement.style.height = `${height}px`
}
@ -157,24 +157,10 @@ export default {
flex-direction: column;
height: 100%;
&.--pinned {
border: 1px solid $color-warning;
}
&.--blur-image > .card-image > .image {
&.--blur-image > .hero-image > .image {
filter: blur(22px);
}
> .card-image {
overflow: hidden;
> .image {
width: 100%;
max-height: 2000px;
object-fit: contain;
}
}
> .content {
flex-grow: 1;
margin-bottom: $space-small;

View File

@ -45,13 +45,13 @@ export default {
border-style: solid;
border-color: $background-color-secondary transparent transparent $background-color-secondary;
}
}
.ribbon--pinned {
background-color: $color-warning-active;
&.--pinned {
background-color: $color-warning;
&::before {
border-color: $color-warning transparent transparent $color-warning;
&::before {
border-color: $color-warning transparent transparent $color-warning;
}
}
}
</style>

View File

@ -91,7 +91,7 @@ export default {
this.closeCropper()
},
setupPreview(url) {
const previewElement = document.querySelector('.preview-image')
const previewElement = document.querySelector('.image-uploader .preview-image')
previewElement.src = url
this.$nextTick((this.isLoadingImage = false))
},
@ -108,6 +108,14 @@ export default {
min-height: 200px;
cursor: pointer;
.image + & {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
&:only-child {
background-color: $color-neutral-85;
}
@ -116,12 +124,14 @@ export default {
pointer-events: none;
}
.preview-image + & {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
&.--blur-image .preview-image {
filter: blur(22px);
}
.preview-image {
width: 100%;
max-height: 2000px;
object-fit: contain;
}
> .crop-overlay {

View File

@ -9,18 +9,53 @@ storiesOf('Generic/BaseCard', module)
components: { BaseCard },
template: `
<base-card>
<h2 class="card-heading">I am a card heading</h2>
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with image', () => ({
.add('with slot: hero image', () => ({
components: { BaseCard },
template: `
<base-card style="width: 400px;">
<img class="card-image" src="https://unsplash.com/photos/R4y_E5ZQDPg/download" />
<h2 class="card-heading">I am a card heading</h2>
<template v-slot:heroImage>
<img class="image" src="https://unsplash.com/photos/R4y_E5ZQDPg/download" />
</template>
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with slot: image column', () => ({
components: { BaseCard },
template: `
<base-card style="width: 600px;">
<template v-slot:imageColumn>
<img class="image" src="/img/sign-up/humanconnection.svg" />
</template>
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with highlight prop', () => ({
components: { BaseCard },
template: `
<base-card highlight style="width: 400px;">
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with wideContent prop', () => ({
components: { BaseCard },
template: `
<base-card wideContent style="width: 400px;">
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,

View File

@ -1,11 +1,49 @@
<template>
<article class="base-card">
<slot />
<article :class="classNames">
<template v-if="$slots.imageColumn">
<aside class="image-column">
<slot name="imageColumn" />
</aside>
<section class="content-column">
<slot />
</section>
</template>
<template v-else-if="$slots.heroImage">
<section class="hero-image">
<slot name="heroImage" />
</section>
<slot />
</template>
<slot v-else />
</article>
</template>
<script>
export default {}
export default {
props: {
highlight: {
type: Boolean,
default: false,
},
wideContent: {
type: Boolean,
default: false,
},
},
computed: {
classNames() {
let classNames = 'base-card'
if (this.$slots.imageColumn) classNames += ' --columns'
if (this.highlight) classNames += ' --highlight'
if (this.wideContent) classNames += ' --wide-content'
return classNames
}
}
}
</script>
<style lang="scss">
@ -16,30 +54,58 @@ export default {}
background-color: $color-neutral-100;
box-shadow: $box-shadow-base;
&.--columns {
display: flex;
}
&.--highlight {
border: $border-size-base solid $color-primary;
border: $border-size-base solid $color-warning;
}
&.--wide-content {
padding: $space-small;
> .card-image {
> .hero-image {
width: calc(100% + (2 * #{$space-small}));
margin: -$space-small;
margin-bottom: $space-base;
margin-bottom: $space-small;
}
}
> .card-image {
width: calc(100% + (2 * #{$space-base}));
margin: -$space-base;
margin-bottom: $space-base;
font-size: $font-size-x-large;
}
> .card-heading {
> .title,
> .content-column > .title {
font-size: $font-size-large;
margin-bottom: $space-x-small;
}
> .hero-image {
width: calc(100% + (2 * #{$space-base}));
max-height: 2000px;
margin: -$space-base;
margin-bottom: $space-base;
overflow: hidden;
> .image {
width: 100%;
object-fit: contain;
}
}
> .image-column {
flex-basis: 50%;
display: flex;
justify-content: center;
align-items: center;
padding-right: $space-base;
> .image {
width: 100%;
max-width: 200px;
}
}
> .content-column {
flex-basis: 50%;
}
}
</style>

View File

@ -1,25 +1,24 @@
<template>
<ds-container width="medium">
<ds-card>
<ds-flex gutter="small">
<ds-flex-item :width="{ base: '100%', sm: '50%' }">
<locale-switch offset="5" />
<ds-space margin-top="small" margin-bottom="xxx-small">
<img class="signup-image" alt="Human Connection" src="/img/sign-up/nicetomeetyou.svg" />
</ds-space>
</ds-flex-item>
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
<nuxt-child />
</ds-flex-item>
</ds-flex>
</ds-card>
<card-with-columns>
<template v-slot:left>
<locale-switch offset="5" />
<img class="signup-image" alt="Human Connection" src="/img/sign-up/nicetomeetyou.svg" />
</template>
<template v-slot:right>
<nuxt-child />
</template>
</card-with-columns>
</ds-container>
</template>
<script>
import CardWithColumns from '~/components/_new/generic/CardWithColumns/CardWithColumns'
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
export default {
components: {
CardWithColumns,
LocaleSwitch,
},
layout: 'no-header',