@soxa-ui/theme-base 0.0.0-stage → 0.3.0-beta.1

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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +399 -2
  3. package/dist/setup/index.d.ts +31 -0
  4. package/dist/setup/index.js +4 -0
  5. package/dist/setup/plugins/index.d.ts +3 -0
  6. package/dist/setup/plugins/table-head.plugin.d.ts +44 -0
  7. package/dist/setup/plugins/table-head.plugin.js +86 -0
  8. package/dist/setup/plugins/tabs-view.plugin.d.ts +32 -0
  9. package/dist/setup/plugins/tabs-view.plugin.js +100 -0
  10. package/dist/setup/plugins/toasts-front.plugin.d.ts +63 -0
  11. package/dist/setup/plugins/toasts-front.plugin.js +131 -0
  12. package/dist/setup/plugins/transitions.d.ts +43 -0
  13. package/dist/setup/plugins/transitions.js +75 -0
  14. package/package.json +55 -4
  15. package/src/anchored.css +75 -0
  16. package/src/caption.css +71 -0
  17. package/src/components/accordion.css +96 -0
  18. package/src/components/button.css +139 -0
  19. package/src/components/calendar.css +594 -0
  20. package/src/components/check-box.css +121 -0
  21. package/src/components/date-input.css +224 -0
  22. package/src/components/date-picker.css +208 -0
  23. package/src/components/dialog.css +95 -0
  24. package/src/components/drag-and-drop.css +18 -0
  25. package/src/components/drawer.css +171 -0
  26. package/src/components/field.css +440 -0
  27. package/src/components/icon.css +61 -0
  28. package/src/components/input.css +189 -0
  29. package/src/components/label.css +160 -0
  30. package/src/components/list-box.css +165 -0
  31. package/src/components/menu.css +224 -0
  32. package/src/components/number-input.css +183 -0
  33. package/src/components/pagination.css +164 -0
  34. package/src/components/popover.css +164 -0
  35. package/src/components/progress-linear.css +88 -0
  36. package/src/components/progress-spinner.css +127 -0
  37. package/src/components/radio-group.css +130 -0
  38. package/src/components/rating.css +141 -0
  39. package/src/components/scroller.css +136 -0
  40. package/src/components/select.css +233 -0
  41. package/src/components/skeleton.css +33 -0
  42. package/src/components/slider.css +482 -0
  43. package/src/components/switch.css +142 -0
  44. package/src/components/table.css +1513 -0
  45. package/src/components/tabs.css +166 -0
  46. package/src/components/tags.css +371 -0
  47. package/src/components/textarea.css +179 -0
  48. package/src/components/toasts.css +447 -0
  49. package/src/components/tooltip.css +59 -0
  50. package/src/direction.css +30 -0
  51. package/src/disabled.css +26 -0
  52. package/src/group.css +90 -0
  53. package/src/index.css +97 -0
  54. package/src/modal.css +183 -0
  55. package/src/place.css +30 -0
  56. package/src/required.css +40 -0
  57. package/src/reset.css +172 -0
  58. package/src/swipe.css +71 -0
  59. package/src/target.css +46 -0
  60. package/theme-contract.json +3373 -0
  61. package/theme-variables.json +336 -0
@@ -0,0 +1,136 @@
1
+ /* ─── Scroller ────────────────────────────────────────────────
2
+ * Лента произвольного содержимого в одну строку, которую двигают две кнопки.
3
+ * Прокрутка нативная (колесо, палец, трекпад), полосы не видно, шаг листания
4
+ * — страница, то есть видимая ширина вьюпорта.
5
+ *
6
+ * .so-scroller корень — ряд из трёх мест
7
+ * .so-scroller__prev кнопка «назад» (Button)
8
+ * .so-scroller__viewport вьюпорт — прокручиваемая область и строка содержимого
9
+ * .so-scroller__next кнопка «вперёд» (Button)
10
+ *
11
+ * Вьюпорт — одновременно прокручиваемый контейнер, строка содержимого и
12
+ * носитель роли от потребителя (например, `role="listbox"`), поэтому дорожки
13
+ * между ним и содержимым нет: лишний узел отрезал бы элементы от этой роли.
14
+ * По той же причине кнопки стоят снаружи вьюпорта.
15
+ *
16
+ * Есть ли куда листать, пишет на корень плагин ленты (`TScrollerViewportPlugin`)
17
+ * — `data-can-prev` и `data-can-next`. Листать нечего вовсе — кнопок нет: их
18
+ * место уходит содержимому. Гаснут обе разом и только вместе: кнопка на своём
19
+ * краю (`data-disabled` у неё пишет сам Button) остаётся на месте, исчезни она
20
+ * поодиночке, ряд на каждом доезде до края дёргался бы на её ширину. Замер от
21
+ * снятия не зацикливается: снятая кнопка вьюпорт расширяет, а в широком
22
+ * вьюпорте содержимое помещается тем более.
23
+ *
24
+ * Кнопки — во всю высоту ряда, а не размерной высоты Button: ленту ставят в
25
+ * строку поля ввода, в ряд мелких элементов, и своя высота кнопки свисала бы
26
+ * из неё. Ширина остаётся размерной — поле кнопки приходит с её классом
27
+ * `--size-*`. Зазор между кнопкой и лентой — по шкале Button
28
+ * (`components/button.css`), тем же числом, что зазор внутри строки кнопки
29
+ * этого размера.
30
+ *
31
+ * Вьюпорт — строка: содержимое стоит в линию и не сжимается, иначе флексбокс
32
+ * ужал бы ряд по месту и листать стало бы нечего. Поперёк строки он ничего не
33
+ * растягивает — лента двигает чужое содержимое, а не задаёт ему высоту.
34
+ * Прокрутка по строке — `auto`: колесо, палец и трекпад работают только у
35
+ * настоящей прокручиваемой области; поперёк — `clip`, а не `hidden`: тот завёл
36
+ * бы второй прокручиваемый контейнер. Полоса прокрутки не рисуется, а
37
+ * докрученная до края лента страницу за собой не тянет — иначе
38
+ * горизонтальный жест на краю уходит в навигацию «назад» браузера.
39
+ *
40
+ * Запас под кольцо фокуса содержимого — поле вьюпорта: прокручиваемая область
41
+ * режет всё, что вышло за её паддинг-бокс, а запаса за краем обрезки
42
+ * (`overflow-clip-margin`) у неё не бывает. Отрицательное поле по оси блока
43
+ * возвращает ленте прежний след: внешняя высота вьюпорта — высота содержимого.
44
+ *
45
+ * Шаг листания — страница, и снап доводит её до начала элемента, а не режет
46
+ * элемент посередине; действует он и на программную прокрутку, поэтому
47
+ * кнопке достаточно сдвинуть ленту на видимую ширину. `mandatory`:
48
+ * недоведённая страница — ровно тот обрезанный элемент, ради которого снап и
49
+ * взят. Точки снапа — прямые дети вьюпорта, то есть сами элементы ленты.
50
+ *
51
+ * Окно снапа — паддинг-бокс вьюпорта, вдвинутый отступом прокрутки на ширину
52
+ * подсказки у края, если её рисует тема (`--so-scroller-fade`, контракт), и не
53
+ * меньше чем на запас под кольцо фокуса. В это окно плагин ленты доводит
54
+ * элемент под фокусом с клавиатуры, и в нём встаёт страница: элемент в её
55
+ * начале виден целиком, а не под подсказкой. В покое лента стоит в начале
56
+ * прокрутки: начало первого элемента лежит на запасе под кольцо — не дальше
57
+ * края окна, — и его точка снапа — само начало прокрутки или раньше него.
58
+ *
59
+ * Запас под кольцо — переменная корня документа; умолчание — число темы oren,
60
+ * тема задаёт своё на корне документа.
61
+ *
62
+ * Облик — подсказка у края маской, кольцо фокуса ленты, вид кнопок и зеркало
63
+ * стрелок в RTL — рисует тема. */
64
+
65
+ :root {
66
+ --so-scroller-bleed: 0.25rem;
67
+ }
68
+
69
+ /* `min-width: 0` — чтобы вьюпорт мог стать уже своего содержимого:
70
+ * автоматический минимум ширины, оставленный хоть одному звену цепочки,
71
+ * переполняет родителя вместо прокрутки. */
72
+ .so-scroller {
73
+ display: flex;
74
+ align-items: stretch;
75
+ min-width: 0;
76
+ max-width: 100%;
77
+ }
78
+
79
+ .so-scroller[data-can-prev='false'][data-can-next='false'] > .so-scroller__prev,
80
+ .so-scroller[data-can-prev='false'][data-can-next='false'] > .so-scroller__next {
81
+ display: none;
82
+ }
83
+
84
+ .so-scroller--size-sm {
85
+ gap: var(--so-button-gap-sm);
86
+ }
87
+
88
+ .so-scroller--size-normal {
89
+ gap: var(--so-button-gap-normal);
90
+ }
91
+
92
+ .so-scroller--size-lg {
93
+ gap: var(--so-button-gap-lg);
94
+ }
95
+
96
+ .so-scroller--size-xl {
97
+ gap: var(--so-button-gap-xl);
98
+ }
99
+
100
+ .so-scroller--size-2xl {
101
+ gap: var(--so-button-gap-2xl);
102
+ }
103
+
104
+ /* Высота — ряда: перекрывает размерную высоту Button той же специфичностью,
105
+ * поэтому файл подключён ниже `button.css`. */
106
+ .so-scroller__prev,
107
+ .so-scroller__next {
108
+ flex-shrink: 0;
109
+ align-self: stretch;
110
+ height: auto;
111
+ }
112
+
113
+ .so-scroller__viewport {
114
+ display: flex;
115
+ flex-grow: 1;
116
+ align-items: center;
117
+ min-width: 0;
118
+ overflow-x: auto;
119
+ overflow-y: clip;
120
+ padding: var(--so-scroller-bleed);
121
+ margin-block: calc(-1 * var(--so-scroller-bleed));
122
+ scroll-padding-inline: max(var(--so-scroller-bleed), var(--so-scroller-fade, 0px));
123
+ overscroll-behavior-inline: contain;
124
+ scrollbar-width: none;
125
+ scroll-snap-type: inline mandatory;
126
+ }
127
+
128
+ /* Полоса в движках WebKit: `scrollbar-width` там появился поздно. */
129
+ .so-scroller__viewport::-webkit-scrollbar {
130
+ display: none;
131
+ }
132
+
133
+ .so-scroller__viewport > * {
134
+ flex-shrink: 0;
135
+ scroll-snap-align: start;
136
+ }
@@ -0,0 +1,233 @@
1
+ /* ─── Select ──────────────────────────────────────────────────
2
+ * Поле выбора из списка. Поле — обычный Input (`components/input.css`): его
3
+ * коробка, слоты и кнопка очистки приходят оттуда, здесь — то, что добавляет
4
+ * Select.
5
+ *
6
+ * .so-select корень — якорь панели
7
+ * .so-select__field поле — Input
8
+ * .so-select__tags теги выбранного в слоте `leading` (Tags)
9
+ * .so-select__arrow обёртка стрелки в слоте `trailing`
10
+ * .so-select__panel панель — Frame, телепортированный в `body`
11
+ * .so-select__handle полоса жеста, пока он включён
12
+ * .so-select__list список опций — прокручивается он
13
+ * .so-select-item опция — строка (Button)
14
+ * .so-select-item__indicator отметка выбранного
15
+ * .so-select-group группа — подпись и её опции
16
+ * .so-select__separator разделитель секций групп (`group.css`)
17
+ * .so-select__filler распорка окна `Virtual`
18
+ *
19
+ * Корень — флекс во всю ширину места, а не блок: поле внутри — `inline-flex`,
20
+ * и у блочного родителя строчный ребёнок попадает в строку текста — высота
21
+ * корня считалась бы по её метрикам, а не по высоте поля. Флекс строк не
22
+ * заводит, и корень ровно по полю. Минимум ширины — минимум поля
23
+ * (`--so-input-min-width`): процент считается от контейнера, а если тот сам
24
+ * сжимается по содержимому, круг замыкается, и поле схлопывается в полосу.
25
+ * Теги в минимум не входят: правило, которое зависит от того, сколько их и
26
+ * какие они, неверно по построению, — ряд берёт остаток строки, а лишнее
27
+ * обрезается.
28
+ *
29
+ * Курсор у поля зависит от режима: в select-only (`editable: false`) поле
30
+ * только для чтения — редактировать нечего, и курсора редактирования нет.
31
+ *
32
+ * Теги — второй компонент в слоте `leading`. В `wrap` поле растёт по строкам
33
+ * тегов: высота авто, сверху и снизу — просвет слота полем, а ввод, очистка и
34
+ * стрелка — на первой строке; одна строка тегов даёт ровно высоту размера.
35
+ * Признак — тег в ряду, а не сам ряд: в `multiple` контейнер тегов в разметке
36
+ * всегда. Строка ввода — строка слота, а слот растёт вместе со строками. В
37
+ * `scroll` ряд стоит одной строкой (`components/tags.css`), и поле — высоты
38
+ * размера. Само поле остаётся `nowrap`: перенос нужен только тегам в слоте. У
39
+ * слота с тегами снят автоматический минимум ширины — ввод рядом получает то,
40
+ * что тегам сейчас не нужно, — а что в слот не влезло, он обрезает по строке
41
+ * (`clip`: прокрутки в слоте быть не должно, а по вертикали там кольцо фокуса
42
+ * тега). Ряд `scroll` — по своим тегам, но не шире половины поля: ввод не
43
+ * садится на свой минимум, когда тегам тесно, а в этой половине ряд
44
+ * прокручивается. Ввод берёт остаток строки после слотов, а не всю ширину
45
+ * поля.
46
+ *
47
+ * Стрелка — обёртка слота, а не сама иконка: подменишь иконку слотом — тема
48
+ * повернёт и её. Обёртка — область нажатия: в `editable` по тексту поля панель
49
+ * не открывается, и стрелка — единственное место, которое её открывает, —
50
+ * поэтому она квадрат в строку слота, как очистка рядом, со значком по
51
+ * центру. Зона нажатия на мелком размере — `target.css`, и обёртка — её
52
+ * опора. Кегль обёртки — кегль поля по размеру (шкала поля): значок разметка
53
+ * рисует размером Select, в `em` от кегля места.
54
+ *
55
+ * Список — то же устройство, что у узла списка ListBox
56
+ * (`components/list-box.css`), с теми же полем и зазором, чтобы отступы до
57
+ * опций совпадали. Потолок высоты — по умолчанию, пока `maxRows` не задан:
58
+ * задали — `TListHeightPlugin` пишет списку инлайновый `max-height` по числу
59
+ * строк. Распорка окна и группы — как у ListBox. Опция — строка во всю ширину
60
+ * списка, содержимое у начала строки, курсор обычный; выключенная гасит
61
+ * строкой (её `data-disabled`), а обёртка — только указатель. Перенос (`wrap`)
62
+ * опция читает с себя: `data-content-fit` ей ставит `TSelectExtension`.
63
+ *
64
+ * Полоса жеста и закрытая панель — `anchored.css`.
65
+ *
66
+ * Потолок списка и поле строки опции — переменные корня документа;
67
+ * умолчания — числа темы oren, тема задаёт свои на корне документа.
68
+ *
69
+ * Облик — поверхность, кромка, тень и скругление панели, её проявление и
70
+ * жест, поворот стрелки открытой панели, рамка ошибки, вид опций — рисует
71
+ * тема. */
72
+
73
+ :root {
74
+ --so-select-max-height: 16rem;
75
+ --so-select-item-padding: 0.5rem;
76
+ }
77
+
78
+ .so-select {
79
+ position: relative;
80
+ display: flex;
81
+ width: 100%;
82
+ min-width: var(--so-input-min-width);
83
+ }
84
+
85
+ .so-select__field {
86
+ width: 100%;
87
+ }
88
+
89
+ .so-select__field input:read-only {
90
+ cursor: default;
91
+ -webkit-user-select: none;
92
+ user-select: none;
93
+ }
94
+
95
+ .so-select__field:has(.so-tags[data-overflow='wrap'] .so-tags-item) {
96
+ align-items: flex-start;
97
+ height: auto;
98
+ padding-block: var(--so-input-slot-gap);
99
+ }
100
+
101
+ .so-select__field:has(.so-tags[data-overflow='wrap'] .so-tags-item) input {
102
+ line-height: var(--so-input-slot-row);
103
+ }
104
+
105
+ .so-select__field:has(.so-tags[data-overflow='wrap'] .so-tags-item) .so-input__leading {
106
+ height: auto;
107
+ }
108
+
109
+ .so-select__field .so-input__leading:has(.so-tags-item) {
110
+ flex-wrap: wrap;
111
+ align-items: center;
112
+ min-width: 0;
113
+ overflow-x: clip;
114
+ }
115
+
116
+ .so-select__field .so-input__leading:has(> .so-tags[data-overflow='scroll'] > .so-tags-item) {
117
+ max-width: 50%;
118
+ }
119
+
120
+ .so-select__field input {
121
+ flex: 1;
122
+ }
123
+
124
+ .so-select__arrow {
125
+ position: relative;
126
+ display: inline-flex;
127
+ flex-shrink: 0;
128
+ align-items: center;
129
+ justify-content: center;
130
+ inline-size: var(--so-input-slot-row);
131
+ block-size: var(--so-input-slot-row);
132
+ }
133
+
134
+ /* Кегля у `normal` в шкале поля нет, и правила тоже: значок берёт кегль
135
+ * места. */
136
+ .so-select--size-sm .so-select__arrow {
137
+ font-size: var(--so-input-font-size-sm);
138
+ line-height: var(--so-input-line-height-sm);
139
+ }
140
+
141
+ .so-select--size-lg .so-select__arrow {
142
+ font-size: var(--so-input-font-size-lg);
143
+ line-height: var(--so-input-line-height-lg);
144
+ }
145
+
146
+ .so-select--size-xl .so-select__arrow {
147
+ font-size: var(--so-input-font-size-xl);
148
+ line-height: var(--so-input-line-height-xl);
149
+ }
150
+
151
+ .so-select--size-2xl .so-select__arrow {
152
+ font-size: var(--so-input-font-size-2xl);
153
+ line-height: var(--so-input-line-height-2xl);
154
+ }
155
+
156
+ .so-select__panel {
157
+ overflow: hidden;
158
+ }
159
+
160
+ .so-select__list {
161
+ display: flex;
162
+ flex-direction: column;
163
+ gap: var(--so-list-box-gap);
164
+ max-height: var(--so-select-max-height);
165
+ padding: var(--so-list-box-padding);
166
+ overflow-y: auto;
167
+ }
168
+
169
+ .so-select__filler {
170
+ flex-shrink: 0;
171
+ margin-block-end: calc(-1 * var(--so-list-box-gap));
172
+ block-size: var(--so-filler-height, 0);
173
+ }
174
+
175
+ .so-select-group {
176
+ display: flex;
177
+ flex-shrink: 0;
178
+ flex-direction: column;
179
+ gap: var(--so-list-box-gap);
180
+ }
181
+
182
+ .so-select-group__label {
183
+ padding-block: var(--so-list-box-group-label-padding);
184
+ }
185
+
186
+ /* Секции без разделителя — то же устройство, что у ListBox: секция
187
+ * отступает на зазор списка и место линии и встаёт там же, где стояла бы за
188
+ * разделителем (`group.css`). */
189
+ :is(.so-select-group, .so-select-item) + .so-select-group,
190
+ .so-select-group + .so-select-item {
191
+ margin-block-start: calc(var(--so-list-box-gap) + var(--so-group-separator-width, 0px));
192
+ }
193
+
194
+ .so-select-item > * {
195
+ justify-content: flex-start;
196
+ width: 100%;
197
+ padding-block: var(--so-select-item-padding);
198
+ }
199
+
200
+ .so-select-item > .so-button {
201
+ cursor: default;
202
+ }
203
+
204
+ .so-select-item > .so-button > .so-button__text {
205
+ min-width: 0;
206
+ text-align: start;
207
+ }
208
+
209
+ :where(.so-select-item > .so-button > .so-button__text > *) {
210
+ margin-inline: 0 auto;
211
+ }
212
+
213
+ .so-select-item__indicator {
214
+ display: inline-flex;
215
+ flex-shrink: 0;
216
+ align-items: center;
217
+ justify-content: center;
218
+ width: 1em;
219
+ height: 1em;
220
+ }
221
+
222
+ .so-select-item[data-content-fit='wrap'] > .so-button {
223
+ min-height: fit-content;
224
+ }
225
+
226
+ .so-select-item[data-content-fit='wrap'] > .so-button > .so-button__text {
227
+ white-space: normal;
228
+ overflow: visible;
229
+ }
230
+
231
+ .so-select-item[data-disabled='true'] {
232
+ pointer-events: none;
233
+ }
@@ -0,0 +1,33 @@
1
+ /* ─── Skeleton ────────────────────────────────────────────────
2
+ * Заглушка — место будущего содержимого, пока оно грузится.
3
+ *
4
+ * .so-skeleton корень — блок
5
+ * .so-skeleton__placeholder заглушка — поверх содержимого, во весь корень
6
+ * … содержимое слота
7
+ *
8
+ * Пока заглушка показана, она лежит поверх содержимого во весь корень, а само
9
+ * содержимое скрыто прозрачностью: место за ним остаётся, и когда заглушку
10
+ * снимут, ничего не сдвинется.
11
+ *
12
+ * Размера по шкале у заглушки нет: `size` у Skeleton нет вовсе, а ширину и
13
+ * высоту из свойств `width` и `height` пишет на корень инлайном библиотека
14
+ * (по умолчанию `auto` — по месту и по содержимому).
15
+ *
16
+ * Облик — цвет заглушки, её формы (`--shape-*`) и анимации (`--animation-*`),
17
+ * вид в режиме принудительных цветов — рисует тема: формы и анимации —
18
+ * значения её наборов. */
19
+
20
+ .so-skeleton {
21
+ position: relative;
22
+ display: block;
23
+ }
24
+
25
+ .so-skeleton:has(.so-skeleton__placeholder) > :not(.so-skeleton__placeholder) {
26
+ opacity: 0;
27
+ }
28
+
29
+ .so-skeleton__placeholder {
30
+ position: absolute;
31
+ inset: 0;
32
+ z-index: 10;
33
+ }