@bigplay/tree-view-core 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigplay/tree-view-core",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Раскладка дерева: чистые функции без привязки к фреймворку",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/styles.css CHANGED
@@ -116,6 +116,9 @@
116
116
  --tv-bracket-my-bg: #00146c;
117
117
  --tv-bracket-my-color: #ffffff;
118
118
  --tv-bracket-radius: 6px;
119
+ --tv-bracket-shadow:
120
+ 0 0 0 1px rgb(15 23 42 / 8%),
121
+ 0 1px 3px rgb(15 23 42 / 10%);
119
122
 
120
123
  position: relative;
121
124
  box-sizing: border-box;
@@ -129,8 +132,21 @@
129
132
  font-size: 12px;
130
133
  }
131
134
 
132
- .tv-bracket--my-team {
135
+ /* Матч — один блок с собственным контуром: два соседних матча иначе
136
+ читаются как один список из четырёх команд. Обрезка по скруглению живёт
137
+ здесь, а не на карточке, — подсказку и кнопку сворачивания обрезать нельзя. */
138
+ .tv-bracket__rows {
139
+ box-sizing: border-box;
140
+ display: flex;
141
+ flex: 1 1 auto;
142
+ flex-direction: column;
143
+ min-height: 0;
144
+ overflow: hidden;
133
145
  border-radius: var(--tv-bracket-radius);
146
+ box-shadow: var(--tv-bracket-shadow);
147
+ }
148
+
149
+ .tv-bracket--my-team .tv-bracket__rows {
134
150
  outline: 2px solid var(--tv-bracket-my-bg);
135
151
  }
136
152
 
@@ -188,6 +204,43 @@
188
204
  background: var(--tv-bracket-loser-bg);
189
205
  }
190
206
 
207
+ /* Кнопка «свернуть предыдущие матчи»: висит на краю карточки, с той стороны,
208
+ откуда приходят линии от детей. Кружок нарочно маленький — карточка узкая. */
209
+ .tv-bracket__toggle {
210
+ position: absolute;
211
+ top: 50%;
212
+ left: -11px;
213
+ z-index: 1;
214
+ display: flex;
215
+ align-items: center;
216
+ justify-content: center;
217
+ width: 22px;
218
+ height: 22px;
219
+ padding: 0;
220
+ font: inherit;
221
+ font-size: 13px;
222
+ font-weight: 700;
223
+ line-height: 1;
224
+ color: var(--tv-bracket-winner-bg);
225
+ cursor: pointer;
226
+ background: #ffffff;
227
+ border: 1px solid #d1d5db;
228
+ border-radius: 50%;
229
+ box-shadow: 0 1px 3px rgb(15 23 42 / 18%);
230
+ transform: translateY(-50%);
231
+ }
232
+
233
+ .tv-bracket__toggle:hover {
234
+ border-color: var(--tv-bracket-winner-bg);
235
+ }
236
+
237
+ /* Свёрнутый матч виден сразу: кнопка залита, а не пустая. */
238
+ .tv-bracket__toggle--collapsed {
239
+ color: var(--tv-bracket-winner-color);
240
+ background: var(--tv-bracket-winner-bg);
241
+ border-color: var(--tv-bracket-winner-bg);
242
+ }
243
+
191
244
  .tv-bracket__name {
192
245
  max-width: 120px;
193
246
  overflow: hidden;
@@ -234,6 +287,9 @@
234
287
  --tv-rounds-border: 2px solid #e5e7eb;
235
288
  --tv-rounds-gap: 20px;
236
289
  --tv-rounds-fade: 220ms;
290
+ /* Как матч складывается, уходя в предыдущий раунд. */
291
+ --tv-rounds-collapse-scale: 0.86;
292
+ --tv-rounds-collapse-ease: cubic-bezier(0.4, 0, 0.2, 1);
237
293
  /* В свайпере линия — единственная подсказка, что матч идёт дальше,
238
294
  поэтому она заметнее, чем на широком экране. */
239
295
  --tv-rounds-link-color: #94a3b8;
@@ -247,14 +303,50 @@
247
303
  min-width: 100%;
248
304
  }
249
305
 
250
- .tv-rounds__bar {
251
- display: flex;
252
- min-width: 100%;
306
+ /* Слой с подписями раундов.
307
+
308
+ Лежит вне прокручиваемого блока и прилипает к верху экрана: раньше полоса
309
+ была внутри блока с `overflow-x`, а такой элемент — скролл-контейнер по обеим
310
+ осям, поэтому прилипать ей было не к чему и при вертикальном скролле она
311
+ уезжала вместе с сеткой. Сдвиг по горизонтали ей задаёт скрипт — тянет за
312
+ прокруткой сетки, поэтому своей полосы прокрутки у слоя нет. */
313
+ .tv-rounds__head {
314
+ position: sticky;
315
+ top: 0;
316
+ z-index: 2;
317
+ overflow: hidden;
253
318
  margin-bottom: var(--tv-rounds-gap);
254
319
  background: var(--tv-rounds-bg);
255
320
  border-bottom: var(--tv-rounds-border);
256
321
  }
257
322
 
323
+ .tv-rounds__bar {
324
+ display: flex;
325
+ min-width: 100%;
326
+ }
327
+
328
+ /* Блок, который листают пальцем. На широком экране прокрутку даёт обёртка
329
+ вокруг компонента — здесь её нет, иначе сетку нельзя было бы перетаскивать. */
330
+ .tv-rounds__scroll {
331
+ position: relative;
332
+ }
333
+
334
+ /* Якоря прилипания: подписи раундов уехали в отдельный слой, а снапиться
335
+ свайпу надо по чему-то внутри прокручиваемого блока. Нулевой высоты,
336
+ поэтому на раскладку не влияют. */
337
+ .tv-rounds__snap {
338
+ display: flex;
339
+ /* Высота в пиксель, а не ноль: нулевую коробку часть браузеров не считает
340
+ областью прилипания. Отрицательный отступ возвращает её из раскладки. */
341
+ height: 1px;
342
+ margin-bottom: -1px;
343
+ }
344
+
345
+ .tv-rounds__snap-item {
346
+ flex: none;
347
+ display: block;
348
+ }
349
+
258
350
  .tv-rounds__item {
259
351
  flex: none;
260
352
  box-sizing: border-box;
@@ -278,6 +370,12 @@
278
370
  * матчами никуда не деваются.
279
371
  */
280
372
 
373
+ /* В свайпере матчи пары стоят вплотную, и подсказка над карточкой налезала бы
374
+ на соседнюю. Свою команду там и так видно по рамке. */
375
+ .tv-rounds--swipe .tv-bracket__hint {
376
+ display: none;
377
+ }
378
+
281
379
  /* Линии в свайпере: единственное, что показывает, куда матч идёт дальше. */
282
380
  .tv-rounds--swipe .tree-view {
283
381
  --tv-link-color: var(--tv-rounds-link-color);
@@ -287,32 +385,41 @@
287
385
  .tv-rounds--swipe {
288
386
  width: 100%;
289
387
  min-width: 0;
388
+ }
389
+
390
+ .tv-rounds--swipe .tv-rounds__scroll {
290
391
  overflow-x: auto;
291
- /* Свайп внутри полосы не должен утягивать за собой страницу. */
392
+ /* Свайп внутри сетки не должен утягивать за собой страницу. */
292
393
  overscroll-behavior-x: contain;
293
394
  scroll-snap-type: x mandatory;
294
395
  scrollbar-width: none;
295
396
  }
296
397
 
297
- .tv-rounds--swipe::-webkit-scrollbar {
398
+ .tv-rounds--swipe .tv-rounds__scroll::-webkit-scrollbar {
298
399
  display: none;
299
400
  }
300
401
 
301
- .tv-rounds--swipe .tv-rounds__item {
402
+ .tv-rounds--swipe .tv-rounds__snap-item {
302
403
  scroll-snap-align: start;
404
+ /* Резкий свайп не должен перепрыгивать через раунд: останавливаемся на каждом. */
405
+ scroll-snap-stop: always;
303
406
  }
304
407
 
305
408
  /* Пройденный раунд: убран с глаз, но не из раскладки — иначе прокрутка
306
409
  и точки прилипания поехали бы прямо под пальцем.
307
410
 
308
- Уходит и возвращается плавно: при свайпе карточки соседнего раунда иначе
309
- пропадают рывком прямо под пальцем. Видимость снимается только после того,
310
- как карточка растворилась, — поэтому у неё отдельная задержка.
411
+ Уходит не просто гаснув, а складываясь в сторону следующего раунда: матч
412
+ ужимается к своему правому краю — туда, откуда уходит линия к продолжению, —
413
+ и растворяется. При возврате назад разворачивается обратно.
311
414
 
312
- Селектор нарочно такой же длины, как у правила ниже: иначе переход
313
- из него перебил бы этот и карточка исчезала бы мгновенно. */
415
+ Видимость снимается только после того, как карточка ужалась, — поэтому
416
+ у неё отдельная задержка. */
417
+ /* Возврат назад — сразу, без проявления. Раньше тут был такой же переход,
418
+ как на уходе, и раунд, в который ты свайпнул обратно, ещё 220 мс всплывал
419
+ из нуля: уход ощущался мгновенным, а возврат заметно медленнее. Уходу
420
+ анимация нужна (матч складывается у тебя на глазах), возврату — нет. */
314
421
  .tv-rounds--swipe [data-depth] {
315
- transition: opacity var(--tv-rounds-fade, 220ms) ease;
422
+ transition: none;
316
423
  }
317
424
 
318
425
  .tv-rounds--swipe [data-depth].tv-hidden {
@@ -320,9 +427,21 @@
320
427
  opacity: 0;
321
428
  transition:
322
429
  opacity var(--tv-rounds-fade, 220ms) ease,
430
+ transform var(--tv-rounds-fade, 220ms) var(--tv-rounds-collapse-ease),
323
431
  visibility 0s linear var(--tv-rounds-fade, 220ms);
324
432
  }
325
433
 
434
+ /* Складывается сам матч — ужимается к правому краю, туда, где продолжение.
435
+ Линии просто гаснут: путь в SVG не масштабируешь так, чтобы он остался ровным.
436
+
437
+ Только сжатие, без сдвига: сдвинутая карточка вылезает за край и увеличивает
438
+ область прокрутки, а `scroll-snap: mandatory` тут же пересчитывает прилипание —
439
+ получается бесконечный круг «сдвинули → переснапили → сдвинули». */
440
+ .tv-rounds--swipe .tree-view__node.tv-hidden {
441
+ transform: scale(var(--tv-rounds-collapse-scale));
442
+ transform-origin: right center;
443
+ }
444
+
326
445
  /* Вне свайпера прятать нечего анимировать — убираем сразу. */
327
446
  .tv-hidden {
328
447
  visibility: hidden;
@@ -334,4 +453,8 @@
334
453
  .tv-rounds--swipe [data-depth].tv-hidden {
335
454
  transition: none;
336
455
  }
456
+
457
+ .tv-rounds--swipe .tree-view__node.tv-hidden {
458
+ transform: none;
459
+ }
337
460
  }