@internetarchive/elements 0.2.14-webdev-8969.0 → 0.3.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.
Files changed (94) hide show
  1. package/README.md +20 -0
  2. package/dist/src/elements/ia-item-navigator/ia-item-navigator.d.ts +97 -0
  3. package/dist/src/elements/ia-item-navigator/ia-item-navigator.d.ts.map +1 -0
  4. package/dist/src/elements/ia-item-navigator/ia-item-navigator.js +588 -0
  5. package/dist/src/elements/ia-item-navigator/ia-item-navigator.js.map +1 -0
  6. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-button.d.ts +39 -0
  7. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-button.d.ts.map +1 -0
  8. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-button.js +249 -0
  9. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-button.js.map +1 -0
  10. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-slider.d.ts +55 -0
  11. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-slider.d.ts.map +1 -0
  12. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-slider.js +394 -0
  13. package/dist/src/elements/ia-item-navigator/ia-itemnav-menu-slider.js.map +1 -0
  14. package/dist/src/elements/ia-item-navigator/ia-itemnav-no-theater-available.d.ts +20 -0
  15. package/dist/src/elements/ia-item-navigator/ia-itemnav-no-theater-available.d.ts.map +1 -0
  16. package/dist/src/elements/ia-item-navigator/ia-itemnav-no-theater-available.js +102 -0
  17. package/dist/src/elements/ia-item-navigator/ia-itemnav-no-theater-available.js.map +1 -0
  18. package/dist/src/elements/ia-item-navigator/icons/collapse-sidebar.svg +7 -0
  19. package/dist/src/elements/ia-item-navigator/icons/ellipses.svg +7 -0
  20. package/dist/src/elements/ia-item-navigator/icons.d.ts +13 -0
  21. package/dist/src/elements/ia-item-navigator/icons.d.ts.map +1 -0
  22. package/dist/src/elements/ia-item-navigator/icons.js +16 -0
  23. package/dist/src/elements/ia-item-navigator/icons.js.map +1 -0
  24. package/dist/src/elements/ia-item-navigator/interfaces/event-interfaces.d.ts +41 -0
  25. package/dist/src/elements/ia-item-navigator/interfaces/event-interfaces.d.ts.map +1 -0
  26. package/dist/src/elements/ia-item-navigator/interfaces/event-interfaces.js +2 -0
  27. package/dist/src/elements/ia-item-navigator/interfaces/event-interfaces.js.map +1 -0
  28. package/dist/src/elements/ia-item-navigator/interfaces/menu-interfaces.d.ts +23 -0
  29. package/dist/src/elements/ia-item-navigator/interfaces/menu-interfaces.d.ts.map +1 -0
  30. package/dist/src/elements/ia-item-navigator/interfaces/menu-interfaces.js +2 -0
  31. package/dist/src/elements/ia-item-navigator/interfaces/menu-interfaces.js.map +1 -0
  32. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-share-panel.d.ts +47 -0
  33. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-share-panel.d.ts.map +1 -0
  34. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-share-panel.js +352 -0
  35. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-share-panel.js.map +1 -0
  36. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-sort-files-button.d.ts +22 -0
  37. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-sort-files-button.d.ts.map +1 -0
  38. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-sort-files-button.js +137 -0
  39. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-sort-files-button.js.map +1 -0
  40. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-viewable-files-panel.d.ts +40 -0
  41. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-viewable-files-panel.d.ts.map +1 -0
  42. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-viewable-files-panel.js +217 -0
  43. package/dist/src/elements/ia-item-navigator/menus/ia-itemnav-viewable-files-panel.js.map +1 -0
  44. package/dist/src/elements/ia-item-navigator/menus/icons/email.svg +6 -0
  45. package/dist/src/elements/ia-item-navigator/menus/icons/facebook.svg +6 -0
  46. package/dist/src/elements/ia-item-navigator/menus/icons/link.svg +6 -0
  47. package/dist/src/elements/ia-item-navigator/menus/icons/list.svg +3 -0
  48. package/dist/src/elements/ia-item-navigator/menus/icons/pinterest.svg +6 -0
  49. package/dist/src/elements/ia-item-navigator/menus/icons/share.svg +6 -0
  50. package/dist/src/elements/ia-item-navigator/menus/icons/sort-asc.svg +22 -0
  51. package/dist/src/elements/ia-item-navigator/menus/icons/sort-desc.svg +21 -0
  52. package/dist/src/elements/ia-item-navigator/menus/icons/sort-neutral.svg +13 -0
  53. package/dist/src/elements/ia-item-navigator/menus/icons/tumblr.svg +6 -0
  54. package/dist/src/elements/ia-item-navigator/menus/icons/twitter.svg +6 -0
  55. package/dist/src/elements/ia-item-navigator/menus/icons/viewable-files.svg +6 -0
  56. package/dist/src/elements/ia-item-navigator/menus/models.d.ts +17 -0
  57. package/dist/src/elements/ia-item-navigator/menus/models.d.ts.map +1 -0
  58. package/dist/src/elements/ia-item-navigator/menus/models.js +2 -0
  59. package/dist/src/elements/ia-item-navigator/menus/models.js.map +1 -0
  60. package/dist/src/elements/ia-item-navigator/menus/share-icons.d.ts +16 -0
  61. package/dist/src/elements/ia-item-navigator/menus/share-icons.d.ts.map +1 -0
  62. package/dist/src/elements/ia-item-navigator/menus/share-icons.js +23 -0
  63. package/dist/src/elements/ia-item-navigator/menus/share-icons.js.map +1 -0
  64. package/dist/src/elements/ia-playback-controls/ia-playback-controls.d.ts +12 -3
  65. package/dist/src/elements/ia-playback-controls/ia-playback-controls.d.ts.map +1 -1
  66. package/dist/src/elements/ia-playback-controls/ia-playback-controls.js +26 -9
  67. package/dist/src/elements/ia-playback-controls/ia-playback-controls.js.map +1 -1
  68. package/dist/src/elements/ia-status-indicator/ia-status-indicator.d.ts +51 -5
  69. package/dist/src/elements/ia-status-indicator/ia-status-indicator.d.ts.map +1 -1
  70. package/dist/src/elements/ia-status-indicator/ia-status-indicator.js +156 -16
  71. package/dist/src/elements/ia-status-indicator/ia-status-indicator.js.map +1 -1
  72. package/dist/src/elements/ia-status-indicator/icons/audio.svg +5 -0
  73. package/dist/src/elements/ia-status-indicator/icons/collection.svg +5 -0
  74. package/dist/src/elements/ia-status-indicator/icons/etree.svg +5 -0
  75. package/dist/src/elements/ia-status-indicator/icons/images.svg +6 -0
  76. package/dist/src/elements/ia-status-indicator/icons/search.svg +10 -0
  77. package/dist/src/elements/ia-status-indicator/icons/software.svg +6 -0
  78. package/dist/src/elements/ia-status-indicator/icons/texts.svg +6 -0
  79. package/dist/src/elements/ia-status-indicator/icons/tv.svg +5 -0
  80. package/dist/src/elements/ia-status-indicator/icons/video.svg +6 -0
  81. package/dist/src/elements/ia-status-indicator/icons/web.svg +6 -0
  82. package/dist/src/elements/ia-transcript-view/ia-transcript-view.d.ts +6 -0
  83. package/dist/src/elements/ia-transcript-view/ia-transcript-view.d.ts.map +1 -1
  84. package/dist/src/elements/ia-transcript-view/ia-transcript-view.js +20 -1
  85. package/dist/src/elements/ia-transcript-view/ia-transcript-view.js.map +1 -1
  86. package/dist/src/elements/index.d.ts +4 -0
  87. package/dist/src/elements/index.d.ts.map +1 -1
  88. package/dist/src/elements/index.js +4 -0
  89. package/dist/src/elements/index.js.map +1 -1
  90. package/dist/src/util/masked-icon.d.ts +24 -0
  91. package/dist/src/util/masked-icon.d.ts.map +1 -0
  92. package/dist/src/util/masked-icon.js +30 -0
  93. package/dist/src/util/masked-icon.js.map +1 -0
  94. package/package.json +1 -1
@@ -0,0 +1,588 @@
1
+ import { __decorate } from "tslib";
2
+ import { css, html, LitElement, nothing, } from 'lit';
3
+ import { customElement, property, state, query } from 'lit/decorators.js';
4
+ import { classMap } from 'lit/directives/class-map.js';
5
+ import themeStyles from '../../themes/theme-styles.js';
6
+ import { ellipsesIcon } from './icons.js';
7
+ import './ia-itemnav-menu-slider.js';
8
+ import './ia-itemnav-no-theater-available.js';
9
+ /**
10
+ * A fullscreen-capable frame that hosts an Archive.org item's theater. The
11
+ * consumer projects a header bar and the theater itself into the `header` and
12
+ * `main` slots; a collapsible left drawer (`ia-itemnav-menu-slider`) is driven by a
13
+ * data array of menu providers, and a minimized rail shows shortcut icons.
14
+ *
15
+ * The navigator is a shell — it does not know how to render any particular
16
+ * viewer. Menu panel bodies are opaque `TemplateResult`s supplied by the
17
+ * consumer via `menuContents`.
18
+ */
19
+ let IAItemNavigator = class IAItemNavigator extends LitElement {
20
+ constructor() {
21
+ super(...arguments);
22
+ this.viewAvailable = true;
23
+ this.baseHost = 'archive.org';
24
+ this.signedIn = false;
25
+ this.menuContents = [];
26
+ this.menuShortcuts = [];
27
+ this.viewportInFullscreen = null;
28
+ this.menuOpened = false;
29
+ /**
30
+ * Whether the slotted theater is ready. While false the reader stays
31
+ * hidden and the frame is empty.
32
+ *
33
+ * The navigator used to fill that gap with its own spinner, but a shell
34
+ * shouldn't decide what loading looks like — and its glyph and fullscreen
35
+ * caption were branding a consumer couldn't override. An indicator returns
36
+ * once WEBDEV-8951 moves the shared status indicators into elements, so the
37
+ * navigator can use that one instead of carrying its own.
38
+ */
39
+ this.loaded = false;
40
+ /**
41
+ * Set for the render where the drawer opens straight to a panel, so the
42
+ * panel holds still and rides in. Every other path clears it, rather
43
+ * than waiting for the drawer's `transitionend` — that never fires when
44
+ * animations are disabled, which would strand the flag on.
45
+ */
46
+ this.drawerEntering = false;
47
+ }
48
+ slotChange(e, type) {
49
+ const slottedContent = e.target.assignedNodes()?.[0];
50
+ this.dispatchEvent(new CustomEvent('slotChange', {
51
+ detail: { slot: slottedContent, type },
52
+ }));
53
+ this.requestUpdate();
54
+ }
55
+ render() {
56
+ const displayReaderClass = this.loaded ? '' : 'hidden';
57
+ return html `
58
+ <div id="frame" class=${this.menuClass}>
59
+ <slot
60
+ name="header"
61
+ @slotchange=${(e) => this.slotChange(e, 'header')}
62
+ ></slot>
63
+ <div class="menu-and-reader">
64
+ ${this.shouldRenderMenu ? this.renderSideMenu : nothing}
65
+ <div id="reader" class=${displayReaderClass}>
66
+ ${this.renderViewport}
67
+ </div>
68
+ </div>
69
+ </div>
70
+ `;
71
+ }
72
+ get noTheaterView() {
73
+ return html `<ia-itemnav-no-theater-available
74
+ .identifier=${this.identifier}
75
+ @loadingStateUpdated=${this.loadingStateUpdated}
76
+ ></ia-itemnav-no-theater-available>`;
77
+ }
78
+ get renderViewport() {
79
+ if (!this.viewAvailable) {
80
+ return this.noTheaterView;
81
+ }
82
+ const slotVisibility = !this.loaded ? 'opacity: 0;' : 'opacity: 1;';
83
+ return html `
84
+ <div slot="main" style=${slotVisibility}>
85
+ <slot
86
+ name="main"
87
+ @slotchange=${(e) => this.slotChange(e, 'main')}
88
+ ></slot>
89
+ </div>
90
+ `;
91
+ }
92
+ loadingStateUpdated(e) {
93
+ const { loaded } = e.detail;
94
+ this.loaded = loaded ?? false;
95
+ }
96
+ /** Fullscreen Management */
97
+ manageViewportFullscreen(e) {
98
+ const fullscreenStatus = !!e.detail.isFullScreen;
99
+ this.viewportInFullscreen = !fullscreenStatus ? null : fullscreenStatus;
100
+ const event = new CustomEvent('fullscreenToggled', {
101
+ detail: e.detail,
102
+ });
103
+ this.dispatchEvent(event);
104
+ }
105
+ /** End Fullscreen Management */
106
+ /** Side menu */
107
+ get shouldRenderMenu() {
108
+ return !!this.menuContents?.length;
109
+ }
110
+ toggleMenu(forceValue = undefined) {
111
+ this.drawerEntering = false;
112
+ this.menuOpened = forceValue !== undefined ? forceValue : !this.menuOpened;
113
+ this.moveFocusForDrawer();
114
+ }
115
+ /**
116
+ * Opening the drawer moves focus into it; closing hands focus back to the
117
+ * toggle. Without this, opening from the shortcut rail would leave focus on
118
+ * a button that is about to be hidden, dropping it to the document.
119
+ */
120
+ moveFocusForDrawer() {
121
+ this.updateComplete.then(() => {
122
+ if (this.menuOpened) {
123
+ this.menuSlider?.focusDrawer();
124
+ }
125
+ else {
126
+ this.toggleMenuButton?.focus();
127
+ }
128
+ });
129
+ }
130
+ /**
131
+ * Closing the drawer also closes whatever panel was open inside it, so the
132
+ * two can't disagree and a stale panel can't reappear on the next open.
133
+ */
134
+ closeMenu() {
135
+ this.openMenu = undefined;
136
+ this.toggleMenu(false);
137
+ }
138
+ setOpenMenu(e) {
139
+ this.drawerEntering = false;
140
+ const { id } = e.detail;
141
+ this.openMenu = id !== this.openMenu ? id : undefined;
142
+ }
143
+ /**
144
+ * Clears the open channel when the slider closes its sub-panel, without
145
+ * closing the drawer. Keeps `openMenu` in sync with the slider so the same
146
+ * channel can be reopened afterwards.
147
+ */
148
+ closeSidePanel() {
149
+ this.drawerEntering = false;
150
+ this.openMenu = undefined;
151
+ }
152
+ setMenuContents(e) {
153
+ const updatedContents = [...e.detail];
154
+ this.menuContents = updatedContents;
155
+ }
156
+ setMenuShortcuts(e) {
157
+ this.menuShortcuts = [...e.detail];
158
+ }
159
+ /** Toggles Side Menu & Sets viewable subpanel */
160
+ manageSideMenuEvents(e) {
161
+ const { menuId, action } = e.detail;
162
+ if (!menuId) {
163
+ return;
164
+ }
165
+ if (action === 'open') {
166
+ this.openShortcut(menuId);
167
+ }
168
+ else if (action === 'toggle') {
169
+ this.openMenu = menuId;
170
+ this.toggleMenu();
171
+ }
172
+ }
173
+ get menuToggleButton() {
174
+ const label = this.menuOpened ? 'Close side panel' : 'Open side panel';
175
+ return html `
176
+ <button
177
+ class="toggle-menu"
178
+ @click=${() => this.toggleMenu()}
179
+ title=${label}
180
+ aria-label=${label}
181
+ aria-expanded=${this.menuOpened}
182
+ aria-controls="menu"
183
+ >
184
+ ${ellipsesIcon}
185
+ </button>
186
+ `;
187
+ }
188
+ get selectedMenuId() {
189
+ return this.openMenu || '';
190
+ }
191
+ get renderSideMenu() {
192
+ return html `
193
+ <nav aria-label="Item navigation">
194
+ <div
195
+ class="minimized ${classMap({ hidden: this.menuOpened })}"
196
+ part="minimized-menu"
197
+ >
198
+ ${this.shortcuts} ${this.menuToggleButton}
199
+ </div>
200
+ <!-- Closed drawers are inert, so what is off-screen is also out of
201
+ the tab order and the accessibility tree. -->
202
+ <div
203
+ id="menu"
204
+ role="group"
205
+ aria-label="Item navigation menu"
206
+ ?inert=${!this.menuOpened}
207
+ >
208
+ <ia-itemnav-menu-slider
209
+ .menus=${this.menuContents}
210
+ .selectedMenu=${this.selectedMenuId}
211
+ @menuTypeSelected=${this.setOpenMenu}
212
+ @menuPanelClosed=${this.closeSidePanel}
213
+ @menuSliderClosed=${this.closeMenu}
214
+ ></ia-itemnav-menu-slider>
215
+ </div>
216
+ </nav>
217
+ `;
218
+ }
219
+ /** End Side menu */
220
+ /** Menu Shortcuts */
221
+ openShortcut(selectedMenuId = '') {
222
+ // Opening straight to a panel is one movement: the drawer carries the
223
+ // panel in, so the panel must not also slide (see `.drawer-entering`).
224
+ // Only when the drawer is actually closed — switching shortcuts while it
225
+ // is already open should still animate the panel across.
226
+ this.drawerEntering = !this.menuOpened;
227
+ this.openMenu = selectedMenuId;
228
+ this.menuOpened = true;
229
+ // The rail hides itself once the drawer opens, taking the focused
230
+ // shortcut with it, so focus has to be placed deliberately.
231
+ this.moveFocusForDrawer();
232
+ }
233
+ get shortcuts() {
234
+ const shortcuts = this.menuShortcuts.map(({ icon, id, label }) => {
235
+ if (id === 'fullscreen') {
236
+ return html `${icon}`;
237
+ }
238
+ return html `
239
+ <li>
240
+ <button
241
+ class="shortcut ${id}"
242
+ @click=${() => this.openShortcut(id)}
243
+ title=${label}
244
+ aria-label=${label}
245
+ aria-expanded=${this.menuOpened && this.openMenu === id}
246
+ >
247
+ ${icon}
248
+ </button>
249
+ </li>
250
+ `;
251
+ });
252
+ return html `<ul class="shortcuts" role="list">
253
+ ${shortcuts}
254
+ </ul>`;
255
+ }
256
+ /** End Menu Shortcuts */
257
+ /** Misc Render */
258
+ get menuClass() {
259
+ const hasMenuOrShortcuts = this.menuContents?.length || this.menuShortcuts?.length;
260
+ const drawerState = this.menuOpened && hasMenuOrShortcuts ? 'open' : '';
261
+ const fullscreenState = this.viewportInFullscreen ? 'fullscreen' : '';
262
+ // When the side menu renders, the minimized rail floats over the left edge;
263
+ // `has-menu` lets the reader reserve its width so the theater isn't covered.
264
+ const railState = this.shouldRenderMenu ? 'has-menu' : '';
265
+ const enteringState = this.drawerEntering ? 'drawer-entering' : '';
266
+ return `${drawerState} ${fullscreenState} ${railState} ${enteringState}`;
267
+ }
268
+ static get styles() {
269
+ const subnavWidth = css `var(--item-navigator-menu-width--)`;
270
+ const transitionTiming = css `var(--item-navigator-animation-timing--)`;
271
+ const transitionEffect = css `transform ${transitionTiming} ease-out`;
272
+ const menuMargin = css `var(--item-navigator-menu-margin--)`;
273
+ const theaterBg = css `var(--item-navigator-theater-bg-color--)`;
274
+ const iconSize = css `var(--item-navigator-icon-size--)`;
275
+ return [
276
+ themeStyles,
277
+ css `
278
+ :host {
279
+ --item-navigator-menu-width--: var(
280
+ --item-navigator-menu-width,
281
+ 320px
282
+ );
283
+ --item-navigator-animation-timing--: var(
284
+ --item-navigator-animation-timing,
285
+ 200ms
286
+ );
287
+ --item-navigator-menu-margin--: var(
288
+ --item-navigator-menu-margin,
289
+ 42px
290
+ );
291
+ --item-navigator-theater-bg-color--: var(
292
+ --item-navigator-theater-bg-color,
293
+ #000
294
+ );
295
+ /* Every glyph is square, so one knob sizes both axes. */
296
+ --item-navigator-icon-size--: var(--item-navigator-icon-size, 2.4em);
297
+ /* Icons follow the adjustable text color by default. */
298
+ --item-navigator-icon-color--: var(
299
+ --item-navigator-icon-color,
300
+ var(--item-navigator-text-color, var(--true-white))
301
+ );
302
+
303
+ /*
304
+ * The component's internal sizing is expressed in em against this
305
+ * base (10px matches petabox's base font size, which the upstream
306
+ * demo set on the document root). Anchoring it here makes the
307
+ * navigator self-contained — its scale no longer depends on the
308
+ * consumer's root font-size. Override to rescale everything.
309
+ */
310
+ --item-navigator-base-font-size--: var(
311
+ --item-navigator-base-font-size,
312
+ 10px
313
+ );
314
+ font-size: var(--item-navigator-base-font-size--);
315
+ }
316
+
317
+ :host,
318
+ #frame,
319
+ .menu-and-reader {
320
+ position: relative;
321
+ overflow: hidden;
322
+ display: block;
323
+ }
324
+
325
+ :host,
326
+ #frame {
327
+ min-height: inherit;
328
+ height: inherit;
329
+ }
330
+
331
+ slot {
332
+ display: block;
333
+ width: 100%;
334
+ }
335
+
336
+ slot * {
337
+ display: block;
338
+ height: inherit;
339
+ }
340
+
341
+ #frame {
342
+ background-color: ${theaterBg};
343
+ color-scheme: dark;
344
+ display: flex;
345
+ flex-direction: column;
346
+ /*
347
+ * The overlay/shift breakpoint keys off the navigator's own width,
348
+ * not the viewport's, so the frame is the query container. This
349
+ * replaces a host-injected resize observer that set the class from
350
+ * JS: layout drives it now, so there is nothing to inject, register
351
+ * or tear down. The inline-size type contains width only, leaving the
352
+ * flex column's height behaviour alone.
353
+ */
354
+ container-type: inline-size;
355
+ container-name: navframe;
356
+ }
357
+
358
+ #frame.fullscreen {
359
+ position: fixed;
360
+ top: 0;
361
+ bottom: 0;
362
+ left: 0;
363
+ right: 0;
364
+ z-index: 9;
365
+ /*
366
+ * Override the inherited height/min-height from the base #frame rule:
367
+ * on a fixed element an explicit height wins over top/bottom, so the
368
+ * inset (0 on all sides) can't fill the viewport unless height is
369
+ * released back to auto.
370
+ */
371
+ height: auto;
372
+ min-height: 0;
373
+ }
374
+
375
+ .hidden {
376
+ display: none !important;
377
+ }
378
+
379
+ button {
380
+ /* Buttons don't inherit font-size from the UA stylesheet; inherit it
381
+ so em-sized icons resolve against the component's base, not the
382
+ browser's default button font-size. */
383
+ font: inherit;
384
+ cursor: pointer;
385
+ padding: 0;
386
+ border: 0;
387
+ }
388
+
389
+ .menu-and-reader {
390
+ position: relative;
391
+ display: flex;
392
+ flex: 1;
393
+ }
394
+
395
+ nav button {
396
+ background: none;
397
+ }
398
+
399
+ nav .minimized {
400
+ background: rgba(0, 0, 0, 0.7);
401
+ padding-top: 6px;
402
+ position: absolute;
403
+ width: ${menuMargin};
404
+ z-index: 2;
405
+ left: 0;
406
+ border-bottom-right-radius: 5%;
407
+ }
408
+
409
+ nav .minimized button {
410
+ margin-bottom: 0.2em;
411
+ margin: auto;
412
+ display: inline-flex;
413
+ vertical-align: middle;
414
+ align-items: center;
415
+ justify-content: center;
416
+ width: ${menuMargin};
417
+ height: ${menuMargin};
418
+ }
419
+
420
+ nav .minimized button > * {
421
+ /** Prevent the icon's SVG description from stealing tooltip message */
422
+ pointer-events: none;
423
+ }
424
+
425
+ nav .minimized button.toggle-menu > * {
426
+ border: 2px solid var(--item-navigator-icon-color--);
427
+ border-radius: ${iconSize};
428
+ width: ${iconSize};
429
+ height: ${iconSize};
430
+ margin: auto;
431
+ }
432
+
433
+ /* The rail is a list for assistive tech; strip the list chrome so it
434
+ still reads as a row of icons. */
435
+ .shortcuts,
436
+ .shortcuts li {
437
+ list-style: none;
438
+ padding: 0;
439
+ margin: 0;
440
+ }
441
+
442
+ .toggle-menu .ia-icon,
443
+ .shortcuts .ia-icon {
444
+ width: ${iconSize};
445
+ height: ${iconSize};
446
+ }
447
+
448
+ /* Our glyphs are masked spans: the mask supplies the shape, this
449
+ supplies the paint. */
450
+ span.ia-icon {
451
+ background-color: var(--item-navigator-icon-color--);
452
+ }
453
+
454
+ /* Host-supplied icons may still be inline svg, so keep theming those
455
+ the original way. */
456
+ .ia-icon .fill-color {
457
+ fill: var(--item-navigator-icon-color--);
458
+ }
459
+
460
+ #menu {
461
+ position: absolute;
462
+ top: 0;
463
+ bottom: 0;
464
+ left: 0;
465
+ z-index: 3;
466
+ overflow: hidden;
467
+ width: ${subnavWidth};
468
+ transform: translateX(calc(${subnavWidth} * -1));
469
+ transition: ${transitionEffect};
470
+ }
471
+
472
+ #reader {
473
+ position: relative;
474
+ z-index: 1;
475
+ transform: translateX(0);
476
+ width: 100%;
477
+ display: flex;
478
+ /*
479
+ * Ease the reader's size/position changes so the slotted theater
480
+ * glides in sync with the sliding drawer (shift mode) and settles
481
+ * smoothly on resize, rather than snapping. Overlay mode opts out
482
+ * below so the full-width theater tracks resizes instantly.
483
+ */
484
+ transition:
485
+ width ${transitionTiming} ease-out,
486
+ margin-left ${transitionTiming} ease-out,
487
+ transform ${transitionTiming} ease-out;
488
+ }
489
+
490
+ #reader > * {
491
+ width: 100%;
492
+ display: flex;
493
+ flex: 1;
494
+ }
495
+
496
+ /*
497
+ * The minimized rail floats over the frame's left edge while the drawer
498
+ * is closed, so pad the theater content by its width to avoid overlap.
499
+ * This lives on the reader's content (not the reader box) and isn't
500
+ * transitioned, so it snaps away on open — letting the reader box track
501
+ * the drawer's edge exactly during the shift, rather than trailing it.
502
+ */
503
+ .has-menu:not(.open) #reader > * {
504
+ box-sizing: border-box;
505
+ padding-left: ${menuMargin};
506
+ }
507
+
508
+ /* Opening straight to a panel is one movement. The panel is nested in
509
+ #menu, so its own slide would compose with the drawer's transform
510
+ and send it twice the distance in the same time — arriving late and
511
+ travelling at double speed. Holding it still lets the drawer carry
512
+ it in. */
513
+ .drawer-entering #menu {
514
+ --item-navigator-panel-transition--: none;
515
+ }
516
+
517
+ .open #menu {
518
+ width: ${subnavWidth};
519
+ transform: translateX(0);
520
+ transition: ${transitionEffect};
521
+ }
522
+
523
+ /* Shift: the drawer pushes the theater aside. */
524
+ .open #reader {
525
+ width: calc(100% - ${subnavWidth});
526
+ margin-left: ${subnavWidth};
527
+ }
528
+
529
+ /*
530
+ * Overlay: too narrow to give the drawer its own column, so it covers
531
+ * a full-width theater. The transition is dropped here so the theater
532
+ * tracks resizes instantly instead of easing behind them.
533
+ */
534
+ @container navframe (max-width: 600px) {
535
+ .open #reader {
536
+ width: 100%;
537
+ margin-left: 0;
538
+ transition: none;
539
+ }
540
+ }
541
+ `,
542
+ ];
543
+ }
544
+ };
545
+ __decorate([
546
+ property({ type: String })
547
+ ], IAItemNavigator.prototype, "identifier", void 0);
548
+ __decorate([
549
+ property({ type: Boolean, reflect: true })
550
+ ], IAItemNavigator.prototype, "viewAvailable", void 0);
551
+ __decorate([
552
+ property({ type: String })
553
+ ], IAItemNavigator.prototype, "baseHost", void 0);
554
+ __decorate([
555
+ property({ type: Boolean })
556
+ ], IAItemNavigator.prototype, "signedIn", void 0);
557
+ __decorate([
558
+ property({ type: Array })
559
+ ], IAItemNavigator.prototype, "menuContents", void 0);
560
+ __decorate([
561
+ property({ type: Array })
562
+ ], IAItemNavigator.prototype, "menuShortcuts", void 0);
563
+ __decorate([
564
+ property({ type: Boolean, reflect: true, attribute: true })
565
+ ], IAItemNavigator.prototype, "viewportInFullscreen", void 0);
566
+ __decorate([
567
+ property({ type: Boolean, reflect: true })
568
+ ], IAItemNavigator.prototype, "menuOpened", void 0);
569
+ __decorate([
570
+ property({ type: String, reflect: true })
571
+ ], IAItemNavigator.prototype, "openMenu", void 0);
572
+ __decorate([
573
+ property({ type: Boolean, reflect: true, attribute: true })
574
+ ], IAItemNavigator.prototype, "loaded", void 0);
575
+ __decorate([
576
+ state()
577
+ ], IAItemNavigator.prototype, "drawerEntering", void 0);
578
+ __decorate([
579
+ query('ia-itemnav-menu-slider')
580
+ ], IAItemNavigator.prototype, "menuSlider", void 0);
581
+ __decorate([
582
+ query('button.toggle-menu')
583
+ ], IAItemNavigator.prototype, "toggleMenuButton", void 0);
584
+ IAItemNavigator = __decorate([
585
+ customElement('ia-item-navigator')
586
+ ], IAItemNavigator);
587
+ export { IAItemNavigator };
588
+ //# sourceMappingURL=ia-item-navigator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ia-item-navigator.js","sourceRoot":"","sources":["../../../../src/elements/ia-item-navigator/ia-item-navigator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n css,\n html,\n LitElement,\n TemplateResult,\n nothing,\n type CSSResultGroup,\n} from 'lit';\nimport { customElement, property, state, query } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\nimport themeStyles from '@src/themes/theme-styles';\n\nimport { ellipsesIcon } from './icons';\nimport './ia-itemnav-menu-slider';\nimport './ia-itemnav-no-theater-available';\nimport type { IAItemNavMenuSlider } from './ia-itemnav-menu-slider';\n\nimport {\n ToggleSideMenuOpenEvent,\n ToggleSidePanelOpenEvent,\n SetSideMenuContentsEvent,\n SetSideMenuShortcutsEvent,\n LoadingStateUpdatedEvent,\n ManageFullscreenEvent,\n} from './interfaces/event-interfaces';\nimport {\n MenuProviderInterface,\n MenuShortcutInterface,\n MenuId,\n} from './interfaces/menu-interfaces';\n\n/**\n * A fullscreen-capable frame that hosts an Archive.org item's theater. The\n * consumer projects a header bar and the theater itself into the `header` and\n * `main` slots; a collapsible left drawer (`ia-itemnav-menu-slider`) is driven by a\n * data array of menu providers, and a minimized rail shows shortcut icons.\n *\n * The navigator is a shell — it does not know how to render any particular\n * viewer. Menu panel bodies are opaque `TemplateResult`s supplied by the\n * consumer via `menuContents`.\n */\n@customElement('ia-item-navigator')\nexport class IAItemNavigator extends LitElement {\n /** archive.org item identifier. */\n @property({ type: String }) identifier?: string;\n\n @property({ type: Boolean, reflect: true }) viewAvailable: boolean = true;\n\n @property({ type: String }) baseHost = 'archive.org';\n\n @property({ type: Boolean }) signedIn = false;\n\n @property({ type: Array }) menuContents: MenuProviderInterface[] = [];\n\n @property({ type: Array }) menuShortcuts: MenuShortcutInterface[] = [];\n\n @property({ type: Boolean, reflect: true, attribute: true })\n viewportInFullscreen: boolean | null = null;\n\n @property({ type: Boolean, reflect: true }) menuOpened = false;\n\n @property({ type: String, reflect: true }) openMenu?: MenuId;\n\n /**\n * Whether the slotted theater is ready. While false the reader stays\n * hidden and the frame is empty.\n *\n * The navigator used to fill that gap with its own spinner, but a shell\n * shouldn't decide what loading looks like — and its glyph and fullscreen\n * caption were branding a consumer couldn't override. An indicator returns\n * once WEBDEV-8951 moves the shared status indicators into elements, so the\n * navigator can use that one instead of carrying its own.\n */\n @property({ type: Boolean, reflect: true, attribute: true }) loaded: boolean =\n false;\n\n /**\n * Set for the render where the drawer opens straight to a panel, so the\n * panel holds still and rides in. Every other path clears it, rather\n * than waiting for the drawer's `transitionend` — that never fires when\n * animations are disabled, which would strand the flag on.\n */\n @state() private drawerEntering = false;\n\n @query('ia-itemnav-menu-slider') private menuSlider!: IAItemNavMenuSlider;\n\n @query('button.toggle-menu') private toggleMenuButton!: HTMLButtonElement;\n\n slotChange(e: Event, type: 'header' | 'main'): void {\n const slottedContent = (\n e.target as HTMLSlotElement\n ).assignedNodes()?.[0] as HTMLElement;\n\n this.dispatchEvent(\n new CustomEvent('slotChange', {\n detail: { slot: slottedContent, type },\n }),\n );\n this.requestUpdate();\n }\n\n render(): TemplateResult {\n const displayReaderClass = this.loaded ? '' : 'hidden';\n return html`\n <div id=\"frame\" class=${this.menuClass}>\n <slot\n name=\"header\"\n @slotchange=${(e: Event) => this.slotChange(e, 'header')}\n ></slot>\n <div class=\"menu-and-reader\">\n ${this.shouldRenderMenu ? this.renderSideMenu : nothing}\n <div id=\"reader\" class=${displayReaderClass}>\n ${this.renderViewport}\n </div>\n </div>\n </div>\n `;\n }\n\n get noTheaterView(): TemplateResult {\n return html`<ia-itemnav-no-theater-available\n .identifier=${this.identifier}\n @loadingStateUpdated=${this.loadingStateUpdated}\n ></ia-itemnav-no-theater-available>`;\n }\n\n get renderViewport(): TemplateResult | typeof nothing {\n if (!this.viewAvailable) {\n return this.noTheaterView;\n }\n\n const slotVisibility = !this.loaded ? 'opacity: 0;' : 'opacity: 1;';\n return html`\n <div slot=\"main\" style=${slotVisibility}>\n <slot\n name=\"main\"\n @slotchange=${(e: Event) => this.slotChange(e, 'main')}\n ></slot>\n </div>\n `;\n }\n\n loadingStateUpdated(e: LoadingStateUpdatedEvent): void {\n const { loaded } = e.detail;\n this.loaded = loaded ?? false;\n }\n\n /** Fullscreen Management */\n manageViewportFullscreen(e: ManageFullscreenEvent): void {\n const fullscreenStatus = !!e.detail.isFullScreen;\n this.viewportInFullscreen = !fullscreenStatus ? null : fullscreenStatus;\n\n const event = new CustomEvent('fullscreenToggled', {\n detail: e.detail,\n }) as ManageFullscreenEvent;\n\n this.dispatchEvent(event);\n }\n /** End Fullscreen Management */\n\n /** Side menu */\n get shouldRenderMenu(): boolean {\n return !!this.menuContents?.length;\n }\n\n toggleMenu(forceValue: boolean | undefined = undefined): void {\n this.drawerEntering = false;\n this.menuOpened = forceValue !== undefined ? forceValue : !this.menuOpened;\n this.moveFocusForDrawer();\n }\n\n /**\n * Opening the drawer moves focus into it; closing hands focus back to the\n * toggle. Without this, opening from the shortcut rail would leave focus on\n * a button that is about to be hidden, dropping it to the document.\n */\n private moveFocusForDrawer(): void {\n this.updateComplete.then(() => {\n if (this.menuOpened) {\n this.menuSlider?.focusDrawer();\n } else {\n this.toggleMenuButton?.focus();\n }\n });\n }\n\n /**\n * Closing the drawer also closes whatever panel was open inside it, so the\n * two can't disagree and a stale panel can't reappear on the next open.\n */\n closeMenu(): void {\n this.openMenu = undefined;\n this.toggleMenu(false);\n }\n\n setOpenMenu(e: ToggleSidePanelOpenEvent): void {\n this.drawerEntering = false;\n const { id } = e.detail;\n this.openMenu = id !== this.openMenu ? id : undefined;\n }\n\n /**\n * Clears the open channel when the slider closes its sub-panel, without\n * closing the drawer. Keeps `openMenu` in sync with the slider so the same\n * channel can be reopened afterwards.\n */\n closeSidePanel(): void {\n this.drawerEntering = false;\n this.openMenu = undefined;\n }\n\n setMenuContents(e: SetSideMenuContentsEvent): void {\n const updatedContents = [...e.detail];\n this.menuContents = updatedContents;\n }\n\n setMenuShortcuts(e: SetSideMenuShortcutsEvent): void {\n this.menuShortcuts = [...e.detail];\n }\n\n /** Toggles Side Menu & Sets viewable subpanel */\n manageSideMenuEvents(e: ToggleSideMenuOpenEvent): void {\n const { menuId, action } = e.detail;\n if (!menuId) {\n return;\n }\n\n if (action === 'open') {\n this.openShortcut(menuId);\n } else if (action === 'toggle') {\n this.openMenu = menuId;\n this.toggleMenu();\n }\n }\n\n get menuToggleButton(): TemplateResult {\n const label = this.menuOpened ? 'Close side panel' : 'Open side panel';\n return html`\n <button\n class=\"toggle-menu\"\n @click=${() => this.toggleMenu()}\n title=${label}\n aria-label=${label}\n aria-expanded=${this.menuOpened}\n aria-controls=\"menu\"\n >\n ${ellipsesIcon}\n </button>\n `;\n }\n\n get selectedMenuId(): MenuId | '' {\n return this.openMenu || '';\n }\n\n get renderSideMenu(): TemplateResult {\n return html`\n <nav aria-label=\"Item navigation\">\n <div\n class=\"minimized ${classMap({ hidden: this.menuOpened })}\"\n part=\"minimized-menu\"\n >\n ${this.shortcuts} ${this.menuToggleButton}\n </div>\n <!-- Closed drawers are inert, so what is off-screen is also out of\n the tab order and the accessibility tree. -->\n <div\n id=\"menu\"\n role=\"group\"\n aria-label=\"Item navigation menu\"\n ?inert=${!this.menuOpened}\n >\n <ia-itemnav-menu-slider\n .menus=${this.menuContents}\n .selectedMenu=${this.selectedMenuId}\n @menuTypeSelected=${this.setOpenMenu}\n @menuPanelClosed=${this.closeSidePanel}\n @menuSliderClosed=${this.closeMenu}\n ></ia-itemnav-menu-slider>\n </div>\n </nav>\n `;\n }\n /** End Side menu */\n\n /** Menu Shortcuts */\n openShortcut(selectedMenuId: MenuId = ''): void {\n // Opening straight to a panel is one movement: the drawer carries the\n // panel in, so the panel must not also slide (see `.drawer-entering`).\n // Only when the drawer is actually closed — switching shortcuts while it\n // is already open should still animate the panel across.\n this.drawerEntering = !this.menuOpened;\n this.openMenu = selectedMenuId;\n this.menuOpened = true;\n // The rail hides itself once the drawer opens, taking the focused\n // shortcut with it, so focus has to be placed deliberately.\n this.moveFocusForDrawer();\n }\n\n get shortcuts(): TemplateResult {\n const shortcuts = this.menuShortcuts.map(({ icon, id, label }) => {\n if (id === 'fullscreen') {\n return html`${icon}`;\n }\n\n return html`\n <li>\n <button\n class=\"shortcut ${id}\"\n @click=${() => this.openShortcut(id)}\n title=${label}\n aria-label=${label}\n aria-expanded=${this.menuOpened && this.openMenu === id}\n >\n ${icon}\n </button>\n </li>\n `;\n });\n return html`<ul class=\"shortcuts\" role=\"list\">\n ${shortcuts}\n </ul>`;\n }\n /** End Menu Shortcuts */\n\n /** Misc Render */\n get menuClass(): string {\n const hasMenuOrShortcuts =\n this.menuContents?.length || this.menuShortcuts?.length;\n const drawerState = this.menuOpened && hasMenuOrShortcuts ? 'open' : '';\n const fullscreenState = this.viewportInFullscreen ? 'fullscreen' : '';\n // When the side menu renders, the minimized rail floats over the left edge;\n // `has-menu` lets the reader reserve its width so the theater isn't covered.\n const railState = this.shouldRenderMenu ? 'has-menu' : '';\n const enteringState = this.drawerEntering ? 'drawer-entering' : '';\n return `${drawerState} ${fullscreenState} ${railState} ${enteringState}`;\n }\n\n static get styles(): CSSResultGroup {\n const subnavWidth = css`var(--item-navigator-menu-width--)`;\n const transitionTiming = css`var(--item-navigator-animation-timing--)`;\n const transitionEffect = css`transform ${transitionTiming} ease-out`;\n const menuMargin = css`var(--item-navigator-menu-margin--)`;\n const theaterBg = css`var(--item-navigator-theater-bg-color--)`;\n const iconSize = css`var(--item-navigator-icon-size--)`;\n\n return [\n themeStyles,\n css`\n :host {\n --item-navigator-menu-width--: var(\n --item-navigator-menu-width,\n 320px\n );\n --item-navigator-animation-timing--: var(\n --item-navigator-animation-timing,\n 200ms\n );\n --item-navigator-menu-margin--: var(\n --item-navigator-menu-margin,\n 42px\n );\n --item-navigator-theater-bg-color--: var(\n --item-navigator-theater-bg-color,\n #000\n );\n /* Every glyph is square, so one knob sizes both axes. */\n --item-navigator-icon-size--: var(--item-navigator-icon-size, 2.4em);\n /* Icons follow the adjustable text color by default. */\n --item-navigator-icon-color--: var(\n --item-navigator-icon-color,\n var(--item-navigator-text-color, var(--true-white))\n );\n\n /*\n * The component's internal sizing is expressed in em against this\n * base (10px matches petabox's base font size, which the upstream\n * demo set on the document root). Anchoring it here makes the\n * navigator self-contained — its scale no longer depends on the\n * consumer's root font-size. Override to rescale everything.\n */\n --item-navigator-base-font-size--: var(\n --item-navigator-base-font-size,\n 10px\n );\n font-size: var(--item-navigator-base-font-size--);\n }\n\n :host,\n #frame,\n .menu-and-reader {\n position: relative;\n overflow: hidden;\n display: block;\n }\n\n :host,\n #frame {\n min-height: inherit;\n height: inherit;\n }\n\n slot {\n display: block;\n width: 100%;\n }\n\n slot * {\n display: block;\n height: inherit;\n }\n\n #frame {\n background-color: ${theaterBg};\n color-scheme: dark;\n display: flex;\n flex-direction: column;\n /*\n * The overlay/shift breakpoint keys off the navigator's own width,\n * not the viewport's, so the frame is the query container. This\n * replaces a host-injected resize observer that set the class from\n * JS: layout drives it now, so there is nothing to inject, register\n * or tear down. The inline-size type contains width only, leaving the\n * flex column's height behaviour alone.\n */\n container-type: inline-size;\n container-name: navframe;\n }\n\n #frame.fullscreen {\n position: fixed;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 9;\n /*\n * Override the inherited height/min-height from the base #frame rule:\n * on a fixed element an explicit height wins over top/bottom, so the\n * inset (0 on all sides) can't fill the viewport unless height is\n * released back to auto.\n */\n height: auto;\n min-height: 0;\n }\n\n .hidden {\n display: none !important;\n }\n\n button {\n /* Buttons don't inherit font-size from the UA stylesheet; inherit it\n so em-sized icons resolve against the component's base, not the\n browser's default button font-size. */\n font: inherit;\n cursor: pointer;\n padding: 0;\n border: 0;\n }\n\n .menu-and-reader {\n position: relative;\n display: flex;\n flex: 1;\n }\n\n nav button {\n background: none;\n }\n\n nav .minimized {\n background: rgba(0, 0, 0, 0.7);\n padding-top: 6px;\n position: absolute;\n width: ${menuMargin};\n z-index: 2;\n left: 0;\n border-bottom-right-radius: 5%;\n }\n\n nav .minimized button {\n margin-bottom: 0.2em;\n margin: auto;\n display: inline-flex;\n vertical-align: middle;\n align-items: center;\n justify-content: center;\n width: ${menuMargin};\n height: ${menuMargin};\n }\n\n nav .minimized button > * {\n /** Prevent the icon's SVG description from stealing tooltip message */\n pointer-events: none;\n }\n\n nav .minimized button.toggle-menu > * {\n border: 2px solid var(--item-navigator-icon-color--);\n border-radius: ${iconSize};\n width: ${iconSize};\n height: ${iconSize};\n margin: auto;\n }\n\n /* The rail is a list for assistive tech; strip the list chrome so it\n still reads as a row of icons. */\n .shortcuts,\n .shortcuts li {\n list-style: none;\n padding: 0;\n margin: 0;\n }\n\n .toggle-menu .ia-icon,\n .shortcuts .ia-icon {\n width: ${iconSize};\n height: ${iconSize};\n }\n\n /* Our glyphs are masked spans: the mask supplies the shape, this\n supplies the paint. */\n span.ia-icon {\n background-color: var(--item-navigator-icon-color--);\n }\n\n /* Host-supplied icons may still be inline svg, so keep theming those\n the original way. */\n .ia-icon .fill-color {\n fill: var(--item-navigator-icon-color--);\n }\n\n #menu {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n z-index: 3;\n overflow: hidden;\n width: ${subnavWidth};\n transform: translateX(calc(${subnavWidth} * -1));\n transition: ${transitionEffect};\n }\n\n #reader {\n position: relative;\n z-index: 1;\n transform: translateX(0);\n width: 100%;\n display: flex;\n /*\n * Ease the reader's size/position changes so the slotted theater\n * glides in sync with the sliding drawer (shift mode) and settles\n * smoothly on resize, rather than snapping. Overlay mode opts out\n * below so the full-width theater tracks resizes instantly.\n */\n transition:\n width ${transitionTiming} ease-out,\n margin-left ${transitionTiming} ease-out,\n transform ${transitionTiming} ease-out;\n }\n\n #reader > * {\n width: 100%;\n display: flex;\n flex: 1;\n }\n\n /*\n * The minimized rail floats over the frame's left edge while the drawer\n * is closed, so pad the theater content by its width to avoid overlap.\n * This lives on the reader's content (not the reader box) and isn't\n * transitioned, so it snaps away on open — letting the reader box track\n * the drawer's edge exactly during the shift, rather than trailing it.\n */\n .has-menu:not(.open) #reader > * {\n box-sizing: border-box;\n padding-left: ${menuMargin};\n }\n\n /* Opening straight to a panel is one movement. The panel is nested in\n #menu, so its own slide would compose with the drawer's transform\n and send it twice the distance in the same time — arriving late and\n travelling at double speed. Holding it still lets the drawer carry\n it in. */\n .drawer-entering #menu {\n --item-navigator-panel-transition--: none;\n }\n\n .open #menu {\n width: ${subnavWidth};\n transform: translateX(0);\n transition: ${transitionEffect};\n }\n\n /* Shift: the drawer pushes the theater aside. */\n .open #reader {\n width: calc(100% - ${subnavWidth});\n margin-left: ${subnavWidth};\n }\n\n /*\n * Overlay: too narrow to give the drawer its own column, so it covers\n * a full-width theater. The transition is dropped here so the theater\n * tracks resizes instantly instead of easing behind them.\n */\n @container navframe (max-width: 600px) {\n .open #reader {\n width: 100%;\n margin-left: 0;\n transition: none;\n }\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ia-item-navigator': IAItemNavigator;\n }\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import { LitElement, TemplateResult, type CSSResultGroup } from 'lit';
2
+ /**
3
+ * A single entry in the item navigator's side menu. Renders either a button
4
+ * (default) or a link (when `href` is set) with an icon, label and optional
5
+ * detail text, and emits `menuTypeSelected` when activated.
6
+ */
7
+ export declare class IAItemNavMenuButton extends LitElement {
8
+ static shadowRootOptions: {
9
+ delegatesFocus: boolean;
10
+ clonable?: boolean;
11
+ customElementRegistry?: CustomElementRegistry;
12
+ mode: ShadowRootMode;
13
+ serializable?: boolean;
14
+ slotAssignment?: SlotAssignmentMode;
15
+ customElements?: CustomElementRegistry;
16
+ registry?: CustomElementRegistry;
17
+ };
18
+ icon: TemplateResult | string;
19
+ href: string;
20
+ label: string;
21
+ menuDetails: TemplateResult | string;
22
+ buttonId: string;
23
+ selected: boolean;
24
+ followable: boolean;
25
+ onClick(e: Event): void;
26
+ dispatchMenuTypeSelectedEvent(): void;
27
+ get iconClass(): string;
28
+ get menuItem(): TemplateResult;
29
+ get linkButton(): TemplateResult;
30
+ get clickButton(): TemplateResult;
31
+ render(): TemplateResult;
32
+ static get styles(): CSSResultGroup;
33
+ }
34
+ declare global {
35
+ interface HTMLElementTagNameMap {
36
+ 'ia-itemnav-menu-button': IAItemNavMenuButton;
37
+ }
38
+ }
39
+ //# sourceMappingURL=ia-itemnav-menu-button.d.ts.map