К содержимому

2. Формат .sharq и директивы шаблона ​

.sharq — Single File Component ​

Каждый файл .sharq — это Vue-подобный SFC с тремя секциями:

СекцияРоль
<template>UXML-разметка с директивами (v-if, :text, @click, …)
<script>C#-поля, методы, Prop<T>, жизненный цикл
<style>USS-стили для этого компонента (опционально scoped)
<template> … </template>
<script>   … </script>
<style>    … </style>          <!-- global USS for this file -->
<style scoped> … </style>      <!-- isolated USS (this component only) -->

Стили живут в .sharq. Предпочитайте USS-классы в <style> вместо element.style.* в C#.
См. также CSS Scoping — важно для root-селекторов.

Полный пример ​

xml
<!-- Card.sharq -->
<template>
  <ui:VisualElement $MainElement class="card">
    <ui:Label v-if="ShowTitle" :text="Title" class="card__title" />
    <ui:Label v-show="ShowSubtitle" :text="Subtitle" class="card__subtitle" />
    <ui:VisualElement v-for="item in Items" :key="item.Id" class="card__row">
      <ui:Label :text="item.Name" />
      <ui:Label :text="item.Description" />
    </ui:VisualElement>
    <ui:Button @click="OnCardClicked" class="card__button" />
    <slot name="footer" />
  </ui:VisualElement>
</template>

<script>
$using UnityEngine;
$using System.Collections.Generic;

public class CardItem
{
    public int Id;
    public string Name;
    public string Description;
}

public string Title = "Card Title";
public string Subtitle = "This is a subtitle";
public bool ShowTitle = true;
public bool ShowSubtitle = true;
public List<CardItem> Items = new()
{
    new CardItem { Id = 1, Name = "Feature A", Description = "First feature" },
    new CardItem { Id = 2, Name = "Feature B", Description = "Second feature" },
};

private void OnCardClicked()
{
    ShowSubtitle = !ShowSubtitle;
    Items.Add(new CardItem { Id = Items.Count + 1, Name = "New", Description = "Added" });
}
</script>

<style>
/* Child selectors — scoping works correctly after compile */
.card__title {
    font-size: 18px;
    color: white;
    -unity-font-style: bold;
}
.card__subtitle {
    font-size: 14px;
    color: rgba(255, 255, 255, 0.7);
    margin-top: 4px;
}
.card__row {
    flex-direction: row;
    margin-top: 8px;
}
.card__button {
    margin-top: 12px;
    height: 36px;
}
</style>

Компилятор создаёт:

  • Card.g.cs — C# компонент
  • Card.g.uss (и/или scoped/static USS) — стили из <style>

Редактируйте .sharq-исходник, а не сгенерированные файлы.


Секция Style (<style>) ​

Это полноценная часть формата — одного уровня значимости с <template> и <script>.

Global <style> ​

USS-правила, скомпилированные для этого компонента. Используйте BEM-подход с дочерними классами (.card__title), а не сам root-класс, когда применяется scoping:

xml
<style>
.card__title { font-size: 18px; }
</style>

<style scoped> ​

Добавляет уникальный класс .s-{hash} на каждый селектор и добавляет этот класс на корневой элемент компонента только (см. CSS Scoping):

xml
<style scoped>
.card { flex-grow: 1; }
.card:hover { opacity: 0.9; }
</style>

Scoped работает для root/host-level правил. Scope-класс находится только на корне, поэтому scoped-селекторы дочерних элементов (.card__title) не совпадут с дочерними элементами. Для стилизации дочерних используйте глобальный <style> с BEM-классами.

Root vs дочерние стили ​

  • Root/host правила → <style scoped> (.card → .card.s-hash, совпадает с корнем) или глобальный <style>.
  • Дочерние правила → глобальный <style> с BEM-классами (.card__title) — эмитируются как есть, надёжно совпадают.
  • Обращение к компоненту извне, или переменные :root → сопутствующий unscoped USS (Resources/SusRuntime/Card.uss), автозагружается в рантайме.

Подробности: CSS Scoping.

Классы вместо C# style.* ​

Делайте такНе делайте так
USS в <style> + AddToClassList / :classelement.style.width = 12f для статичной раскладки
Модификаторные классы (.card--open)Копирование тех же токенов в C#
EnableInClassList для состояния/переключателя (hover, open, hidden…)element.style.display = DisplayStyle.None

Используйте C# style.* только для непрерывного значения, вычисленного из данных в рантайме, которое невозможно перечислить классами (размер из prop, кадр tweena, позиция из resolvedStyle/GeometryChangedEvent) — и отметьте строку комментарием // sus:uss-impossible <почему>, чтобы исключение было намеренным, а не забытым костылём. Переключатель (display, position, flex*, align*, justify* и подобные) никогда не является валидным использованием style.*, маркер или нет — это всегда должны быть классы. Полное правило см. в Design tokens §1.4.

Инлайн-атрибут style="…" ​

Отличается от секции <style>. Атрибутные стили компилируются в статические USS-классы (с дедупликацией):

xml
<ui:VisualElement style="flex-grow: 1; font-size: 24px; color: white;" />

Предпочитайте секцию <style> для всего переиспользуемого.


$using — пространства имён ​

Аналог C# using. Размещается в начале <script>; компилятор эмитит их в сгенерированный .g.cs:

csharp
$using UnityEngine;
$using System.Collections.Generic;

$MainElement — корневой элемент ​

Отмечает, какой элемент является компонентом. Без него Sharq оборачивает содержимое в пустой VisualElement.

xml
<!-- Root = Label -->
<ui:Label $MainElement :text="Greeting" />

<!-- Root = VisualElement -->
<ui:VisualElement $MainElement class="wrapper" />

Директивы шаблона ​

v-if — условный рендер ​

Элемент добавляется/удаляется из иерархии (BindVisibility).

xml
<ui:Label v-if="IsVisible" :text="Message" />

v-show — скрытие через класс sus-hidden ​

Элемент остаётся в DOM; переключает класс sus-hidden, который разрешается в display: none через собственный USS компонента — аналогично переключателю на классах, описанному выше, никогда не прямая запись style.display.

xml
<ui:Label v-show="IsActive" :text="Status" />

v-for — списки ​

xml
<ui:VisualElement v-for="person in People" :key="person.Id" class="row">
    <ui:Label :text="person.Name" />
    <ui:Label :text="person.Role" />
</ui:VisualElement>

:key обязателен для key-based diff (StrictVForKey).

:text — биндинг текста ​

xml
<ui:Label :text="Greeting" />

text="literal" — статичный текст ​

xml
<ui:Button text="Click Me" @click="OnClick" />

@click / @event — обработчики ​

xml
<ui:Button @click="OnButtonClick" text="Click me" />

Поддерживаются: click, mouseenter, mouseleave, change.

:class — реактивные классы ​

xml
<ui:VisualElement :class='{ "card--open": IsOpen }' />