VueJS - применение стилей к элементам упаковки слотов
Я хочу создать Card
компонент с использованием vue, у которого есть заголовок и раздел содержимого с использованием api слотов vuejs.
Мне трудно понять, как я могу создать структуру в моем компоненте Card. Карточка должна содержать все стили каркаса (отступы и поля), но должна быть открыта для расширения из дочернего шаблона, который использует компонент.
Я хотел бы знать, есть ли какие-либо стандартные способы стилизации элементов оболочки со слотами внутри них из дочернего компонента.
Основа Card
Элемент имеет 2 слота, обернутых структурными элементами, которые я хотел бы разрешить дочернему шаблону при необходимости изменять.
<div class="Card">
<header class="CardHeader">
<slot name="header"></slot>
</header>
<div class="CardContent">
<slot></slot>
</div>
</div>
В идеале я бы хотел добавить :class
атрибут к #slot
element и использовать его в элементе в слоте, однако, похоже, что старый синтаксис был обесценен в новейшей версии платформы, например.
<Card>
<template #header class="extendHeader">
stuff
</template>
</Card>
Я могу думать о 3 -х способов сделать это, что воля работы, каждый со своими недостатками.
- Используйте реквизиты для добавления класса через корневой элемент
- Используйте класс для корневого элемента и установите привязки стиля для элементов компонентов
- Оберните содержимое слота в другой элемент
Обязательным условием для моего проекта является использование css-модулей, чтобы уменьшить как можно больше глобальных css, потому что это причиняло мне столько горя в прошлом.
1. Используйте props, чтобы добавить класс через корневой элемент.
Этот метод позволил бы мне использовать css-модули и сделал бы Card
элементы, открытые для расширения
В шаблоне компонента потребуется составить дополнительные стили из дочернего шаблона.
<header :class="$style.Header, ...headerClasses]">
<slot name="header"></slot>
</header>
Затем дочерний шаблон может использовать :header-classes
для расширения базовых классов Card или перезаписи ненужных стилей.
<Card :header-classes="[$style.header]">
<template #header>
stuff
</template>
</Card>
<style module>
.header {
background: var(--v-blue);
}
</style>
2. Используйте класс для корневого элемента и установите привязки стиля к элементам компонентов.
В Card
шаблону потребуется назначить дополнительные классы, которые можно использовать для расширения базовых структурных компонентов компонента. Классы css-модуля не должны использоваться, поскольку имена хешируются во избежание коллизий.
<div :class="[$style.Card, 'CardStyleHook']">
<header :class="[$style.Header, 'CardHeaderStyleHook']">
<slot name="header"></slot>
</header>
<div :class="[$style.Content, 'CardContentStyleHook']">
<slot></slot>
</div>
</div>
Затем к компоненту можно добавить один класс и использовать стили для дополнительных классов.
<Card :class="$style.extendCard">
<template #header>
stuff
</template>
</Card>
<style module>
.extendCard .CardHeaderStyleHook {
background: var(--v-blue);
}
</style>
3. Оберните содержимое слота в другой элемент.
Я думаю, что такого рода неудачи противоречат назначению слотов, и это приведет к проблемам, при которых мне нужно удалить прокладку из структурного элемента упаковки в Card
, на самом деле он не открыт для расширения и потребует дополнительных обходных путей, чтобы этот стиль работал.
<Card>
<template #header>
<div :class="$style.customHeader">
stuff
</div>
</template>
</Card>
<style module>
.customHeader{
background: var(--v-blue);
}
</style>
Есть ли какой-нибудь стандартный способ справиться с этими случаями, поскольку старые слоты обесценились? я что-то пропустил в документации, я играл с vue всего пару дней, так что это определенно возможно.
В приведенном ниже фрагменте есть полные примеры с рабочим кодом, если вам нужно предоставить какие-либо примеры.
Спасибо за любую помощь!
const use$styleMocking = {
beforeMount() {
// mock css-modules output
this.$style = {
Card: 'Card__x1337',
Header: 'CardHeader__x1337',
Content: 'CardContent__x1337',
}
}
}
Vue.component('Card', {
template: '#card-template',
mixins: [ use$styleMocking ],
props: {
cardClasses: { default: () => [] },
headerClasses: { default: () => [] },
contentClasses: { default: () => [] },
}
})
Vue.component('style-hooked-card', {
template: '#style-hooked-card-template',
mixins: [ use$styleMocking ]
})
const app = new Vue({
el: '#app',
template: '#app-template'
})
:root {
--white: #fff;
--v-teal: #00c58e;
--v-jade: #108775;
--v-blue: #2f495e;
--card-padding: 0.5rem 1rem;
--grid-gap: 1.25rem;
}
html, body {
font-family: sans-serif;
line-height: 1.44;
}
.main {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: var(--grid-gap);
grid-row-gap: var(--grid-gap);
}
/* --------- Card Styles --------- */
/*
styles have __x1337 to mock css-modules, the client should never know the hash
and should not use it to style elements
*/
.Card__x1337 {
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
transition: 400ms box-shadow;
margin: 0;
flex: 1;
}
.CardHeader__x1337 {
padding: var(--card-padding);
background: var(--v-teal);
}
.CardHeader__x1337 h3 {
color: var(--v-blue);
margin: 0;
padding: 0;
font-size: 1rem;
}
.CardContent__x1337 {
padding: var(--card-padding);
}
/* --------- End Card Styles --------- */
.extendHeader {
background: var(--v-blue);
}
.extendHeader h3 {
color: var(--white);
}
.extendCard .CardHeaderStyleHook {
background: var(--v-jade);
}
.extendCard .CardHeaderStyleHook h3 {
color: var(--white);
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<template type="text/x-template" id="app-template">
<main class="main">
<Card>
<template #header class="extendHeader">
<h3>Basic Card Styles</h3>
</template>
<template>
<p>It would be ideal to add a class to the template and have it available to add to the classes of the wrapper element around slots</p>
</template>
</Card>
<Card :header-classes="['extendHeader']">
<template #header>
<h3>1. Extend Header Styles</h3>
</template>
<template>
<p>This card uses props on the Card to extend the wrapper class, this example is using a global class, but this would use css-modules locally scoped classes correctly</p>
</template>
</Card>
<Style-Hooked-Card class="extendCard">
<template #header>
<h3>2. Extend Card Styles</h3>
</template>
<template>
<p>This card adds a hook class to every component so that it can be extended from the outside</p>
</template>
</Style-Hooked-Card>
</main>
</template>
<template type="text/x-template" id="card-template">
<div :class="[$style.Card, ...cardClasses]">
<header :class="[$style.Header, ...headerClasses]">
<slot name="header"></slot>
</header>
<div :class="[$style.Content, ...contentClasses]">
<slot></slot>
</div>
</div>
</template>
<template type="text/x-template" id="style-hooked-card-template">
<div :class="[$style.Card, 'CardStyleHook']">
<header :class="[$style.Header, 'CardHeaderStyleHook']">
<slot name="header"></slot>
</header>
<div :class="[$style.Content, 'CardContentStyleHook']">
<slot></slot>
</div>
</div>
</template>
<div id="app"></div>