|
3 | 3 | @inject BlazorDownloadFile.IBlazorDownloadFileService BlazorDownloadFileService |
4 | 4 | @inject NavigationManager NavManager |
5 | 5 |
|
6 | | -<div class="mb-3"> |
7 | | - <div class="form-check form-check-inline"> |
8 | | - <input id="radio-compress" class="form-check-input" type="radio" |
9 | | - checked="@(compressionMode == CompressionMode.Compress)" |
10 | | - @onchange="@(() => compressionMode = CompressionMode.Compress)"> |
11 | | - <label class="form-check-label" for="radio-compress">Compress</label> |
12 | | - </div> |
13 | | - <div class="form-check form-check-inline"> |
14 | | - <input id="radio-decompress" class="form-check-input" type="radio" |
15 | | - checked="@(compressionMode == CompressionMode.Decompress)" |
16 | | - @onchange="@(() => compressionMode = CompressionMode.Decompress)"> |
17 | | - <label class="form-check-label" for="radio-decompress">Decompress</label> |
18 | | - </div> |
| 6 | +<ul class="nav nav-pills mb-4 pb-1 border-bottom" role="navigation" aria-label="Compression format"> |
| 7 | + @foreach (var fmt in new[] { CompressionFormat.GZip, CompressionFormat.Brotli, CompressionFormat.Deflate, CompressionFormat.ZLib, CompressionFormat.ZStd }) |
| 8 | + { |
| 9 | + <li class="nav-item"> |
| 10 | + @if (fmt == _format) |
| 11 | + { |
| 12 | + <span class="nav-link active" aria-current="page">@FormatDisplayName(fmt)</span> |
| 13 | + } |
| 14 | + else |
| 15 | + { |
| 16 | + <a class="nav-link" href="@FormatUrl(fmt)">@FormatDisplayName(fmt)</a> |
| 17 | + } |
| 18 | + </li> |
| 19 | + } |
| 20 | +</ul> |
| 21 | + |
| 22 | +<div class="btn-group mb-4" role="group" aria-label="Mode"> |
| 23 | + <input id="radio-compress" class="btn-check" type="radio" autocomplete="off" |
| 24 | + checked="@(compressionMode == CompressionMode.Compress)" |
| 25 | + @onchange="@(() => compressionMode = CompressionMode.Compress)"> |
| 26 | + <label class="btn btn-outline-primary px-4" for="radio-compress">Compress</label> |
| 27 | + <input id="radio-decompress" class="btn-check" type="radio" autocomplete="off" |
| 28 | + checked="@(compressionMode == CompressionMode.Decompress)" |
| 29 | + @onchange="@(() => compressionMode = CompressionMode.Decompress)"> |
| 30 | + <label class="btn btn-outline-primary px-4" for="radio-decompress">Decompress</label> |
19 | 31 | </div> |
| 32 | + |
20 | 33 | @if (compressionMode == CompressionMode.Compress) |
21 | 34 | { |
22 | 35 | <div class="mb-3"> |
23 | | - <label class="form-label">Compression Level:</label> |
24 | | - <div> |
25 | | - <div class="form-check form-check-inline"> |
26 | | - <input id="radio-smallest" class="form-check-input" type="radio" |
27 | | - checked="@(compressionLevel == CompressionLevel.SmallestSize)" |
28 | | - @onchange="@(() => compressionLevel = CompressionLevel.SmallestSize)"> |
29 | | - <label class="form-check-label" for="radio-smallest">Smallest Size</label> |
30 | | - </div> |
31 | | - <div class="form-check form-check-inline"> |
32 | | - <input id="radio-optimal" class="form-check-input" type="radio" |
33 | | - checked="@(compressionLevel == CompressionLevel.Optimal)" |
34 | | - @onchange="@(() => compressionLevel = CompressionLevel.Optimal)"> |
35 | | - <label class="form-check-label" for="radio-optimal">Optimal</label> |
36 | | - </div> |
37 | | - <div class="form-check form-check-inline"> |
38 | | - <input id="radio-fastest" class="form-check-input" type="radio" |
39 | | - checked="@(compressionLevel == CompressionLevel.Fastest)" |
40 | | - @onchange="@(() => compressionLevel = CompressionLevel.Fastest)"> |
41 | | - <label class="form-check-label" for="radio-fastest">Fastest</label> |
42 | | - </div> |
43 | | - <div class="form-check form-check-inline"> |
44 | | - <input id="radio-no-compression" class="form-check-input" type="radio" |
45 | | - checked="@(compressionLevel == CompressionLevel.NoCompression)" |
46 | | - @onchange="@(() => compressionLevel = CompressionLevel.NoCompression)"> |
47 | | - <label class="form-check-label" for="radio-no-compression">No Compression</label> |
48 | | - </div> |
| 36 | + <label class="form-label text-muted small text-uppercase fw-semibold ls-1">Compression Level</label> |
| 37 | + <div class="btn-group d-flex btn-group-sm" role="group" aria-label="Compression level"> |
| 38 | + <input id="radio-smallest" class="btn-check" type="radio" autocomplete="off" |
| 39 | + checked="@(compressionLevel == CompressionLevel.SmallestSize)" |
| 40 | + @onchange="@(() => compressionLevel = CompressionLevel.SmallestSize)"> |
| 41 | + <label class="btn btn-outline-secondary w-100" for="radio-smallest">Smallest Size</label> |
| 42 | + <input id="radio-optimal" class="btn-check" type="radio" autocomplete="off" |
| 43 | + checked="@(compressionLevel == CompressionLevel.Optimal)" |
| 44 | + @onchange="@(() => compressionLevel = CompressionLevel.Optimal)"> |
| 45 | + <label class="btn btn-outline-secondary w-100" for="radio-optimal">Optimal</label> |
| 46 | + <input id="radio-fastest" class="btn-check" type="radio" autocomplete="off" |
| 47 | + checked="@(compressionLevel == CompressionLevel.Fastest)" |
| 48 | + @onchange="@(() => compressionLevel = CompressionLevel.Fastest)"> |
| 49 | + <label class="btn btn-outline-secondary w-100" for="radio-fastest">Fastest</label> |
| 50 | + <input id="radio-no-compression" class="btn-check" type="radio" autocomplete="off" |
| 51 | + checked="@(compressionLevel == CompressionLevel.NoCompression)" |
| 52 | + @onchange="@(() => compressionLevel = CompressionLevel.NoCompression)"> |
| 53 | + <label class="btn btn-outline-secondary w-100" for="radio-no-compression">No Compression</label> |
49 | 54 | </div> |
50 | 55 | </div> |
51 | 56 | } |
52 | 57 |
|
53 | | -<div class="drop-zone mb-3 @(_dragCount > 0 ? "drop-zone--active" : "")" |
| 58 | +<div class="drop-zone mb-4 @(_dragCount > 0 ? "drop-zone--active" : "")" |
54 | 59 | @ondragenter="@(() => _dragCount++)" |
55 | 60 | @ondragleave="@(() => _dragCount--)" |
56 | 61 | @ondrop="@(() => _dragCount = 0)" |
|
71 | 76 | <div class="mb-3"> |
72 | 77 | @if (compressionMode == CompressionMode.Compress) |
73 | 78 | { |
74 | | - <button role="button" class="btn btn-primary" @onclick="CompressFiles" disabled="@(!anyFiles)">Compress Files</button> |
| 79 | + <button role="button" class="btn btn-primary btn-lg w-100" @onclick="CompressFiles" disabled="@(!anyFiles)">Compress Files</button> |
75 | 80 | } |
76 | 81 | else |
77 | 82 | { |
78 | | - <button role="button" class="btn btn-primary" @onclick="DecompressFiles" disabled="@(!anyFiles)">Decompress Files</button> |
| 83 | + <button role="button" class="btn btn-primary btn-lg w-100" @onclick="DecompressFiles" disabled="@(!anyFiles)">Decompress Files</button> |
79 | 84 | } |
80 | | - |
81 | 85 | @if(!anyFiles){ |
82 | | - <span class="text-danger">No files selected</span> |
| 86 | + <div class="text-danger small text-center mt-1">No files selected</div> |
83 | 87 | } |
84 | 88 | </div> |
85 | 89 |
|
86 | 90 | <ul class="list-group"> |
87 | 91 | @foreach (var file in files) |
88 | 92 | { |
89 | | - <li class="list-group-item"> |
90 | | - @file.OriginalName |
| 93 | + <li class="list-group-item d-flex align-items-center gap-2 py-2"> |
| 94 | + <span class="text-truncate flex-grow-1 small fw-medium">@file.OriginalName</span> |
91 | 95 | @if(file.Status == "Compressing" || file.Status == "Decompressing") |
92 | 96 | { |
93 | | - <div class="spinner-grow text-primary spinner-grow-sm" role="status"> |
| 97 | + <div class="spinner-grow text-primary spinner-grow-sm flex-shrink-0" role="status"> |
94 | 98 | <span class="visually-hidden">@file.Status</span> |
95 | 99 | </div> |
| 100 | + <small class="text-muted text-nowrap">@file.Status…</small> |
96 | 101 | } |
97 | | - |
98 | 102 | @if(file.Status.StartsWith("Error")) |
99 | 103 | { |
100 | | - <br /> |
101 | | - <span class="text-danger">⚠ @file.Status</span> |
| 104 | + <small class="text-danger text-nowrap">⚠ @file.Status</small> |
102 | 105 | } |
103 | 106 | @if(file.Status == "Finished") |
104 | 107 | { |
105 | | - <span class="text-success">✔ Finished</span> |
106 | | - <span class="text-muted ms-2 small">@FormatBytes(file.OriginalSize) → @FormatBytes(file.OutputSize)</span> |
| 108 | + <span class="text-success flex-shrink-0">✔ Finished</span> |
| 109 | + <span class="text-muted small text-nowrap">@FormatBytes(file.OriginalSize) → @FormatBytes(file.OutputSize)</span> |
107 | 110 | @if (compressionMode == CompressionMode.Compress) |
108 | 111 | { |
109 | 112 | <span class="@RatioBadgeCss(file.OriginalSize, file.OutputSize) ms-1">@RatioText(file.OriginalSize, file.OutputSize)</span> |
|
123 | 126 |
|
124 | 127 | private CompressionFormat _format = CompressionFormat.GZip; |
125 | 128 | private int _dragCount; |
126 | | - private string FormatName => _format switch |
| 129 | + |
| 130 | + private static string FormatSlug(CompressionFormat fmt) => fmt switch |
127 | 131 | { |
128 | 132 | CompressionFormat.Brotli => "brotli", |
129 | 133 | CompressionFormat.Deflate => "deflate", |
130 | 134 | CompressionFormat.ZLib => "zlib", |
131 | 135 | CompressionFormat.ZStd => "zstd", |
132 | 136 | _ => "gzip" |
133 | 137 | }; |
| 138 | + |
| 139 | + private static string FormatDisplayName(CompressionFormat fmt) => fmt switch |
| 140 | + { |
| 141 | + CompressionFormat.Brotli => "Brotli", |
| 142 | + CompressionFormat.Deflate => "Deflate", |
| 143 | + CompressionFormat.ZLib => "ZLib", |
| 144 | + CompressionFormat.ZStd => "Zstandard", |
| 145 | + _ => "GZIP" |
| 146 | + }; |
| 147 | + |
| 148 | + private string FormatUrl(CompressionFormat fmt) |
| 149 | + { |
| 150 | + var uri = new Uri(NavManager.Uri); |
| 151 | + var host = uri.Host; |
| 152 | + var currentSlug = FormatName; |
| 153 | + if (host.Contains(currentSlug)) |
| 154 | + { |
| 155 | + var newHost = host.Replace(currentSlug, FormatSlug(fmt)); |
| 156 | + var port = uri.IsDefaultPort ? "" : $":{uri.Port}"; |
| 157 | + return $"{uri.Scheme}://{newHost}{port}"; |
| 158 | + } |
| 159 | + return "#"; |
| 160 | + } |
| 161 | + |
| 162 | + private string FormatName => FormatSlug(_format); |
134 | 163 | private string Extension => _format switch |
135 | 164 | { |
136 | 165 | CompressionFormat.Brotli => ".br", |
|
0 commit comments