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 -х способов сделать это, что воля работы, каждый со своими недостатками.

  1. Используйте реквизиты для добавления класса через корневой элемент
  2. Используйте класс для корневого элемента и установите привязки стиля для элементов компонентов
  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>

0 ответов

Другие вопросы по тегам