|
13 | 13 | // limitations under the License. |
14 | 14 | --> |
15 | 15 | <script lang="ts"> |
| 16 | + import { onDestroy, onMount } from 'svelte' |
| 17 | +
|
16 | 18 | export let size: 'small' | 'big' = 'big' |
| 19 | +
|
| 20 | + let barContainer: HTMLDivElement | undefined |
| 21 | + let rowContainer: HTMLDivElement | undefined |
| 22 | + let scale = 1 |
| 23 | +
|
| 24 | + let resizeObserver: ResizeObserver | undefined |
| 25 | + const HORIZONTAL_PADDING = 60 |
| 26 | + const SCALE_CHANGE_THRESHOLD = 0.001 |
| 27 | +
|
| 28 | + function measureWidths (): { barWidth: number, rowWidth: number } { |
| 29 | + if (barContainer === undefined || rowContainer === undefined) { |
| 30 | + return { barWidth: 0, rowWidth: 0 } |
| 31 | + } |
| 32 | +
|
| 33 | + const availableWidth = barContainer.clientWidth - HORIZONTAL_PADDING |
| 34 | + const barWidth = availableWidth > 0 ? availableWidth : 0 |
| 35 | + const rowWidth = rowContainer.scrollWidth |
| 36 | +
|
| 37 | + return { barWidth, rowWidth } |
| 38 | + } |
| 39 | +
|
| 40 | + function updateScale (): void { |
| 41 | + const { barWidth: containerWidth, rowWidth } = measureWidths() |
| 42 | +
|
| 43 | + if (containerWidth === 0 || rowWidth === 0) { |
| 44 | + if (scale !== 1) scale = 1 |
| 45 | + return |
| 46 | + } |
| 47 | +
|
| 48 | + const nextScale = Math.min(1, containerWidth / rowWidth) |
| 49 | +
|
| 50 | + if (Math.abs(nextScale - scale) > SCALE_CHANGE_THRESHOLD) { |
| 51 | + scale = Number(nextScale.toFixed(3)) |
| 52 | + } |
| 53 | + } |
| 54 | +
|
| 55 | + onMount(() => { |
| 56 | + resizeObserver = new ResizeObserver(() => { |
| 57 | + updateScale() |
| 58 | + }) |
| 59 | + if (barContainer !== undefined) resizeObserver.observe(barContainer) |
| 60 | + if (rowContainer !== undefined) resizeObserver.observe(rowContainer) |
| 61 | + updateScale() |
| 62 | + }) |
| 63 | +
|
| 64 | + onDestroy(() => { |
| 65 | + resizeObserver?.disconnect() |
| 66 | + resizeObserver = undefined |
| 67 | + }) |
17 | 68 | </script> |
18 | 69 |
|
19 | | -<div class="bar" data-size={size}> |
20 | | - <div class="row"> |
| 70 | +<div class="bar" data-size={size} bind:this={barContainer}> |
| 71 | + <div class="row" bind:this={rowContainer} style={`--row-scale:${scale};`}> |
21 | 72 | <div class="left"><slot name="left" /></div> |
22 | 73 | <div class="center"><slot name="center" /></div> |
23 | 74 | <div class="right"><slot name="right" /></div> |
|
35 | 86 | border-top: 1px solid var(--theme-divider-color); |
36 | 87 | container-type: inline-size; |
37 | 88 | width: 100%; |
| 89 | + overflow: hidden; |
38 | 90 | } |
39 | 91 |
|
40 | 92 | .row { |
| 93 | + --row-scale: 1; |
41 | 94 | position: relative; |
42 | 95 | display: flex; |
43 | 96 | align-items: center; |
44 | 97 | justify-content: space-between; |
45 | 98 | gap: var(--g); |
| 99 | + transform-origin: center center; |
| 100 | + transform: scale(var(--row-scale)); |
| 101 | + will-change: transform; |
46 | 102 | } |
47 | 103 | .left, |
48 | 104 | .center, |
|
52 | 108 | gap: var(--g); |
53 | 109 | white-space: nowrap; |
54 | 110 | min-width: 0; |
| 111 | + flex-shrink: 0; |
55 | 112 | } |
56 | 113 | .center { |
57 | 114 | position: absolute; |
58 | 115 | left: 50%; |
59 | 116 | transform: translateX(-50%); |
60 | 117 | } |
61 | 118 |
|
62 | | - @container (max-width: 350px) { |
| 119 | + @container (max-width: 440px) { |
63 | 120 | .bar[data-size='small'] .row { |
64 | 121 | justify-content: center; |
65 | 122 | gap: var(--g); |
|
0 commit comments