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 }"> <template slot-scope="{ errors }">
<base-card> <base-card>
<section :class="['card-image', form.blurImage && '--blur-image']"> <template v-slot:heroImage>
<img v-if="contribution" class="preview-image" :src="contribution.image | proxyApiUrl" /> <img
v-if="contribution"
:src="contribution.image | proxyApiUrl"
:class="['image', form.blurImage && '--blur-image']"
/>
<teaser-image <teaser-image
:contribution="contribution" :contribution="contribution"
:class="[form.blurImage && '--blur-image']"
@addTeaserImage="addTeaserImage" @addTeaserImage="addTeaserImage"
@addImageAspectRatio="addImageAspectRatio" @addImageAspectRatio="addImageAspectRatio"
/> />
</section> </template>
<div v-if="form.teaserImage || form.image" class="blur-toggle"> <div v-if="form.teaserImage || form.image" class="blur-toggle">
<label for="blur-img">{{ $t('contribution.inappropriatePicture') }}</label> <label for="blur-img">{{ $t('contribution.inappropriatePicture') }}</label>
<input type="checkbox" id="blur-img" v-model="form.blurImage" /> <input type="checkbox" id="blur-img" v-model="form.blurImage" />
@ -259,45 +264,35 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.contribution-form { .contribution-form > .base-card {
> .base-card { display: flex;
display: flex; flex-direction: column;
flex-direction: column;
> .card-image { > .hero-image {
position: relative; 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;
}
} }
.preview-image { .image.--blur-image {
width: 100%; filter: blur(22px);
max-height: 2000px; }
object-fit: contain;
> .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 { .blur-toggle {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,18 +9,53 @@ storiesOf('Generic/BaseCard', module)
components: { BaseCard }, components: { BaseCard },
template: ` template: `
<base-card> <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> <p>And I am a paragraph.</p>
</base-card> </base-card>
`, `,
})) }))
.add('with image', () => ({ .add('with slot: hero image', () => ({
components: { BaseCard }, components: { BaseCard },
template: ` template: `
<base-card style="width: 400px;"> <base-card style="width: 400px;">
<img class="card-image" src="https://unsplash.com/photos/R4y_E5ZQDPg/download" /> <template v-slot:heroImage>
<h2 class="card-heading">I am a card heading</h2> <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> <p>And I am a paragraph.</p>
</base-card> </base-card>
`, `,

View File

@ -1,11 +1,49 @@
<template> <template>
<article class="base-card"> <article :class="classNames">
<slot /> <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> </article>
</template> </template>
<script> <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> </script>
<style lang="scss"> <style lang="scss">
@ -16,30 +54,58 @@ export default {}
background-color: $color-neutral-100; background-color: $color-neutral-100;
box-shadow: $box-shadow-base; box-shadow: $box-shadow-base;
&.--columns {
display: flex;
}
&.--highlight { &.--highlight {
border: $border-size-base solid $color-primary; border: $border-size-base solid $color-warning;
} }
&.--wide-content { &.--wide-content {
padding: $space-small; padding: $space-small;
> .card-image { > .hero-image {
width: calc(100% + (2 * #{$space-small})); width: calc(100% + (2 * #{$space-small}));
margin: -$space-small; margin: -$space-small;
margin-bottom: $space-base; margin-bottom: $space-small;
} }
} }
> .card-image { > .title,
width: calc(100% + (2 * #{$space-base})); > .content-column > .title {
margin: -$space-base;
margin-bottom: $space-base;
font-size: $font-size-x-large;
}
> .card-heading {
font-size: $font-size-large; font-size: $font-size-large;
margin-bottom: $space-x-small; 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> </style>

View File

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