> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Блоки пользовательского кода

> Блок Custom code в Aftersell Cart: добавляйте собственный HTML или React в любое место выдвижной корзины, включая внутрь Cart items.

> Блок **Custom code** добавляет в корзину ваш собственный HTML или React. Разместите его в любой секции выдвижной корзины или вложите внутрь [**Cart items**](/ru/aftersell/cart/cart-items-block) как вложенный блок, чтобы он повторялся для каждой строки. В отличие от других блоков, у него нет настроек Content и раздела Design: блок *и есть* код, поэтому вы работаете полностью на его вкладке **Code**.

<Frame>
  <img src="https://mintcdn.com/aftersell/1Y3gBpUfxv16VGSW/images/aftersell/cart-custom-code-block-add-and-enable.gif?s=6717cc64a8765b0c06b65990f99e12ff" alt="Анимированный предпросмотр добавления и включения блока Custom code в редакторе Aftersell Cart" title="Анимированный предпросмотр добавления и включения блока Custom code в редакторе Aftersell Cart" width="1200" height="558" data-path="images/aftersell/cart-custom-code-block-add-and-enable.gif" />
</Frame>

<div id="add-and-turn-on-a-custom-code-block">
  ## Добавление и включение блока Custom code
</div>

1. Добавьте блок **Custom code** в любую секцию или как вложенный блок под **Cart items**.
2. Выберите его и откройте вкладку **Code**.
3. Выберите **HTML** или **React component**. Новые блоки по умолчанию используют HTML.
4. Напишите свой код.
5. Если вы выбрали React, нажмите <span style={{display:'inline-flex',alignItems:'center',gap:'4px',background:'#1C1C1C',color:'#fff',border:'1px solid #0A0A0A',borderRadius:'4px',padding:'0 6px',fontSize:'0.85em',fontWeight:500,lineHeight:'1.4em',verticalAlign:'middle'}}><svg width="8" height="9" viewBox="0 0 10 12" fill="none" aria-hidden="true" style={{display:'block'}}><path d="M1.5 1.2v9.6L8.8 6 1.5 1.2Z" stroke="#fff" strokeWidth="1.5" strokeLinejoin="round" /></svg>Compile</span>.
6. Включите **«Use custom template»**. Для этого блока этот переключатель означает «показывать мой пользовательский код», и по умолчанию он выключен, поэтому ничего не отображается, пока вы его не включите.
7. Держите переключатель-глаз в боковой панели включённым, чтобы блок оставался видимым для покупателей.

Чтобы блок появился, должны быть включены и переключатель-глаз, и **«Use custom template»**.

<div id="behavior">
  ## Поведение
</div>

* Блок ничего не отображает, пока корзина не загрузится.
* Он также ничего не отображает, когда выключен глаз в боковой панели, выключен **«Use custom template»**, код пуст или React не смог скомпилироваться или отобразиться. Поскольку сбой происходит без сообщений, проверьте блок в [предпросмотре](/ru/aftersell/cart/previewing-carts) перед публикацией.

<div id="html-mode">
  ## Режим HTML
</div>

Режим HTML подставляет небольшой набор токенов в вашу разметку. Он предназначен для статического или управляемого токенами контента, а не для выполнения логики.

* **Встроенные теги `<script>` не выполняются**, и режим HTML **не имеет доступа к SDK или `window`.**
* Для логики используйте [**режим React**](#react-mode) или [пользовательские скрипты](/ru/aftersell/cart/custom-scripts) с [Cart SDK](/ru/aftersell/cart/sdk-overview).

<div id="tokens">
  ### Токены
</div>

Значения токенов — это **отформатированные строки** (денежный формат магазина, процент со знаком `%` или количество), готовые к вставке в разметку:

| Токен                    | Что показывает                                                |
| ------------------------ | ------------------------------------------------------------- |
| `{{pre_cart_total}}`     | Итог корзины до скидок.                                       |
| `{{post_cart_total}}`    | Итог корзины после скидок.                                    |
| `{{savings_amount}}`     | Сэкономленная сумма (итог до скидок минус итог после скидок). |
| `{{savings_percentage}}` | Экономия в процентах, включая знак `%` (например `15%`).      |
| `{{cart_quantity}}`      | Количество видимых товаров в корзине.                         |

<div id="example">
  ### Пример
</div>

```html theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
<div class="cart-external-custom-code_html">
  You saved {{savings_amount}} ({{savings_percentage}})
</div>
```

<div id="react-mode">
  ## Режим React
</div>

Режим React компилирует компонент и передаёт ему данные корзины плюс действие `add-to-cart`.

* Редактор фиксирует обёртку `function CustomCode(props: CustomCodeProps) { … }`, и вы редактируете только тело между этими строками.
* Прежде чем блок появится, необходимо нажать <span style={{display:'inline-flex',alignItems:'center',gap:'4px',background:'#1C1C1C',color:'#fff',border:'1px solid #0A0A0A',borderRadius:'4px',padding:'0 6px',fontSize:'0.85em',fontWeight:500,lineHeight:'1.4em',verticalAlign:'middle'}}><svg width="8" height="9" viewBox="0 0 10 12" fill="none" aria-hidden="true" style={{display:'block'}}><path d="M1.5 1.2v9.6L8.8 6 1.5 1.2Z" stroke="#fff" strokeWidth="1.5" strokeLinejoin="round" /></svg>Compile</span>, а затем включить **«Use custom template»**.
* Ваш компонент может использовать `useState`, `useEffect`, `useMemo`, `useRef` и `useCallback`.
* В отличие от режима HTML, React выполняется в контексте страницы, поэтому может обращаться к `window` и [Cart SDK](/ru/aftersell/cart/sdk-overview), когда они доступны.
* Если ваш компонент выбрасывает ошибки во время выполнения, блок ничего не отображает, а остальная часть корзины продолжает работать.

<div id="props">
  ### Пропсы
</div>

Итоги и суммы экономии — целые числа в [младших единицах](/ru/aftersell/cart/sdk-actions#formatmoneycents) валюты (центы для USD), поэтому `$12.50` — это `1250`, а не `12.50`. Это не отформатированные денежные строки, как токены HTML.

| Проп                                            | Тип                         | Описание                                                                                                                       |
| ----------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `cart`                                          | `AftersellCart`             | Текущая корзина. См. [справочник объекта корзины](/ru/aftersell/cart/sdk-cart-object).                                         |
| `line`                                          | `AftersellCartLine \| null` | Задан только когда блок является вложенным блоком Cart items (одно отображение на строку); `null` в секции.                    |
| `preCartTotal`                                  | `number`                    | Итог корзины **до скидок** (`original_total_price` в Shopify), в младших единицах валюты (например, центах).                   |
| `postCartTotal`                                 | `number`                    | Итог корзины **после скидок**, в младших единицах валюты.                                                                      |
| `savings`                                       | `{ amount, percentage }`    | Сумма и процент экономии.                                                                                                      |
| `addProduct(variantId, quantity?, properties?)` | `function`                  | Добавляет товар в корзину с меткой атрибуции этого блока, чтобы [аналитика](/ru/aftersell/cart/analytics) могла его засчитать. |

<div id="the-cart-and-line-shapes">
  ### Структуры cart и line
</div>

`cart` и `line` — те же объекты, которые SDK предоставляет везде, поэтому они задокументированы один раз в **[справочнике объекта корзины](/ru/aftersell/cart/sdk-cart-object)**: каждое поле корзины, строки и бандла.

Те, к которым вы будете обращаться чаще всего: `cart.items`, `cart.itemCount`, `cart.totalPrice`, `line.title`, `line.quantity`, `line.finalLinePrice`.

Три особенности этого блока:

* **`line` задан только у вложенного блока Cart items**, где ваш компонент отображается один раз на строку. При размещении в секции `line` равен `null`, и вместо этого вы читаете `cart.items`.
* **Дочерние элементы бандла отсутствуют в `cart.items`.** Когда строки [сгруппированы в бандл](/ru/aftersell/cart/sdk-use-case-bundles), появляется только якорная строка; её дочерние элементы находятся в `line.bundle.children`.
* **Строк, скрытых [трансформацией строк](/ru/aftersell/cart/sdk-hooks#registerlinetransform), там тоже нет**, хотя они по-прежнему учитываются в `cart.totalPrice`.

<div id="examples">
  ### Примеры
</div>

Показать количество товаров:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  return (
    <div className="cart-external-custom-code_jsx">
      {props.cart.itemCount} items
    </div>
  );
}
```

Как вложенный блок Cart items, используйте `props.line` для контента по каждому товару. Блок отображается один раз на строку, помеченный товаром и вариантом этой строки:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  if (!props.line) return null;
  return (
    <div className="cart-external-custom-code_jsx">
      {props.line.productTitle}
      {props.line.variantTitle ? ` · ${props.line.variantTitle}` : ''}
    </div>
  );
}
```

<div id="reading-enrichment-metadata">
  ### Чтение метаданных обогащения
</div>

Каждый элемент в `cart.items` несёт поле `metadata`: пустой объект `{}`, пока [обогатитель корзины](/ru/aftersell/cart/sdk-hooks#registercartenricher) не заполнит его. После заполнения оно индексируется по `id` обогатителя и содержит данные Storefront для товара или варианта этой строки:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  return (
    <ul>
      {(props.cart.items ?? []).map((item) => {
        const note = item.metadata?.shipping?.shippingNote;
        return (
          <li key={item.key}>
            {item.title}
            {note ? ` · ${note.value}` : ''}
          </li>
        );
      })}
    </ul>
  );
}
```

`metadata` всегда присутствует и по умолчанию является пустым объектом `{}`, пока асинхронная загрузка обогатителя не завершится (проверка «ещё не обогащено» — `Object.keys(item.metadata).length === 0`). Используйте опциональную цепочку (`item.metadata?.enricherId`) при чтении ключа конкретного обогатителя, поскольку этот ключ отсутствует, пока обогащение не завершится.

<div id="reading-discount-codes-and-line-discounts">
  ### Чтение кодов скидок и скидок по строкам
</div>

`cart.discountCodes` перечисляет коды скидок, применённые к корзине, а `discountAllocations` каждой строки перечисляет скидки, применённые к этой конкретной строке:

```jsx theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
function CustomCode(props) {
  const codes = props.cart.discountCodes;
  return (
    <div>
      {codes.length > 0 && (
        <p>Active discounts: {codes.join(', ')}</p>
      )}
      <ul>
        {(props.cart.items ?? []).map((item) => {
          return (
            <li key={item.key}>
              {item.title}
              {item.discountAllocations.map(
                (discount) => ` · ${discount.title} (-${(discount.amount / 100).toFixed(2)})`
              )}
            </li>
          );
        })}
      </ul>
    </div>
  );
}
```

<div id="placement-and-limits">
  ## Размещение и ограничения
</div>

* **Область:** любая (верх, тело или низ). Также доступен как вложенный блок Cart items.
* **Максимум:** без ограничений.
* **Состояние:** заполненная и пустая корзина (как блок секции). Как вложенный блок Cart items отображается только когда в корзине есть строки, по одному экземпляру на строку.
* Не заблокирован, поэтому вы можете удалить или скрыть его.
* Поблочного раздела Design нет. Оформляйте через собственную разметку, [**пользовательский CSS**](/ru/aftersell/cart/custom-css) и глобальные [**настройки дизайна**](/ru/aftersell/cart/design-settings).

<div id="when-to-use-custom-code-block-vs-custom-template-vs-custom-script">
  ## Когда использовать блок пользовательского кода, пользовательский шаблон или пользовательский скрипт
</div>

|                                                                    | Что делает                                                                          | Когда использовать                                                            | Пример                                                                                                                                                                                     |
| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Блок Custom code**                                               | Добавляет *новый* блок вашего собственного HTML или React.                          | Что-то, чего встроенные блоки не покрывают.                                   | Строка расчётного итога, добавляющая вашу фиксированную стоимость доставки к итогу корзины, или обратный отсчёт до времени отправки над кнопкой оформления заказа.                         |
| **[Пользовательский шаблон](/ru/aftersell/cart/custom-templates)** | Заменяет отображение *существующего* блока вашим JSX, используя данные этого блока. | Встроенный блок почти подходит, но вам нужна другая разметка.                 | Перестроить [строку Product](/ru/aftersell/cart/cart-items-block#custom-template), чтобы название варианта, экономия и выбор количества располагались в одну строку.                       |
| **[Пользовательский скрипт](/ru/aftersell/cart/custom-scripts)**   | Выполняет JavaScript для корзины через [Cart SDK](/ru/aftersell/cart/sdk-overview). | Логика всей корзины, события и конфигурация, а не разметка выдвижной корзины. | Потратьте \$75 — получите бесплатную сумку: [добавьте подарок](/ru/aftersell/cart/sdk-use-case-free-gift), когда корзина пересекает порог, и уберите его, если покупатель опускается ниже. |
