@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.
- package/LICENSE +21 -0
- package/README.md +399 -2
- package/dist/setup/index.d.ts +31 -0
- package/dist/setup/index.js +4 -0
- package/dist/setup/plugins/index.d.ts +3 -0
- package/dist/setup/plugins/table-head.plugin.d.ts +44 -0
- package/dist/setup/plugins/table-head.plugin.js +86 -0
- package/dist/setup/plugins/tabs-view.plugin.d.ts +32 -0
- package/dist/setup/plugins/tabs-view.plugin.js +100 -0
- package/dist/setup/plugins/toasts-front.plugin.d.ts +63 -0
- package/dist/setup/plugins/toasts-front.plugin.js +131 -0
- package/dist/setup/plugins/transitions.d.ts +43 -0
- package/dist/setup/plugins/transitions.js +75 -0
- package/package.json +55 -4
- package/src/anchored.css +75 -0
- package/src/caption.css +71 -0
- package/src/components/accordion.css +96 -0
- package/src/components/button.css +139 -0
- package/src/components/calendar.css +594 -0
- package/src/components/check-box.css +121 -0
- package/src/components/date-input.css +224 -0
- package/src/components/date-picker.css +208 -0
- package/src/components/dialog.css +95 -0
- package/src/components/drag-and-drop.css +18 -0
- package/src/components/drawer.css +171 -0
- package/src/components/field.css +440 -0
- package/src/components/icon.css +61 -0
- package/src/components/input.css +189 -0
- package/src/components/label.css +160 -0
- package/src/components/list-box.css +165 -0
- package/src/components/menu.css +224 -0
- package/src/components/number-input.css +183 -0
- package/src/components/pagination.css +164 -0
- package/src/components/popover.css +164 -0
- package/src/components/progress-linear.css +88 -0
- package/src/components/progress-spinner.css +127 -0
- package/src/components/radio-group.css +130 -0
- package/src/components/rating.css +141 -0
- package/src/components/scroller.css +136 -0
- package/src/components/select.css +233 -0
- package/src/components/skeleton.css +33 -0
- package/src/components/slider.css +482 -0
- package/src/components/switch.css +142 -0
- package/src/components/table.css +1513 -0
- package/src/components/tabs.css +166 -0
- package/src/components/tags.css +371 -0
- package/src/components/textarea.css +179 -0
- package/src/components/toasts.css +447 -0
- package/src/components/tooltip.css +59 -0
- package/src/direction.css +30 -0
- package/src/disabled.css +26 -0
- package/src/group.css +90 -0
- package/src/index.css +97 -0
- package/src/modal.css +183 -0
- package/src/place.css +30 -0
- package/src/required.css +40 -0
- package/src/reset.css +172 -0
- package/src/swipe.css +71 -0
- package/src/target.css +46 -0
- package/theme-contract.json +3373 -0
- package/theme-variables.json +336 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* ─── Popover ─────────────────────────────────────────────────
|
|
2
|
+
* Триггер и панель поверх страницы с произвольным содержимым.
|
|
3
|
+
*
|
|
4
|
+
* .so-popover корень — обёртка триггера и якорь панели
|
|
5
|
+
* .so-popover__panel панель — Frame, телепортированный в `body`
|
|
6
|
+
* .so-popover__handle полоса жеста, пока он включён
|
|
7
|
+
* .so-popover__content содержимое — прокручивается оно
|
|
8
|
+
* .so-popover__close кнопка закрытия — Button
|
|
9
|
+
*
|
|
10
|
+
* Корень — строчный флекс: в строке текста он встаёт строчным элементом и не
|
|
11
|
+
* рвёт её, во флекс-ряду — обычным элементом ряда. Не `inline-block`: вокруг
|
|
12
|
+
* строчного триггера он завёл бы строку со своей высотой, и якорь оказался бы
|
|
13
|
+
* выше триггера. Не `contents`: у такого узла нет прямоугольника, и якорю было
|
|
14
|
+
* бы нечего мерить. Панель телепортирована, поэтому прямоугольник корня — это
|
|
15
|
+
* прямоугольник триггера.
|
|
16
|
+
*
|
|
17
|
+
* Панель — ряд из двух колонок: содержимое и кнопка закрытия. У кнопки своя
|
|
18
|
+
* колонка, поэтому содержимое под неё не заходит, и признак «закрываемая»
|
|
19
|
+
* не нужен: без кнопки колонки нет. Ширина — по содержимому, но не шире
|
|
20
|
+
* потолка и окна: минус `1rem` — чтобы панель всегда помещалась в окно, сдвиг
|
|
21
|
+
* `TAnchorPlugin` держит её в окне, только пока она в него помещается. Высота:
|
|
22
|
+
* панель встаёт на ту сторону от якоря, где места больше, а там его не меньше
|
|
23
|
+
* половины окна без половины якоря, — поэтому потолок — половина окна с
|
|
24
|
+
* запасом на якорь и отступ, и длинное содержимое прокручивается, а не уезжает
|
|
25
|
+
* за край окна вместе с кнопкой закрытия.
|
|
26
|
+
*
|
|
27
|
+
* Содержимое просит ширину целиком (`max-content`): панель позиционирована с
|
|
28
|
+
* `left`, и с шириной по содержимому место под текст было бы от `left` до края
|
|
29
|
+
* окна — у якоря справа длинный текст сложился бы в узкий столбец. Панель
|
|
30
|
+
* упирается в свой потолок, и содержимое переносится уже внутри, длинное
|
|
31
|
+
* слово тоже. Прокручивается содержимое, а не панель: кнопка закрытия в
|
|
32
|
+
* соседней колонке остаётся на месте. Отступ — внутри прокрутки: у края
|
|
33
|
+
* прокручиваемой области кольцо фокуса вложенного контрола обрезалось бы.
|
|
34
|
+
* Таблица прямо в содержимом встаёт закреплённой шапкой к краю, как в теле
|
|
35
|
+
* окна (`modal.css`).
|
|
36
|
+
*
|
|
37
|
+
* Кнопка закрытия — квадрат высоты своего размера в углу со стороны конца
|
|
38
|
+
* строки.
|
|
39
|
+
*
|
|
40
|
+
* Панель внутри контейнера (`contained`): не в `body` у триггера, а у края
|
|
41
|
+
* ближайшего позиционированного предка (`data-edge`), как выезжающая панель в
|
|
42
|
+
* контейнере. Раскладка ставит её `absolute` без координат: вдоль края — во
|
|
43
|
+
* всю его длину, поперёк — по содержимому, но не больше контейнера. Потолков
|
|
44
|
+
* окна у неё нет: её мера — контейнер. Полоса жеста у неё — у края,
|
|
45
|
+
* противоположного её краю (панель сверху — полоса снизу, у начала строки —
|
|
46
|
+
* у конца): зона захвата — поле панели с этой стороны, и содержимое с кнопкой
|
|
47
|
+
* закрытия она не накрывает. У бокового края полоса — во всю высоту панели,
|
|
48
|
+
* штрих поперёк строки. Полоса у якоря и закрытая панель — `anchored.css`.
|
|
49
|
+
*
|
|
50
|
+
* Потолки, отступ содержимого и кнопки — переменные корня документа;
|
|
51
|
+
* умолчания — числа темы oren, тема задаёт свои на корне документа.
|
|
52
|
+
*
|
|
53
|
+
* Панель у бокового края во время жеста идёт за указателем вбок: сдвиг
|
|
54
|
+
* плагина жеста (`--so-swipe-offset`, `swipe.css`) — по горизонтали.
|
|
55
|
+
*
|
|
56
|
+
* Облик — поверхность, кромка, тень, скругление, вид кнопки закрытия,
|
|
57
|
+
* проявление, въезд от края, возврат отпущенной и уход смахнутой — рисует
|
|
58
|
+
* тема. */
|
|
59
|
+
|
|
60
|
+
:root {
|
|
61
|
+
--so-popover-max-width: 20rem;
|
|
62
|
+
--so-popover-max-height: 24rem;
|
|
63
|
+
--so-popover-padding: 0.5rem;
|
|
64
|
+
--so-popover-close-offset: 0.5rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.so-popover {
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.so-popover__panel {
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: flex-start;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
max-width: min(var(--so-popover-max-width), calc(100vw - 1rem));
|
|
76
|
+
max-height: min(var(--so-popover-max-height), calc(50dvh - 1.5rem));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.so-popover__panel[data-contained='true'] {
|
|
80
|
+
max-width: 100%;
|
|
81
|
+
max-height: 100%;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.so-popover__panel[data-contained='true'][data-edge='top'] {
|
|
85
|
+
inset: 0 0 auto;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.so-popover__panel[data-contained='true'][data-edge='bottom'] {
|
|
89
|
+
inset: auto 0 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.so-popover__panel[data-contained='true'][data-edge='start'] {
|
|
93
|
+
inset-block: 0;
|
|
94
|
+
inset-inline: 0 auto;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.so-popover__panel[data-contained='true'][data-edge='end'] {
|
|
98
|
+
inset-block: 0;
|
|
99
|
+
inset-inline: auto 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.so-popover__content {
|
|
103
|
+
align-self: stretch;
|
|
104
|
+
width: max-content;
|
|
105
|
+
min-width: 0;
|
|
106
|
+
padding: var(--so-popover-padding);
|
|
107
|
+
overflow-y: auto;
|
|
108
|
+
overflow-wrap: break-word;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:where(.so-popover__content > .so-table__viewport) {
|
|
112
|
+
--so-table-head-offset: calc(-1 * var(--so-popover-padding));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.so-popover__close {
|
|
116
|
+
flex-shrink: 0;
|
|
117
|
+
aspect-ratio: 1;
|
|
118
|
+
padding-inline: 0;
|
|
119
|
+
margin-block-start: var(--so-popover-close-offset);
|
|
120
|
+
margin-inline-end: var(--so-popover-close-offset);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.so-popover__panel:has(> .so-popover__handle)[data-edge='top'] {
|
|
124
|
+
padding-block-start: 0;
|
|
125
|
+
padding-block-end: var(--so-target-size);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.so-popover__panel:has(> .so-popover__handle):is([data-edge='start'], [data-edge='end']) {
|
|
129
|
+
padding-block-start: 0;
|
|
130
|
+
translate: var(--so-swipe-offset) 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.so-popover__panel:has(> .so-popover__handle)[data-edge='start'] {
|
|
134
|
+
padding-inline-end: var(--so-target-size);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.so-popover__panel:has(> .so-popover__handle)[data-edge='end'] {
|
|
138
|
+
padding-inline-start: var(--so-target-size);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.so-popover__panel[data-edge='top'] > .so-popover__handle {
|
|
142
|
+
top: auto;
|
|
143
|
+
bottom: 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.so-popover__panel:is([data-edge='start'], [data-edge='end']) > .so-popover__handle {
|
|
147
|
+
inset-block: 0;
|
|
148
|
+
inset-inline: auto;
|
|
149
|
+
inline-size: var(--so-target-size);
|
|
150
|
+
block-size: auto;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.so-popover__panel:is([data-edge='start'], [data-edge='end']) > .so-popover__handle::before {
|
|
154
|
+
inline-size: var(--so-swipe-grip-thickness);
|
|
155
|
+
block-size: var(--so-swipe-grip-length);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.so-popover__panel[data-edge='start'] > .so-popover__handle {
|
|
159
|
+
inset-inline-end: 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.so-popover__panel[data-edge='end'] > .so-popover__handle {
|
|
163
|
+
inset-inline-start: 0;
|
|
164
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Linear progress: the root is the track, __range is the determinate fill,
|
|
3
|
+
* and the root's ::after is the indeterminate segment. Core supplies a percentage
|
|
4
|
+
* through --so-progress-linear-percent (absent while indeterminate),
|
|
5
|
+
* data-indeterminate and --orientation-horizontal/--orientation-vertical.
|
|
6
|
+
*
|
|
7
|
+
* The track clips its fill and moving segment. Horizontal tracks are blocks,
|
|
8
|
+
* vertical tracks inline blocks. Default lengths have zero specificity: the
|
|
9
|
+
* Input minimum width horizontally and Slider length vertically. Thickness uses
|
|
10
|
+
* the Slider rail scale for the same size. Logical properties fill from inline
|
|
11
|
+
* start (right in RTL); vertical fill rises from the bottom. Visibility rather
|
|
12
|
+
* than display allows the determinate fill to resume its transition from zero.
|
|
13
|
+
* The theme owns colors, rounding, transitions, motion and forced-color appearance.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* Размер без модификатора — `normal`: ядро пишет `--size-*` всегда, но блок и
|
|
17
|
+
* без него обязан выглядеть умолчанием. */
|
|
18
|
+
.so-progress-linear {
|
|
19
|
+
--so-progress-linear-thickness: var(--so-slider-rail-size-normal);
|
|
20
|
+
position: relative;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.so-progress-linear--orientation-horizontal {
|
|
25
|
+
display: block;
|
|
26
|
+
block-size: var(--so-progress-linear-thickness);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.so-progress-linear--orientation-vertical {
|
|
30
|
+
display: inline-block;
|
|
31
|
+
inline-size: var(--so-progress-linear-thickness);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.so-progress-linear__range {
|
|
35
|
+
position: absolute;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.so-progress-linear--orientation-horizontal > .so-progress-linear__range {
|
|
39
|
+
inset-block: 0;
|
|
40
|
+
inset-inline-start: 0;
|
|
41
|
+
inline-size: var(--so-progress-linear-percent, 0%);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.so-progress-linear--orientation-vertical > .so-progress-linear__range {
|
|
45
|
+
inset-inline: 0;
|
|
46
|
+
inset-block-end: 0;
|
|
47
|
+
block-size: var(--so-progress-linear-percent, 0%);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.so-progress-linear[data-indeterminate='true'] > .so-progress-linear__range {
|
|
51
|
+
visibility: hidden;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.so-progress-linear[data-indeterminate='true']::after {
|
|
55
|
+
content: '';
|
|
56
|
+
position: absolute;
|
|
57
|
+
inset: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.so-progress-linear--size-sm {
|
|
61
|
+
--so-progress-linear-thickness: var(--so-slider-rail-size-sm);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.so-progress-linear--size-normal {
|
|
65
|
+
--so-progress-linear-thickness: var(--so-slider-rail-size-normal);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.so-progress-linear--size-lg {
|
|
69
|
+
--so-progress-linear-thickness: var(--so-slider-rail-size-lg);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.so-progress-linear--size-xl {
|
|
73
|
+
--so-progress-linear-thickness: var(--so-slider-rail-size-xl);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.so-progress-linear--size-2xl {
|
|
77
|
+
--so-progress-linear-thickness: var(--so-slider-rail-size-2xl);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Длина по умолчанию — нулевой специфичности: класс или стиль потребителя её
|
|
81
|
+
* перекрывает, не споря. */
|
|
82
|
+
:where(.so-progress-linear--orientation-horizontal) {
|
|
83
|
+
min-width: var(--so-input-min-width);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
:where(.so-progress-linear--orientation-vertical) {
|
|
87
|
+
height: var(--so-slider-length);
|
|
88
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* ─── ProgressSpinner ─────────────────────────────────────────
|
|
2
|
+
* Индикатор выполнения кольцом: доля готового, когда она известна, и бег,
|
|
3
|
+
* когда неизвестна. Модель та же, что у ProgressLinear, своё у кольца —
|
|
4
|
+
* рисунок.
|
|
5
|
+
*
|
|
6
|
+
* .so-progress-spinner корень — строчный, диаметр кольца
|
|
7
|
+
* svg.so-progress-spinner__ring рисунок
|
|
8
|
+
* .so-progress-spinner__track дорожка — полное кольцо
|
|
9
|
+
* .so-progress-spinner__range дуга доли
|
|
10
|
+
* .so-progress-spinner__runner бегущая дуга, пока доля неизвестна
|
|
11
|
+
*
|
|
12
|
+
* От ядра на корне приходят доля готового — `--so-progress-spinner-fraction`
|
|
13
|
+
* в `style`, число от 0 до 1, без единиц: длину дуги SVG меряет в долях
|
|
14
|
+
* окружности (`pathLength="1"` в разметке), и доля ложится в штрих как есть;
|
|
15
|
+
* пока кольцо бежит, переменной нет, — и `data-indeterminate`.
|
|
16
|
+
*
|
|
17
|
+
* Рисунок — SVG в разметке: дорожка и две дуги на одной окружности радиуса 7
|
|
18
|
+
* в поле 16 × 16. Окружности — только штрих: заливка у фигуры SVG по
|
|
19
|
+
* умолчанию сплошная, и кольцо стало бы кругом. Толщина — 2 в тех же
|
|
20
|
+
* единицах, восьмая часть диаметра, и кольцо ровно касается краёв поля: 7 +
|
|
21
|
+
* 2 / 2 = 8. Толщина — доля диаметра, а не пиксели: с диаметром она растёт
|
|
22
|
+
* сама, и у кольца, которому потребитель задал свой размер, тоже. За край
|
|
23
|
+
* поля рисунок не обрезается: кольцо касается его, и обрезка срезала бы
|
|
24
|
+
* сглаживание внешнего края.
|
|
25
|
+
*
|
|
26
|
+
* Дуги начинаются сверху: окружность SVG начинается справа, на трёх часах, и
|
|
27
|
+
* поворот рисунка на четверть назад ставит её начало на двенадцать. Идут они
|
|
28
|
+
* по часовой в любом направлении письма: кольцо читается как циферблат, а у
|
|
29
|
+
* кольца строки нет. Поворот — `transform`, а бег, который рисует тема, —
|
|
30
|
+
* свойство `rotate`: они складываются, и начало дуг от бега не зависит.
|
|
31
|
+
*
|
|
32
|
+
* Дуга доли — от начала кольца на долю готового. Промежуток штриха дополняет
|
|
33
|
+
* дугу до целой окружности, а не равен ей: на 100 % он нулевой, и браузер
|
|
34
|
+
* рисует кольцо сплошным, без шва на двенадцати. Доля неизвестна — дуга доли
|
|
35
|
+
* спрятана (`visibility`), а не убрана из отрисовки: когда бег снимут, тема
|
|
36
|
+
* ведёт её к доле от начала кольца переходом. Вместо неё — бегущая дуга: в
|
|
37
|
+
* покое она — всё кольцо, сплошным штрихом, как полная доля, а её бег — дугой
|
|
38
|
+
* в четверть кольца — движение, и его рисует тема.
|
|
39
|
+
*
|
|
40
|
+
* Строчный: кольцо встаёт в строку текста и во флекс кнопки рядом с текстом,
|
|
41
|
+
* и в сжатой кнопке не сжимается. Диаметр — нулевой специфичности: класс или
|
|
42
|
+
* стиль потребителя его перекрывает, не споря, и толщина вырастает вместе с
|
|
43
|
+
* ним.
|
|
44
|
+
*
|
|
45
|
+
* Диаметр по размеру — переменные корня документа (`sm`…`2xl`); блок берёт
|
|
46
|
+
* ступень своего размера в `--so-progress-spinner-size`. Умолчания — числа
|
|
47
|
+
* темы oren, тема задаёт свои на корне документа.
|
|
48
|
+
*
|
|
49
|
+
* Облик — цвет дорожки и дуг, переход доли, бег и мерцание и вид в режиме
|
|
50
|
+
* принудительных цветов — рисует тема. */
|
|
51
|
+
|
|
52
|
+
:root {
|
|
53
|
+
--so-progress-spinner-size-sm: 0.75rem;
|
|
54
|
+
--so-progress-spinner-size-normal: 1rem;
|
|
55
|
+
--so-progress-spinner-size-lg: 1.25rem;
|
|
56
|
+
--so-progress-spinner-size-xl: 1.5rem;
|
|
57
|
+
--so-progress-spinner-size-2xl: 1.75rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Размер без модификатора — `normal`: ядро пишет `--size-*` всегда, но блок и
|
|
61
|
+
* без него обязан выглядеть умолчанием. */
|
|
62
|
+
.so-progress-spinner {
|
|
63
|
+
--so-progress-spinner-size: var(--so-progress-spinner-size-normal);
|
|
64
|
+
display: inline-flex;
|
|
65
|
+
flex-shrink: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.so-progress-spinner__ring {
|
|
69
|
+
width: 100%;
|
|
70
|
+
height: 100%;
|
|
71
|
+
overflow: visible;
|
|
72
|
+
transform: rotate(-90deg);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.so-progress-spinner__track,
|
|
76
|
+
.so-progress-spinner__range,
|
|
77
|
+
.so-progress-spinner__runner {
|
|
78
|
+
fill: none;
|
|
79
|
+
stroke-width: 2;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.so-progress-spinner__range {
|
|
83
|
+
stroke-dasharray: var(--so-progress-spinner-fraction, 0)
|
|
84
|
+
calc(1 - var(--so-progress-spinner-fraction, 0));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.so-progress-spinner__runner {
|
|
88
|
+
visibility: hidden;
|
|
89
|
+
stroke-dasharray: 1 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.so-progress-spinner[data-indeterminate='true']
|
|
93
|
+
> .so-progress-spinner__ring
|
|
94
|
+
> .so-progress-spinner__range {
|
|
95
|
+
visibility: hidden;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.so-progress-spinner[data-indeterminate='true']
|
|
99
|
+
> .so-progress-spinner__ring
|
|
100
|
+
> .so-progress-spinner__runner {
|
|
101
|
+
visibility: visible;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.so-progress-spinner--size-sm {
|
|
105
|
+
--so-progress-spinner-size: var(--so-progress-spinner-size-sm);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.so-progress-spinner--size-normal {
|
|
109
|
+
--so-progress-spinner-size: var(--so-progress-spinner-size-normal);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.so-progress-spinner--size-lg {
|
|
113
|
+
--so-progress-spinner-size: var(--so-progress-spinner-size-lg);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.so-progress-spinner--size-xl {
|
|
117
|
+
--so-progress-spinner-size: var(--so-progress-spinner-size-xl);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.so-progress-spinner--size-2xl {
|
|
121
|
+
--so-progress-spinner-size: var(--so-progress-spinner-size-2xl);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
:where(.so-progress-spinner) {
|
|
125
|
+
inline-size: var(--so-progress-spinner-size);
|
|
126
|
+
block-size: var(--so-progress-spinner-size);
|
|
127
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/* ─── RadioGroup.Item ─────────────────────────────────────────
|
|
2
|
+
* Структура — только у элемента. У контейнера группы её нет намеренно
|
|
3
|
+
* (решение владельца): радио одной группы стоят где угодно в разметке — в
|
|
4
|
+
* строках списка, в ячейках таблицы, — и раскладку задаёт потребитель.
|
|
5
|
+
*
|
|
6
|
+
* Элемент — `label`: нативное радио, кольцо с точкой и подпись. Радио
|
|
7
|
+
* визуально скрыто, но остаётся в дереве доступности и в порядке фокуса:
|
|
8
|
+
* группировку по `name`, стрелки и один Tab-стоп даёт браузер, а клик по
|
|
9
|
+
* корню-`label` доходит до него сам.
|
|
10
|
+
*
|
|
11
|
+
* Сторона кольца по размеру — сторона коробки CheckBox (`check-box.css`):
|
|
12
|
+
* радио и чекбокс одного размера стоят в форме рядом, и кольцо обязано
|
|
13
|
+
* совпасть с коробкой. Кегль текста радио и зазор до него — шкала подписи
|
|
14
|
+
* (`caption.css`), общая с `Label` и `Field`: радио со своим текстом и радио
|
|
15
|
+
* в подписи выглядят одинаково. Сторона кольца — в переменной
|
|
16
|
+
* `--so-radio-control-size`: от неё считаются точка и выравнивание по первой
|
|
17
|
+
* строке подписи.
|
|
18
|
+
*
|
|
19
|
+
* Облик — рамка и поверхность кольца, точка и её появление, толщина кольца у
|
|
20
|
+
* вида `ring`, цвет текста, кольцо фокуса, варианты — рисует тема. */
|
|
21
|
+
|
|
22
|
+
/* Контрол и подпись в строку. `inline-flex`, как у нативной пары «радио +
|
|
23
|
+
* подпись»: ширина по содержимому, и клик мимо текста радио не выбирает. Ни
|
|
24
|
+
* одного `left`/`right` — только `gap` и логические отступы, поэтому в RTL
|
|
25
|
+
* контрол сам встаёт справа: `dir` приходит на корень. */
|
|
26
|
+
.so-radio-group-item {
|
|
27
|
+
position: relative;
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
align-items: flex-start;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
-webkit-user-select: none;
|
|
32
|
+
user-select: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.so-radio-group-item--size-sm {
|
|
36
|
+
--so-radio-control-size: var(--so-check-box-size-sm);
|
|
37
|
+
gap: var(--so-caption-gap-sm);
|
|
38
|
+
font-size: var(--so-caption-font-size-sm);
|
|
39
|
+
line-height: var(--so-caption-line-height-sm);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.so-radio-group-item--size-normal {
|
|
43
|
+
--so-radio-control-size: var(--so-check-box-size-normal);
|
|
44
|
+
gap: var(--so-caption-gap-normal);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.so-radio-group-item--size-lg {
|
|
48
|
+
--so-radio-control-size: var(--so-check-box-size-lg);
|
|
49
|
+
gap: var(--so-caption-gap-lg);
|
|
50
|
+
font-size: var(--so-caption-font-size-lg);
|
|
51
|
+
line-height: var(--so-caption-line-height-lg);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.so-radio-group-item--size-xl {
|
|
55
|
+
--so-radio-control-size: var(--so-check-box-size-xl);
|
|
56
|
+
gap: var(--so-caption-gap-xl);
|
|
57
|
+
font-size: var(--so-caption-font-size-xl);
|
|
58
|
+
line-height: var(--so-caption-line-height-xl);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.so-radio-group-item--size-2xl {
|
|
62
|
+
--so-radio-control-size: var(--so-check-box-size-2xl);
|
|
63
|
+
gap: var(--so-caption-gap-2xl);
|
|
64
|
+
font-size: var(--so-caption-font-size-2xl);
|
|
65
|
+
line-height: var(--so-caption-line-height-2xl);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Нативное радио скрыто визуально, а не убрано: в дереве доступности и в
|
|
69
|
+
* порядке фокуса оно остаётся. */
|
|
70
|
+
.so-radio-group-item__input {
|
|
71
|
+
position: absolute;
|
|
72
|
+
width: 1px;
|
|
73
|
+
height: 1px;
|
|
74
|
+
margin: -1px;
|
|
75
|
+
padding: 0;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
clip-path: inset(50%);
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
border-width: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.so-radio-group-item__control {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
flex-shrink: 0;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
width: var(--so-radio-control-size);
|
|
88
|
+
height: var(--so-radio-control-size);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Точка — половина кольца. Стоит всегда, а видна ли она, решает вид темы. */
|
|
92
|
+
.so-radio-group-item__indicator {
|
|
93
|
+
width: calc(var(--so-radio-control-size) / 2);
|
|
94
|
+
height: calc(var(--so-radio-control-size) / 2);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Подпись. Её может не быть: радио без текста — законный случай, подпись
|
|
98
|
+
* тогда лежит в соседнем элементе. Пустая обёртка места не занимает, иначе
|
|
99
|
+
* зазор остался бы рядом с одиноким кольцом.
|
|
100
|
+
*
|
|
101
|
+
* `baseline` делает первую строку подписи базовой линией всего элемента: в
|
|
102
|
+
* строке формы рядом с чужим текстом подпись встаёт с ним на одну линию.
|
|
103
|
+
* Контрол выше строки (крупный размер при плотном интерлиньяже) — подпись
|
|
104
|
+
* опускается к его середине. */
|
|
105
|
+
.so-radio-group-item__text {
|
|
106
|
+
align-self: baseline;
|
|
107
|
+
margin-block-start: max(0px, (var(--so-radio-control-size) - 1lh) / 2);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.so-radio-group-item__text:empty {
|
|
111
|
+
display: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Многострочная подпись: контрол стоит у первой строки, а не посередине
|
|
115
|
+
* всего текста — опускается к середине строки высотой `1lh`. Без подписи
|
|
116
|
+
* сдвига нет: одинокое радио ровно в размер коробки CheckBox. */
|
|
117
|
+
.so-radio-group-item:has(> .so-radio-group-item__text:not(:empty)) > .so-radio-group-item__control {
|
|
118
|
+
margin-block-start: max(0px, (1lh - var(--so-radio-control-size)) / 2);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Выключенное — `data-disabled`: ядро пишет его на корне при любом теге.
|
|
122
|
+
* Гаснет элемент целиком — и контрол, и подпись (`disabled.css`). */
|
|
123
|
+
.so-radio-group-item[data-disabled='true'] {
|
|
124
|
+
opacity: var(--so-disabled-opacity);
|
|
125
|
+
cursor: default;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.so-radio-group-item[data-disabled='true'] > * {
|
|
129
|
+
--so-disabled-opacity: 1;
|
|
130
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/* ─── Rating ──────────────────────────────────────────────────
|
|
2
|
+
* Оценка звёздами: ползунок с одной ручкой, у которого шкала — ряд звёзд.
|
|
3
|
+
*
|
|
4
|
+
* .so-rating корень — строчный
|
|
5
|
+
* .so-rating__track дорожка — ряд звёзд
|
|
6
|
+
* .so-rating__star звезда — ячейка ряда, в ней иконка пустой звезды
|
|
7
|
+
* .so-rating__star-fill слой заливки — та же иконка, обрезанная по доле
|
|
8
|
+
* .so-rating__thumb ручка — невидимая, во весь ряд
|
|
9
|
+
* .so-rating__input поле — нативный range, прозрачный, во всю ручку
|
|
10
|
+
*
|
|
11
|
+
* Доля заливки звезды приходит от ядра переменной в `style` звезды:
|
|
12
|
+
* `--so-rating-fraction`, число от 0 до 1. Слой заливки — от начала строки на
|
|
13
|
+
* эту долю звезды: он обрезает иконку, а не сжимает её, и в RTL заливка сама
|
|
14
|
+
* идёт справа налево, без селектора по направлению. Иконка обоих слоёв — во
|
|
15
|
+
* всю звезду: и `svg` роли, и иконка из слота, у которой размер свой (шкала
|
|
16
|
+
* иконок, `components/icon.css`).
|
|
17
|
+
*
|
|
18
|
+
* Контракт с плагином указателя — как у Slider: коробка `__track` и есть
|
|
19
|
+
* шкала, 0 и 1 доли — её края по оси. Поэтому звёзды стоят в ней встык и
|
|
20
|
+
* равными ячейками, ничего сверх них в ряду нет: доля коробки ложится на
|
|
21
|
+
* звёзды без остатка, а половина звезды по указателю — ровно половина значка.
|
|
22
|
+
* Звезда — коробка иконки целиком, с полями, которые оставляет вокруг значка
|
|
23
|
+
* пакет: зазор между звёздами — поля соседних коробок, своего у ряда нет.
|
|
24
|
+
*
|
|
25
|
+
* Корень строчный: оценку ставят в строку текста — «4,5 ★★★★☆ (120)».
|
|
26
|
+
* Нажатие в любом месте ряда ставит оценку, протяжка не выделяет текст
|
|
27
|
+
* вокруг. Ручка — невидимая и во весь ряд: в ней поле, а на ней тема рисует
|
|
28
|
+
* кольцо фокуса — вокруг всех звёзд. Указатель ни она, ни поле не ловят:
|
|
29
|
+
* наведение и нажатие достаются звёздам и корню, где их ловят плагины. Только
|
|
30
|
+
* для показа — курсор обычный: нажатие оценку не меняет.
|
|
31
|
+
*
|
|
32
|
+
* Шкала размеров — сторона звезды, переменная корня документа по размеру
|
|
33
|
+
* (`sm`…`2xl`); блок берёт ступень своего размера в `--so-rating-star-size`.
|
|
34
|
+
* Умолчания — числа темы oren, тема задаёт свои на корне документа.
|
|
35
|
+
*
|
|
36
|
+
* Облик — цвет пустой и залитой звезды, вид ошибки, подъём звезды под
|
|
37
|
+
* указателем, кольцо фокуса и вид в режиме принудительных цветов — рисует
|
|
38
|
+
* тема. */
|
|
39
|
+
|
|
40
|
+
:root {
|
|
41
|
+
--so-rating-star-size-sm: 1.25rem;
|
|
42
|
+
--so-rating-star-size-normal: 1.5rem;
|
|
43
|
+
--so-rating-star-size-lg: 1.75rem;
|
|
44
|
+
--so-rating-star-size-xl: 2rem;
|
|
45
|
+
--so-rating-star-size-2xl: 2.25rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Размер без модификатора — `normal`: ядро пишет `--size-*` всегда, но блок и
|
|
49
|
+
* без него обязан выглядеть умолчанием. */
|
|
50
|
+
.so-rating {
|
|
51
|
+
--so-rating-star-size: var(--so-rating-star-size-normal);
|
|
52
|
+
display: inline-flex;
|
|
53
|
+
vertical-align: middle;
|
|
54
|
+
-webkit-user-select: none;
|
|
55
|
+
user-select: none;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.so-rating__track {
|
|
60
|
+
position: relative;
|
|
61
|
+
display: flex;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.so-rating__star {
|
|
65
|
+
position: relative;
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-shrink: 0;
|
|
68
|
+
inline-size: var(--so-rating-star-size);
|
|
69
|
+
block-size: var(--so-rating-star-size);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.so-rating__star-fill {
|
|
73
|
+
position: absolute;
|
|
74
|
+
inset-block: 0;
|
|
75
|
+
inset-inline-start: 0;
|
|
76
|
+
display: flex;
|
|
77
|
+
inline-size: calc(var(--so-rating-fraction, 0) * 100%);
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Слой заливки уже звезды, и иконка в нём не сжимается. */
|
|
82
|
+
.so-rating__star > :not(.so-rating__star-fill),
|
|
83
|
+
.so-rating__star > .so-rating__star-fill > * {
|
|
84
|
+
flex: none;
|
|
85
|
+
inline-size: var(--so-rating-star-size);
|
|
86
|
+
block-size: var(--so-rating-star-size);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.so-rating__thumb {
|
|
90
|
+
position: absolute;
|
|
91
|
+
inset: 0;
|
|
92
|
+
pointer-events: none;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.so-rating__input {
|
|
96
|
+
position: absolute;
|
|
97
|
+
inset: 0;
|
|
98
|
+
width: 100%;
|
|
99
|
+
height: 100%;
|
|
100
|
+
margin: 0;
|
|
101
|
+
padding: 0;
|
|
102
|
+
opacity: 0;
|
|
103
|
+
appearance: none;
|
|
104
|
+
pointer-events: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.so-rating:where([data-readonly='true']) {
|
|
108
|
+
cursor: default;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Выключенная — `data-disabled`: ядро пишет его на корне при любом теге.
|
|
112
|
+
* Гаснет оценка целиком (`disabled.css`) и нажатий не ловит. */
|
|
113
|
+
.so-rating[data-disabled='true'] {
|
|
114
|
+
opacity: var(--so-disabled-opacity);
|
|
115
|
+
cursor: default;
|
|
116
|
+
pointer-events: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.so-rating[data-disabled='true'] > * {
|
|
120
|
+
--so-disabled-opacity: 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.so-rating--size-sm {
|
|
124
|
+
--so-rating-star-size: var(--so-rating-star-size-sm);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.so-rating--size-normal {
|
|
128
|
+
--so-rating-star-size: var(--so-rating-star-size-normal);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.so-rating--size-lg {
|
|
132
|
+
--so-rating-star-size: var(--so-rating-star-size-lg);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.so-rating--size-xl {
|
|
136
|
+
--so-rating-star-size: var(--so-rating-star-size-xl);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.so-rating--size-2xl {
|
|
140
|
+
--so-rating-star-size: var(--so-rating-star-size-2xl);
|
|
141
|
+
}
|