@omnisass/library 0.2.1 → 0.4.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +120 -3
  2. package/README.md +94 -0
  3. package/index.scss +6 -0
  4. package/modules/utilities/converters/_convert-camel2kebab.scss +5 -4
  5. package/modules/utilities/converters/_convert-em2px.scss +5 -3
  6. package/modules/utilities/converters/_convert-hex2rgb.scss +4 -0
  7. package/modules/utilities/converters/_convert-hex2rgba.scss +5 -0
  8. package/modules/utilities/converters/_convert-kebab2camel.scss +5 -4
  9. package/modules/utilities/converters/_convert-kebab2snake.scss +4 -4
  10. package/modules/utilities/converters/_convert-px2em.scss +5 -4
  11. package/modules/utilities/converters/_convert-px2rem.scss +5 -4
  12. package/modules/utilities/converters/_convert-rem2px.scss +5 -3
  13. package/modules/utilities/converters/_convert-snake2kebab.scss +4 -4
  14. package/modules/utilities/getters/color/_get-color-brightness.scss +4 -0
  15. package/modules/utilities/getters/color/_get-color-darkest.scss +5 -1
  16. package/modules/utilities/getters/list/_get-list-item-end.scss +4 -2
  17. package/modules/utilities/getters/list/_get-list-item-start.scss +4 -2
  18. package/modules/utilities/getters/list/_get-list-item.scss +6 -4
  19. package/modules/utilities/getters/misc/_get-uid.scss +135 -0
  20. package/modules/utilities/getters/number/_get-number-from-percent.scss +6 -2
  21. package/modules/utilities/getters/number/_get-number-height-by-ratio.scss +5 -0
  22. package/modules/utilities/getters/number/_get-number-max.scss +4 -0
  23. package/modules/utilities/getters/number/_get-number-min.scss +4 -0
  24. package/modules/utilities/getters/number/_get-number-percentage-of.scss +6 -1
  25. package/modules/utilities/getters/number/_get-number-unit.scss +4 -2
  26. package/modules/utilities/getters/number/_get-number-width-by-ratio.scss +5 -0
  27. package/modules/utilities/getters/string/_get-string-hash.scss +143 -0
  28. package/modules/utilities/helpers/color/_color-blend-steps.scss +6 -0
  29. package/modules/utilities/helpers/color/_color-blend.scss +6 -0
  30. package/modules/utilities/helpers/color/_color-hue-shift.scss +5 -0
  31. package/modules/utilities/helpers/color/_color-scale.scss +5 -0
  32. package/modules/utilities/helpers/color/_color-shade.scss +5 -0
  33. package/modules/utilities/helpers/color/_color-tint.scss +5 -0
  34. package/modules/utilities/helpers/color/_color-triad.scss +4 -1
  35. package/modules/utilities/helpers/list/_list-dedupe.scss +4 -0
  36. package/modules/utilities/helpers/list/_list-insert-at.scss +5 -0
  37. package/modules/utilities/helpers/list/_list-merge.scss +54 -8
  38. package/modules/utilities/helpers/list/_list-remove-at.scss +5 -0
  39. package/modules/utilities/helpers/list/_list-sum-numbers-safe.scss +3 -0
  40. package/modules/utilities/helpers/list/_list-sum-numbers.scss +3 -0
  41. package/modules/utilities/helpers/misc/_url-encode.scss +7 -8
  42. package/modules/utilities/helpers/number/_number-ceil-to.scss +56 -4
  43. package/modules/utilities/helpers/number/_number-clamp-max.scss +65 -10
  44. package/modules/utilities/helpers/number/_number-clamp-min.scss +65 -10
  45. package/modules/utilities/helpers/number/_number-clamp.scss +88 -12
  46. package/modules/utilities/helpers/number/_number-denormalize.scss +87 -9
  47. package/modules/utilities/helpers/number/_number-fibonacci.scss +62 -9
  48. package/modules/utilities/helpers/number/_number-floor-to.scss +57 -3
  49. package/modules/utilities/helpers/number/_number-format-with-separator.scss +99 -16
  50. package/modules/utilities/helpers/number/_number-normalize.scss +88 -10
  51. package/modules/utilities/helpers/number/_number-random-between-int.scss +74 -13
  52. package/modules/utilities/helpers/number/_number-random-between.scss +76 -15
  53. package/modules/utilities/helpers/number/_number-range.scss +105 -12
  54. package/modules/utilities/helpers/number/_number-round-to-nearest.scss +58 -1
  55. package/modules/utilities/helpers/number/_number-round-to.scss +65 -2
  56. package/modules/utilities/helpers/number/_number-strip-unit.scss +43 -9
  57. package/modules/utilities/helpers/string/_string-capitalize.scss +46 -5
  58. package/modules/utilities/helpers/string/_string-count.scss +173 -0
  59. package/modules/utilities/helpers/string/_string-lorips.config.scss +81 -0
  60. package/modules/utilities/helpers/string/_string-lorips.scss +198 -0
  61. package/modules/utilities/helpers/string/_string-repeat.scss +147 -0
  62. package/modules/utilities/helpers/string/_string-replace.scss +82 -4
  63. package/modules/utilities/helpers/string/_string-reverse.scss +139 -0
  64. package/modules/utilities/helpers/string/_string-trim-end.scss +49 -6
  65. package/modules/utilities/helpers/string/_string-trim-start.scss +49 -6
  66. package/modules/utilities/helpers/string/_string-trim.scss +56 -11
  67. package/modules/utilities/loggers/_log-invalid-type.scss +1 -1
  68. package/modules/utilities/loggers/_log-invalid-value.scss +1 -1
  69. package/modules/utilities/validators/color/_is-color-light.scss +61 -4
  70. package/modules/utilities/validators/color/_is-color-list.scss +41 -9
  71. package/modules/utilities/validators/list/_is-list-contained.scss +42 -3
  72. package/modules/utilities/validators/misc/_is-time.scss +29 -0
  73. package/modules/utilities/validators/number/_is-int-even.scss +50 -8
  74. package/modules/utilities/validators/number/_is-int-odd.scss +51 -9
  75. package/modules/utilities/validators/number/_is-int.scss +71 -12
  76. package/modules/utilities/validators/number/_is-number-has-unit.scss +55 -13
  77. package/modules/utilities/validators/number/_is-number-negative.scss +53 -11
  78. package/modules/utilities/validators/number/_is-number-positive.scss +52 -10
  79. package/modules/utilities/validators/number/_is-number-unitless.scss +53 -12
  80. package/modules/utilities/validators/number/_is-number-zero.scss +54 -10
  81. package/modules/utilities/validators/string/_is-string-contained.scss +59 -4
  82. package/modules/utilities/validators/string/_is-string-empty.scss +48 -8
  83. package/modules/utilities/validators/string/_is-string-ending-with.scss +64 -5
  84. package/modules/utilities/validators/string/_is-string-starting-with.scss +60 -8
  85. package/package.json +1 -1
  86. package/package.scss +9 -8
  87. package/test2.sh +158 -0
  88. package/modules/utilities/setters/_index.scss +0 -3
@@ -1,4 +1,6 @@
1
1
  @use 'sass:list';
2
+ @use '../../loggers/log-invalid-type' as *;
3
+ @use '../../validators/type-of/is-number' as *;
2
4
 
3
5
  /// Генерирует последовательность чисел в заданном диапазоне
4
6
  /// с определенным шагом.
@@ -242,27 +244,118 @@
242
244
  /// - Последний элемент: ближайшее к `$end` значение, кратное шагу
243
245
  /// - Все элементы: равномерно распределены с шагом `$step`
244
246
  /// - Направление: соответствует соотношению `$start` и `$end`
247
+ /// @throws {Error} - Выбрасывает ошибку, если в параметры передано
248
+ /// значение, не соответствующее типу `number`. Компиляция Sass
249
+ /// будет остановлена с ошибкой.
245
250
  @function number-range($from, $to, $step: 1) {
246
- $-result: ();
247
251
 
248
- @if $from < $to {
249
- @for $i from $from through $to {
250
- @if ($i - $from) % $step == 0 {
252
+ // Проверка типа первого параметра: ожидается числовое значение
253
+ // начальной точки диапазона. $from определяет начальное значение
254
+ // для генерации последовательности чисел.
255
+ @if not is-number($from) {
251
256
 
252
- $-result: list.append($-result, $i);
257
+ // Если $from не является числом, возвращаем ошибку через
258
+ // стандартную функцию логирования. Это предотвращает
259
+ // некорректные вычисления с нечисловыми данными.
260
+ @return log-invalid-type(
261
+ 'number-range',
262
+ $from,
263
+ '$from',
264
+ 'number'
265
+ );
266
+
267
+ }
268
+
269
+ // Проверка типа второго параметра: ожидается числовое значение
270
+ // конечной точки диапазона. $to определяет конечное значение
271
+ // для генерации последовательности чисел.
272
+ @else if not is-number($to) {
273
+
274
+ // Если $to не является числом, возвращаем ошибку.
275
+ // Проверка выполняется только если $from прошел валидацию.
276
+ @return log-invalid-type(
277
+ 'number-range',
278
+ $to,
279
+ '$to',
280
+ 'number'
281
+ );
282
+
283
+ }
284
+
285
+ // Проверка типа третьего параметра: ожидается числовое значение
286
+ // шага последовательности. $step определяет интервал между
287
+ // числами в генерируемой последовательности.
288
+ @else if not is-number($step) {
289
+
290
+ // Если $step не является числом, возвращаем ошибку.
291
+ // Проверка выполняется только если $from и $to
292
+ // прошли валидацию.
293
+ @return log-invalid-type(
294
+ 'number-range',
295
+ $step,
296
+ '$step',
297
+ 'number'
298
+ );
299
+
300
+ }
301
+
302
+ // Все параметры прошли валидацию - генерируем числовую последовательность.
303
+ @else {
304
+
305
+ // Инициализация переменной для хранения результата.
306
+ // $-result будет содержать список чисел в указанном диапазоне
307
+ // с заданным шагом.
308
+ $-result: ();
309
+
310
+ // Проверка направления диапазона: восходящая последовательность.
311
+ // Если начальное значение меньше конечного, генерируем числа
312
+ // от меньшего к большему.
313
+ @if $from < $to {
314
+
315
+ // Цикл по всем числам от $from до $to включительно.
316
+ // Переменная $i принимает значения от $from до $to.
317
+ @for $i from $from through $to {
318
+
319
+ // Проверка: соответствует ли текущее число шагу последовательности.
320
+ // Условие ($i - $from) % $step == 0 проверяет, что разница
321
+ // между текущим числом и начальным значением кратна шагу.
322
+ @if ($i - $from) % $step == 0 {
323
+
324
+ // Если число соответствует шагу, добавляем его в результат.
325
+ // Функция list.append() добавляет число в конец списка.
326
+ $-result: list.append($-result, $i);
327
+
328
+ }
253
329
 
254
330
  }
255
- }
256
- } @else {
257
- @for $i from $from through $to {
258
- @if ($from - $i) % $step == 0 {
259
331
 
260
- $-result: list.append($-result, $i);
332
+ } @else {
333
+
334
+ // Нисходящая последовательность.
335
+ // Если начальное значение больше или равно конечному,
336
+ // генерируем числа от большего к меньшему.
337
+ @for $i from $from through $to {
338
+
339
+ // Проверка: соответствует ли текущее число шагу последовательности
340
+ // для нисходящего направления.
341
+ // Условие ($from - $i) % $step == 0 проверяет, что разница
342
+ // между начальным значением и текущим числом кратна шагу.
343
+ @if ($from - $i) % $step == 0 {
344
+
345
+ // Если число соответствует шагу, добавляем его в результат.
346
+ $-result: list.append($-result, $i);
347
+
348
+ }
261
349
 
262
350
  }
351
+
263
352
  }
264
- }
265
353
 
266
- @return $-result;
354
+ // Возвращаем список чисел в указанном диапазоне с заданным шагом.
355
+ // Последовательность включает начальное и конечное значения,
356
+ // если они соответствуют шагу.
357
+ @return $-result;
358
+
359
+ }
267
360
 
268
361
  }
@@ -1,4 +1,6 @@
1
1
  @use 'sass:math';
2
+ @use '../../loggers/log-invalid-type' as *;
3
+ @use '../../validators/type-of/is-number' as *;
2
4
 
3
5
  /// Округляет число до ближайшего кратного значения с
4
6
  /// использованием стандартного математического округления
@@ -126,6 +128,61 @@
126
128
  /// @throws {Error} - Может выбросить ошибку если `$value` или
127
129
  /// `$nearest` не являются числами, или если происходит
128
130
  /// деление на ноль.
131
+ /// @throws {Error} - Выбрасывает ошибку, если в параметры передано
132
+ /// значение, не соответствующее типу `number`. Компиляция Sass
133
+ /// будет остановлена с ошибкой.
129
134
  @function number-round-to-nearest($value, $nearest) {
130
- @return math.round(math.div($value, $nearest)) * $nearest;
135
+
136
+ // Проверка типа первого параметра: ожидается числовое значение.
137
+ // Функция is-number() проверяет, является ли $value числом.
138
+ // Это может быть целое число, дробное число или число с единицами измерения.
139
+ @if not is-number($value) {
140
+
141
+ // Если $value не является числом, возвращаем ошибку через
142
+ // стандартную функцию логирования. Это предотвращает
143
+ // некорректные вычисления с нечисловыми данными.
144
+ @return log-invalid-type(
145
+ 'number-round-to-nearest',
146
+ $value,
147
+ '$value',
148
+ 'number'
149
+ );
150
+
151
+ }
152
+
153
+ // Проверка типа второго параметра: ожидается числовое значение
154
+ // для шага округления. $nearest определяет, до какого ближайшего
155
+ // кратного значения нужно округлить исходное число.
156
+ @else if not is-number($nearest) {
157
+
158
+ // Если $nearest не является числом, возвращаем ошибку.
159
+ // Проверка выполняется только если $value прошел валидацию.
160
+ @return log-invalid-type(
161
+ 'number-round-to-nearest',
162
+ $nearest,
163
+ '$nearest',
164
+ 'number'
165
+ );
166
+
167
+ }
168
+
169
+ // Все параметры прошли валидацию - выполняем округление к ближайшему.
170
+ @else {
171
+
172
+ // Формула округления к ближайшему кратному значению:
173
+ // 1. math.div($value, $nearest) - делим исходное значение на шаг округления
174
+ // 2. math.round() - округляем результат деления к ближайшему целому
175
+ // (использует математическое округление: .5 округляется вверх)
176
+ // 3. * $nearest - умножаем округленное целое на шаг округления
177
+ //
178
+ // Математическая формула:
179
+ // result = round(value / nearest) × nearest
180
+ //
181
+ // Ключевая особенность: функция округляет значение к ближайшему
182
+ // кратному с использованием математического округления,
183
+ // где .5 всегда округляется вверх.
184
+ @return math.round(math.div($value, $nearest)) * $nearest;
185
+
186
+ }
187
+
131
188
  }
@@ -1,4 +1,6 @@
1
1
  @use 'sass:math';
2
+ @use '../../loggers/log-invalid-type' as *;
3
+ @use '../../validators/type-of/is-number' as *;
2
4
 
3
5
  /// Округляет число до указанного количества знаков после
4
6
  /// запятой.
@@ -109,10 +111,71 @@
109
111
  /// @throws {Error} - Может выбросить ошибку если `$precision`
110
112
  /// не является целым неотрицательным числом или если
111
113
  /// `$value` не является числом.
114
+ /// @throws {Error} - Выбрасывает ошибку, если в параметры передано
115
+ /// значение, не соответствующее типу `number`. Компиляция Sass
116
+ /// будет остановлена с ошибкой.
112
117
  @function number-round-to($value, $precision: 2) {
113
118
 
114
- $-multiplier: math.pow(10, $precision);
119
+ // Проверка типа первого параметра: ожидается числовое значение.
120
+ // Функция is-number() проверяет, является ли $value числом.
121
+ // Это может быть целое число, дробное число или число с единицами измерения.
122
+ @if not is-number($value) {
115
123
 
116
- @return math.div(math.round($value * $-multiplier), $-multiplier);
124
+ // Если $value не является числом, возвращаем ошибку через
125
+ // стандартную функцию логирования. Это предотвращает
126
+ // некорректные вычисления с нечисловыми данными.
127
+ @return log-invalid-type(
128
+ 'number-round-to',
129
+ $value,
130
+ '$value',
131
+ 'number'
132
+ );
133
+
134
+ }
135
+
136
+ // Проверка типа второго параметра: ожидается числовое значение
137
+ // для точности округления. $precision определяет количество
138
+ // знаков после запятой в результате округления.
139
+ @else if not is-number($precision) {
140
+
141
+ // Если $precision не является числом, возвращаем ошибку.
142
+ // Проверка выполняется только если $value прошел валидацию.
143
+ @return log-invalid-type(
144
+ 'number-round-to',
145
+ $precision,
146
+ '$precision',
147
+ 'number'
148
+ );
149
+
150
+ }
151
+
152
+ // Все параметры прошли валидацию - выполняем округление с заданной точностью.
153
+ @else {
154
+
155
+ // Вычисление множителя для округления.
156
+ // math.pow(10, $precision) возводит 10 в степень $precision,
157
+ // создавая множитель для перемещения нужного количества
158
+ // знаков после запятой в целую часть числа.
159
+ // Пример: при $precision = 2 → множитель = 100
160
+ $-multiplier: math.pow(10, $precision);
161
+
162
+ // Формула округления с заданной точностью:
163
+ // 1. $value * $-multiplier - умножаем значение на множитель,
164
+ // чтобы переместить нужное количество знаков после запятой
165
+ // в целую часть (например, 3.14159 × 100 = 314.159)
166
+ // 2. math.round(...) - округляем результат к ближайшему целому
167
+ // (314.159 → 314)
168
+ // 3. math.div(..., $-multiplier) - делим обратно на множитель,
169
+ // чтобы вернуть десятичную точку на исходное место (314 → 3.14)
170
+ //
171
+ // Математическая формула:
172
+ // result = round(value × 10^precision) ÷ 10^precision
173
+ //
174
+ // Ключевая особенность: функция округляет значение до указанного
175
+ // количества знаков после запятой с использованием математического
176
+ // округления, где .5 всегда округляется вверх.
177
+ @return math.div(math.round($value * $-multiplier), $-multiplier);
178
+
179
+ }
117
180
 
118
181
  }
@@ -79,19 +79,53 @@
79
79
  /// @return {Number} - Безразмерное числовое значение, равное
80
80
  /// числовой части входного значения. Если входное значение
81
81
  /// уже было безразмерным, возвращается то же самое значение.
82
- /// @throws {Error} - Может выбросить ошибку, если передано
83
- /// нечисловое значение, так как математические операции
84
- /// Sass требуют числовых операндов.
82
+ /// @throws {Error} - Выбрасывает ошибку, если в параметры передано
83
+ /// значение, не соответствующее типу `number`. Компиляция Sass
84
+ /// будет остановлена с ошибкой.
85
85
  @function number-strip-unit($value) {
86
86
 
87
- $-result: 'null';
87
+ // Проверка типа входного параметра: ожидается числовое значение.
88
+ // Функция is-number() проверяет, является ли $value числом.
89
+ // Это может быть целое число, дробное число или число с единицами измерения.
90
+ @if not is-number($value) {
91
+
92
+ // Если $value не является числом, возвращаем ошибку через
93
+ // стандартную функцию логирования. Это предотвращает
94
+ // некорректные вычисления с нечисловыми данными.
95
+ @return log-invalid-type(
96
+ 'number-strip-unit',
97
+ $value,
98
+ '$value',
99
+ 'number'
100
+ );
88
101
 
89
- @if is-number($value) {
90
- $-result: math.div($value, $value * 0 + 1);
91
- } @else {
92
- @debug log-invalid-type('number-strip-unit', $value, '$value', 'number');
93
102
  }
94
103
 
95
- @return $-result;
104
+ // Основная логика выполняется только если $value является
105
+ // корректным числом.
106
+ @else {
107
+
108
+ // Формула удаления единиц измерения из числа:
109
+ // Используем математический трюк для конвертации числа
110
+ // в безразмерное значение.
111
+ //
112
+ // Алгоритм вычислений:
113
+ // 1. $value * 0 + 1 - создаем безразмерное число 1:
114
+ // - $value * 0 = 0 с теми же единицами измерения, что и $value
115
+ // - 0 + 1 = 1 без единиц измерения (единицы теряются при сложении
116
+ // с безразмерным числом)
117
+ // 2. math.div($value, ...) - делим исходное значение на безразмерную 1
118
+ // Деление числа с единицами измерения на безразмерное число
119
+ // удаляет единицы измерения, сохраняя числовое значение.
120
+ //
121
+ // Математическая формула:
122
+ // result = value ÷ (value × 0 + 1)
123
+ //
124
+ // Ключевая особенность: этот метод корректно работает с любыми
125
+ // единицами измерения (px, rem, em, %, и т.д.) и всегда возвращает
126
+ // безразмерное числовое значение.
127
+ @return math.div($value, $value * 0 + 1);
128
+
129
+ }
96
130
 
97
131
  }
@@ -1,4 +1,6 @@
1
1
  @use 'sass:string';
2
+ @use '../../loggers/log-invalid-type' as *;
3
+ @use '../../validators/type-of/is-string' as *;
2
4
 
3
5
  /// Преобразует первый символ строки в верхний регистр, остальные
4
6
  /// оставляет без изменений.
@@ -72,13 +74,52 @@
72
74
  /// первый символ преобразован в верхний регистр, а все
73
75
  /// остальные символы остаются без изменений. Для пустой
74
76
  /// строки возвращает пустую строку.
75
- /// @throws Не выбрасывает ошибок, корректно обрабатывает строки
76
- /// любой длины и содержания.
77
+ /// @throws {Error} - Выбрасывает ошибку, если в параметры передано
78
+ /// значение, не соответствующее типу `string`. Компиляция Sass
79
+ /// будет остановлена с ошибкой.
77
80
  @function string-capitalize($string) {
78
81
 
79
- $-first: string.to-upper-case(string.slice($string, 1, 1));
80
- $-rest: string.slice($string, 2);
82
+ // Проверка типа входного параметра: ожидается строковое значение.
83
+ // Функция is-string() проверяет, является ли $string строкой
84
+ // (тип данных string). Строка будет преобразована в формат,
85
+ // где первый символ становится заглавным.
86
+ @if not is-string($string) {
81
87
 
82
- @return $-first + $-rest;
88
+ // Если $string не является строкой, возвращаем ошибку через
89
+ // стандартную функцию логирования. Это предотвращает
90
+ // некорректные операции преобразования с нестроковыми данными.
91
+ @return log-invalid-type(
92
+ 'string-capitalize',
93
+ $string,
94
+ '$string',
95
+ 'string'
96
+ );
97
+
98
+ }
99
+
100
+ // Основная логика выполняется только если $string является
101
+ // корректной строкой.
102
+ @else {
103
+
104
+ // Извлечение первого символа строки и преобразование его
105
+ // в верхний регистр.
106
+ // Алгоритм:
107
+ // 1. string.slice($string, 1, 1) - извлекает первый символ строки
108
+ // (позиции с 1 по 1 включительно)
109
+ // 2. string.to-upper-case(...) - преобразует символ в верхний регистр
110
+ $-first: string.to-upper-case(string.slice($string, 1, 1));
111
+
112
+ // Извлечение оставшейся части строки (все символы после первого).
113
+ // string.slice($string, 2) возвращает подстроку, начиная со второго символа
114
+ // и до конца строки. Если строка состоит из одного символа,
115
+ // возвращается пустая строка.
116
+ $-rest: string.slice($string, 2);
117
+
118
+ // Соединение заглавного первого символа с оставшейся частью строки.
119
+ // Результат - строка, где первый символ заглавный, а остальные
120
+ // символы сохраняют свой исходный регистр.
121
+ @return $-first + $-rest;
122
+
123
+ }
83
124
 
84
125
  }
@@ -0,0 +1,173 @@
1
+ @use 'sass:string';
2
+ @use '../../loggers/log-invalid-type' as *;
3
+ @use '../../validators/type-of/is-string' as *;
4
+
5
+ /// Подсчитывает количество вхождений подстроки в строку.
6
+ ///
7
+ /// Функция принимает строку и подстроку, затем возвращает
8
+ /// количество непересекающихся вхождений подстроки в строку.
9
+ /// Поиск чувствителен к регистру и выполняется слева направо.
10
+ /// Это полезно для анализа текста, подсчета символов или слов,
11
+ /// проверки частоты встречаемости определенных паттернов в
12
+ /// строковых данных.
13
+ ///
14
+ /// Важные особенности функции:
15
+ /// - Выполняет регистрозависимый поиск подстроки
16
+ /// - Подсчитывает только непересекающиеся вхождения
17
+ /// - Выполняет строгую проверку типов обоих параметров
18
+ /// - Использует цикл while для последовательного поиска
19
+ /// - Возвращает 0, если подстрока не найдена ни разу
20
+ /// - Работает с любыми строками, включая пустые
21
+ /// - Обрабатывает Unicode-символы и многосимвольные подстроки
22
+ /// - Полезна для текстового анализа и статистики
23
+ /// - Может использоваться для подсчета слов или символов
24
+ /// - Подходит для валидации форматов строк
25
+ /// - Эффективна для строк средней длины
26
+ /// - Для пустой подстроки всегда возвращает 0 (поскольку
27
+ /// поиск пустой строки в Sass возвращает null)
28
+ /// - Функция использует алгоритм последовательного поиска
29
+ /// через `string.index()` и усечение строки после каждого
30
+ /// найденного вхождения
31
+ /// - Для длинных строк и часто встречающихся подстрок
32
+ /// производительность может снижаться из-за многократного
33
+ /// вызова `string.slice()`
34
+ /// - Поиск чувствителен к регистру: "Hello" и "hello"
35
+ /// считаются разными подстроками
36
+ /// - Функция корректно обрабатывает многосимвольные
37
+ /// подстроки и Unicode-последовательности
38
+ /// ---
39
+ /// @name string-count
40
+ /// @group utilities-helpers
41
+ /// @since 2026.01.18
42
+ /// @access public
43
+ /// @author Murad Rustamov (therteenten)
44
+ /// @link https://sourcecraft.dev/users/therteenten/overview SourceCraft - therteenten
45
+ /// @link https://sourcecraft.dev/omnisass/library SourceCraft - OmniSass
46
+ /// @link https://sass-lang.com/documentation/modules/string См. также: Официальная документация Sass - Модуль String
47
+ /// @link https://sass-lang.com/documentation/values/strings См. также: Официальная документация Sass - Тип данных "Строки"
48
+ /// @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String См. также: MDN Web Docs - Объект String
49
+ /// @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes См. также: MDN Web Docs - Метод String.includes()
50
+ /// @link https://en.wikipedia.org/wiki/String_(computer_science) См. также: Wikipedia - Строка (информатика)
51
+ /// @link https://en.wikipedia.org/wiki/Substring См. также: Wikipedia - Подстрока
52
+ /// @link https://css-tricks.com/snippets/sass/string-count-function/ См. также: CSS-Tricks - Функция подсчета подстрок в Sass
53
+ /// @link https://css-tricks.com/sass-string-manipulation-functions/ См. также: CSS-Tricks - Функции для работы со строками в Sass
54
+ /// @link https://sass-guidelin.es/ru/#section-44 См. также: Sass Guidelines - Раздел про функции
55
+ /// @link https://github.com/sass/sass/issues/1402 См. также: GitHub - Обсуждение строковых операций в Sass
56
+ /// @link https://www.30secondsofcode.org/sass/p/1/string-count См. также: 30 seconds of code - Функция string-count
57
+ /// @example scss - Подсчет одиночных символов
58
+ /// @debug string-count("hello", "l"); // 2
59
+ /// @debug string-count("banana", "a"); // 3
60
+ /// @debug string-count("test", "t"); // 2
61
+ /// @debug string-count("abc", "z"); // 0
62
+ /// @debug string-count("", "a"); // 0
63
+ /// @debug string-count("aaa", "aa"); // 1 (непересекающиеся)
64
+ /// @example scss - Подсчет многосимвольных подстрок
65
+ /// @debug string-count("hello world", "lo"); // 1
66
+ /// @debug string-count("test test test", "test"); // 3
67
+ /// @debug string-count("ababab", "ab"); // 3
68
+ /// @debug string-count("one two one", "one"); // 2
69
+ /// @debug string-count("hello hello", "hello"); // 2
70
+ /// @example scss - Регистрозависимый поиск
71
+ /// @debug string-count("Hello World", "hello"); // 0
72
+ /// @debug string-count("Hello World", "Hello"); // 1
73
+ /// @debug string-count("ABC abc ABC", "ABC"); // 2
74
+ /// @debug string-count("Test test", "test"); // 1
75
+ /// @example scss - Граничные случаи
76
+ /// @debug string-count("aaaa", "aa"); // 2
77
+ /// @debug string-count("aaaaa", "aa"); // 2
78
+ /// @debug string-count("a", "a"); // 1
79
+ /// @debug string-count("abc", ""); // 0
80
+ /// @debug string-count("", ""); // 0
81
+ /// @param {String} $string - Исходная строка, в которой
82
+ /// выполняется поиск. Может быть любой строкой: пустой,
83
+ /// односимвольной, содержащей пробелы, Unicode-символы
84
+ /// или специальные символы.
85
+ /// @param {String} $substring - Подстрока, количество
86
+ /// вхождений которой нужно подсчитать. Может быть любой
87
+ /// строкой, включая пустую, односимвольную или
88
+ /// многосимвольную. Поиск регистрозависимый.
89
+ /// @return {Number} - Целое неотрицательное число,
90
+ /// представляющее количество непересекающихся вхождений
91
+ /// подстроки `$substring` в строку `$string`.
92
+ /// @throws {Error} - Выбрасывает ошибку, если в параметр
93
+ /// передано значение, не соответствующее необходимому типу:
94
+ ///
95
+ /// - `$string` → `string`
96
+ /// - `$substring` → `string`
97
+ @function string-count($string, $substring) {
98
+
99
+ // Проверка типа первого параметра: ожидается строковое значение.
100
+ // Исходная строка должна быть строкой для выполнения поиска
101
+ // подстроки с помощью функции string.index().
102
+ @if not is-string($string) or $string == '' {
103
+
104
+ // Если $string не является строкой, возвращаем ошибку через
105
+ // стандартную функцию логирования. Это предотвращает
106
+ // некорректные операции с нестроковыми типами данных.
107
+ @return log-invalid-type(
108
+ 'string-count',
109
+ $string,
110
+ '$string',
111
+ 'string'
112
+ );
113
+
114
+ }
115
+
116
+ // Проверка типа второго параметра: ожидается строковое значение.
117
+ // Подстрока также должна быть строкой, так как string.index()
118
+ // работает только со строковыми аргументами.
119
+ @else if not is-string($substring) or $substring == '' {
120
+
121
+ // Если $substring не является строкой, возвращаем ошибку.
122
+ // Проверка выполняется только если $string прошел валидацию.
123
+ @return log-invalid-type(
124
+ 'string-count',
125
+ $substring,
126
+ '$substring',
127
+ 'string'
128
+ );
129
+
130
+ }
131
+
132
+ // Все параметры прошли валидацию - подсчитываем вхождения.
133
+ @else {
134
+
135
+ // Инициализация счетчика вхождений.
136
+ // Начальное значение 0 соответствует случаю, когда подстрока
137
+ // не найдена ни разу.
138
+ $-count: 0;
139
+
140
+ // Поиск первого вхождения подстроки в строку.
141
+ // string.index() возвращает позицию первого вхождения
142
+ // подстроки или null, если подстрока не найдена.
143
+ $-index: string.index($string, $substring);
144
+
145
+ // Цикл поиска всех вхождений подстроки.
146
+ // Цикл продолжается, пока находятся новые вхождения
147
+ // подстроки ($-index != null).
148
+ @while $-index != null {
149
+
150
+ // Увеличение счетчика найденных вхождений.
151
+ // Каждое найденное вхождение увеличивает счетчик на 1.
152
+ $-count: $-count + 1;
153
+
154
+ // Усечение строки после найденного вхождения.
155
+ // Новая строка начинается с символа, следующего за
156
+ // концом найденной подстроки, чтобы исключить
157
+ // пересекающиеся вхождения.
158
+ $string: string.slice($string, $-index + string.length($substring));
159
+
160
+ // Поиск следующего вхождения подстроки в усеченной строке.
161
+ // Если подстрока найдена снова, цикл продолжится,
162
+ // иначе $-index станет null, и цикл завершится.
163
+ $-index: string.index($string, $substring);
164
+
165
+ }
166
+
167
+ // Возвращаем общее количество найденных вхождений.
168
+ // Если подстрока не была найдена ни разу, $-count останется 0.
169
+ @return $-count;
170
+
171
+ }
172
+
173
+ }