@webority/theme 0.21.0 → 0.23.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": "@webority/theme",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {
@@ -50,6 +50,7 @@
50
50
  @import "wui/shell";
51
51
  @import "wui/dock";
52
52
  @import "wui/consent-banner";
53
+ @import "wui/file-viewer";
53
54
  @import "wui/toolbar";
54
55
  @import "wui/theme-toggle";
55
56
  // After `overlays`: anchors the shared modal shell to the top when it holds the palette card.
@@ -0,0 +1,502 @@
1
+ // ── File viewer ──
2
+ // AppFileViewer / <app-file-viewer> / <wui-file-viewer>: a file shown inside the page, with a
3
+ // toolbar above it. The element builds the markup and the PDF pages; everything visual is here.
4
+ //
5
+ // Part of the wui-components layer: see _wui-components.scss for load order.
6
+
7
+ wui-file-viewer {
8
+ position: relative;
9
+ display: flex;
10
+ flex-direction: column;
11
+ height: 70vh;
12
+ min-height: 420px;
13
+ border: 1px solid var(--color-border);
14
+ border-radius: var(--wui-radius-lg);
15
+ overflow: hidden;
16
+ background: var(--color-bg-muted);
17
+ color: var(--color-text);
18
+ }
19
+ // Full screen fills the screen edge to edge.
20
+ wui-file-viewer:fullscreen {
21
+ border: 0;
22
+ border-radius: 0;
23
+ }
24
+
25
+ // In a narrow viewer the name gives way first, down to a short ellipsis; past that the page and
26
+ // zoom controls wrap onto a second row instead of shrinking over one another.
27
+ .wui-file-viewer-toolbar {
28
+ flex: 0 0 auto;
29
+ display: flex;
30
+ flex-wrap: wrap;
31
+ align-items: center;
32
+ justify-content: space-between;
33
+ gap: 6px 12px;
34
+ padding: 8px 12px;
35
+ background: var(--color-surface);
36
+ border-bottom: 1px solid var(--color-border);
37
+ }
38
+ .wui-file-viewer-group {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 6px;
42
+ min-width: 0;
43
+ }
44
+ .wui-file-viewer-start {
45
+ flex: 1 1 140px;
46
+ }
47
+ .wui-file-viewer-center,
48
+ .wui-file-viewer-end {
49
+ flex: 0 0 auto;
50
+ }
51
+ .wui-file-viewer-end {
52
+ justify-content: flex-end;
53
+ }
54
+ .wui-file-viewer-center:empty,
55
+ .wui-file-viewer-view-end:empty,
56
+ .wui-file-viewer-actions:empty {
57
+ display: none;
58
+ }
59
+ .wui-file-viewer-actions {
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 6px;
63
+ }
64
+ .wui-file-viewer-actions[hidden],
65
+ .wui-file-viewer-download[hidden],
66
+ .wui-file-viewer-fullscreen[hidden] {
67
+ display: none;
68
+ }
69
+ .wui-file-viewer-name {
70
+ min-width: 0;
71
+ max-width: 320px;
72
+ overflow: hidden;
73
+ white-space: nowrap;
74
+ text-overflow: ellipsis;
75
+ font-size: 13px;
76
+ font-weight: 500;
77
+ }
78
+ .wui-file-viewer-page {
79
+ display: inline-flex;
80
+ align-items: center;
81
+ gap: 6px;
82
+ font-size: 13px;
83
+ color: var(--color-text-subtle);
84
+ }
85
+ .wui-file-viewer-page-input {
86
+ width: 40px;
87
+ height: 30px;
88
+ text-align: center;
89
+ border: 1px solid var(--color-border);
90
+ border-radius: var(--wui-radius-sm);
91
+ background: var(--color-bg-muted);
92
+ color: var(--color-text);
93
+ font-size: 13px;
94
+ font-weight: 600;
95
+ }
96
+ .wui-file-viewer-page-input:focus {
97
+ outline: none;
98
+ border-color: var(--color-primary);
99
+ background: var(--color-surface);
100
+ }
101
+ .wui-file-viewer-page-input:focus-visible {
102
+ box-shadow: 0 0 0 2px var(--color-primary);
103
+ }
104
+ .wui-file-viewer-page-total {
105
+ white-space: nowrap;
106
+ }
107
+ // The zoom level, which opens the zoom menu.
108
+ .wui-file-viewer-zoom {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ gap: 2px;
113
+ min-width: 58px;
114
+ height: 30px;
115
+ padding: 0 6px;
116
+ border: 0;
117
+ border-radius: var(--wui-radius-sm);
118
+ background: transparent;
119
+ color: var(--color-text);
120
+ font-size: 13px;
121
+ font-weight: 600;
122
+ font-variant-numeric: tabular-nums;
123
+ }
124
+ .wui-file-viewer-zoom:hover {
125
+ background: var(--color-bg-subtle);
126
+ }
127
+ .wui-file-viewer-zoom:focus-visible {
128
+ outline: 2px solid var(--color-primary);
129
+ outline-offset: 1px;
130
+ }
131
+ // The zoom menu, placed against its button by the element (position: fixed).
132
+ .wui-file-viewer-menu {
133
+ z-index: var(--wui-z-dropdown);
134
+ min-width: 150px;
135
+ }
136
+ .wui-file-viewer-menu .dropdown-item {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: 8px;
140
+ }
141
+ // Keeps an unticked choice's label in line with the ticked one's.
142
+ .wui-file-viewer-menu-tick {
143
+ display: inline-flex;
144
+ width: 16px;
145
+ }
146
+ .wui-file-viewer-divider {
147
+ width: 1px;
148
+ height: 22px;
149
+ margin: 0 2px;
150
+ background: var(--color-border);
151
+ }
152
+
153
+ // The search row: a full-width second row of the toolbar while search is open.
154
+ .wui-file-viewer-search {
155
+ flex: 1 0 100%;
156
+ display: flex;
157
+ align-items: center;
158
+ gap: 6px;
159
+ min-width: 0;
160
+ }
161
+ .wui-file-viewer-search[hidden] {
162
+ display: none;
163
+ }
164
+ .wui-file-viewer-search-field {
165
+ flex: 0 1 320px;
166
+ min-width: 0;
167
+ }
168
+ .wui-file-viewer-search-count {
169
+ min-width: 72px;
170
+ font-size: 13px;
171
+ color: var(--color-text-subtle);
172
+ font-variant-numeric: tabular-nums;
173
+ white-space: nowrap;
174
+ }
175
+
176
+ .wui-file-viewer-body {
177
+ flex: 1 1 auto;
178
+ display: flex;
179
+ min-height: 0;
180
+ }
181
+ .wui-file-viewer-status {
182
+ display: flex;
183
+ flex-direction: column;
184
+ align-items: center;
185
+ justify-content: center;
186
+ gap: 12px;
187
+ width: 100%;
188
+ padding: 48px;
189
+ font-size: 14px;
190
+ color: var(--color-text-subtle);
191
+ text-align: center;
192
+ }
193
+ .wui-file-viewer-status p {
194
+ margin: 0;
195
+ }
196
+
197
+ .wui-file-viewer-thumbs {
198
+ flex: 0 0 auto;
199
+ width: 0;
200
+ display: flex;
201
+ flex-direction: column;
202
+ align-items: center;
203
+ overflow-y: auto;
204
+ overflow-x: hidden;
205
+ background: var(--color-surface);
206
+ border-right: 1px solid transparent;
207
+ opacity: 0;
208
+ transition:
209
+ width 0.24s var(--wui-ease-ui),
210
+ padding 0.24s var(--wui-ease-ui),
211
+ opacity 0.2s ease 0.04s;
212
+ }
213
+ .wui-file-viewer-thumbs.open {
214
+ width: 210px;
215
+ padding: 18px 0;
216
+ border-right-color: var(--color-border);
217
+ opacity: 1;
218
+ }
219
+ .wui-file-viewer-thumb {
220
+ display: block;
221
+ flex: none;
222
+ width: calc(100% - 36px);
223
+ margin-bottom: 18px;
224
+ padding: 0;
225
+ border: 2px solid transparent;
226
+ border-radius: var(--wui-radius-sm);
227
+ background: transparent;
228
+ cursor: pointer;
229
+ line-height: 0;
230
+ }
231
+ .wui-file-viewer-thumb.active {
232
+ border-color: var(--color-primary);
233
+ }
234
+ .wui-file-viewer-thumb-frame {
235
+ display: block;
236
+ width: 100%;
237
+ background: #fff;
238
+ }
239
+ .wui-file-viewer-thumb-frame canvas {
240
+ display: block;
241
+ width: 100%;
242
+ height: 100%;
243
+ }
244
+ .wui-file-viewer-thumb-no {
245
+ display: block;
246
+ padding: 3px 0 1px;
247
+ font-size: 11px;
248
+ line-height: 1.2;
249
+ color: var(--color-text-subtle);
250
+ text-align: center;
251
+ }
252
+
253
+ .wui-file-viewer-scroll {
254
+ flex: 1 1 auto;
255
+ overflow: auto;
256
+ min-width: 0;
257
+ // The pages fit the width; a scrollbar appearing must not change that width and set off a
258
+ // second fit.
259
+ scrollbar-gutter: stable;
260
+ }
261
+ .wui-file-viewer-scroll:focus-visible {
262
+ outline: 2px solid var(--color-primary);
263
+ outline-offset: -2px;
264
+ }
265
+ // Grab-to-pan: while a drag is active the cursor becomes a fist and selection is suppressed, so
266
+ // dragging across the text pans instead of selecting.
267
+ .wui-file-viewer-scroll.wui-file-viewer-panning {
268
+ cursor: grabbing;
269
+ user-select: none;
270
+ }
271
+ // The element places rows from this padding and gap (PAGE_PAD, PAGE_GAP in fileViewerPdf.js);
272
+ // change both together.
273
+ .wui-file-viewer-pages {
274
+ display: flex;
275
+ flex-direction: column;
276
+ align-items: center;
277
+ gap: 10px;
278
+ padding: 12px;
279
+ // Shrink-wraps to the widest page but never below the viewer, so a zoomed page centred by
280
+ // align-items can still be scrolled to both of its edges.
281
+ width: max-content;
282
+ min-width: 100%;
283
+ margin-inline: auto;
284
+ }
285
+ .wui-file-viewer-row {
286
+ display: flex;
287
+ align-items: flex-start;
288
+ gap: 10px;
289
+ }
290
+ // Sized by the element to the page's drawn size, so a page outside the drawn window keeps its place.
291
+ .wui-file-viewer-sheet {
292
+ --user-unit: 1;
293
+ --total-scale-factor: calc(var(--scale-factor) * var(--user-unit));
294
+ --scale-round-x: 1px;
295
+ --scale-round-y: 1px;
296
+ position: relative;
297
+ flex: none;
298
+ box-shadow: var(--wui-shadow-card);
299
+ border-radius: 2px;
300
+ background: #fff;
301
+ line-height: 0;
302
+ }
303
+ .wui-file-viewer-canvas {
304
+ display: block;
305
+ width: 100%;
306
+ height: 100%;
307
+ }
308
+ .wui-file-viewer-skeleton {
309
+ width: 100%;
310
+ height: 100%;
311
+ background: var(--color-bg-muted);
312
+ }
313
+
314
+ // The text layer: transparent text laid over the drawing for selecting, copying and searching.
315
+ // From pdf.js's own text layer rules (Apache-2.0, Mozilla Foundation).
316
+ .wui-file-viewer-text {
317
+ --min-font-size: 1;
318
+ --text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));
319
+ --min-font-size-inv: calc(1 / var(--min-font-size));
320
+ position: absolute;
321
+ inset: 0;
322
+ overflow: clip;
323
+ z-index: 0;
324
+ opacity: 1;
325
+ line-height: 1;
326
+ text-align: initial;
327
+ text-size-adjust: none;
328
+ forced-color-adjust: none;
329
+ transform-origin: 0 0;
330
+ caret-color: CanvasText;
331
+ color-scheme: only light;
332
+ }
333
+ .wui-file-viewer-text :is(span, br) {
334
+ position: absolute;
335
+ margin: 0;
336
+ color: transparent;
337
+ white-space: pre;
338
+ cursor: text;
339
+ transform-origin: 0 0;
340
+ }
341
+ // pdf.js wraps tagged runs in span.markedContent; matched by attribute, since that class is
342
+ // pdf.js's name, not ours.
343
+ .wui-file-viewer-text > :not([class~="markedContent"]),
344
+ .wui-file-viewer-text [class~="markedContent"] span:not([class~="markedContent"]) {
345
+ --font-height: 0;
346
+ --scale-x: 1;
347
+ --rotate: 0deg;
348
+ z-index: 1;
349
+ font-size: calc(var(--text-scale-factor) * var(--font-height));
350
+ transform: rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv));
351
+ }
352
+ .wui-file-viewer-text [class~="markedContent"] {
353
+ display: contents;
354
+ }
355
+ .wui-file-viewer-text ::selection {
356
+ background: color-mix(in srgb, var(--color-primary) 25%, transparent);
357
+ }
358
+ .wui-file-viewer-text br::selection {
359
+ background: transparent;
360
+ }
361
+ // A search hit inside a text run sits in the run's flow, not positioned like the runs themselves.
362
+ // The text is transparent over the drawing, so the hit is a see-through tint: an opaque one
363
+ // would hide the words it marks.
364
+ .wui-file-viewer-text mark.wui-file-viewer-hit {
365
+ position: static;
366
+ margin: -1px;
367
+ padding: 1px;
368
+ border-radius: 2px;
369
+ color: transparent;
370
+ background: color-mix(in srgb, var(--color-warning) 40%, transparent);
371
+ }
372
+ .wui-file-viewer-text mark.wui-file-viewer-hit-current {
373
+ background: color-mix(in srgb, var(--color-primary) 45%, transparent);
374
+ box-shadow: 0 0 0 1px var(--color-primary);
375
+ }
376
+
377
+ // Links on the page: invisible boxes over the drawn link, a pointer cursor saying they are links.
378
+ .wui-file-viewer-links {
379
+ position: absolute;
380
+ inset: 0;
381
+ z-index: 2;
382
+ pointer-events: none;
383
+ }
384
+ .wui-file-viewer-links a {
385
+ position: absolute;
386
+ pointer-events: auto;
387
+ }
388
+ .wui-file-viewer-links a:focus-visible {
389
+ outline: 2px solid var(--color-primary);
390
+ }
391
+
392
+ // The hidden frame Print fills with page images. Kept in the layout at no size rather than
393
+ // display: none, which can stop a frame's document from laying out and print blank.
394
+ .wui-file-viewer-print-frame {
395
+ position: fixed;
396
+ right: 0;
397
+ bottom: 0;
398
+ width: 0;
399
+ height: 0;
400
+ border: 0;
401
+ opacity: 0;
402
+ pointer-events: none;
403
+ }
404
+
405
+ // The wrapper React and Razor put a page's own controls in: it takes no part in the layout, so
406
+ // the controls sit in the actions row like its direct children.
407
+ .wui-file-viewer-slot {
408
+ display: contents;
409
+ }
410
+
411
+ // The image view: the stage takes the image's turned size and sits centred while it fits, and
412
+ // scrolls from its edges once zoomed past the view. The image is turned about the stage's centre.
413
+ .wui-file-viewer-image-scroll {
414
+ display: flex;
415
+ padding: 16px;
416
+ }
417
+ .wui-file-viewer-image-stage {
418
+ position: relative;
419
+ flex: none;
420
+ margin: auto;
421
+ }
422
+ .wui-file-viewer-image {
423
+ position: absolute;
424
+ top: 50%;
425
+ left: 50%;
426
+ max-width: none;
427
+ user-select: none;
428
+ // A checkerboard behind transparent pixels, so a transparent PNG or SVG reads as transparent.
429
+ background: repeating-conic-gradient(var(--color-bg-muted) 0% 25%, var(--color-surface) 0% 50%) 50% / 16px 16px;
430
+ }
431
+
432
+ // The video and audio player: the picture (or, for audio, a music mark) fills the body, and the
433
+ // controls run along the bottom. The browser's own controls are never shown.
434
+ .wui-file-viewer-player {
435
+ display: flex;
436
+ flex-direction: column;
437
+ width: 100%;
438
+ min-width: 0;
439
+ }
440
+ .wui-file-viewer-stage {
441
+ flex: 1 1 auto;
442
+ display: flex;
443
+ align-items: center;
444
+ justify-content: center;
445
+ min-height: 0;
446
+ background: #000;
447
+ }
448
+ .wui-file-viewer-stage:focus-visible {
449
+ outline: 2px solid var(--color-primary);
450
+ outline-offset: -2px;
451
+ }
452
+ .wui-file-viewer-video {
453
+ display: block;
454
+ max-width: 100%;
455
+ max-height: 100%;
456
+ cursor: pointer;
457
+ }
458
+ .wui-file-viewer-player-audio .wui-file-viewer-stage {
459
+ background: var(--color-bg-muted);
460
+ }
461
+ .wui-file-viewer-audio-art {
462
+ display: grid;
463
+ place-items: center;
464
+ width: 96px;
465
+ height: 96px;
466
+ border-radius: 50%;
467
+ background: color-mix(in srgb, var(--color-primary) 12%, transparent);
468
+ color: var(--color-primary);
469
+ }
470
+ .wui-file-viewer-controls {
471
+ flex: 0 0 auto;
472
+ display: flex;
473
+ align-items: center;
474
+ gap: 8px;
475
+ padding: 8px 12px;
476
+ background: var(--color-surface);
477
+ border-top: 1px solid var(--color-border);
478
+ }
479
+ .wui-file-viewer-time {
480
+ flex: none;
481
+ font-size: 13px;
482
+ color: var(--color-text-subtle);
483
+ font-variant-numeric: tabular-nums;
484
+ }
485
+ .wui-file-viewer-seek {
486
+ flex: 1 1 auto;
487
+ min-width: 60px;
488
+ }
489
+ // On a phone the volume slider gives way; the mute button stays.
490
+ .wui-file-viewer-volume {
491
+ display: none;
492
+ flex: 0 0 90px;
493
+ }
494
+ @include media-breakpoint-up(sm) {
495
+ .wui-file-viewer-volume {
496
+ display: block;
497
+ }
498
+ }
499
+ .wui-file-viewer-speed {
500
+ min-width: 48px;
501
+ }
502
+