griffincss-ui 0.28.2 → 0.29.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/scss/_prose.scss CHANGED
@@ -15,11 +15,20 @@
15
15
  //
16
16
  // Меры строки в классе нет: ширину решает место — колонка, карточка, окно.
17
17
  // Мера текста — утилита .gr-max-w-prose. Вид таблицы — в _table.scss:
18
- // <table> в прозе оформлен теми же правилами, что .gr-table.
18
+ // <table> в прозе оформлен теми же правилами, что .gr-table, и широкая
19
+ // прокручивается сама.
19
20
 
20
21
  // Длинная ссылка или слово без пробелов не выталкивают колонку вбок:
21
22
  // текст в редакторе пишет кто угодно.
23
+ //
24
+ // min-inline-size: 0 — для прозы, которая сама флекс-элемент или ячейка
25
+ // сетки: такие не сжимаются меньше самого длинного слова и самой длинной
26
+ // строки кода (min-width: auto), и статья рядом с боковой колонкой
27
+ // становилась шире окна. Вне флекса и сетки ноль и так. Колонке со своим
28
+ // CSS, внутри которой лежит проза, нужно то же (.gr-min-w-0); у сеток
29
+ // библиотеки — minmax(0, 1fr).
22
30
  .gr-prose {
31
+ min-inline-size: 0;
23
32
  overflow-wrap: break-word;
24
33
  }
25
34
 
@@ -89,6 +98,28 @@
89
98
  block-size: auto;
90
99
  }
91
100
 
101
+ // Фрейм из кода вставки (плеер, карта, форма) тоже не шире колонки. Своей
102
+ // пропорции у фрейма нет: атрибуты width и height браузер, в отличие
103
+ // от картинки, в неё не превращает, поэтому высота остаётся из атрибута,
104
+ // а плеер рисует полосы по краям видео.
105
+ :where(.gr-prose iframe) {
106
+ max-inline-size: 100%;
107
+ }
108
+
109
+ // Где движок читает атрибут как число, пропорция берётся из width и height
110
+ // в пикселях — у любого плеера и любой карты, без списка хостингов. Низкий
111
+ // виджет (аудиоплеер 560×80) не становится ниже своей высоты: при сужении
112
+ // высота не падает меньше min(атрибут, 150px). Без поддержки attr() одно
113
+ // block-size: auto сплющило бы фрейм до 150 px — поэтому всё под @supports.
114
+ // Ширина в процентах (width="100%") пропорции не задаёт — высота остаётся.
115
+ @supports (aspect-ratio: #{'attr(width type(<number>)) / attr(height type(<number>))'}) {
116
+ :where(.gr-prose iframe[width][height]:not([width$='%'], [height$='%'])) {
117
+ aspect-ratio: #{'attr(width type(<number>)) / attr(height type(<number>))'};
118
+ block-size: auto;
119
+ min-block-size: #{'min(attr(height px), 150px)'};
120
+ }
121
+ }
122
+
92
123
  // Длинная строка кода прокручивается внутри блока, а не всей страницей.
93
124
  :where(.gr-prose pre) {
94
125
  overflow-x: auto;
package/scss/_table.scss CHANGED
@@ -10,7 +10,8 @@
10
10
  // Таблица внутри .gr-prose оформлена теми же правилами: класс на <table>
11
11
  // редактор не поставит. Селекторы прозы — под :where(), с нулевым весом:
12
12
  // варианты (.gr-table-compact и другие) на такой таблице по-прежнему
13
- // сильнее базы.
13
+ // сильнее базы. Отличий от .gr-table два: заголовки переносятся, а широкая
14
+ // таблица прокручивается сама (см. .gr-table-scroll).
14
15
 
15
16
  @use 'sass:string';
16
17
  @use 'griffincss-core/scss/breakpoints' as bp;
@@ -39,6 +40,13 @@
39
40
  color: var(--gr-color-text-secondary);
40
41
  font-size: 0.875rem;
41
42
  font-weight: 600;
43
+ }
44
+
45
+ // Заголовок в одну строку — только у таблицы библиотеки: перенос ей снимают
46
+ // классом (.gr-whitespace-normal). В прозе снять некому — класс на <th>
47
+ // редактор не поставит, — и таблица из четырёх столбцов с заголовками
48
+ // становилась шире колонки.
49
+ .gr-table th {
42
50
  white-space: nowrap;
43
51
  }
44
52
 
@@ -226,6 +234,114 @@
226
234
  overscroll-behavior-x: contain;
227
235
  }
228
236
 
237
+ // Прокрутка на самой таблице, без обёртки: таблица становится блоком,
238
+ // строки собирает анонимная таблица внутри него. Блок по ширине содержимого,
239
+ // но не шире колонки; шире — прокручивается сам. Цена — узкая таблица
240
+ // не растягивается во всю колонку: анонимная таблица сжимается по
241
+ // содержимому, поэтому класс — для широких таблиц. position: relative —
242
+ // по той же причине, что у обёртки.
243
+ //
244
+ // Таблица прозы ведёт себя так же без класса: автор текста о классах
245
+ // не знает. Таблица библиотеки в прозе (.gr-table) — своя раскладка,
246
+ // во всю ширину и без прокрутки; .gr-table-scroll возвращает прокрутку.
247
+ .gr-table-scroll,
248
+ :where(.gr-prose table:not(.gr-table)) {
249
+ display: block;
250
+ position: relative;
251
+ inline-size: max-content;
252
+ max-inline-size: 100%;
253
+ overflow-x: auto;
254
+ overscroll-behavior-x: contain;
255
+ }
256
+
257
+ // Word и Google Docs кладут ширину таблицы в style — 100 %, пиксели,
258
+ // пункты. Блок растягивался по ней, а строки — анонимная таблица внутри —
259
+ // вставали по содержимому: рамка и фон уходили далеко за ячейки. Встроенный
260
+ // стиль сильнее любого правила без !important, поэтому здесь — единственное
261
+ // !important библиотеки, и только у таблицы прозы со встроенной шириной:
262
+ // у остальных таблиц ширину по-прежнему перебивает любой CSS сайта.
263
+ // Тема из подслоя ниже ui снимает его своим !important.
264
+ :where(.gr-prose table:not(.gr-table)[style*="width" i]) {
265
+ // stylelint-disable-next-line declaration-no-important
266
+ inline-size: max-content !important;
267
+ }
268
+
269
+ // Подсказка прокрутки. Край колонки обрезает таблицу ровно по столбцу,
270
+ // а полосы прокрутки macOS и телефонов видны только при касании — скрытые
271
+ // столбцы легко не заметить. У края, за которым ещё есть содержимое,
272
+ // лежит мягкая тень: радиальная, растянутая по высоте области и гаснущая
273
+ // к верху и низу. Докрутили до конца — тень у этого края гаснет.
274
+ //
275
+ // Силу теней (--gr-scroll-start / -end, _keyframes.scss) ведёт анимация
276
+ // по прокрутке самой области: шторка цвета подложки не нужна, и подсказка
277
+ // верна на любой подложке — карточке, цветной секции, в тёмной теме.
278
+ // Без переполнения шкала прокрутки неактивна, и теней нет. Цвет и сила —
279
+ // как у теней библиотеки: тёмная тема сильнее, на печати теней нет.
280
+ // Спад не линейный: промежуточная точка собирает тень у самого края,
281
+ // и хвост растворяется — линейный рисовал «линзу» с заметной границей.
282
+ @function gr-scroll-shade($side, $amount) {
283
+ @return radial-gradient(
284
+ farthest-side at $side 50%,
285
+ hsl(var(--gr-shadow-color), calc(#{$amount} * 16% * var(--gr-shadow-strength))),
286
+ hsl(var(--gr-shadow-color), calc(#{$amount} * 6% * var(--gr-shadow-strength))) 45%,
287
+ transparent
288
+ );
289
+ }
290
+
291
+ $gr-scroll-areas: '.gr-table-wrap, .gr-table-scroll, :where(.gr-prose table:not(.gr-table))';
292
+
293
+ // Первые два слоя — шторки для Firefox (см. ниже); здесь они прозрачные.
294
+ //
295
+ // --gr-scroll-hint-inset — отступ подсказки от начала строки: у таблицы
296
+ // с липким первым столбцом тень «в начале ещё есть столбцы» лежит у его
297
+ // края, а не у края области, где её закрывает или (в строке без липкой
298
+ // ячейки) пятнает сам столбец. Сдвигаются начальная тень и шторка.
299
+ #{$gr-scroll-areas} {
300
+ background-image:
301
+ linear-gradient(to right, var(--gr-scroll-cover, transparent) 30%, transparent),
302
+ linear-gradient(to left, var(--gr-scroll-cover, transparent) 30%, transparent),
303
+ gr-scroll-shade(0, var(--gr-scroll-start)),
304
+ gr-scroll-shade(100%, var(--gr-scroll-end));
305
+ background-repeat: no-repeat;
306
+ background-position: var(--gr-scroll-hint-inset, 0) 50%, 100% 50%;
307
+ background-size: 2.5rem 100%, 2.5rem 100%, 1rem 100%, 1rem 100%;
308
+ background-attachment: local, local, scroll, scroll;
309
+ }
310
+
311
+ @supports (animation-timeline: scroll()) {
312
+ #{$gr-scroll-areas} {
313
+ animation: gr-scroll-start linear both, gr-scroll-end linear both;
314
+ animation-timeline: scroll(self inline);
315
+ animation-range: 0 2rem, calc(100% - 2rem) 100%;
316
+ }
317
+ }
318
+
319
+ // В RTL строка начинается справа: отступ — у правого края, а та же пара
320
+ // анимаций идёт в обратную сторону — тень у левого края гаснет к концу
321
+ // шкалы, у правого появляется с её начала. Вне @supports: отступ нужен
322
+ // и шторке Firefox, а animation-* там без анимации ничего не делают.
323
+ .gr-table-wrap:dir(rtl),
324
+ .gr-table-scroll:dir(rtl),
325
+ :where(.gr-prose table:not(.gr-table):dir(rtl)) {
326
+ background-position: 0 50%, right var(--gr-scroll-hint-inset, 0) center;
327
+ animation-direction: reverse;
328
+ animation-range: calc(100% - 2rem) 100%, 0 2rem;
329
+ }
330
+
331
+ // Firefox шкалы прокрутки не знает: там тени у краёв стоят на полной силе,
332
+ // а прячет их шторка — первые два слоя фона, которые едут вместе
333
+ // с содержимым (background-attachment: local) и закрывают тень у края,
334
+ // до которого докрутили. Шторка — цвета подложки: --gr-scroll-hint-bg,
335
+ // по умолчанию цвет страницы. На другой подложке его задают, иначе у края
336
+ // в покое ляжет полоса цвета страницы.
337
+ @supports not (animation-timeline: scroll()) {
338
+ #{$gr-scroll-areas} {
339
+ --gr-scroll-start: 1;
340
+ --gr-scroll-end: 1;
341
+ --gr-scroll-cover: var(--gr-scroll-hint-bg, var(--gr-color-bg));
342
+ }
343
+ }
344
+
229
345
  // Шапка остаётся видна при прокрутке тела. Работает внутри .gr-table-wrap
230
346
  // с заданной высотой; собственный фон обязателен — без него сквозь ячейки
231
347
  // просвечивали бы уезжающие строки.
@@ -95,6 +95,10 @@ $gr-megamenu-expand: map.get(bp.$gr-breakpoints, md);
95
95
  border-block-end: var(--gr-border-width) solid var(--gr-color-border);
96
96
  background-color: var(--gr-color-surface-overlay);
97
97
  color: var(--gr-color-text);
98
+
99
+ // Подложка — и шторке подсказки прокрутки (_table.scss): без шкалы прокрутки
100
+ // (Firefox) тень у края таблицы прячет шторка этого цвета.
101
+ --gr-scroll-hint-bg: var(--gr-color-surface-overlay);
98
102
  }
99
103
 
100
104
  // Ajax-панель, пока грузится: место под содержимое, чтобы полоса
@@ -1,10 +1,10 @@
1
1
  /*!
2
- * Griffincss UI — Runtime v0.28.2
2
+ * Griffincss UI — Runtime v0.29.1
3
3
  * Опциональный рантайм пакета компонентов. Делает ровно то, чего платформа
4
4
  * не даёт вовсе; всё, что умеют <details>, <dialog> и Popover API, остаётся
5
5
  * за ними. Без этого файла компоненты работают — просто без перечисленного.
6
6
  *
7
- * Здесь четыре вещи:
7
+ * Здесь пять вещей:
8
8
  *
9
9
  * 1. Закрытие модального окна и выдвижной панели щелчком по подложке.
10
10
  * <dialog> закрывается по Esc сам, но щелчок мимо окна ему безразличен,
@@ -27,6 +27,12 @@
27
27
  * для разметки с [data-gr-tabs]. Без скрипта та же разметка остаётся
28
28
  * списком ссылок-якорей и секциями подряд.
29
29
  *
30
+ * 5. Широкая таблица в .gr-prose — в порядке Tab. Она прокручивается
31
+ * сама, и Chromium с Firefox ставят такую область в порядок Tab сами,
32
+ * а Safari — нет; tabindex в тексте из редактора поставить некому.
33
+ * Перед каждым Tab рантайм ставит его сам — и снимает, когда таблица
34
+ * стала помещаться.
35
+ *
30
36
  * Всё, что умеют <details>, <dialog> и Popover API, остаётся за ними.
31
37
  */
32
38
  (function (factory) {
@@ -50,7 +56,7 @@
50
56
  })(function () {
51
57
  'use strict';
52
58
 
53
- var VERSION = '0.28.2';
59
+ var VERSION = '0.29.1';
54
60
 
55
61
  var OVERLAY_ATTR = 'data-gr-overlay-close';
56
62
 
@@ -73,12 +79,21 @@
73
79
 
74
80
  var TABS_ATTR = 'data-gr-tabs';
75
81
 
82
+ // Таблица прозы со своей прокруткой (_table.scss) и то, что делает
83
+ // таблицу доступной с клавиатуры и без tabindex: Tab по ссылке или полю
84
+ // внутри и так докручивает таблицу до них.
85
+ var PROSE_TABLES = '.gr-prose table:not(.gr-table)';
86
+ var FOCUSABLE = 'a[href],button,input,select,textarea,summary,iframe,[tabindex],[contenteditable]';
87
+
76
88
  // Причина закрытия попадает в dialog.returnValue: обработчик close
77
89
  // на странице отличит щелчок мимо от кнопки «Сохранить».
78
90
  var OVERLAY_REASON = 'overlay';
79
91
 
80
92
  var started = false;
81
93
 
94
+ // Таблицы, которым tabindex поставил рантайм: снимается только свой.
95
+ var focusTables = [];
96
+
82
97
  // Цель нажатия. Щелчок — это пара «нажал» и «отпустил», и закрывать окно
83
98
  // нужно, только если обе половины пришлись на подложку: выделение текста,
84
99
  // начатое в окне и законченное за его краем, щелчком по подложке не является.
@@ -611,6 +626,38 @@
611
626
  return Array.prototype.indexOf.call(list, node);
612
627
  }
613
628
 
629
+ // --- Широкая таблица прозы в порядке Tab ------------------------------------
630
+
631
+ // Перед каждым Tab, в перехвате: порядок обхода браузер считает после
632
+ // обработчиков, и отметка, поставленная здесь, уже в нём. Так не нужны
633
+ // ни наблюдатели, ни замеры вне нажатия. Ширина — по прокрутке самой
634
+ // таблицы: шире колонки — значит, прокручивается.
635
+ function onTabPress(event) {
636
+ if (event.key !== 'Tab') return;
637
+
638
+ focusTables = focusTables.filter(function (table) { return table.isConnected; });
639
+
640
+ var tables = document.querySelectorAll(PROSE_TABLES);
641
+
642
+ for (var i = 0; i < tables.length; i++) {
643
+ var table = tables[i];
644
+ var marked = focusTables.indexOf(table) !== -1;
645
+ var wide = table.scrollWidth > table.clientWidth;
646
+
647
+ if (wide && !marked && !table.hasAttribute('tabindex') && !table.querySelector(FOCUSABLE)) {
648
+ table.setAttribute('tabindex', '0');
649
+ focusTables.push(table);
650
+ } else if (!wide && marked) {
651
+ unmarkTable(table);
652
+ }
653
+ }
654
+ }
655
+
656
+ function unmarkTable(table) {
657
+ table.removeAttribute('tabindex');
658
+ focusTables.splice(focusTables.indexOf(table), 1);
659
+ }
660
+
614
661
  // Один делегированный обработчик на документ, а не по слушателю на окно:
615
662
  // окна появляются и исчезают вместе с разметкой, и переподписываться
616
663
  // на каждое пришлось бы вручную.
@@ -630,13 +677,17 @@
630
677
  pressed = null;
631
678
  listen('removeEventListener');
632
679
 
680
+ while (focusTables.length) unmarkTable(focusTables[0]);
681
+
633
682
  return publicApi;
634
683
  }
635
684
 
636
685
  // Подписка и отписка — один список: диспетчер на каждый тип события
637
- // из реестра плюс перехват нажатия для щелчка по подложке.
686
+ // из реестра плюс перехват нажатия для щелчка по подложке и Tab
687
+ // для широких таблиц прозы.
638
688
  function listen(method) {
639
689
  document[method]('pointerdown', onPointerDown, true);
690
+ document[method]('keydown', onTabPress, true);
640
691
 
641
692
  for (var type in registry) {
642
693
  if (Object.prototype.hasOwnProperty.call(registry, type)) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * GriffinJS — Core v0.28.2
2
+ * GriffinJS — Core v0.29.1
3
3
  * Опциональный слой виджетов с состоянием поверх Griffincss: слайдер,
4
4
  * галерея, лайтбокс, параллакс, мегаменю. Подключается одной строкой
5
5
  * и одним файлом; страница без него — законное и рабочее состояние.
@@ -45,7 +45,7 @@
45
45
  })(function () {
46
46
  'use strict';
47
47
 
48
- var VERSION = '0.28.2';
48
+ var VERSION = '0.29.1';
49
49
 
50
50
  // Реестры. Модуль зовёт defineEngine/defineWidget; ядро — единственное
51
51
  // место, которое знает, как их применить.