@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,121 @@
1
+ /* ─── CheckBox ────────────────────────────────────────────────
2
+ * Коробка с отметкой и невидимое поле поверх неё: клик по коробке — клик по
3
+ * полю, и переключает его браузер. Поле — нативный `<input type="checkbox">`:
4
+ * состояние, фокус, участие в форме и доступное имя у него свои, а коробка —
5
+ * декор (`aria-hidden`).
6
+ *
7
+ * Сторона коробки по размеру — переменные корня документа (`sm`…`2xl`): её же
8
+ * берёт кольцо радио (`radio-group.css`) — радио и чекбокс одного размера
9
+ * стоят в форме рядом, и кольцо обязано совпасть с коробкой. Отметка — `svg`
10
+ * иконки по роли, без компонента Icon (его собирала бы каждая смена отметки),
11
+ * поэтому размер ей даёт флажок по своему `size` — ступенями шкалы иконки
12
+ * (`icon.css`). Умолчания — числа темы oren; тема задаёт свои на корне
13
+ * документа.
14
+ *
15
+ * Облик — рамка, поверхность, скругление, отметка, кольцо фокуса, виды и
16
+ * варианты — рисует тема. */
17
+
18
+ :root {
19
+ --so-check-box-size-sm: 1rem;
20
+ --so-check-box-size-normal: 1.25rem;
21
+ --so-check-box-size-lg: 1.5rem;
22
+ --so-check-box-size-xl: 1.75rem;
23
+ --so-check-box-size-2xl: 2rem;
24
+ }
25
+
26
+ /* Блочный при любом теге: корень рисуется по `tag`, по умолчанию `span`, и у
27
+ * строчного корня размер коробки без `display` схлопнулся бы. */
28
+ .so-check-box {
29
+ position: relative;
30
+ display: block;
31
+ width: var(--so-check-box-size-normal);
32
+ height: var(--so-check-box-size-normal);
33
+ -webkit-user-select: none;
34
+ user-select: none;
35
+ }
36
+
37
+ /* Поле — поверх коробки, во весь корень, и невидимо: нажатие ловит оно. */
38
+ .so-check-box input {
39
+ position: absolute;
40
+ inset: 0;
41
+ z-index: 10;
42
+ width: 100%;
43
+ height: 100%;
44
+ margin: 0;
45
+ padding: 0;
46
+ opacity: 0;
47
+ cursor: pointer;
48
+ }
49
+
50
+ .so-check-box input:disabled {
51
+ cursor: default;
52
+ pointer-events: none;
53
+ }
54
+
55
+ /* Выключенная коробка гаснет целиком (`disabled.css`). */
56
+ .so-check-box input:disabled + .so-check-box__container {
57
+ opacity: var(--so-disabled-opacity);
58
+ }
59
+
60
+ .so-check-box input:disabled + .so-check-box__container > * {
61
+ --so-disabled-opacity: 1;
62
+ }
63
+
64
+ /* Только для чтения: клик по коробке её не переключает — его отменяет
65
+ * `TInputBoolPlugin`, — поэтому курсор обычный, как у выключенной. */
66
+ .so-check-box:where([data-readonly='true']) input {
67
+ cursor: default;
68
+ }
69
+
70
+ .so-check-box__container {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ width: 100%;
75
+ height: 100%;
76
+ }
77
+
78
+ .so-check-box__mark {
79
+ width: var(--so-icon-size-normal);
80
+ height: var(--so-icon-size-normal);
81
+ }
82
+
83
+ .so-check-box--size-sm {
84
+ width: var(--so-check-box-size-sm);
85
+ height: var(--so-check-box-size-sm);
86
+ }
87
+
88
+ .so-check-box--size-sm .so-check-box__mark {
89
+ width: var(--so-icon-size-sm);
90
+ height: var(--so-icon-size-sm);
91
+ }
92
+
93
+ .so-check-box--size-lg {
94
+ width: var(--so-check-box-size-lg);
95
+ height: var(--so-check-box-size-lg);
96
+ }
97
+
98
+ .so-check-box--size-lg .so-check-box__mark {
99
+ width: var(--so-icon-size-lg);
100
+ height: var(--so-icon-size-lg);
101
+ }
102
+
103
+ .so-check-box--size-xl {
104
+ width: var(--so-check-box-size-xl);
105
+ height: var(--so-check-box-size-xl);
106
+ }
107
+
108
+ .so-check-box--size-xl .so-check-box__mark {
109
+ width: var(--so-icon-size-xl);
110
+ height: var(--so-icon-size-xl);
111
+ }
112
+
113
+ .so-check-box--size-2xl {
114
+ width: var(--so-check-box-size-2xl);
115
+ height: var(--so-check-box-size-2xl);
116
+ }
117
+
118
+ .so-check-box--size-2xl .so-check-box__mark {
119
+ width: var(--so-icon-size-2xl);
120
+ height: var(--so-icon-size-2xl);
121
+ }
@@ -0,0 +1,224 @@
1
+ /* ─── DateInput ───────────────────────────────────────────────
2
+ * Поле даты из частей по формату локали — коробка поля семьи Input.
3
+ *
4
+ * .so-date-input корень — коробка поля
5
+ * .so-date-input__leading слот перед датой
6
+ * .so-date-input__segments ряд частей — `inline-flex`, свой `dir`
7
+ * .so-date-input__segment часть даты или времени
8
+ * .so-date-input__literal разделитель — литерал формата
9
+ * .so-date-input__trailing слот после даты: кнопка календаря DatePicker
10
+ * .so-date-input__clear кнопка очистки, первой в слоте — Button
11
+ *
12
+ * Коробка — та же, что у Input (`input.css`): высота размера, строка слота —
13
+ * высота без рамки и двух просветов, слоты у краёв логическими сторонами.
14
+ * Шкала — шкала поля: высота, кегль и отступ текста от рамки те же, что у
15
+ * Input того же размера, и в одной форме оба стоят вровень по высоте и по
16
+ * началу текста. Кегль — ряду частей, а не корню: слоты — разметка
17
+ * потребителя. Строка ряда — строка слота, по ней же высота подсветки части.
18
+ * Ширина — по дате и слотам, а не минимум Input: дата — строка известной длины
19
+ * и не схлопывается. Слот у конца уходит к концу поля: ряд частей шириной по
20
+ * дате, и без автоотступа кнопка календаря встала бы вплотную к дате, а не у
21
+ * края растянутого поля. Курсор текста — по всему полю: нажатие на пустое
22
+ * место поля отдаёт фокус части (это решает плагин).
23
+ *
24
+ * Ряд частей — флекс, а не строка текста (условие поведения, а не вкус):
25
+ * часть и разделитель — свои коробки. В строчной раскладке Chrome переносит
26
+ * начало выделения от края части в соседний текст, и протяжка мышью с края
27
+ * дня ничего не выделяет. Не таблица: внутри `label` HTML разрешает только
28
+ * строчную разметку. Направление ряда — свой `dir` от ядра, по дате в локали,
29
+ * а не направление поля, поэтому отступ ряда — поровну с обеих сторон, а не
30
+ * логической стороной: дата встаёт у начала поля с тем же отступом текста,
31
+ * что у Input, — отступ ряда и поле части вместе дают отступ ввода. Выделение
32
+ * — текстом, явно: дату протягивают мышью и выделяют целиком Ctrl/Cmd+A, и ряд
33
+ * выделяется и внутри обёртки с `user-select: none`. Пробелы литералов
34
+ * сохранены как есть (`cs-CZ` «12. 5. 2026»): на краю флекс-элемента они
35
+ * схлопнулись бы, а перенос в ряду не нужен вовсе.
36
+ *
37
+ * Ширина ряда не меняется, пока дату набирают. Цифры одной ширины: ↑/↓ и
38
+ * набор поверх части её ширину не меняют. Год держит место под четыре цифры
39
+ * (`ch` — ширина нуля), в минимум входят поля части: ядро пишет год как есть
40
+ * — «2», «20», «2026», — и без места поле росло бы с каждой цифрой, а у
41
+ * формата с годом в начале (`ja-JP`) за ним сдвигалась бы вся дата. Другим
42
+ * частям места не нужно — ядро дописывает их нулём до двух цифр. Минимум ряда
43
+ * — по самой широкой дате, пустой или набранной (подсказка бывает шире цифр):
44
+ * снят в Chromium по локалям — дата до 14.9ch (`lt-LT`), дата со временем до
45
+ * 23.1ch, до секунды до 26.2ch (`ko-KR`); поле со временем узнаётся по части
46
+ * часа, время до секунды — по части секунды. Предел: цифры шире латинского
47
+ * нуля (`bn-BD`) минимум перерастают, и корень при наборе растёт — ширина
48
+ * таких цифр зависит от шрифтов машины, минимума под язык ряда нет.
49
+ *
50
+ * Поле подсветки части от её цифр — узкое, в шестнадцатую кегля: дата
51
+ * читается строкой, как текст Input, а не вразрядку, и в RTL у флекс-элемента
52
+ * Chrome красит выделением текст, а не поля. Разделитель — с полями, дата не
53
+ * слипается в сплошную строку. Оба поля — в `em`, растут с текстом.
54
+ *
55
+ * Выключенное поле гаснет коробкой целиком, с кнопками и содержимым слотов
56
+ * (`disabled.css`); выключенность — `data-disabled` корня: нативного
57
+ * `disabled` у корня нет, а частей много. Нажатия коробка не гасит: в слотах
58
+ * — разметка потребителя.
59
+ *
60
+ * Рамку рисует тема и задаёт её толщину на блоке
61
+ * (`--so-date-input-border`, контракт): строка слота считается без неё. Поля
62
+ * части и разделителя — переменные корня документа; умолчания — числа темы
63
+ * oren, тема задаёт свои на корне документа.
64
+ *
65
+ * Облик — рамка, поверхность, скругление, кольцо фокуса, подсветка части под
66
+ * фокусом и выделения, цвет подсказки, вид кнопки очистки, каретка, цвет
67
+ * точки обязательного поля, варианты и ошибка — рисует тема. */
68
+
69
+ :root {
70
+ --so-date-input-segment-padding: 0.0625em;
71
+ --so-date-input-literal-padding: 0.125em;
72
+ }
73
+
74
+ /* Размер без модификатора — `normal`: ядро пишет `--size-*` всегда, но блок и
75
+ * без него обязан выглядеть умолчанием. `relative` — опора точки
76
+ * обязательного поля (`required.css`). */
77
+ .so-date-input {
78
+ --so-date-input-height: var(--so-input-height-normal);
79
+ --so-date-input-indent: var(--so-input-indent-normal);
80
+ --so-date-input-slot-row: calc(
81
+ var(--so-date-input-height) - 2 * var(--so-date-input-border, 0px) - 2 *
82
+ var(--so-input-slot-gap)
83
+ );
84
+ position: relative;
85
+ display: inline-flex;
86
+ align-items: center;
87
+ height: var(--so-date-input-height);
88
+ cursor: text;
89
+ }
90
+
91
+ /* Фокуса у частей выключенного поля нет — ядро не ставит им остановку Tab. */
92
+ .so-date-input[data-disabled='true'] {
93
+ opacity: var(--so-disabled-opacity);
94
+ cursor: default;
95
+ }
96
+
97
+ .so-date-input[data-disabled='true'] > * {
98
+ --so-disabled-opacity: 1;
99
+ }
100
+
101
+ .so-date-input .so-date-input__segments {
102
+ line-height: var(--so-date-input-slot-row);
103
+ }
104
+
105
+ .so-date-input--size-sm {
106
+ --so-date-input-height: var(--so-input-height-sm);
107
+ --so-date-input-indent: var(--so-input-indent-sm);
108
+ }
109
+
110
+ .so-date-input--size-sm .so-date-input__segments {
111
+ font-size: var(--so-input-font-size-sm);
112
+ line-height: var(--so-date-input-slot-row);
113
+ }
114
+
115
+ .so-date-input--size-normal {
116
+ --so-date-input-height: var(--so-input-height-normal);
117
+ --so-date-input-indent: var(--so-input-indent-normal);
118
+ }
119
+
120
+ .so-date-input--size-normal .so-date-input__segments {
121
+ line-height: var(--so-date-input-slot-row);
122
+ }
123
+
124
+ .so-date-input--size-lg {
125
+ --so-date-input-height: var(--so-input-height-lg);
126
+ --so-date-input-indent: var(--so-input-indent-lg);
127
+ }
128
+
129
+ .so-date-input--size-lg .so-date-input__segments {
130
+ font-size: var(--so-input-font-size-lg);
131
+ line-height: var(--so-date-input-slot-row);
132
+ }
133
+
134
+ .so-date-input--size-xl {
135
+ --so-date-input-height: var(--so-input-height-xl);
136
+ --so-date-input-indent: var(--so-input-indent-xl);
137
+ }
138
+
139
+ .so-date-input--size-xl .so-date-input__segments {
140
+ font-size: var(--so-input-font-size-xl);
141
+ line-height: var(--so-date-input-slot-row);
142
+ }
143
+
144
+ .so-date-input--size-2xl {
145
+ --so-date-input-height: var(--so-input-height-2xl);
146
+ --so-date-input-indent: var(--so-input-indent-2xl);
147
+ }
148
+
149
+ .so-date-input--size-2xl .so-date-input__segments {
150
+ font-size: var(--so-input-font-size-2xl);
151
+ line-height: var(--so-date-input-slot-row);
152
+ }
153
+
154
+ /* Высота слота — строка поля, как у Input. */
155
+ .so-date-input__leading,
156
+ .so-date-input__trailing {
157
+ display: flex;
158
+ flex-shrink: 0;
159
+ align-items: center;
160
+ justify-content: center;
161
+ gap: var(--so-input-slot-spacing);
162
+ height: var(--so-date-input-slot-row);
163
+ cursor: default;
164
+ }
165
+
166
+ .so-date-input__leading {
167
+ padding-inline-start: var(--so-input-slot-padding);
168
+ }
169
+
170
+ .so-date-input__trailing {
171
+ margin-inline-start: auto;
172
+ padding-inline-end: var(--so-input-slot-padding);
173
+ }
174
+
175
+ /* Кнопка очистки — квадрат в строку слота, как у Input; кнопка календаря
176
+ * DatePicker рядом — квадрат той же стороны. Цель на мелком размере ей
177
+ * добирает зона нажатия (`target.css`). */
178
+ .so-date-input__clear {
179
+ flex-shrink: 0;
180
+ padding-inline: 0;
181
+ inline-size: var(--so-date-input-slot-row);
182
+ block-size: var(--so-date-input-slot-row);
183
+ }
184
+
185
+ .so-date-input__segments {
186
+ display: inline-flex;
187
+ flex-shrink: 0;
188
+ align-items: center;
189
+ white-space: pre;
190
+ -webkit-user-select: text;
191
+ user-select: text;
192
+ padding-inline: calc(var(--so-date-input-indent) - var(--so-date-input-segment-padding));
193
+ font-variant-numeric: tabular-nums;
194
+ min-inline-size: calc(
195
+ 15ch + 2 * (var(--so-date-input-indent) - var(--so-date-input-segment-padding))
196
+ );
197
+ }
198
+
199
+ /* Правило секунды идёт за правилом часа и перекрывает его. */
200
+ .so-date-input__segments:has(> [data-type='hour']) {
201
+ min-inline-size: calc(
202
+ 23.5ch + 2 * (var(--so-date-input-indent) - var(--so-date-input-segment-padding))
203
+ );
204
+ }
205
+
206
+ .so-date-input__segments:has(> [data-type='second']) {
207
+ min-inline-size: calc(
208
+ 26.5ch + 2 * (var(--so-date-input-indent) - var(--so-date-input-segment-padding))
209
+ );
210
+ }
211
+
212
+ .so-date-input__literal {
213
+ padding-inline: var(--so-date-input-literal-padding);
214
+ }
215
+
216
+ .so-date-input__segment {
217
+ padding-inline: var(--so-date-input-segment-padding);
218
+ }
219
+
220
+ /* Цифры стоят у начала части, свободное место — за ними. Коробка — от рамки,
221
+ * поэтому в минимум входят и поля части. */
222
+ .so-date-input__segment[data-type='year'] {
223
+ min-inline-size: calc(4ch + 2 * var(--so-date-input-segment-padding));
224
+ }
@@ -0,0 +1,208 @@
1
+ /* ─── DatePicker ──────────────────────────────────────────────
2
+ * Поле даты и календарь в панели, как Select — поле и список.
3
+ *
4
+ * .so-date-picker корень — якорь панели; у диапазона — коробка поля
5
+ * .so-date-picker__field поле одной даты — DateInput
6
+ * .so-date-input__trailing слоты поля `clear` и `trailing` (`date-input.css`)
7
+ * .so-date-picker__clear кнопка очистки — Button, если `clearable`
8
+ * .so-date-picker__trigger кнопка календаря — Button
9
+ * .so-date-picker__start поле начала диапазона — DateInput
10
+ * .so-date-picker__dash тире между полями
11
+ * .so-date-picker__end поле конца диапазона — DateInput
12
+ * .so-date-picker__clear кнопка очистки периода, если `clearable`
13
+ * .so-date-picker__trigger кнопка календаря после поля конца
14
+ *
15
+ * .so-date-picker__panel панель — Frame, телепортирована в `body`
16
+ * .so-date-picker__handle полоса жеста, если он включён (`swipe`)
17
+ * .so-date-picker__content содержимое — прокручивается оно, а не панель
18
+ * .so-date-picker__calendar календарь — Calendar
19
+ * .so-calendar__footer подвал календаря, при `confirmable`
20
+ *
21
+ * Корень — строчный флекс по полю: DatePicker встаёт в строку, как само поле
22
+ * даты, и ширина у него — по дате и кнопкам. Задали корню ширину — поле
23
+ * растягивается до неё, а кнопки остаются у его конца.
24
+ *
25
+ * Кнопки очистки и календаря — квадрат со стороной в строку слота: в коробке
26
+ * поля они стоят на том же просвете от рамки, что подсветка части, и на любом
27
+ * размере не выше её. Сторону задаёт режим: у одной даты кнопки стоят в слоте
28
+ * поля, у диапазона — в коробке корня. Цель на мелком размере им добирает
29
+ * зона нажатия (`target.css`): кнопке календаря она нужна не меньше очистки —
30
+ * без `clearable` поле даты стоит вплотную к ней.
31
+ *
32
+ * Диапазон — одна коробка на оба поля: период — одно значение, два конца, одна
33
+ * очистка, одна кнопка календаря, поэтому и рамка у него одна, а поля внутри —
34
+ * без своей коробки. Коробка та же, что у DateInput: высота размера по шкале
35
+ * поля, строка слота, тире кеглем поля. Поле внутри — во всю строку коробки:
36
+ * нажатие у её края попадает в поле, и плагин указателя поля отдаёт фокус
37
+ * ближайшей части; точки обязательного поля у него нет — она у коробки.
38
+ * Раскладка — сетка из пяти колонок: начало, тире, конец и две кнопки, каждая
39
+ * по своей ширине; без очистки кнопка календаря встаёт в четвёртую колонку, а
40
+ * пятая пуста. Колонки полей равны, и держит их поле начала своим минимумом
41
+ * ряда — шириной самой широкой даты: ширина не меняется, пока даты набирают.
42
+ * Поле конца шириной по дате стоит у конца своей колонки, и тире встаёт
43
+ * посередине между датами. Колонки логические: в RTL начало справа. Кнопки —
44
+ * с теми же отступами друг от друга и от рамки, что в слоте поля. Выключенный
45
+ * период гаснет коробкой целиком — с датами, тире и кнопками
46
+ * (`disabled.css`).
47
+ *
48
+ * Панель — та же семья, что панели Select и Popover, — колонка: полоса жеста —
49
+ * её поле, содержимое берёт остаток высоты и прокручивается само. Пока жест
50
+ * включён, плагин жеста отдаёт касание вдоль оси панели жесту
51
+ * (`touch-action`), и прокручиваемую саму панель палец бы не прокрутил. Высота
52
+ * — по календарю, потолок — окно целиком, а не половина, как у Popover:
53
+ * месяц держит высоту под шесть недель и срезался бы уже на экране ноутбука.
54
+ * Когда ни над полем, ни под ним места нет, панель встаёт туда, где места
55
+ * больше, и заходит за край: сдвиг `TAnchorPlugin` держит её в окне только по
56
+ * строке. Ширину по содержимому просит сам календарь: раскладка Frame пишет
57
+ * панели ширину `auto`, а у панели с `left` это место до края окна — у поля
58
+ * справа месяцы сложились бы в столбец. Поле вокруг календаря — внутри
59
+ * прокрутки: у края прокручиваемой области кольцо фокуса дня обрезалось бы.
60
+ * Закрытая панель, полоса жеста у края со стороны поля — `anchored.css` и
61
+ * `swipe.css`.
62
+ *
63
+ * Рамку коробки диапазона рисует тема и задаёт её толщину на блоке
64
+ * (`--so-date-picker-border`, контракт): строка слота считается без неё. Поле
65
+ * панели — переменная корня документа; умолчание — число темы oren, тема
66
+ * задаёт своё на корне документа.
67
+ *
68
+ * Облик — рамка коробки и её снятие у полей внутри, поверхность, кольцо
69
+ * фокуса, цвет тире пустого периода, вид кнопок, варианты и ошибка,
70
+ * поверхность, кромка, тень и скругление панели, её проявление и жест — рисует
71
+ * тема. */
72
+
73
+ :root {
74
+ --so-date-picker-padding: 0.5rem;
75
+ }
76
+
77
+ .so-date-picker {
78
+ display: inline-flex;
79
+ }
80
+
81
+ .so-date-picker__field {
82
+ --so-date-picker-button: var(--so-date-input-slot-row);
83
+ flex-grow: 1;
84
+ }
85
+
86
+ .so-date-picker__clear,
87
+ .so-date-picker__trigger {
88
+ flex-shrink: 0;
89
+ padding-inline: 0;
90
+ inline-size: var(--so-date-picker-button);
91
+ block-size: var(--so-date-picker-button);
92
+ }
93
+
94
+ /* Размер без модификатора — `normal`. `relative` — опора точки обязательного
95
+ * поля (`required.css`). */
96
+ .so-date-picker--mode-range {
97
+ --so-date-picker-height: var(--so-input-height-normal);
98
+ --so-date-picker-slot-row: calc(
99
+ var(--so-date-picker-height) - 2 * var(--so-date-picker-border, 0px) - 2 *
100
+ var(--so-input-slot-gap)
101
+ );
102
+ --so-date-picker-button: var(--so-date-picker-slot-row);
103
+ position: relative;
104
+ display: inline-grid;
105
+ grid-template-columns: 1fr auto 1fr auto auto;
106
+ align-items: center;
107
+ height: var(--so-date-picker-height);
108
+ }
109
+
110
+ .so-date-picker--mode-range[data-disabled='true'] {
111
+ opacity: var(--so-disabled-opacity);
112
+ }
113
+
114
+ .so-date-picker--mode-range[data-disabled='true'] > * {
115
+ --so-disabled-opacity: 1;
116
+ }
117
+
118
+ .so-date-picker--mode-range > .so-date-picker__dash {
119
+ line-height: var(--so-date-picker-slot-row);
120
+ }
121
+
122
+ .so-date-picker--mode-range.so-date-picker--size-sm {
123
+ --so-date-picker-height: var(--so-input-height-sm);
124
+ }
125
+
126
+ .so-date-picker--mode-range.so-date-picker--size-sm > .so-date-picker__dash {
127
+ font-size: var(--so-input-font-size-sm);
128
+ line-height: var(--so-date-picker-slot-row);
129
+ }
130
+
131
+ .so-date-picker--mode-range.so-date-picker--size-normal {
132
+ --so-date-picker-height: var(--so-input-height-normal);
133
+ }
134
+
135
+ .so-date-picker--mode-range.so-date-picker--size-normal > .so-date-picker__dash {
136
+ line-height: var(--so-date-picker-slot-row);
137
+ }
138
+
139
+ .so-date-picker--mode-range.so-date-picker--size-lg {
140
+ --so-date-picker-height: var(--so-input-height-lg);
141
+ }
142
+
143
+ .so-date-picker--mode-range.so-date-picker--size-lg > .so-date-picker__dash {
144
+ font-size: var(--so-input-font-size-lg);
145
+ line-height: var(--so-date-picker-slot-row);
146
+ }
147
+
148
+ .so-date-picker--mode-range.so-date-picker--size-xl {
149
+ --so-date-picker-height: var(--so-input-height-xl);
150
+ }
151
+
152
+ .so-date-picker--mode-range.so-date-picker--size-xl > .so-date-picker__dash {
153
+ font-size: var(--so-input-font-size-xl);
154
+ line-height: var(--so-date-picker-slot-row);
155
+ }
156
+
157
+ .so-date-picker--mode-range.so-date-picker--size-2xl {
158
+ --so-date-picker-height: var(--so-input-height-2xl);
159
+ }
160
+
161
+ .so-date-picker--mode-range.so-date-picker--size-2xl > .so-date-picker__dash {
162
+ font-size: var(--so-input-font-size-2xl);
163
+ line-height: var(--so-date-picker-slot-row);
164
+ }
165
+
166
+ .so-date-picker--mode-range > .so-date-input {
167
+ align-self: stretch;
168
+ height: auto;
169
+ }
170
+
171
+ .so-date-picker--mode-range > .so-date-input.so-date-input:where([data-required='true'])::after {
172
+ content: none;
173
+ }
174
+
175
+ /* Минимум ряда держит поле начала: формат у полей один, и минимумы равны. */
176
+ .so-date-picker--mode-range > .so-date-picker__end {
177
+ justify-self: flex-end;
178
+ }
179
+
180
+ .so-date-picker--mode-range > .so-date-picker__end .so-date-input__segments {
181
+ min-inline-size: 0;
182
+ }
183
+
184
+ .so-date-picker--mode-range > .so-date-picker__clear {
185
+ margin-inline-end: var(--so-input-slot-spacing);
186
+ }
187
+
188
+ .so-date-picker--mode-range > .so-date-picker__trigger {
189
+ margin-inline-end: var(--so-input-slot-padding);
190
+ }
191
+
192
+ .so-date-picker__panel {
193
+ display: flex;
194
+ flex-direction: column;
195
+ overflow: hidden;
196
+ max-width: calc(100vw - 1rem);
197
+ max-height: calc(100dvh - 1rem);
198
+ }
199
+
200
+ .so-date-picker__content {
201
+ min-height: 0;
202
+ padding: var(--so-date-picker-padding);
203
+ overflow-y: auto;
204
+ }
205
+
206
+ .so-date-picker__content > .so-date-picker__calendar {
207
+ width: max-content;
208
+ }
@@ -0,0 +1,95 @@
1
+ /* ─── Dialog ──────────────────────────────────────────────────
2
+ * Модальное окно. Корень — сама панель: она телепортирована в `body` целиком
3
+ * и несёт слой окна, подложка — её сосед. Подложка, сетка панели, заголовок,
4
+ * тело, подвал и кнопки шапки — общие с выезжающей панелью (`modal.css`).
5
+ * Своё у окна — место, размер, отступ от краёв экрана и разворот: раскладка
6
+ * без замеров. Код отдаёт значения модификатором (`--placement-*`),
7
+ * переменными и `data-maximized`, остальное делает CSS.
8
+ *
9
+ * Место — полями, как у нативного `<dialog>`. Панель растянута по области —
10
+ * экрану без отступов, — а `auto`-поля по обе стороны оси ставят её по центру
11
+ * области: и размер, и место считает браузер. Отступы — `inset`, а не поля:
12
+ * процент поля считается от ширины экрана и по вертикали, а у `inset` — от
13
+ * экрана по своей оси. Неравные отступы сдвигают центр: окно стоит по центру
14
+ * области, а не экрана. Окно у края прижато к краю области и стоит по центру
15
+ * по другой оси; `start` и `end` — логические стороны, в RTL окно само
16
+ * встаёт у другого края.
17
+ *
18
+ * Отступ по сторонам код отдаёт переменными — из свойства `offset` и поправок
19
+ * подписчиков `layout:offset:before` (`--so-dialog-offset-top`, `-bottom`,
20
+ * `-start`, `-end`), — и только заданные: у незаданной стороны отступ по
21
+ * умолчанию (`--so-dialog-offset`). Он один на место и на потолок размера,
22
+ * поэтому окно у края, выросшее до потолка, встаёт от края до края с тем же
23
+ * отступом.
24
+ *
25
+ * Размер — `--so-dialog-width` и `--so-dialog-height`: умолчания — у корня
26
+ * документа (постоянная ширина: форма с полями на всю строку иначе сжалась бы
27
+ * до их ширины; высота по содержимому), а свойства `width` и `height` код
28
+ * пишет на окно инлайном. Значения — размер панели, растянутой по области,
29
+ * поэтому `auto` здесь значит «по экрану с отступом», а по содержимому —
30
+ * `fit-content`. Потолок — область, какой бы размер ни задали: на узком экране
31
+ * окно сжимается само, но не разворачивается. Проценты — от области просмотра
32
+ * без полосы прокрутки, а не `vw`: с полосой окно заходило бы под неё.
33
+ *
34
+ * Развёрнутое окно — на весь экран: место, размер и отступ не действуют.
35
+ *
36
+ * Облик — поверхность, кромка, тень и скругление, их снятие у развёрнутого
37
+ * окна, появление и исчезание — рисует тема. */
38
+
39
+ :root {
40
+ --so-dialog-width: 32rem;
41
+ --so-dialog-height: fit-content;
42
+ --so-dialog-offset: 1rem;
43
+ }
44
+
45
+ /* Без кнопки её колонка пуста и ширины не занимает: колонки между собой без
46
+ * зазора, промежутки держат сами кнопки. */
47
+ .so-dialog {
48
+ position: fixed;
49
+ inset-block: var(--so-dialog-offset-top, var(--so-dialog-offset))
50
+ var(--so-dialog-offset-bottom, var(--so-dialog-offset));
51
+ inset-inline: var(--so-dialog-offset-start, var(--so-dialog-offset))
52
+ var(--so-dialog-offset-end, var(--so-dialog-offset));
53
+ margin: auto;
54
+ grid-template-columns: minmax(0, 1fr) auto auto;
55
+ grid-template-rows: auto minmax(0, 1fr) auto;
56
+ grid-template-areas:
57
+ 'title maximize close'
58
+ 'body body body'
59
+ 'footer footer footer';
60
+ width: var(--so-dialog-width);
61
+ height: var(--so-dialog-height);
62
+ max-width: calc(
63
+ 100% - var(--so-dialog-offset-start, var(--so-dialog-offset)) -
64
+ var(--so-dialog-offset-end, var(--so-dialog-offset))
65
+ );
66
+ max-height: calc(
67
+ 100% - var(--so-dialog-offset-top, var(--so-dialog-offset)) -
68
+ var(--so-dialog-offset-bottom, var(--so-dialog-offset))
69
+ );
70
+ }
71
+
72
+ .so-dialog--placement-top {
73
+ margin-block-start: 0;
74
+ }
75
+
76
+ .so-dialog--placement-bottom {
77
+ margin-block-end: 0;
78
+ }
79
+
80
+ .so-dialog--placement-start {
81
+ margin-inline-start: 0;
82
+ }
83
+
84
+ .so-dialog--placement-end {
85
+ margin-inline-end: 0;
86
+ }
87
+
88
+ .so-dialog[data-maximized='true'] {
89
+ inset: 0;
90
+ margin: 0;
91
+ width: auto;
92
+ height: auto;
93
+ max-width: none;
94
+ max-height: none;
95
+ }
@@ -0,0 +1,18 @@
1
+ /* ─── DragAndDrop ─────────────────────────────────────────────
2
+ * Перестановка элементов Tabs, ListBox и Accordion перетаскиванием. Своего
3
+ * узла у обёртки нет (у Web Components — автономный элемент без коробки), и
4
+ * своё у неё в CSS одно: класс взятого элемента. Его ставит плагин жеста
5
+ * (`TDragPlugin`) на узел элемента, пока жест идёт, и снимает, когда кончился.
6
+ *
7
+ * .so-drag-and-drop__item взятый элемент коллекции
8
+ *
9
+ * Взятый элемент текст не выделяет: протяжка — жест перестановки, а не
10
+ * выделение подписи под указателем.
11
+ *
12
+ * Облик взятого — приглушённость, тень, скругление и узор поверх него — рисует
13
+ * тема. */
14
+
15
+ .so-drag-and-drop__item {
16
+ -webkit-user-select: none;
17
+ user-select: none;
18
+ }