Skip to content

Глобальная конфигурация ​

CDS 4 позволяет задать значения пропсов по умолчанию — глобально, для конкретного компонента или только внутри определённого поддерева. Так вы один раз описываете «как выглядят кнопки в нашем продукте» и перестаёте дублировать одни и те же пропсы в каждом шаблоне.

Настройка ​

Значения по умолчанию задаются опцией defaults в createCds. Ключ global действует на все компоненты, ключ с именем компонента — только на него.

ts
import { createApp } from 'vue';
import '@central-design-system/components/styles';
import { createCds, components, directives } from '@central-design-system/components';

import App from './App.vue';

const cds = createCds({
  components,
  directives,
  defaults: {
    global: {
      size: 'sm'
    },
    CdsButton: {
      appearance: 'secondary'
    },
    CdsTextInput: {
      hideDetails: 'auto'
    }
  }
});

createApp(App).use(cds);

Важно. Пропсы в defaults пишутся в camelCase: hideDetails, а не hide-details.

Ключ компонента — это его имя из defineOptions({ name }), то есть CdsButton, CdsTextInput, CdsCardActions и так далее. Имена подкомпонентов (CdsCardTitle, CdsListItem) работают так же, как имена корневых.

Порядок разрешения значения ​

Для каждого пропса CDS берёт первое подходящее значение:

  1. проп, явно переданный в шаблоне;
  2. defaults.<ИмяКомпонента>.<проп> из ближайшего контекста дефолтов;
  3. defaults.global.<проп>;
  4. значение по умолчанию, объявленное в самом компоненте.

Явно переданный проп всегда выигрывает — дефолты не могут «перебить» шаблон.

Изменение дефолтов в рантайме ​

createCds возвращает инстанс, у которого дефолты лежат в реактивной ссылке. Их можно менять на лету — все смонтированные компоненты пересчитаются:

ts
const cds = createCds({ components, directives });

// например, включить компактный режим по настройке пользователя
cds.defaults.value = {
  ...cds.defaults.value,
  global: { size: 'sm' }
};

Контекстные дефолты ​

Дефолты можно задать для компонентов, вложенных в другой компонент. Например, чтобы все кнопки внутри карточки по умолчанию были вторичными:

ts
createCds({
  defaults: {
    CdsCard: {
      CdsButton: { appearance: 'secondary' }
    }
  }
});

Правило простое: ключ с заглавной буквы — это имя вложенного компонента, ключ со строчной — проп текущего компонента. Вложенность не ограничена одним уровнем:

ts
createCds({
  defaults: {
    CdsCard: {
      CdsCardActions: {
        CdsButton: { appearance: 'tertiary', size: 'sm' }
      }
    }
  }
});

Тот же механизм библиотека использует внутри себя, чтобы группирующие компоненты раздавали свои настройки детям:

  • CdsButtonGroup задаёт вложенным CdsButton пропсы appearance, color, baseColor, size;
  • CdsTagGroup задаёт CdsTag пропсы color, activeColor, disabled, filter;
  • CdsSelectionControlGroup раздаёт настройки контролам, имя целевого компонента задаётся пропом defaultsTarget (CdsCheckbox, CdsRadio и т. д.);
  • так же работают CdsTabs, CdsSteps, CdsList, CdsBreadcrumbs, CdsButtonToggle, CdsExpandablePanels, CdsOtpInput.

Поэтому <CdsButtonGroup size="sm"> делает маленькими все кнопки внутри — вам не нужно проставлять size каждой.

Глобальные класс и стиль ​

В дефолтах компонента, помимо пропсов, можно задать class и style. Это удобный способ навесить продуктовые правила на все экземпляры компонента, не трогая шаблоны:

ts
createCds({
  defaults: {
    CdsButton: {
      class: 'my-button'
    }
  }
});

Работает любое значение, которое принимает Vue, — строка, объект, массив:

ts
createCds({
  defaults: {
    CdsButton: {
      style: [{ textTransform: 'none' }]
    }
  }
});

В отличие от обычных пропсов, class и style не перезаписываются шаблоном, а объединяются с ним. То есть <CdsButton class="mt-2"> получит и my-button, и mt-2.

Важно. class и style работают только в ключе конкретного компонента. В global они игнорируются.

Алиасы ​

Алиас — это тот же компонент, зарегистрированный под другим именем и со своими дефолтами. Из алиасов удобно собирать «продуктовые» варианты компонентов.

ts
import { createCds, components, directives, CdsButton } from '@central-design-system/components';

const cds = createCds({
  components,
  directives,
  aliases: {
    CdsButtonPrimary: CdsButton,
    CdsButtonDanger: CdsButton
  },
  defaults: {
    CdsButtonPrimary: {
      appearance: 'primary',
      class: 'my-button--primary'
    },
    CdsButtonDanger: {
      appearance: 'primary',
      color: 'red',
      class: 'my-button--danger'
    }
  }
});

Алиасы регистрируются глобально, поэтому в шаблоне их можно использовать сразу:

vue
<template>
  <CdsButtonPrimary>Сохранить</CdsButtonPrimary>
  <CdsButtonDanger class="ml-2">Удалить</CdsButtonDanger>
</template>

Важно. Алиас не наследует дефолты базового компонента. Если в конфигурации есть defaults.CdsButton, к <CdsButtonPrimary> они не применятся — дефолты ищутся по имени алиаса. Общие настройки для обоих выносите в global или дублируйте в ключе алиаса.

Дефолты для части приложения ​

Чтобы задать дефолты не глобально, а в поддереве, используйте компонент CdsDefaultsProvider. На нём же построена панель Controls в Storybook: значения из панели применяются к компонентам примера как дефолты.

vue
<script setup lang="ts">
import { CdsButton, CdsDefaultsProvider } from '@central-design-system/components';
</script>

<template>
  <CdsDefaultsProvider :defaults="{ CdsButton: { appearance: 'tertiary', size: 'sm' } }">
    <CdsButton>Второстепенная маленькая кнопка</CdsButton>
  </CdsDefaultsProvider>
</template>
ПропТипНазначение
defaultsDefaultsOptionsДефолты для поддерева; по умолчанию мержатся с унаследованными
scopedbooleanНе смешивать с родительскими дефолтами — действуют только заданные здесь
resetnumber | stringПодняться на N уровней вверх по цепочке дефолтов
rootboolean | stringВернуться к корневым дефолтам из createCds; строкой — к именованной группе внутри них
disabledbooleanНичего не провайдить — поддерево получает дефолты родителя как есть

Типичный сценарий scoped — «островок» в чужом окружении: вложенные группы (например, CdsButtonGroup) не должны навязывать свои настройки вашему блоку.

Свои компоненты ​

Собственные компоненты приложения тоже могут участвовать в системе дефолтов. Для этого пропустите пропсы через useDefaults:

vue
<script setup lang="ts">
import { useDefaults } from '@central-design-system/components';

const _props = defineProps<{ foo?: string }>();
const props = useDefaults(_props, 'MyComponent');
</script>

<template>
  <div>Я {{ props.foo }}</div>
</template>

У useDefaults два аргумента, оба опциональные:

  • props — объект пропсов, возвращённый defineProps;
  • name — имя компонента, по которому ищется ключ в defaults. Если не передать, берётся имя из defineOptions({ name }), а при его отсутствии — имя файла.

Теперь компонент настраивается из общей конфигурации:

ts
createCds({
  defaults: {
    MyComponent: { foo: 'бар' }
  }
});

Важно. В шаблоне обращайтесь к props.foo, а не к foo. useDefaults возвращает прокси над пропсами, и распакованные defineProps переменные его минуют:

diff
-<div>Я {{ foo }}</div>
+<div>Я {{ props.foo }}</div>

Вложенные дефолты в своих компонентах ​

Чтобы ваш компонент раздавал дефолты детям (как это делает CdsButtonGroup), достаточно вызвать useDefaults() без аргументов — вложенные ключи из его секции defaults уйдут в дочерний контекст:

vue
<script setup lang="ts">
import { useDefaults } from '@central-design-system/components';

useDefaults();
</script>

<template>
  <div class="my-panel">
    <slot />
  </div>
</template>
ts
createCds({
  defaults: {
    MyPanel: {
      CdsButton: { appearance: 'tertiary' }
    }
  }
});

Если дефолты детей зависят от пропсов компонента, используйте provideDefaults напрямую:

vue
<script setup lang="ts">
import { provideDefaults } from '@central-design-system/components';
import { toRef } from 'vue';

const props = defineProps<{ size?: 'sm' | 'md' }>();

provideDefaults({
  CdsButton: {
    size: toRef(() => props.size)
  }
});
</script>

<template>
  <div class="my-toolbar">
    <slot />
  </div>
</template>

Blueprint — пресет конфигурации ​

Если один и тот же набор опций нужен нескольким приложениям, вынесите его в blueprint: это обычный объект опций createCds, на который сверху накладывается остальная конфигурация.

ts
import { createCds, components, directives } from '@central-design-system/components';
import type { Blueprint } from '@central-design-system/components';

const productBlueprint: Blueprint = {
  theme: { defaultTheme: 'b2bLight' },
  defaults: {
    CdsButton: { appearance: 'secondary' }
  }
};

const cds = createCds({
  blueprint: productBlueprint,
  components,
  directives,
  // перекрывает значение из blueprint
  defaults: {
    CdsButton: { size: 'sm' }
  }
});

Опции сливаются глубоко (mergeDeep), поэтому в примере выше у кнопок останется appearance: 'secondary' из пресета и добавится size: 'sm'.