@rotcetihra/c2c 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,659 @@
1
+ @use "sass:map";
2
+ @use "sass:meta";
3
+ @use "sass:math";
4
+ @use "var";
5
+ @use "render";
6
+ @use "validate";
7
+ @use "utility";
8
+
9
+ $REQUIRED: (
10
+ "blocks": (
11
+ "values": (),
12
+ ),
13
+ "properties": (
14
+ "values": (),
15
+ ),
16
+ "classes": (
17
+ "values": (),
18
+ ),
19
+ "steps": (
20
+ "meta": (
21
+ "step",
22
+ "from",
23
+ "to",
24
+ ),
25
+ "values": (),
26
+ ),
27
+ "fractions": (
28
+ "meta": (
29
+ "from",
30
+ "to",
31
+ ),
32
+ "values": (),
33
+ ),
34
+ "palettes": (
35
+ "values": (),
36
+ ),
37
+ );
38
+
39
+ /// Главный оркестратор конфигурации (Точка входа).
40
+ /// Рекурсивно обходит переданную карту конфигурации модуля (например, $config для "effects" или "svg"),
41
+ /// разделяет её на типы блоков (properties, steps, palettes) и обогащает каждый подконфиг
42
+ /// служебными метаданными для последующей валидации и генерации атомарных CSS-классов.
43
+ ///
44
+ /// @param {Map} $config - Полный конфигурационный Sass-map отдельного модуля.
45
+ /// Ожидает обязательную структуру:
46
+ /// - "name": Имя модуля (String).
47
+ /// - Любые другие ключи-группы (List), например: "properties", "steps", "palettes", "classes".
48
+ ///
49
+ /// @output Передает управление миксину `@include group($sub-config)`, отправляя туда модифицированный блок данных.
50
+ ///
51
+ /// @example scss - Пример входных данных через переменную $config
52
+ /// $config: (
53
+ /// "name": "interactivity",
54
+ /// "properties": ( ( "use": (...), "values": (...) ) )
55
+ /// );
56
+ /// @include by($config);
57
+ ///
58
+ @mixin by($config) {
59
+ // Извлекаем имя модуля для трассировки ошибок, дефолтимся на "unknown" во избежание креша компиляции
60
+ $name: map.get($config, "name") or "unknown";
61
+
62
+ // Итерируемся по всем ключам конфигурации (например: 'name', 'properties', 'steps', 'palettes')
63
+ @each $group, $sub-configs in $config {
64
+ // Игнорируем служебный ключ "name", так как он не является списком конфигураций утилит
65
+ @if $group != "name" {
66
+ $index: 1; // Счётчик для определения порядкового номера блока внутри группы
67
+
68
+ // Обходим массив подконфигов (каждый элемент — это отдельный блок настроек утилит)
69
+ @each $sub-config in $sub-configs {
70
+ // Обогащаем локальный map служебными данными (контекстом).
71
+ // Это позволяет вложенным миксинам (валидаторам, генераторам) точно знать,
72
+ // в каком модуле, какой группе и на какой строчке произошла ошибка или генерируется класс.
73
+ $sub-config: map.set($sub-config, "name", $name);
74
+ $sub-config: map.set($sub-config, "group", $group);
75
+ $sub-config: map.set($sub-config, "index", $index);
76
+
77
+ // Делегируем сборку и обработку конкретной группы утилит нижестоящему миксину
78
+ @include group($sub-config);
79
+
80
+ $index: $index + 1; // Инкрементируем индекс для следующего блока в этой группе
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ /// Контроллер группы утилит (Диспетчер генерации).
87
+ /// Валидирует переданный подконфиг, динамически определяет нужный миксин-генератор
88
+ /// на основе имени группы и разворачивает утилиты во всех комбинациях вариантов:
89
+ /// базовые классы, классы состояний (псевдоклассы), адаптивные классы (media queries)
90
+ /// и их пересечения (например, `md:h:class`).
91
+ ///
92
+ /// @param {Map} $config - Карта конфигурации отдельного блока, обогащенная метаданными оркестратора `by`.
93
+ /// Ожидает ключи:
94
+ /// - "group" (String) - Имя группы/генератора (например, "properties", "steps", "palettes").
95
+ /// - "meta" (Map) - Метаданные блока (для steps: step, from, to, unit).
96
+ /// - "use" (Map) - Флаги компиляции вариантов (breakpoints: Boolean, states: List/Boolean).
97
+ /// - "values" (Map) - Набор утилит и их CSS-свойств.
98
+ ///
99
+ /// @require {mixin} validate.config - Для проверки наличия обязательных полей.
100
+ /// @require {map} $REQUIRED - Глобальная карта со схемами валидации для каждого типа группы.
101
+ /// @require {mixin} utility.states - Для генерации оберток псевдоклассов (hover, focus...).
102
+ /// @require {mixin} utility.breakpoints - Для генерации медиа-запросов адаптивности.
103
+ ///
104
+ /// @output Динамически вызывает миксин, имя которого совпадает со значением `map.get($config, "group")`.
105
+ ///
106
+ @mixin group($config) {
107
+ // 1. Валидация: проверяем подконфиг по схеме, соответствующей текущей группе (properties, steps и т.д.)
108
+ @include validate.config($config, map.get($REQUIRED, map.get($config, "group")));
109
+
110
+ // 2. Инициализация: безопасно извлекаем основные блоки данных, дефолтясь на пустые карты
111
+ $meta: map.get($config, "meta") or ();
112
+ $use: map.get($config, "use") or ();
113
+ $values: map.get($config, "values") or ();
114
+
115
+ // 3. Рефлексия (Динамический поиск): получаем ссылку на миксин-генератор,
116
+ // имя которого совпадает с именем группы (например, миксин `properties` или `steps`)
117
+ $group-mixin: meta.get-mixin(map.get($config, "group"));
118
+
119
+ // ГЕНЕРАЦИЯ БАЗОВЫХ КЛАССОВ
120
+ // Первичный вызов генератора без модификаторов состояний и брейкпоинтов
121
+ @include meta.apply($group-mixin, $meta, $values);
122
+
123
+ // ГЕНЕРАЦИЯ ЧИСТЫХ СОСТОЯНИЙ (например: h:opacity-100, f:outline-none)
124
+ @if map.get($use, "states") {
125
+ @include utility.states(map.get($use, "states")) using ($state-key, $state-selector) {
126
+ // Обогащаем $meta префиксами (`h\:`) и селекторами (`:hover`) для генератора
127
+ $state-meta: map.set($meta, "state-key", "#{$state-key}\\:");
128
+ $state-meta: map.set($state-meta, "state-selector", ":#{$state-selector}");
129
+
130
+ @include meta.apply($group-mixin, $state-meta, $values);
131
+ }
132
+ }
133
+
134
+ // ГЕНЕРАЦИЯ АДАПТИВНЫХ КЛАССОВ (Media Queries)
135
+ @if map.get($use, "breakpoints") {
136
+ @include utility.breakpoints() using ($bp-name) {
137
+ // Создаем метаданные с префиксом брейкпоинта (например, `md\:`)
138
+ $bp-meta: map.set($meta, "breakpoint", "#{$bp-name}\\:");
139
+
140
+ // Генерируем чистые адаптивные классы (например: md:w-1/2, lg:block)
141
+ @include meta.apply($group-mixin, $bp-meta, $values);
142
+
143
+ // КОМБИНАЦИЯ: АДАПТИВНОСТЬ + СОСТОЯНИЕ (например: md:h:bg-red-500)
144
+ // Если внутри адаптивного блока разрешены состояния, вкладываем их друг в друга
145
+ @if map.get($use, "states") {
146
+ @include utility.states(map.get($use, "states")) using ($state-key, $state-selector) {
147
+ // Наслаиваем префиксы состояний поверх уже существующего префикса брейкпоинта
148
+ $state-meta: map.set($bp-meta, "state-key", "#{$state-key}\\:");
149
+ $state-meta: map.set($state-meta, "state-selector", ":#{$state-selector}");
150
+
151
+ @include meta.apply($group-mixin, $state-meta, $values);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ /// Генератор глобальных именованных блоков (на данный момент — CSS Keyframes).
159
+ /// Принимает карту сложноструктурированных значений, перебирает её и динамически
160
+ /// разворачивает валидные CSS-директивы `@keyframes` с их внутренними шагами и свойствами.
161
+ ///
162
+ /// @param {Map} $meta - Метаданные блока конфигурации (передаются из контроллера `group`,
163
+ /// могут содержать префиксы брейкпоинтов или состояний, хотя для keyframes они обычно игнорируются).
164
+ /// @param {Map} $values - Карта зарегистрированных блоков.
165
+ /// Ожидает строго структурированную трехмерную карту (3D Map):
166
+ /// - Ключ 1-го уровня ($block-name): Тип блока (например, "keyframes").
167
+ /// - Ключ 2-го уровня ($name): Уникальное имя анимации (например, "spin", "fade-in").
168
+ /// - Ключ 3-го уровня ($selector): Шаг анимации (например, "from", "to", "0%", "50%").
169
+ /// - Ключ 4-го уровня ($property): Набор CSS-свойств для этого шага.
170
+ ///
171
+ /// @require {mixin} render.selector - Для компиляции шагов внутри keyframes (from/to/проценты).
172
+ /// @require {mixin} render.property - Для безопасной генерации итоговых CSS-свойств и значений.
173
+ ///
174
+ /// @example scss - Пример входного аргумента $values
175
+ /// $values: (
176
+ /// "keyframes": (
177
+ /// "spin": (
178
+ /// "from": ( "transform": "rotate(0deg)" ),
179
+ /// "to": ( "transform": "rotate(360deg)" )
180
+ /// )
181
+ /// )
182
+ /// );
183
+ /// @include blocks((), $values);
184
+ ///
185
+ /// @example css - Результат компиляции
186
+ /// @keyframes spin {
187
+ /// from { transform: rotate(0deg); }
188
+ /// to { transform: rotate(360deg); }
189
+ /// }
190
+ ///
191
+ @mixin blocks($meta, $values) {
192
+ // 1. Уровень типа блока: перебираем типы (например, "keyframes", в будущем можно расширить для "font-face")
193
+ @each $block-name, $block-values in $values {
194
+ // 2. Уровень сущности: перебираем конкретные имена анимаций (например, 'spin', 'bounce')
195
+ @each $name, $selectors in $block-values {
196
+ // Проверяем тип блока. Разворачиваем CSS-директиву @keyframes только если ключ равен "keyframes"
197
+ @if $block-name == "keyframes" {
198
+ @keyframes #{$name} {
199
+ // 3. Уровень селектора шага: перебираем этапы анимации (например, '0%', '50%', '100%')
200
+ @each $selector, $properties in $selectors {
201
+ // Рендерим селектор шага внутри тела анимации (использует интерполяцию)
202
+ @include render.selector($selector) {
203
+ // 4. Уровень CSS-свойств: перебираем пары свойство/значение для текущего шага
204
+ @each $property, $value in $properties {
205
+ @include render.property($property, $value);
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+ }
214
+
215
+ /// Генератор атомарных классов для группы статических свойств (`properties`).
216
+ /// Перебирает карту утилит, собирает из метаданных контекста финальное имя класса
217
+ /// с учетом адаптивности (брейкпоинтов) и интерактивности (состояний),
218
+ /// после чего делегирует рендеринг утилите нижнего уровня.
219
+ ///
220
+ /// @param {Map} $meta - Карта метаданных контекста компиляции (передается из контроллера `group`).
221
+ /// Используется для конструирования селектора. Ожидает ключи:
222
+ /// - "breakpoint" (String | Null) - Экранированный префикс медиа-запроса (например, `md\:`).
223
+ /// - "state-key" (String | Null) - Экранированный префикс псевдокласса (например, `hover\:`).
224
+ /// - "state-selector" (String | Null) - Сам псевдокласс, добавляемый к селектору (например, `:hover`).
225
+ ///
226
+ /// @param {Map} $values - Карта свойств и классов, подлежащих генерации.
227
+ /// Ожидает двумерную структуру (2D Map):
228
+ /// - Ключ 1-го уровня ($property): Имя CSS-свойства (например, "position", "opacity").
229
+ /// - Ключ 2-го уровня ($class): Имя генерируемого CSS-класса (например, "static", "opacity-50").
230
+ /// - Значение ($value): Итоговое CSS-значение свойства (например, "static", "0.5").
231
+ ///
232
+ /// @require {mixin} render.utility - Для конечного вывода сформированного класса и свойства в CSS.
233
+ ///
234
+ /// @example scss - Входные данные при обработке модуля opacity в режиме ховера на брейкпоинте md
235
+ /// $meta: (
236
+ /// "breakpoint": "md\\:",
237
+ /// "state-key": "hover\\:",
238
+ /// "state-selector": ":hover"
239
+ /// );
240
+ /// $values: (
241
+ /// "opacity": (
242
+ /// "opacity-0": "0",
243
+ /// "opacity-100": "1"
244
+ /// )
245
+ /// );
246
+ /// @include properties($meta, $values);
247
+ ///
248
+ /// @example css - Результат компиляции (внутри медиа-запроса md)
249
+ /// .md\:hover\:opacity-0:hover {
250
+ /// opacity: 0;
251
+ /// }
252
+ /// .md\:hover\:opacity-100:hover {
253
+ /// opacity: 1;
254
+ /// }
255
+ ///
256
+ @mixin properties($meta, $values) {
257
+ // 1. Уровень свойства: перебираем CSS-свойства (например: 'display', 'position')
258
+ @each $property, $classes in $values {
259
+ // 2. Уровень утилиты: перебираем пары "название класса / значение" для этого свойства
260
+ @each $class, $value in $classes {
261
+ // Интерполируем и склеиваем строку селектора класса.
262
+ // Результат сборки: [брейкпоинт][префикс-состояния][имя-класса][псевдокласс]
263
+ // Пример сборки: "md\:" + "hover\:" + "block" + ":hover" => "md\:hover\:block:hover"
264
+ $rendered-class: "#{map.get($meta, "breakpoint")}#{map.get($meta, "state-key")}#{$class}#{map.get($meta, "state-selector")}";
265
+
266
+ // Передаем собранное имя класса, CSS-свойство и его значение в рендерер
267
+ @include render.utility($rendered-class, $property, $value);
268
+ }
269
+ }
270
+ }
271
+
272
+ /// Генератор составных утилит и комплексных атомарных классов (`classes`).
273
+ /// Итерируется по карте кастомных классов, собирает финальное имя селектора
274
+ /// (наслаивая брейкпоинты и состояния из метаданных контекста) и разворачивает
275
+ /// блоки с множественными CSS-свойствами или переменными.
276
+ ///
277
+ /// @param {Map} $meta - Карта метаданных контекста компиляции (передается из контроллера `group`).
278
+ /// Используется для конструирования селектора. Ожидает ключи:
279
+ /// - "breakpoint" (String | Null) - Экранированный префикс медиа-запроса (например, `lg\:`).
280
+ /// - "state-key" (String | Null) - Экранированный префикс псевдокласса (например, `focus\:`).
281
+ /// - "state-selector" (String | Null) - Сам псевдокласс, добавляемый к селектору (например, `:focus`).
282
+ ///
283
+ /// @param {Map} $values - Карта комплексных классов, подлежащих генерации.
284
+ /// Ожидает двумерную структуру (2D Map):
285
+ /// - Ключ 1-го уровня ($class): Имя генерируемого CSS-класса (например, "translate-full", "clearfix").
286
+ /// - Вложенное значение ($properties): Карта (Map) пар "свойство / значение" для текущего класса.
287
+ ///
288
+ /// @require {mixin} render.class - Для создания внешней оболочки CSS-класса.
289
+ /// @require {mixin} render.property - Для вывода пар свойство/значение внутри тела класса.
290
+ ///
291
+ /// @example scss - Входные данные при обработке утилиты переноса осей в режиме ховера
292
+ /// $meta: (
293
+ /// "breakpoint": "md\\:",
294
+ /// "state-key": "hover\\:",
295
+ /// "state-selector": ":hover"
296
+ /// );
297
+ /// $values: (
298
+ /// "translate-px": (
299
+ /// "--translate-x": "1px",
300
+ /// "--translate-y": "1px",
301
+ /// "translate": "var(--translate-x) var(--translate-y)"
302
+ /// )
303
+ /// );
304
+ /// @include classes($meta, $values);
305
+ ///
306
+ /// @example css - Результат компиляции
307
+ /// .md\:hover\:translate-px:hover {
308
+ /// --translate-x: 1px;
309
+ /// --translate-y: 1px;
310
+ /// translate: var(--translate-x) var(--translate-y);
311
+ /// }
312
+ ///
313
+ @mixin classes($meta, $values) {
314
+ // 1. Уровень класса: перебираем названия утилит (например: 'translate-full', '-translate-x-px')
315
+ @each $class, $properties in $values {
316
+ // Интерполируем и склеиваем строку селектора класса.
317
+ // Результат сборки: [брейкпоинт][префикс-состояния][имя-класса][псевдокласс]
318
+ // Пример сборки: "md\:" + "hover\:" + "translate-full" + ":hover" => "md\:hover\:translate-full:hover"
319
+ $rendered-class: "#{map.get($meta, "breakpoint")}#{map.get($meta, "state-key")}#{$class}#{map.get($meta, "state-selector")}";
320
+
321
+ // Открываем блок селектора с помощью рендерера нижнего уровня
322
+ @include render.class($rendered-class) {
323
+ // 2. Уровень свойств: обходим внутреннюю карту CSS-свойств и переменных для данного класса
324
+ @each $property, $value in $properties {
325
+ @include render.property($property, $value);
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ /// Динамический генератор линейных диапазонов утилит (`steps`).
332
+ /// На основе метаданных ($meta) запускает математический цикл от и до указанного значения с заданным шагом.
333
+ /// Умеет генерировать три типа утилит в зависимости от формата значения свойства:
334
+ /// 1. Простая строка — классическая утилита (одно свойство = один динамический шаг).
335
+ /// 2. Список (List) — составная утилита (пачка свойств = одно и то же динамическое значение).
336
+ /// 3. Карта (Map) — комплексная утилита с кастомной шаблонизацией (свойства = CSS-функции со встроенным шагом).
337
+ ///
338
+ /// @param {Map} $meta - Контекст и математические параметры генерации. Ожидает ключи:
339
+ /// - "from" (Number) - Стартовая точка цикла (например, 0 или -360).
340
+ /// - "to" (Number) - Конечная точка цикла (например, 10 или 360).
341
+ /// - "step" (Number) - Шаг инкремента (например, 0.125 или 5).
342
+ /// - "unit" (String) - Единица измерения (например, "rem", "deg", "%" или "").
343
+ /// - "breakpoint", "state-key", "state-selector" - Служебные префиксы адаптивности и состояний.
344
+ ///
345
+ /// @param {Map} $values - Карта правил генерации имен классов и целевых CSS-свойств.
346
+ /// Ключ ($class) может быть плоской строкой (например, "rotate") или шаблоном (например, "translate-*\\%").
347
+ ///
348
+ /// @require {function} utility.escape-double - Для экранирования спецсимволов и точек в именах классов (например, `0\.5rem` -> `0\.5`).
349
+ /// @require {function} utility.is-template - Проверяет, содержит ли имя класса паттерн-шаблон для замены.
350
+ /// @require {function} utility.template - Подставляет числовое значение в шаблон имени класса или в тело CSS-свойства (`*`).
351
+ /// @require {function} utility.is-list - Проверка типа данных на список Sass.
352
+ /// @require {function} utility.is-map - Проверка типа данных на карту Sass.
353
+ ///
354
+ /// @example scss - Пример обработки для простой строки (rotate)
355
+ /// $meta: ("from": 0, "to": 10, "step": 5, "unit": "deg");
356
+ /// $values: ("rotate": "rotate");
357
+ /// // Результат CSS: .rotate-0 { rotate: 0deg; } .rotate-5 { rotate: 5deg; } ...
358
+ ///
359
+ /// @example scss - Пример обработки для карты-шаблона (CSS-переменные + функции)
360
+ /// $meta: ("from": 0, "to": 90, "step": 90, "unit": "deg");
361
+ /// $values: ("rotate-x": ( "--rotate-x": "rotateX(*)" ));
362
+ /// // Результат CSS: .rotate-x-0 { --rotate-x: rotateX(0deg); } .rotate-x-90 { --rotate-x: rotateX(90deg); }
363
+ ///
364
+ @mixin steps($meta, $values) {
365
+ // 1. Обходим переданные базовые имена утилит
366
+ @each $class, $property in $values {
367
+ $current: map.get($meta, "from"); // Инициализируем указатель цикла
368
+
369
+ // 2. Математический цикл генерации числового ряда
370
+ @while $current <= map.get($meta, "to") {
371
+ // Формируем валидное CSS-значение, склеивая число с единицей измерения (например, 1.25rem)
372
+ $rendered-value: "#{$current}#{map.get($meta, "unit")}";
373
+ // Получаем модуль числа и экранируем его для безопасного использования в имени селектора (например, 0.125 -> 0\.125)
374
+ $escaped-value: utility.escape-double(math.abs($current));
375
+
376
+ // 3. Формирование локального имени класса
377
+ // Если имя класса является шаблоном (например, содержит '*'), подставляем значение внутрь строки.
378
+ // Иначе — стандартно склеиваем через дефис: [имя]-[значение]
379
+ // prettier-ignore
380
+ $rendered-class: if(
381
+ sass(utility.is-template($class)): "#{utility.template($class, $escaped-value)}";
382
+ else: "#{$class}-#{$escaped-value}"
383
+ );
384
+
385
+ // 4. Сборка полного селектора с учетом глобального контекста (брейкпоинты и состояния)
386
+ // Результат: md\:hover\:rotate-45:hover
387
+ $rendered-class: "#{map.get($meta, "breakpoint")}#{map.get($meta, "state-key")}#{$rendered-class}#{map.get($meta, "state-selector")}";
388
+
389
+ // 5. ВЕТВЛЕНИЕ ПО ТИПАМ ДАННЫХ (Рендеринг)
390
+
391
+ // Ветвь А: Если $property — это список (List), например: ("scroll-margin-top", "scroll-margin-bottom")
392
+ // Генерируем один класс, внутри которого одно и то же число присваивается массиву свойств.
393
+ @if utility.is-list($property) {
394
+ @include render.class($rendered-class) {
395
+ @each $prop in $property {
396
+ @include render.property($prop, $rendered-value);
397
+ }
398
+ }
399
+ }
400
+
401
+ // Ветвь Б: Если $property — это карта (Map), например: ("--skew-x": "skewX(*)", "transform": "var(--skew-x)")
402
+ // Используется для сложных утилит, где динамическое значение нужно обернуть в CSS-функцию.
403
+ @else if utility.is-map($property) {
404
+ @include render.class($rendered-class) {
405
+ @each $prop, $value in $property {
406
+ // Подставляем текущий шаг с единицей измерения вместо знака плейсхолдера `*` в значение свойства
407
+ $templated-value: utility.template(
408
+ $value,
409
+ #{$current}#{map.get($meta, "unit")}
410
+ );
411
+ @include render.property($prop, $templated-value);
412
+ }
413
+ }
414
+ }
415
+
416
+ // Ветвь В: Если $property — это обычная плоская строка, например: "scroll-margin"
417
+ // Самый легкий и быстрый рендеринг классической единичной утилиты через атомарный рендерер.
418
+ @else {
419
+ @include render.utility($rendered-class, $property, $rendered-value);
420
+ }
421
+
422
+ // Инкрементируем счетчик цикла на шаг, заданный в конфигурации
423
+ $current: $current + map.get($meta, "step");
424
+ }
425
+ }
426
+ }
427
+
428
+ /// Динамический генератор дробных и сеточных утилит (`fractions`).
429
+ /// На основе метаданных ($meta) запускает вложенный цикл (числитель внутри знаменателя)
430
+ /// для генерации комбинаций дробей (например, от 1/2 до 11/12).
431
+ ///
432
+ /// Поддерживает два режима рендеринга значения:
433
+ /// 1. Математический (`divide: true`) — вычисляет процентное соотношение сторон (например, `1/2` -> `50%`).
434
+ /// 2. Строковый (`divide: false`) — генерирует нативную CSS-дробь для свойств вроде `grid-column` (например, `1 / 2`).
435
+ ///
436
+ /// Также поддерживает флаг `incorrect` для переключения между математически валидными
437
+ /// правильными дробями (где числитель < знаменателя) и полными матрицами (включая избыточные дроби вроде 4/3).
438
+ ///
439
+ /// @param {Map} $meta - Параметры математических циклов и режимов генерации. Ожидает ключи:
440
+ /// - "from" (Number) - Стартовая точка для числителя и знаменателя (обычно 0 или 1).
441
+ /// - "to" (Number) - Максимальный знаменатель сетки (например, 4 для четвертей, 12 для двенадцатиколонника).
442
+ /// - "divide" (Boolean) - Флаг вычисления процентов. Если true, делит числитель на знаменатель.
443
+ /// - "unit" (String) - Единица измерения при вычислении процентов (обычно "%").
444
+ /// - "negative" (Boolean) - Флаг инверсии знака итогового вычисленного значения.
445
+ /// - "incorrect" (Boolean) - Флаг генерации неправильных дробей (числитель может быть >= знаменателю).
446
+ /// - "breakpoint", "state-key", "state-selector" - Служебные префиксы адаптивности и состояний.
447
+ ///
448
+ /// @param {Map} $values - Карта базовых префиксов классов и целевых CSS-свойств.
449
+ /// Ключ ($class) — это префикс класса (например, "w", "inset-x", "col-span").
450
+ /// Значение ($property) — плоское CSS-свойство или список (List) свойств.
451
+ ///
452
+ /// @require {function} utility.is-list - Проверка типа данных на список Sass.
453
+ ///
454
+ /// @example scss - Групповой двенадцатиколонник по типу Tailwind (w-1/2, w-11/12...)
455
+ /// $meta: ("from": 0, "to": 12, "divide": true, "unit": "%", "incorrect": false);
456
+ /// $values: ("w": "width");
457
+ /// // Результат CSS: .w-1\/2 { width: 50%; } .w-1\/3 { width: 33.33333%; } ...
458
+ ///
459
+ /// @example scss - Сетки CSS Grid (col-start-1/2, col-start-2/3...)
460
+ /// $meta: ("from": 1, "to": 5, "divide": false, "incorrect": true);
461
+ /// $values: ("col-start": "grid-column-start");
462
+ /// // Результат CSS: .col-start-1\/2 { grid-column-start: 1 / 2; } .col-start-4\/2 { grid-column-start: 4 / 2; }
463
+ ///
464
+ @mixin fractions($meta, $values) {
465
+ // Обходим переданные базовые имена утилит (например, 'w', 'h', 'inset')
466
+ @each $class, $property in $values {
467
+ // 1. Определение стартового знаменателя.
468
+ // Если разрешены неправильные дроби, начинаем прямо с базового значения (обычно 0).
469
+ // Если нужны только правильные дроби, сдвигаем старт на +1, так как знаменатель не может быть равен 0 при старте числителя с 0.
470
+ // prettier-ignore
471
+ $denominator: if(
472
+ sass(map.get($meta, "incorrect")): map.get($meta, "from");
473
+ else: map.get($meta, "from") + 1
474
+ );
475
+
476
+ // ВНЕШНИЙ ЦИКЛ: Итерируемся по знаменателю (от стартовой точки до максимального разрешения сетки, например, 12)
477
+ @while $denominator <= map.get($meta, "to") {
478
+ $numerator: map.get(
479
+ $meta,
480
+ "from"
481
+ ); // Сбрасываем числитель на старт для каждого нового знаменателя
482
+
483
+ // ВНУТРЕННИЙ ЦИКЛ: Итерируемся по числителю.
484
+ // Условие 1 (при incorrect: true): числитель идет до максимального упора `to` независимо от знаменателя.
485
+ // Условие 2 (при incorrect: false): числитель строго меньше знаменателя (только правильные дроби: 1/2, 2/3 и т.д.)
486
+ @while (map.get($meta, "incorrect") and $numerator <= map.get($meta, "to")) or
487
+ (not map.get($meta, "incorrect") and $numerator < $denominator)
488
+ {
489
+ $value: null;
490
+
491
+ // РЕЖИМ А: Вычисление процентного соотношения (например, для width, height, left)
492
+ @if map.get($meta, "divide") {
493
+ // Предотвращаем деление на ноль, если в конфигурации случайно совпали нули
494
+ $div-denominator: if(
495
+ sass($denominator == 0): 1; else: $denominator,
496
+ );
497
+ $value: math.div($numerator, $div-denominator) * 100;
498
+
499
+ // Если утилита должна быть отрицательной (например, -inset-x-1/2)
500
+ @if map.get($meta, "negative") {
501
+ $value: $value * -1;
502
+ }
503
+
504
+ $value: "#{$value}#{map.get($meta, "unit")}";
505
+ }
506
+
507
+ // РЕЖИМ Б: Генерация строковой записи дроби (например, для grid-column: 1 / 3)
508
+ @else {
509
+ $value: "#{$numerator} / #{$denominator}";
510
+ }
511
+
512
+ // Сборка полного селектора атомарного класса.
513
+ // Результат сборки содержит экранированный слэш: "md\:hover\:w-1\/2:hover"
514
+ $rendered-class: "#{map.get($meta, "breakpoint")}#{map.get($meta, "state-key")}#{$class}-#{$numerator}\\/#{$denominator}#{map.get($meta, "state-selector")}";
515
+
516
+ // ВЫВОД В CSS
517
+ // Если передана пачка свойств (например, "inset-x" управляет и left, и right одновременно)
518
+ @if utility.is-list($property) {
519
+ @include render.class($rendered-class) {
520
+ @each $prop in $property {
521
+ @include render.property($prop, $value);
522
+ }
523
+ }
524
+ }
525
+
526
+ // Если передано одиночное свойство — рендерим максимально быструю атомарную утилиту
527
+ @else {
528
+ @include render.utility($rendered-class, $property, $value);
529
+ }
530
+
531
+ $numerator: $numerator + 1; // Инкрементируем числитель
532
+ }
533
+
534
+ $denominator: $denominator + 1; // Инкрементируем знаменатель
535
+ }
536
+ }
537
+ }
538
+
539
+ /// Динамический генератор цветовых утилит на основе глобальной палитры (`palettes`).
540
+ /// Обходит карту классов и сопоставляет их с глобальной переменной палитры цветов `var.$palette`.
541
+ /// Автоматически обрабатывает два типа структуры цветов в палитре:
542
+ /// 1. Карта (Map) оттенков — для именованных цветов со шкалой яркости (например, `blue-50`, `blue-500`).
543
+ /// 2. Плоская строка/значение — для одиночных или системных цветов (например, `white`, `black`, `transparent`).
544
+ ///
545
+ /// @param {Map} $meta - Карта метаданных контекста компиляции (передается из контроллера `group`).
546
+ /// Используется для конструирования адаптивных и интерактивных селекторов. Ожидает ключи:
547
+ /// - "breakpoint" (String | Null) - Экранированный префикс медиа-запроса (например, `xl\:`).
548
+ /// - "state-key" (String | Null) - Экранированный префикс псевдокласса (например, `hover\:`).
549
+ /// - "state-selector" (String | Null) - Сам псевдокласс, добавляемый к селектору (например, `:hover`).
550
+ ///
551
+ /// @param {Map} $values - Карта связей между префиксами классов и CSS-свойствами.
552
+ /// Ожидает плоскую структуру (1D Map):
553
+ /// - Ключ ($class) — префикс генерируемого класса (например, "bg", "text", "border", "fill").
554
+ /// - Значение ($property) — целевое CSS-свойство (например, "background-color", "color").
555
+ ///
556
+ /// @require {variable} var.$palette - Глобальный Sass-map со всеми зарегистрированными в проекте цветами.
557
+ /// @require {function} utility.is-map - Проверка типа данных на карту Sass для определения наличия оттенков.
558
+ /// @require {mixin} palette - Специализированный рендерер нижнего уровня для вывода цветовых CSS-свойств.
559
+ ///
560
+ /// @example scss - Глобальная палитра в var.$palette
561
+ /// $palette: (
562
+ /// "red": ( "500": "#f87171" ),
563
+ /// "white": "#ffffff"
564
+ /// );
565
+ ///
566
+ /// @example scss - Вызов миксина для генерации фонов на ховере
567
+ /// $meta: ( "breakpoint": "md\\:", "state-key": "hover\\:", "state-selector": ":hover" );
568
+ /// $values: ( "bg": "background-color" );
569
+ /// @include palettes($meta, $values);
570
+ ///
571
+ /// @example css - Результат компиляции
572
+ /// .md\:hover\:bg-red-500:hover { background-color: #f87171; }
573
+ /// .md\:hover\:bg-white:hover { background-color: #ffffff; }
574
+ ///
575
+ @mixin palettes($meta, $values) {
576
+ // 1. Уровень утилиты: перебираем типы генерируемых классов (например, 'bg' -> 'background-color')
577
+ @each $class, $property in $values {
578
+ // 2. Уровень глобальной палитры: обходим все зарегистрированные в системе цвета
579
+ @each $color, $shades in var.$palette {
580
+ // ВЕТВЬ А: Цвет имеет подкарту оттенков (например: 'blue' -> ('50': ..., '100': ..., '500': ...))
581
+ @if utility.is-map($shades) {
582
+ @each $number, $value in $shades {
583
+ // Сборка трехсоставного имени класса: [префиксы]-[база]-[цвет]-[оттенок][суффикс]
584
+ // Пример: "md\:" + "hover\:" + "bg" + "-" + "blue" + "-" + "500" + ":hover"
585
+ $rendered-class: "#{map.get($meta, "breakpoint")}#{map.get($meta, "state-key")}#{$class}-#{$color}-#{$number}#{map.get($meta, "state-selector")}";
586
+
587
+ // Делегируем вывод специализированному цветовому рендереру
588
+ @include palette($rendered-class, $property, $value);
589
+ }
590
+ }
591
+
592
+ // ВЕТВЬ Б: Одиночный плоский цвет без шкалы оттенков (например: 'white', 'black', 'transparent')
593
+ @else {
594
+ // Сборка двухсоставного имени класса: [префиксы]-[база]-[цвет][суффикс]
595
+ // Пример: "text" + "-" + "white" => "text-white"
596
+ $rendered-class: "#{map.get($meta, "breakpoint")}#{map.get($meta, "state-key")}#{$class}-#{$color}#{map.get($meta, "state-selector")}";
597
+
598
+ // Передаем плоское значение ($shades здесь является строкой с цветом) в рендерер
599
+ @include palette($rendered-class, $property, $shades);
600
+ }
601
+ }
602
+ }
603
+ }
604
+
605
+ /// Специализированный рендерер цветовых утилит (`palette`).
606
+ /// Принимает сформированное имя класса, целевое свойство (или карту свойств) и итоговое значение цвета.
607
+ /// Поддерживает два режима вывода в зависимости от типа аргумента `$property`:
608
+ /// 1. Обычная строка — классический атомарный вывод (одно свойство = один цвет).
609
+ /// 2. Карта (Map) — комплексный вывод пачки свойств с возможностью шаблонизации цвета через замену плейсхолдера `*`.
610
+ ///
611
+ /// Используется как мост между генератором `palettes` и базовыми рендерерами `render.*`.
612
+ ///
613
+ /// @param {String} $class - Полностью собранное имя CSS-класса утилиты (включая все экранированные префиксы и суффиксы).
614
+ /// @param {String | Map} $property - Целевое CSS-свойство (строка) или карта (Map) пар "свойство / шаблон-значения".
615
+ /// @param {String} $value - Итоговое значение цвета (HEX, RGB, HSL или ключевое слово вроде `transparent`).
616
+ ///
617
+ /// @require {function} utility.is-map - Проверка типа данных на карту Sass.
618
+ /// @require {function} utility.template - Подставляет значение цвета вместо знака плейсхолдера `*` в шаблон CSS-свойства.
619
+ /// @require {mixin} render.class - Для создания внешней оболочки составного CSS-класса.
620
+ /// @require {mixin} render.property - Для вывода пар свойство/значение внутри тела класса.
621
+ /// @require {mixin} render.utility - Для быстрого вывода стандартных атомарных утилит.
622
+ ///
623
+ /// @example scss - Пример А: Стандартный атомарный вывод (Передана строка свойства)
624
+ /// @include palette("bg-red-500", "background-color", "#f87171");
625
+ /// // Результат CSS: .bg-red-500 { background-color: #f87171; }
626
+ ///
627
+ /// @example scss - Пример Б: Комплексный вывод с поддержкой RGBA-переменных прозрачности (Передана карта-шаблон)
628
+ /// $property-map: (
629
+ /// "--tw-bg-opacity": "1",
630
+ /// "background-color": "rgba(*, var(--tw-bg-opacity))"
631
+ /// );
632
+ /// @include palette("bg-blue-500", $property-map, "59, 130, 246"); // Цвет передан в формате RGB-компонентов
633
+ /// // Результат CSS:
634
+ /// // .bg-blue-500 {
635
+ /// // --tw-bg-opacity: 1;
636
+ /// // background-color: rgba(59, 130, 246, var(--tw-bg-opacity));
637
+ /// // }
638
+ ///
639
+ @mixin palette($class, $property, $value) {
640
+ // ВЕТВЬ А: Если аргумент $property является картой (Map).
641
+ // Применяется для сложных утилит, требующих модификации цвета или управления переменными прозрачности.
642
+ @if utility.is-map($property) {
643
+ // Открываем блок CSS-селектора
644
+ @include render.class($class) {
645
+ // Итерируемся по внутренней карте шаблонов свойств
646
+ @each $prop, $v in $property {
647
+ // Подставляем значение цвета вместо знака плейсхолдера `*` в строку шаблона
648
+ // и выводим готовое CSS-свойство
649
+ @include render.property($prop, utility.template($v, $value));
650
+ }
651
+ }
652
+ }
653
+
654
+ // ВЕТВЬ Б: Если аргумент $property — обычная плоская строка (например, "color" или "fill").
655
+ // Выполняет максимально быструю и легкую компиляцию стандартной одиночной утилиты.
656
+ @else {
657
+ @include render.utility($class, $property, $value);
658
+ }
659
+ }