ooxml-ui 0.20.0 → 0.22.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 (114) hide show
  1. package/README.md +47 -2
  2. package/dist/base.d.ts +118 -0
  3. package/dist/{account.d.ts → chrome/account-profile.d.ts} +0 -6
  4. package/dist/chrome/account.d.ts +17 -0
  5. package/dist/chrome/backstage.d.ts +78 -0
  6. package/dist/chrome/find-bar.d.ts +111 -0
  7. package/dist/chrome/print-preview.d.ts +39 -0
  8. package/dist/chrome/ruler.d.ts +43 -0
  9. package/dist/chrome/status-bar.d.ts +80 -0
  10. package/dist/chrome/status-item.d.ts +40 -0
  11. package/dist/chrome/tab-strip.d.ts +88 -0
  12. package/dist/chrome/title-bar-types.d.ts +75 -0
  13. package/dist/chrome/title-bar.d.ts +81 -0
  14. package/dist/{chunk-7XBKSYGM.js → chunk-2X6QYQ3Q.js} +10 -1
  15. package/dist/{chunk-4JQLQROF.js → chunk-3MDKXKKQ.js} +53 -65
  16. package/dist/chunk-5A4REH6T.js +259 -0
  17. package/dist/chunk-E47762NY.js +90 -0
  18. package/dist/chunk-HNGJI5EZ.js +834 -0
  19. package/dist/{chunk-G3LAGZEW.js → chunk-HSJH6SUW.js} +33 -1
  20. package/dist/{chunk-W5PYFBEZ.js → chunk-HZ422LTR.js} +1 -49
  21. package/dist/chunk-LKLZOPTO.js +4536 -0
  22. package/dist/controls.d.ts +56 -46
  23. package/dist/controls.js +5 -5
  24. package/dist/dialog/dialog-footer.d.ts +40 -0
  25. package/dist/dialog/dialog.d.ts +52 -0
  26. package/dist/{options.d.ts → dialog/options-dialog.d.ts} +46 -1
  27. package/dist/form/checkable.d.ts +49 -0
  28. package/dist/form/checkbox.d.ts +8 -0
  29. package/dist/form/radio.d.ts +68 -0
  30. package/dist/form/search-field.d.ts +54 -0
  31. package/dist/{select-menu.d.ts → form/select-menu.d.ts} +3 -13
  32. package/dist/form/select.d.ts +115 -0
  33. package/dist/form/switch.d.ts +11 -0
  34. package/dist/form/zoom-slider.d.ts +78 -0
  35. package/dist/glyph.d.ts +2 -0
  36. package/dist/icon.d.ts +19 -0
  37. package/dist/icons.d.ts +0 -3
  38. package/dist/icons.js +1 -3
  39. package/dist/index.d.ts +4 -1
  40. package/dist/index.js +16 -12
  41. package/dist/menu/command-search.d.ts +74 -0
  42. package/dist/menu/context-menu.d.ts +89 -0
  43. package/dist/menu/menu-button.d.ts +77 -0
  44. package/dist/menu/menu-item.d.ts +52 -0
  45. package/dist/{menu-model.d.ts → menu/menu-model.d.ts} +2 -0
  46. package/dist/menu/menu-separator.d.ts +7 -0
  47. package/dist/notices/paste-options.d.ts +41 -0
  48. package/dist/notices/read-only-banner.d.ts +53 -0
  49. package/dist/notices/toasts.d.ts +40 -0
  50. package/dist/presence/presence.d.ts +53 -0
  51. package/dist/presence.d.ts +1 -31
  52. package/dist/presence.js +3 -3
  53. package/dist/ribbon/button.d.ts +60 -0
  54. package/dist/ribbon/gallery-styles.d.ts +2 -0
  55. package/dist/ribbon/gallery.d.ts +97 -0
  56. package/dist/ribbon/ribbon-group.d.ts +35 -0
  57. package/dist/{ribbon-section.d.ts → ribbon/ribbon-section.d.ts} +14 -1
  58. package/dist/ribbon/ribbon-stack.d.ts +10 -0
  59. package/dist/ribbon/ribbon-tabs.d.ts +82 -0
  60. package/dist/ribbon/ribbon-toggle.d.ts +49 -0
  61. package/dist/ribbon/ribbon-toolbar.d.ts +15 -0
  62. package/dist/smartart/smartart.d.ts +43 -0
  63. package/dist/smartart.d.ts +1 -14
  64. package/dist/smartart.js +3 -3
  65. package/dist/styles.d.ts +1 -4
  66. package/dist/teams/app-rail.d.ts +29 -0
  67. package/dist/teams/avatar.d.ts +39 -0
  68. package/dist/teams/base.d.ts +17 -0
  69. package/dist/teams/call-controls.d.ts +61 -0
  70. package/dist/teams/call-grid.d.ts +33 -0
  71. package/dist/teams/channel-list.d.ts +40 -0
  72. package/dist/teams/chat-composer.d.ts +60 -0
  73. package/dist/teams/chat-list.d.ts +51 -0
  74. package/dist/teams/icons.d.ts +3 -0
  75. package/dist/teams/index.d.ts +14 -0
  76. package/dist/teams/prejoin.d.ts +40 -0
  77. package/dist/teams.js +5 -0
  78. package/dist/theme.js +2 -2
  79. package/dist/tokens.d.ts +245 -214
  80. package/package.json +12 -6
  81. package/dist/backstage.d.ts +0 -28
  82. package/dist/button.d.ts +0 -14
  83. package/dist/checkable.d.ts +0 -4
  84. package/dist/chrome-controls.d.ts +0 -52
  85. package/dist/chunk-24MMSCKW.js +0 -119
  86. package/dist/chunk-UEJ4F33J.js +0 -78
  87. package/dist/chunk-W7YI5DSJ.js +0 -5970
  88. package/dist/command-search.d.ts +0 -18
  89. package/dist/context-menu.d.ts +0 -20
  90. package/dist/dialog.d.ts +0 -13
  91. package/dist/document-notices.d.ts +0 -55
  92. package/dist/find-bar.d.ts +0 -15
  93. package/dist/gallery-styles.d.ts +0 -7
  94. package/dist/gallery-view.d.ts +0 -53
  95. package/dist/gallery.d.ts +0 -20
  96. package/dist/menu.d.ts +0 -15
  97. package/dist/print-preview.d.ts +0 -11
  98. package/dist/radio.d.ts +0 -1
  99. package/dist/ribbon-tabs.d.ts +0 -14
  100. package/dist/ribbon-toggle.d.ts +0 -1
  101. package/dist/ribbon.d.ts +0 -24
  102. package/dist/ruler.d.ts +0 -10
  103. package/dist/search-field.d.ts +0 -1
  104. package/dist/select-styles.d.ts +0 -6
  105. package/dist/select.d.ts +0 -19
  106. package/dist/status-bar-view.d.ts +0 -50
  107. package/dist/status-bar.d.ts +0 -19
  108. package/dist/tab-strip.d.ts +0 -20
  109. package/dist/title-bar-search.d.ts +0 -43
  110. package/dist/title-bar-strip.d.ts +0 -20
  111. package/dist/title-bar.d.ts +0 -49
  112. package/dist/zoom-slider.d.ts +0 -9
  113. /package/dist/{safe-svg.d.ts → ribbon/safe-svg.d.ts} +0 -0
  114. /package/dist/{smartart-svg.d.ts → smartart/smartart-svg.d.ts} +0 -0
@@ -0,0 +1,4536 @@
1
+ import { createIconSvg, paintIcon, getIcon } from './chunk-HZ422LTR.js';
2
+ import { initialsOf } from './chunk-E47762NY.js';
3
+ import { OfficeElement, controlStyles, definer, valueOn, flag, present, withTokens, triState } from './chunk-5A4REH6T.js';
4
+ import { tok, OFFICE_TOKENS } from './chunk-HSJH6SUW.js';
5
+ import { html, LitElement, render, nothing, svg } from 'lit';
6
+ import { ifDefined } from 'lit/directives/if-defined.js';
7
+ import { repeat } from 'lit/directives/repeat.js';
8
+ import { unsafeStatic, html as html$1 } from 'lit/static-html.js';
9
+ import { live } from 'lit/directives/live.js';
10
+ import { keyed } from 'lit/directives/keyed.js';
11
+
12
+ function glyph(name, className = "glyph", hidden = false) {
13
+ const found = getIcon(name);
14
+ return html`<svg
15
+ class=${className}
16
+ viewBox=${found?.viewBox ?? "0 0 20 20"}
17
+ aria-hidden="true"
18
+ ?data-painted=${Boolean(found)}
19
+ ?hidden=${hidden}
20
+ >${found ? svg`<path d=${found.d}></path>` : ""}</svg>`;
21
+ }
22
+
23
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/dialog/dialog-footer.css?raw
24
+ var dialog_footer_default = ":host {\n display: block;\n flex: 1 1 auto;\n min-width: 0;\n}\n:host([hidden]) {\n display: none !important;\n}\nsvg {\n flex: none;\n width: var(--office-icon-size);\n height: var(--office-icon-size);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.footer {\n box-sizing: border-box;\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: var(--office-space-2);\n}\nbutton {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-1-5);\n min-width: var(--office-control-height);\n min-height: var(--office-control-height-lg);\n padding: var(--office-space-1-5) var(--office-space-3-5);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-md);\n background: var(--office-surface);\n color: var(--office-foreground);\n font: 500 var(--office-font-size)/1.2 var(--office-font);\n cursor: pointer;\n touch-action: manipulation;\n}\nbutton:hover:not(:disabled) {\n background: var(--office-selected);\n}\nbutton.primary {\n border-color: var(--office-accent);\n background: var(--office-accent);\n color: var(--office-accent-foreground);\n}\nbutton.primary:hover:not(:disabled) {\n opacity: 0.9;\n background: var(--office-accent);\n}\nbutton.warning {\n border-color: var(--office-warning);\n background: var(--office-warning);\n color: var(--office-warning-foreground);\n}\nbutton.warning:hover:not(:disabled) {\n opacity: 0.9;\n background: var(--office-warning);\n}\nbutton.danger {\n border-color: var(--office-danger);\n background: var(--office-danger);\n color: var(--office-danger-foreground);\n}\nbutton.danger:hover:not(:disabled) {\n opacity: 0.9;\n background: var(--office-danger);\n}\nbutton.start {\n margin-right: auto;\n}\nbutton.busy::before {\n content: '';\n width: var(--office-icon-size-sm);\n height: var(--office-icon-size-sm);\n border: var(--office-border-width-thick) solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: office-footer-spin var(--office-duration-spin) linear infinite;\n}\n@keyframes office-footer-spin {\n to {\n transform: rotate(360deg);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n button.busy::before {\n animation: none;\n }\n}\nbutton:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n@media (pointer: coarse), (max-width: 767px) {\n button {\n min-width: var(--office-target-size-touch);\n min-height: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n button,\n button.primary,\n button.warning,\n button.danger {\n border-color: ButtonText;\n background: ButtonFace;\n color: ButtonText;\n }\n button:hover:not(:disabled) {\n background: Highlight;\n color: HighlightText;\n }\n button.primary {\n border-width: var(--office-border-width-thick);\n }\n button:focus-visible {\n outline-color: Highlight;\n }\n}\n";
25
+
26
+ // src/dialog/dialog-footer.ts
27
+ var OfficeUiDialogFooter = class extends OfficeElement {
28
+ static requestEvent = "office-dialog-footer-request";
29
+ static testIdPrefix = "office-dialog-footer";
30
+ static styles = controlStyles(dialog_footer_default);
31
+ // A product subclass overrides `state` with its own accessor, so Lit is not told about it: its
32
+ // first update would read the property before the subclass's fields exist.
33
+ model = { actions: [] };
34
+ get state() {
35
+ return this.model;
36
+ }
37
+ set state(value) {
38
+ this.model = value;
39
+ this.requestUpdate("state");
40
+ }
41
+ /** Move keyboard focus to an action, for example the primary one when a dialog opens. */
42
+ focusAction(id) {
43
+ this.renderRoot.querySelector(`button[data-action="${id}"]`)?.focus();
44
+ }
45
+ classes(action) {
46
+ return [
47
+ action.variant === "secondary" ? "" : action.variant ?? "",
48
+ action.align === "start" ? "start" : "",
49
+ action.busy ? "busy" : ""
50
+ ].filter(Boolean).join(" ");
51
+ }
52
+ render() {
53
+ const { requestEvent } = this.constructor;
54
+ return html`
55
+ <div class="footer" part="footer">
56
+ ${repeat(
57
+ this.model.actions,
58
+ (action) => action.id,
59
+ (action) => html`<button
60
+ type="button"
61
+ class=${this.classes(action)}
62
+ data-action=${action.id}
63
+ data-testid=${ifDefined(action.testId)}
64
+ title=${ifDefined(action.title)}
65
+ aria-busy=${ifDefined(action.busy ? "true" : void 0)}
66
+ ?disabled=${action.disabled === true || action.busy === true}
67
+ @click=${() => this.fire(requestEvent, { id: action.id })}
68
+ >${action.icon ? glyph(action.icon, "icon") : ""}${action.label}</button
69
+ >`
70
+ )}
71
+ </div>
72
+ `;
73
+ }
74
+ };
75
+ var defineDialogFooter = definer("office-ui-dialog-footer", () => OfficeUiDialogFooter);
76
+
77
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/notices/toasts.css?raw
78
+ var toasts_default = ":host {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n max-height: 70%;\n pointer-events: none;\n}\n:host([hidden]) {\n display: none !important;\n}\n[hidden] {\n display: none !important;\n}\nsvg {\n flex: none;\n width: var(--office-icon-size);\n height: var(--office-icon-size);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.header,\n.toast,\n.overflow {\n pointer-events: auto;\n}\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 var(--office-space-1);\n color: var(--office-muted-foreground);\n font: 600 var(--office-font-size-xs)/1.4 var(--office-font);\n}\n.toast {\n box-sizing: border-box;\n display: flex;\n align-items: flex-start;\n gap: var(--office-space-2);\n padding: var(--office-space-2) var(--office-space-2-5);\n margin-top: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n color: var(--office-foreground);\n box-shadow: var(--office-shadow-lg);\n font: var(--office-font-size-sm)/1.4 var(--office-font);\n}\n.toast svg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n margin-top: var(--office-space-px);\n}\n.toast[data-severity='warning'] svg {\n color: var(--office-warning);\n}\n.toast[data-severity='info'] svg {\n color: var(--office-info);\n}\n.message {\n flex: 1;\n min-width: 0;\n margin: 0;\n overflow-wrap: anywhere;\n}\n.overflow {\n margin: var(--office-space-2) 0 0;\n text-align: center;\n color: var(--office-muted-foreground);\n font: var(--office-font-size-xs) var(--office-font);\n}\nbutton {\n box-sizing: border-box;\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--office-control-height);\n min-height: var(--office-control-height);\n padding: var(--office-space-0) var(--office-space-1);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-muted-foreground);\n font: inherit;\n cursor: pointer;\n touch-action: manipulation;\n}\nbutton:hover {\n background: var(--office-surface);\n color: var(--office-foreground);\n}\n.dismiss-all {\n font-weight: 500;\n text-decoration: underline;\n text-underline-offset: var(--office-space-0);\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\n@media (pointer: coarse) {\n button {\n min-width: var(--office-target-size-touch);\n min-height: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n .toast {\n border-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n .toast svg,\n button {\n color: ButtonText;\n }\n button:hover {\n background: Highlight;\n color: HighlightText;\n }\n button:focus-visible {\n outline-color: Highlight;\n }\n}\n";
79
+
80
+ // src/notices/toasts.ts
81
+ var OFFICE_TOAST_VISIBLE_LIMIT = 5;
82
+ var OfficeUiToasts = class extends OfficeElement {
83
+ static requestEvent = "office-toasts-request";
84
+ static testIdPrefix = "office-toast";
85
+ static styles = controlStyles(toasts_default);
86
+ // A product subclass overrides `state` with its own accessor, so Lit is not told about it: its
87
+ // first update would read the property before the subclass's fields exist.
88
+ model = { toasts: [] };
89
+ get state() {
90
+ return this.model;
91
+ }
92
+ set state(value) {
93
+ this.model = value;
94
+ this.requestUpdate("state");
95
+ }
96
+ willUpdate(_changed) {
97
+ const { testIdPrefix } = this.constructor;
98
+ this.setAttribute("data-testid", `${testIdPrefix}s`);
99
+ this.toggleAttribute("hidden", this.model.toasts.length === 0);
100
+ }
101
+ render() {
102
+ const { requestEvent, testIdPrefix: p } = this.constructor;
103
+ const s = this.model;
104
+ const visible2 = s.toasts.slice(0, OFFICE_TOAST_VISIBLE_LIMIT);
105
+ const hiddenCount = (s.overflowCount ?? 0) + s.toasts.length - visible2.length;
106
+ const labels = s.labels ?? {};
107
+ return html`
108
+ <div>
109
+ <div class="header">
110
+ <span>${labels.title ?? "Compatibility"}</span>
111
+ <button
112
+ type="button"
113
+ class="dismiss-all"
114
+ data-testid="${p}s-dismiss-all"
115
+ @click=${() => this.fire(requestEvent, { id: "dismissAll" })}
116
+ >${labels.dismissAll ?? "Dismiss all"}</button
117
+ >
118
+ </div>
119
+ ${visible2.map(
120
+ (toast) => html`<div
121
+ class="toast"
122
+ role="status"
123
+ data-testid=${p}
124
+ data-code=${ifDefined(toast.code)}
125
+ data-severity=${toast.severity}
126
+ >
127
+ ${glyph(toast.severity === "warning" ? "warning" : "info", "icon")}
128
+ <p class="message">${toast.message}</p>
129
+ <button
130
+ type="button"
131
+ data-testid="${p}-dismiss"
132
+ aria-label=${labels.dismiss ?? "Dismiss"}
133
+ @click=${() => this.fire(requestEvent, { id: "dismiss", toastId: toast.id })}
134
+ >${glyph("close", "icon")}</button
135
+ >
136
+ </div>`
137
+ )}
138
+ ${hiddenCount > 0 ? html`<p class="overflow">+${hiddenCount}</p>` : ""}
139
+ </div>
140
+ `;
141
+ }
142
+ };
143
+ var defineToasts = definer("office-ui-toasts", () => OfficeUiToasts);
144
+
145
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/notices/paste-options.css?raw
146
+ var paste_options_default = ":host {\n position: fixed;\n z-index: 1100;\n display: block;\n}\n:host([hidden]) {\n display: none !important;\n}\n.toolbar {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--office-space-0);\n padding: var(--office-space-1);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius);\n background: var(--office-popover);\n box-shadow: var(--office-shadow-lg);\n font: var(--office-font-size-xs)/1.2 var(--office-font);\n}\nbutton {\n box-sizing: border-box;\n min-height: var(--office-control-height);\n padding: var(--office-space-1) var(--office-space-2);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-foreground);\n font: inherit;\n white-space: nowrap;\n cursor: pointer;\n touch-action: manipulation;\n}\nbutton:hover {\n background: var(--office-surface);\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\n@media (pointer: coarse) {\n button {\n min-height: var(--office-target-size-touch);\n min-width: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n .toolbar {\n border-color: CanvasText;\n background: Canvas;\n }\n button {\n color: ButtonText;\n }\n button:hover {\n background: Highlight;\n color: HighlightText;\n }\n button:focus-visible {\n outline-color: Highlight;\n }\n}\n";
147
+
148
+ // src/notices/paste-options.ts
149
+ var OfficeUiPasteOptions = class extends OfficeElement {
150
+ static requestEvent = "office-paste-options-request";
151
+ static dismissEvent = "office-paste-options-dismiss";
152
+ static testIdPrefix = "office-paste-options";
153
+ static styles = controlStyles(paste_options_default);
154
+ // A product subclass overrides `state` with its own accessor, so Lit is not told about it: its
155
+ // first update would read the property before the subclass's fields exist.
156
+ model = { left: 0, top: 0, options: [] };
157
+ get state() {
158
+ return this.model;
159
+ }
160
+ set state(value) {
161
+ this.model = value;
162
+ this.requestUpdate("state");
163
+ }
164
+ disarm;
165
+ dismiss() {
166
+ this.fire(this.constructor.dismissEvent, void 0);
167
+ }
168
+ connectedCallback() {
169
+ super.connectedCallback();
170
+ const view = this.ownerDocument.defaultView;
171
+ if (!view) return;
172
+ const dismiss = (event) => {
173
+ const toolbar = this.renderRoot.querySelector(".toolbar");
174
+ if (toolbar && event.composedPath().includes(toolbar) && (!(event instanceof KeyboardEvent) || event.key !== "Escape"))
175
+ return;
176
+ this.dismiss();
177
+ };
178
+ const timer = view.setTimeout(() => {
179
+ view.addEventListener("pointerdown", dismiss, true);
180
+ view.addEventListener("keydown", dismiss, true);
181
+ }, 0);
182
+ this.disarm = () => {
183
+ view.clearTimeout(timer);
184
+ view.removeEventListener("pointerdown", dismiss, true);
185
+ view.removeEventListener("keydown", dismiss, true);
186
+ };
187
+ }
188
+ disconnectedCallback() {
189
+ super.disconnectedCallback();
190
+ this.disarm?.();
191
+ this.disarm = void 0;
192
+ }
193
+ choose(option) {
194
+ this.fire(this.constructor.requestEvent, {
195
+ format: option.id
196
+ });
197
+ this.dismiss();
198
+ }
199
+ willUpdate() {
200
+ const { left, top } = this.model;
201
+ this.hostWrite(() => {
202
+ this.style.left = `calc(${left}px + ${tok("--office-space-1")})`;
203
+ this.style.top = `calc(${top}px + ${tok("--office-space-1")})`;
204
+ });
205
+ }
206
+ render() {
207
+ return html`
208
+ <div
209
+ class="toolbar"
210
+ role="toolbar"
211
+ tabindex="-1"
212
+ aria-label=${this.model.label ?? "Paste Options"}
213
+ @mousedown=${(event) => event.stopPropagation()}
214
+ >
215
+ ${repeat(
216
+ this.model.options,
217
+ (option) => option.id,
218
+ (option) => html`<button
219
+ type="button"
220
+ data-format=${option.id}
221
+ title=${option.label}
222
+ @click=${() => this.choose(option)}
223
+ >${option.label}</button
224
+ >`
225
+ )}
226
+ </div>
227
+ `;
228
+ }
229
+ };
230
+ var definePasteOptions = definer("office-ui-paste-options", () => OfficeUiPasteOptions);
231
+
232
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/notices/read-only-banner.css?raw
233
+ var read_only_banner_default = ":host {\n display: block;\n flex: none;\n}\n:host([hidden]) {\n display: none !important;\n}\n[hidden] {\n display: none !important;\n}\nsvg {\n flex: none;\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n color: var(--office-notice-accent);\n}\n.banner {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n padding: var(--office-space-1-5) var(--office-space-4);\n border-bottom: var(--office-border-width) solid var(--office-notice-border);\n background: var(--office-notice-background);\n color: var(--office-notice-foreground);\n font: var(--office-font-size-sm)/1.4 var(--office-font);\n}\n.text {\n flex: 1 1 auto;\n min-width: 0;\n margin: 0;\n}\n.form {\n display: flex;\n flex: none;\n align-items: center;\n gap: var(--office-space-2);\n}\n.sr-only {\n position: absolute;\n width: var(--office-space-px);\n height: var(--office-space-px);\n margin: calc(-1 * var(--office-space-px));\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\nbutton,\ninput {\n font: inherit;\n}\nbutton {\n box-sizing: border-box;\n flex: none;\n min-height: var(--office-control-height);\n padding: var(--office-space-1) var(--office-space-3);\n border: var(--office-border-width) solid transparent;\n border-radius: var(--office-radius);\n background: transparent;\n color: inherit;\n font-weight: 500;\n cursor: pointer;\n touch-action: manipulation;\n}\nbutton.primary {\n border-color: color-mix(in srgb, var(--office-notice-accent) 50%, transparent);\n}\nbutton:hover {\n background: color-mix(in srgb, var(--office-notice-accent) 15%, transparent);\n}\nbutton:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\nbutton:focus-visible,\ninput:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\ninput {\n box-sizing: border-box;\n min-height: var(--office-control-height);\n padding: var(--office-space-1) var(--office-space-2);\n border: var(--office-border-width) solid\n color-mix(in srgb, var(--office-notice-accent) 40%, transparent);\n border-radius: var(--office-radius);\n background: var(--office-background);\n color: var(--office-foreground);\n}\n.error {\n flex: none;\n color: var(--office-danger);\n}\n@media (pointer: coarse) {\n button,\n input {\n min-height: var(--office-target-size-touch);\n min-width: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n .banner {\n border-bottom-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n svg,\n .error {\n color: CanvasText;\n }\n button,\n input {\n border-color: ButtonText;\n color: ButtonText;\n background: ButtonFace;\n }\n button:focus-visible,\n input:focus-visible {\n outline-color: Highlight;\n }\n}\n";
234
+
235
+ // src/notices/read-only-banner.ts
236
+ var OfficeUiReadOnlyBanner = class extends OfficeElement {
237
+ static requestEvent = "office-read-only-request";
238
+ static testIdPrefix = "office-readonly";
239
+ static styles = controlStyles(read_only_banner_default);
240
+ // A product subclass overrides `state` with its own accessor, so Lit is not told about it: its
241
+ // first update would read the property before the subclass's fields exist.
242
+ model = { kind: null, message: "" };
243
+ get state() {
244
+ return this.model;
245
+ }
246
+ set state(value) {
247
+ this.model = value;
248
+ this.requestUpdate("state");
249
+ }
250
+ promptWasOpen = false;
251
+ send(intent) {
252
+ this.fire(this.constructor.requestEvent, intent);
253
+ }
254
+ get input() {
255
+ return this.renderRoot.querySelector("input");
256
+ }
257
+ onSubmit(event) {
258
+ event.preventDefault();
259
+ this.send({ id: "submitPassword", password: this.input?.value ?? "" });
260
+ }
261
+ willUpdate() {
262
+ const { testIdPrefix } = this.constructor;
263
+ this.setAttribute("data-testid", `${testIdPrefix}-banner`);
264
+ if (this.model.kind) this.setAttribute("data-kind", this.model.kind);
265
+ else this.removeAttribute("data-kind");
266
+ }
267
+ /** The password field takes focus when the prompt opens and is cleared when it closes. */
268
+ updated(_changed) {
269
+ const open = this.model.passwordPromptOpen === true;
270
+ if (open && !this.promptWasOpen) this.input?.focus();
271
+ else if (!open && this.promptWasOpen && this.input) this.input.value = "";
272
+ this.promptWasOpen = open;
273
+ }
274
+ render() {
275
+ const { testIdPrefix: p } = this.constructor;
276
+ const s = this.model;
277
+ const l = s.labels ?? {};
278
+ const open = s.passwordPromptOpen === true;
279
+ const failed = s.passwordError ?? null;
280
+ return html`
281
+ <div class="banner" role="status" part="banner">
282
+ ${glyph("lock", "icon")}
283
+ <p class="text"
284
+ ><strong>${l.title ?? "Read-only recommended"}</strong>: <span>${s.message}</span></p
285
+ >
286
+ <button
287
+ type="button"
288
+ class="primary"
289
+ data-testid="${p}-edit-anyway"
290
+ ?hidden=${open}
291
+ @click=${() => this.send({ id: "editAnyway" })}
292
+ >${l.editAnyway ?? "Edit anyway"}</button
293
+ >
294
+ <button
295
+ type="button"
296
+ data-testid="${p}-dismiss"
297
+ ?hidden=${open}
298
+ @click=${() => this.send({ id: "dismiss" })}
299
+ >${l.dismiss ?? "Dismiss"}</button
300
+ >
301
+ <form
302
+ class="form"
303
+ data-testid="${p}-password-form"
304
+ ?hidden=${!open}
305
+ @submit=${this.onSubmit}
306
+ >
307
+ <label class="sr-only" for="password">${l.passwordLabel ?? "Password"}</label>
308
+ <input
309
+ id="password"
310
+ type="password"
311
+ data-testid="${p}-password-input"
312
+ placeholder=${l.passwordPlaceholder ?? "Password"}
313
+ aria-invalid=${String(failed !== null)}
314
+ aria-describedby=${ifDefined(failed ? "error" : void 0)}
315
+ ?disabled=${s.checkingPassword === true}
316
+ />
317
+ <button
318
+ type="submit"
319
+ class="primary"
320
+ data-testid="${p}-unlock"
321
+ ?disabled=${s.checkingPassword === true}
322
+ >${l.unlock ?? "Unlock"}</button
323
+ >
324
+ <button
325
+ type="button"
326
+ data-testid="${p}-password-cancel"
327
+ @click=${() => this.send({ id: "cancelPassword" })}
328
+ >${l.cancel ?? "Cancel"}</button
329
+ >
330
+ <span
331
+ class="error"
332
+ id="error"
333
+ role="alert"
334
+ data-testid="${p}-password-error"
335
+ ?hidden=${failed === null}
336
+ >${failed ?? ""}</span
337
+ >
338
+ </form>
339
+ </div>
340
+ `;
341
+ }
342
+ };
343
+ var defineReadOnlyBanner = definer(
344
+ "office-ui-read-only-banner",
345
+ () => OfficeUiReadOnlyBanner
346
+ );
347
+
348
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/radio.css?raw
349
+ var radio_default = "/* Tokens: --office-checkbox-size, --office-radio-dot-size and their -touch variants. */\n:host {\n display: inline-grid;\n box-sizing: border-box;\n width: var(--office-checkbox-size);\n height: var(--office-checkbox-size);\n flex: none;\n place-items: center;\n border: var(--office-border-width) solid var(--office-checkbox-border);\n border-radius: 50%;\n background: var(--office-checkbox-background);\n cursor: pointer;\n vertical-align: middle;\n}\n:host([checked]) {\n border-color: var(--office-checkbox-accent);\n}\n:host(:focus-visible) {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n:host([disabled]) {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.dot {\n display: none;\n width: var(--office-radio-dot-size);\n height: var(--office-radio-dot-size);\n border-radius: 50%;\n background: var(--office-checkbox-accent);\n}\n:host([checked]) .dot {\n display: block;\n}\n\n@media (pointer: coarse), (max-width: 767px) {\n :host {\n width: var(--office-checkbox-size-touch);\n height: var(--office-checkbox-size-touch);\n }\n .dot {\n width: var(--office-radio-dot-size-touch);\n height: var(--office-radio-dot-size-touch);\n }\n}\n@media (forced-colors: active) {\n :host {\n border-color: CanvasText;\n background: Canvas;\n forced-color-adjust: auto;\n }\n :host([checked]) {\n border-color: Highlight;\n }\n .dot {\n background: Highlight;\n }\n :host([disabled]) {\n border-color: GrayText;\n }\n :host([disabled]) .dot {\n background: GrayText;\n }\n :host(:focus-visible) {\n outline-color: Highlight;\n }\n}\n";
350
+
351
+ // src/form/radio.ts
352
+ function groupOf(radio, scope) {
353
+ const name = radio.name;
354
+ if (!name || !scope) return [radio];
355
+ return [...scope.querySelectorAll(radio.localName)].filter(
356
+ (other) => other.name === name && other.form === radio.form
357
+ );
358
+ }
359
+ function labelText(label, self) {
360
+ let text = "";
361
+ for (const node of label.childNodes) {
362
+ if (node === self) continue;
363
+ if (node.nodeType === 3) text += node.textContent ?? "";
364
+ else if (node instanceof Element && !node.localName.includes("-") && !/^(select|input|textarea)$/u.test(node.localName))
365
+ text += labelText(node, self);
366
+ }
367
+ return text;
368
+ }
369
+ function refreshTabStops(group) {
370
+ const enabled = group.filter((item) => !present(item.disabled));
371
+ const stop = enabled.find((item) => present(item.checked)) ?? enabled[0];
372
+ for (const item of group) item.tabIndex = item === stop ? 0 : -1;
373
+ }
374
+ var OfficeUiRadio = class extends OfficeElement {
375
+ static formAssociated = true;
376
+ static styles = controlStyles(radio_default);
377
+ static properties = {
378
+ checked: flag,
379
+ disabled: flag,
380
+ value: valueOn,
381
+ name: { type: String, reflect: true }
382
+ };
383
+ internals;
384
+ defaultChecked = false;
385
+ scope = null;
386
+ autoNamed = false;
387
+ constructor() {
388
+ super();
389
+ this.checked = false;
390
+ this.disabled = false;
391
+ this.value = "on";
392
+ this.name = "";
393
+ try {
394
+ this.internals = this.attachInternals();
395
+ } catch {
396
+ this.internals = void 0;
397
+ }
398
+ this.addEventListener("click", (event) => {
399
+ if (present(this.disabled)) event.preventDefault();
400
+ else this.select(true);
401
+ });
402
+ this.addEventListener("keydown", (event) => this.onKey(event));
403
+ this.addEventListener("focus", () => this.syncName());
404
+ }
405
+ get form() {
406
+ return this.internals?.form ?? null;
407
+ }
408
+ connectedCallback() {
409
+ this.defaultChecked = present(this.checked);
410
+ const root = this.getRootNode();
411
+ this.scope = root instanceof Document || root instanceof ShadowRoot ? root : null;
412
+ super.connectedCallback();
413
+ this.syncName();
414
+ }
415
+ disconnectedCallback() {
416
+ super.disconnectedCallback();
417
+ if (this.scope)
418
+ refreshTabStops(groupOf(this, this.scope).filter((item) => item !== this));
419
+ }
420
+ formResetCallback() {
421
+ this.checked = this.defaultChecked;
422
+ }
423
+ formDisabledCallback(disabled) {
424
+ this.disabled = disabled;
425
+ }
426
+ /** Check this radio and, for user intent, announce it. No-op when already checked. */
427
+ select(emit) {
428
+ if (present(this.checked)) return;
429
+ this.checked = true;
430
+ if (emit) {
431
+ this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
432
+ this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
433
+ }
434
+ }
435
+ /**
436
+ * A custom element is not named by a wrapping `<label>` in every browser's accessibility
437
+ * tree, so mirror the label text into `aria-label` unless the host named it.
438
+ */
439
+ syncName() {
440
+ if (this.hasAttribute("aria-labelledby")) return;
441
+ if (this.hasAttribute("aria-label") && !this.autoNamed) return;
442
+ const label = this.closest("label") ?? this.internals?.labels?.[0];
443
+ const text = label ? labelText(label, this).replace(/\s+/gu, " ").trim() : "";
444
+ if (text) {
445
+ this.autoNamed = true;
446
+ this.setAttribute("aria-label", text);
447
+ }
448
+ }
449
+ peers() {
450
+ return groupOf(this, this.scope ?? this.getRootNode());
451
+ }
452
+ onKey(event) {
453
+ if (present(this.disabled) || event.ctrlKey || event.metaKey || event.altKey) return;
454
+ if (event.key === " ") {
455
+ event.preventDefault();
456
+ this.select(true);
457
+ return;
458
+ }
459
+ const list = this.peers().filter((item) => !present(item.disabled));
460
+ const index = list.indexOf(this);
461
+ const last = list.length - 1;
462
+ const next = event.key === "ArrowRight" || event.key === "ArrowDown" ? (index + 1) % list.length : event.key === "ArrowLeft" || event.key === "ArrowUp" ? (index + last) % list.length : event.key === "Home" ? 0 : event.key === "End" ? last : -1;
463
+ if (next < 0 || index < 0) return;
464
+ event.preventDefault();
465
+ event.stopPropagation();
466
+ const target = list[next];
467
+ target.select(true);
468
+ target.focus();
469
+ }
470
+ /** The host carries the semantics: role, state, form value and the group's tab stop. */
471
+ willUpdate() {
472
+ const checked = present(this.checked);
473
+ const disabled = present(this.disabled);
474
+ this.setAttribute("role", "radio");
475
+ this.setAttribute("aria-checked", String(checked));
476
+ this.setAttribute("aria-disabled", String(disabled));
477
+ this.internals?.setFormValue?.(checked && !disabled ? this.value : null);
478
+ if (this.isConnected) {
479
+ this.scope = this.getRootNode();
480
+ if (checked) {
481
+ for (const peer of this.peers())
482
+ if (peer !== this && present(peer.checked)) peer.checked = false;
483
+ }
484
+ refreshTabStops(this.peers());
485
+ } else this.setAttribute("tabindex", disabled ? "-1" : "0");
486
+ }
487
+ render() {
488
+ return html`<span class="dot"></span>`;
489
+ }
490
+ };
491
+ var defineRadio = definer("office-ui-radio", () => OfficeUiRadio);
492
+
493
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-toggle.css?raw
494
+ var ribbon_toggle_default = ":host {\n display: block;\n}\nlabel {\n display: flex;\n align-items: center;\n gap: var(--office-space-1);\n min-height: var(--office-ribbon-row-height);\n padding: 0 var(--office-space-1);\n color: var(--office-foreground);\n font: inherit;\n font-size: var(--office-font-size-sm);\n white-space: nowrap;\n cursor: pointer;\n}\n:host([disabled]) label {\n color: var(--office-muted-foreground);\n cursor: not-allowed;\n}\n\n@media (pointer: coarse), (max-width: 767px) {\n label {\n min-height: var(--office-target-size-touch);\n font-size: var(--office-font-size-sm);\n }\n}\n";
495
+
496
+ // src/ribbon/ribbon-toggle.ts
497
+ var OfficeUiRibbonToggle = class extends OfficeElement {
498
+ static requestEvent = "office-toggle";
499
+ static idAttribute = "command";
500
+ static detailKey = "command";
501
+ /** The checkbox drawn inside; a product subclass may use its own aliased tag. */
502
+ static checkboxTag = "office-ui-checkbox";
503
+ static styles = controlStyles(ribbon_toggle_default);
504
+ static properties = {
505
+ label: { type: String },
506
+ checked: flag,
507
+ disabled: flag
508
+ };
509
+ /** `title` is a native attribute, so it is watched rather than declared as a property. */
510
+ static get observedAttributes() {
511
+ return [...super.observedAttributes, "title"];
512
+ }
513
+ constructor() {
514
+ super();
515
+ this.label = "";
516
+ this.checked = false;
517
+ this.disabled = false;
518
+ }
519
+ attributeChangedCallback(name, old, value) {
520
+ super.attributeChangedCallback(name, old, value);
521
+ if (name === "title") this.requestUpdate();
522
+ }
523
+ /** Clicking the label text presses the checkbox, which is the only control that toggles. */
524
+ onLabelClick(event) {
525
+ event.preventDefault();
526
+ const checkbox = this.renderRoot.querySelector('[part="checkbox"]');
527
+ if (event.target !== checkbox) checkbox?.click();
528
+ }
529
+ onChange(event) {
530
+ event.stopPropagation();
531
+ const checkbox = event.target;
532
+ const checked = checkbox.checked;
533
+ checkbox.checked = present(this.checked);
534
+ const { requestEvent, idAttribute, detailKey } = this.constructor;
535
+ const id = this.getAttribute(idAttribute);
536
+ if (!present(this.disabled) && id) this.fire(requestEvent, { [detailKey]: id, checked });
537
+ }
538
+ render() {
539
+ const checkboxTag = this.constructor.checkboxTag;
540
+ const tag = unsafeStatic(checkboxTag);
541
+ return html$1`
542
+ <label @click=${this.onLabelClick}>
543
+ <${tag}
544
+ part="checkbox"
545
+ aria-label=${this.label}
546
+ title=${this.getAttribute("title") ?? this.label}
547
+ ?checked=${present(this.checked)}
548
+ ?disabled=${present(this.disabled)}
549
+ @input=${(event) => event.stopPropagation()}
550
+ @change=${this.onChange}
551
+ ></${tag}>${this.label}</label>
552
+ `;
553
+ }
554
+ };
555
+ var defineRibbonToggle = definer("office-ui-ribbon-toggle", () => OfficeUiRibbonToggle);
556
+
557
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/search-field.css?raw
558
+ var search_field_default = "/* Tokens: --office-field-*. */\n:host {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n box-sizing: border-box;\n width: 100%;\n height: var(--office-field-height-lg);\n padding: 0 var(--office-space-3);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-field-radius);\n background: var(--office-field-background);\n color: var(--office-field-placeholder);\n font: inherit;\n}\n:host(:focus-within) {\n border-color: var(--office-field-border-focus);\n}\n:host([variant='titlebar']) {\n height: var(--office-field-height);\n gap: var(--office-space-1-5);\n padding-inline: var(--office-space-3-5);\n border-color: var(--office-border);\n border-radius: var(--office-radius-md);\n background: var(--office-background);\n}\n:host([variant='titlebar']:focus-within) {\n border-color: var(--office-field-border-focus);\n color: var(--office-foreground);\n}\n:host([disabled]) {\n opacity: 0.5;\n cursor: not-allowed;\n}\nsvg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n flex: none;\n}\n:host([variant='titlebar']) svg {\n width: var(--office-icon-size);\n height: var(--office-icon-size);\n}\ninput {\n min-width: 0;\n width: 100%;\n height: 100%;\n flex: 1;\n padding: 0;\n border: 0;\n outline: 0;\n background: transparent;\n color: var(--office-field-foreground);\n font: inherit;\n font-size: var(--office-font-size);\n}\n:host([variant='titlebar']) input {\n font-size: var(--office-font-size-xs);\n color: var(--office-foreground);\n}\ninput::placeholder {\n color: var(--office-field-placeholder);\n opacity: 0.8;\n}\ninput::-webkit-search-cancel-button {\n display: none;\n}\n\n@media (forced-colors: active) {\n :host {\n border-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n :host(:focus-within) {\n outline: var(--office-focus-width) solid Highlight;\n outline-offset: var(--office-focus-offset);\n }\n input {\n color: CanvasText;\n }\n}\n";
559
+
560
+ // src/form/search-field.ts
561
+ var OfficeUiSearch = class extends OfficeElement {
562
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
563
+ static styles = controlStyles(search_field_default);
564
+ static properties = {
565
+ value: { type: String },
566
+ placeholder: { type: String, reflect: true },
567
+ disabled: flag,
568
+ variant: { type: String, reflect: true }
569
+ };
570
+ /** `aria-label` names the inner input, so it is watched rather than declared as a property. */
571
+ static get observedAttributes() {
572
+ return [...super.observedAttributes, "aria-label"];
573
+ }
574
+ constructor() {
575
+ super();
576
+ this.value = "";
577
+ this.placeholder = null;
578
+ this.disabled = false;
579
+ this.variant = "";
580
+ }
581
+ attributeChangedCallback(name, old, value) {
582
+ super.attributeChangedCallback(name, old, value);
583
+ if (name === "aria-label") this.requestUpdate();
584
+ }
585
+ get input() {
586
+ return this.renderRoot.querySelector("input");
587
+ }
588
+ focus(options) {
589
+ this.input?.focus(options);
590
+ }
591
+ select() {
592
+ this.input?.select();
593
+ }
594
+ onInput(event) {
595
+ event.stopPropagation();
596
+ this.value = event.target.value;
597
+ this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
598
+ }
599
+ onChange(event) {
600
+ event.stopPropagation();
601
+ this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
602
+ }
603
+ render() {
604
+ return html`
605
+ <svg
606
+ part="icon"
607
+ viewBox="0 0 24 24"
608
+ fill="none"
609
+ stroke="currentColor"
610
+ stroke-width="2"
611
+ stroke-linecap="round"
612
+ aria-hidden="true"
613
+ >
614
+ <circle cx="11" cy="11" r="8"></circle>
615
+ <path d="m21 21-4.35-4.35"></path>
616
+ </svg>
617
+ <input
618
+ part="input"
619
+ type="search"
620
+ placeholder=${this.placeholder ?? ""}
621
+ aria-label=${this.getAttribute("aria-label") ?? this.placeholder ?? ""}
622
+ .value=${this.value}
623
+ ?disabled=${present(this.disabled)}
624
+ @input=${this.onInput}
625
+ @change=${this.onChange}
626
+ />
627
+ `;
628
+ }
629
+ };
630
+ var defineSearchField = definer("office-ui-search", () => OfficeUiSearch);
631
+
632
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/backstage.css?raw
633
+ var backstage_default = ":host {\n position: absolute;\n inset: 0;\n z-index: 30;\n display: none;\n min-width: 0;\n background: var(--office-background);\n color: var(--office-foreground);\n font-family: var(--office-font);\n}\n:host([open]) {\n display: block;\n}\n.frame {\n display: flex;\n height: 100%;\n min-width: 0;\n}\nnav {\n display: flex;\n flex: 0 0 var(--office-backstage-nav-width);\n flex-direction: column;\n padding: var(--office-space-3) 0;\n overflow: auto;\n background: var(--office-backstage-nav-background);\n color: var(--office-backstage-nav-foreground);\n}\nnav button {\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n.back {\n width: var(--office-backstage-back-size);\n min-height: var(--office-backstage-back-size);\n margin: 0 0 var(--office-space-3) var(--office-space-4);\n padding: 0;\n border: calc(var(--office-border-width-thick) * 0.75) solid currentColor;\n border-radius: 50%;\n font-size: var(--office-font-size-xl);\n text-align: center;\n}\n.item {\n min-height: var(--office-backstage-item-height);\n padding: var(--office-space-1-5) var(--office-space-6);\n font-size: var(--office-font-size-md);\n}\n.item:hover:enabled,\n.item[aria-current='page'] {\n background: var(--office-backstage-item-hover);\n}\n.item:disabled {\n opacity: 0.55;\n cursor: default;\n}\nnav button:focus-visible {\n outline: var(--office-focus-width) solid currentColor;\n outline-offset: calc(-1 * var(--office-focus-width));\n}\n.items {\n display: flex;\n flex-direction: column;\n}\n.footer {\n display: flex;\n flex-direction: column;\n margin-top: auto;\n padding-top: var(--office-space-4);\n}\n.body {\n flex: 1;\n min-width: 0;\n overflow: auto;\n padding: var(--office-backstage-padding);\n}\n@media (max-width: 760px) {\n .frame {\n flex-direction: column;\n }\n nav {\n flex: none;\n flex-direction: row;\n flex-wrap: wrap;\n padding: var(--office-space-1-5);\n }\n .back {\n margin: var(--office-space-1);\n }\n .item {\n min-height: var(--office-target-size-touch);\n padding: var(--office-space-1-5) var(--office-space-3);\n }\n .items,\n .footer {\n flex-direction: row;\n flex-wrap: wrap;\n margin-top: 0;\n padding-top: 0;\n }\n .body {\n padding: var(--office-space-4);\n }\n}\n@media (forced-colors: active) {\n nav {\n border-inline-end: var(--office-border-width) solid CanvasText;\n }\n .item[aria-current='page'] {\n outline: var(--office-focus-width) solid Highlight;\n }\n}\n";
634
+
635
+ // src/chrome/backstage.ts
636
+ var OfficeUiBackstage = class extends OfficeElement {
637
+ static styles = controlStyles(backstage_default);
638
+ static properties = {
639
+ open: flag,
640
+ items: { attribute: false },
641
+ label: { type: String },
642
+ backLabel: { attribute: "back-label", type: String },
643
+ // The pages hide against `selected`, which reads as '' when unset, so it has an accessor.
644
+ selected: { type: String, noAccessor: true }
645
+ };
646
+ picked = null;
647
+ constructor() {
648
+ super();
649
+ this.open = false;
650
+ this.items = [];
651
+ this.label = null;
652
+ this.backLabel = null;
653
+ this.addEventListener("keydown", (event) => {
654
+ if (event.key !== "Escape" || !present(this.open)) return;
655
+ event.preventDefault();
656
+ event.stopPropagation();
657
+ this.requestClose("escape");
658
+ });
659
+ }
660
+ get selected() {
661
+ return this.picked ?? "";
662
+ }
663
+ set selected(id) {
664
+ this.picked = id;
665
+ this.reflectAttribute("selected", id, {});
666
+ this.requestUpdate("selected");
667
+ }
668
+ /** Open on a page (default: the selected one) and focus its item. */
669
+ show(page = this.selected || this.items.find((item) => this.page(item.id))?.id || "") {
670
+ this.open = true;
671
+ if (page) this.selected = page;
672
+ this.button(this.selected)?.focus();
673
+ }
674
+ /** Close without asking (emits `office-backstage-close` with reason `api`). */
675
+ close() {
676
+ if (!present(this.open)) return;
677
+ this.open = false;
678
+ this.fire("office-backstage-close", { reason: "api" });
679
+ }
680
+ page(id) {
681
+ return [...this.children].find(
682
+ (child) => child instanceof HTMLElement && child.dataset.backstagePage === id
683
+ );
684
+ }
685
+ button(id) {
686
+ return [...this.renderRoot.querySelectorAll("[data-backstage-item]")].find(
687
+ (button) => button.dataset.backstageItem === id
688
+ ) ?? null;
689
+ }
690
+ choose(id) {
691
+ if (this.fire("office-backstage-select", { id }, true) && this.page(id)) this.selected = id;
692
+ }
693
+ requestClose(reason) {
694
+ if (this.fire("office-backstage-close", { reason }, true)) this.open = false;
695
+ }
696
+ onNavClick(event) {
697
+ const item = event.target.closest?.("[data-backstage-item]");
698
+ if (item && !item.disabled) this.choose(item.dataset.backstageItem);
699
+ }
700
+ /** Exactly the selected page of the light DOM shows. */
701
+ syncPages() {
702
+ for (const child of this.children)
703
+ if (child instanceof HTMLElement && child.dataset.backstagePage !== void 0)
704
+ child.hidden = child.dataset.backstagePage !== this.selected;
705
+ }
706
+ updated() {
707
+ this.syncPages();
708
+ }
709
+ item(item) {
710
+ return html`<button
711
+ type="button"
712
+ class="item"
713
+ data-backstage-item=${item.id}
714
+ title=${ifDefined(item.title || void 0)}
715
+ aria-current=${ifDefined(item.id === this.selected ? "page" : void 0)}
716
+ ?disabled=${Boolean(item.disabled)}
717
+ >${item.label}</button
718
+ >`;
719
+ }
720
+ render() {
721
+ const label = this.label ?? "File";
722
+ const back = this.backLabel ?? "Back";
723
+ return html`
724
+ <div class="frame" role="dialog" aria-modal="true" aria-label=${label}>
725
+ <nav part="nav" aria-label=${label} @click=${this.onNavClick}>
726
+ <button
727
+ type="button"
728
+ class="back"
729
+ data-backstage="back"
730
+ aria-label=${back}
731
+ title="${back} (Esc)"
732
+ @click=${() => this.requestClose("back")}
733
+ >←</button
734
+ >
735
+ <div class="items"
736
+ >${this.items.filter((item) => item.group !== "footer").map((item) => this.item(item))}</div
737
+ >
738
+ <div class="footer"
739
+ >${this.items.filter((item) => item.group === "footer").map((item) => this.item(item))}</div
740
+ >
741
+ </nav>
742
+ <div class="body" part="body"><slot @slotchange=${this.syncPages}></slot></div>
743
+ </div>
744
+ `;
745
+ }
746
+ };
747
+ var defineBackstage = definer("office-ui-backstage", () => OfficeUiBackstage);
748
+
749
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/find-bar.css?raw
750
+ var find_bar_default = ":host {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n min-height: var(--office-find-bar-height);\n padding: var(--office-space-1) var(--office-space-2) var(--office-space-1) var(--office-space-3);\n border-bottom: var(--office-border-width) solid var(--office-border);\n background: var(--office-surface);\n color: var(--office-foreground);\n font: var(--office-font-size-sm) var(--office-font);\n}\n:host(:not([open])) {\n display: none;\n}\n[role='search'] {\n display: flex;\n flex: 1;\n align-items: center;\n gap: var(--office-space-1);\n min-width: 0;\n max-width: var(--office-find-max-width);\n}\nlabel {\n display: flex;\n flex: 1;\n align-items: center;\n gap: var(--office-space-1-5);\n min-width: 0;\n}\ninput {\n flex: 1;\n min-width: 0;\n min-height: var(--office-input-height);\n padding: var(--office-space-0) var(--office-space-1-5);\n font: inherit;\n color: inherit;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-sm);\n background: var(--office-background);\n}\nbutton {\n min-height: var(--office-input-height);\n padding: var(--office-space-0) var(--office-space-2);\n font: inherit;\n color: inherit;\n cursor: pointer;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-sm);\n background: var(--office-background);\n}\nbutton:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.status {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n color: var(--office-muted-foreground);\n}\n.close {\n margin-inline-start: auto;\n width: var(--office-target-size);\n padding: 0;\n border-color: transparent;\n background: transparent;\n font-size: var(--office-font-size-xl);\n line-height: 1;\n}\ninput:focus-visible,\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\n@media (max-width: 760px) {\n :host {\n flex-wrap: wrap;\n }\n [role='search'] {\n flex-wrap: wrap;\n }\n}\n";
751
+
752
+ // src/chrome/find-bar.ts
753
+ var OfficeUiFindBar = class extends OfficeElement {
754
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
755
+ static styles = controlStyles(find_bar_default);
756
+ static properties = {
757
+ open: flag,
758
+ disabled: flag,
759
+ navigationDisabled: { attribute: "navigation-disabled", ...flag },
760
+ label: { type: String },
761
+ inputLabel: { attribute: "input-label", type: String },
762
+ placeholder: { type: String },
763
+ maxlength: { type: String },
764
+ previousLabel: { attribute: "previous-label", type: String },
765
+ nextLabel: { attribute: "next-label", type: String },
766
+ closeLabel: { attribute: "close-label", type: String },
767
+ value: { attribute: false },
768
+ status: { attribute: false },
769
+ statusTitle: { attribute: false }
770
+ };
771
+ constructor() {
772
+ super();
773
+ this.open = false;
774
+ this.disabled = false;
775
+ this.navigationDisabled = false;
776
+ this.label = null;
777
+ this.inputLabel = null;
778
+ this.placeholder = null;
779
+ this.maxlength = null;
780
+ this.previousLabel = null;
781
+ this.nextLabel = null;
782
+ this.closeLabel = null;
783
+ this.value = "";
784
+ this.status = "";
785
+ this.statusTitle = "";
786
+ }
787
+ get input() {
788
+ return this.renderRoot.querySelector("input");
789
+ }
790
+ /** Open, focus the field and select its query. */
791
+ show() {
792
+ this.open = true;
793
+ this.input?.focus();
794
+ this.input?.select();
795
+ }
796
+ /** Close and emit `office-find-close`. */
797
+ close() {
798
+ if (!present(this.open)) return;
799
+ this.open = false;
800
+ this.fire("office-find-close", {});
801
+ }
802
+ step(direction) {
803
+ if (!present(this.navigationDisabled)) this.fire("office-find-step", { direction });
804
+ }
805
+ onInput(event) {
806
+ this.value = event.target.value;
807
+ this.fire("office-find-input", { query: this.value });
808
+ }
809
+ onKey(event) {
810
+ if (event.isComposing) return;
811
+ if (event.key === "Enter") {
812
+ event.preventDefault();
813
+ this.step(event.shiftKey ? "previous" : "next");
814
+ } else if (event.key === "Escape") {
815
+ event.preventDefault();
816
+ event.stopPropagation();
817
+ if (this.value) {
818
+ this.value = "";
819
+ this.fire("office-find-input", { query: "" });
820
+ } else this.close();
821
+ }
822
+ }
823
+ render() {
824
+ const max = Number(this.maxlength);
825
+ const disabled = present(this.disabled);
826
+ const stuck = present(this.navigationDisabled) || disabled;
827
+ const close = this.closeLabel ?? "Close Find";
828
+ return html`
829
+ <div role="search" aria-label=${this.label ?? "Find"}>
830
+ <label>
831
+ Find
832
+ <input
833
+ type="search"
834
+ autocomplete="off"
835
+ spellcheck="false"
836
+ aria-describedby="status"
837
+ aria-label=${this.inputLabel ?? "Find"}
838
+ placeholder=${this.placeholder ?? ""}
839
+ maxlength=${ifDefined(Number.isInteger(max) && max > 0 ? String(max) : void 0)}
840
+ .value=${live(this.value)}
841
+ ?disabled=${disabled}
842
+ @input=${this.onInput}
843
+ @keydown=${this.onKey}
844
+ />
845
+ </label>
846
+ <button
847
+ type="button"
848
+ data-action="previous"
849
+ aria-label=${this.previousLabel ?? "Previous result"}
850
+ ?disabled=${stuck}
851
+ @click=${() => this.step("previous")}
852
+ >Previous</button
853
+ >
854
+ <button
855
+ type="button"
856
+ data-action="next"
857
+ aria-label=${this.nextLabel ?? "Next result"}
858
+ ?disabled=${stuck}
859
+ @click=${() => this.step("next")}
860
+ >Next</button
861
+ >
862
+ <span
863
+ id="status"
864
+ class="status"
865
+ role="status"
866
+ aria-live="polite"
867
+ aria-atomic="true"
868
+ title=${this.statusTitle}
869
+ >${this.status}</span
870
+ >
871
+ </div>
872
+ <button
873
+ type="button"
874
+ class="close"
875
+ data-action="close"
876
+ aria-label=${close}
877
+ title="${close} (Esc)"
878
+ @click=${this.close}
879
+ >×</button
880
+ >
881
+ `;
882
+ }
883
+ };
884
+ var defineFindBar = definer("office-ui-find-bar", () => OfficeUiFindBar);
885
+
886
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/print-preview.css?raw
887
+ var print_preview_default = ":host {\n display: block;\n max-width: var(--office-print-preview-width);\n font: var(--office-font-size-sm) var(--office-font);\n color: var(--office-foreground);\n}\n.sheet {\n padding: var(--office-space-3);\n border: var(--office-border-width) solid var(--office-border);\n background: var(--office-paper);\n box-shadow: var(--office-shadow-sm);\n}\n.sheet > * {\n display: block;\n width: 100%;\n height: auto;\n}\n.empty {\n color: var(--office-muted-foreground);\n}\n.nav {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-2);\n margin-top: var(--office-space-2);\n}\n.nav[hidden] {\n display: none;\n}\nbutton {\n min-width: var(--office-target-size);\n min-height: var(--office-target-size);\n font: inherit;\n color: inherit;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-sm);\n background: var(--office-background);\n cursor: pointer;\n}\nbutton:disabled {\n opacity: 0.5;\n cursor: default;\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\n";
888
+
889
+ // src/chrome/print-preview.ts
890
+ var OfficeUiPrintPreview = class extends OfficeElement {
891
+ static styles = controlStyles(print_preview_default);
892
+ static properties = {
893
+ // `pages` clamps the index and `index` clamps itself to the pages, so both have accessors.
894
+ label: { type: String },
895
+ emptyLabel: { attribute: "empty-label", type: String }
896
+ };
897
+ list = [];
898
+ current = 0;
899
+ clone;
900
+ constructor() {
901
+ super();
902
+ this.label = null;
903
+ this.emptyLabel = null;
904
+ }
905
+ get pages() {
906
+ return this.list;
907
+ }
908
+ set pages(value) {
909
+ this.list = value;
910
+ this.current = Math.min(this.current, Math.max(0, value.length - 1));
911
+ this.requestUpdate("pages");
912
+ }
913
+ get index() {
914
+ return this.current;
915
+ }
916
+ set index(value) {
917
+ const next = Math.max(0, Math.min(this.list.length - 1, Math.trunc(value) || 0));
918
+ if (next === this.current) return;
919
+ this.current = next;
920
+ this.requestUpdate("index");
921
+ }
922
+ go(index) {
923
+ const before = this.current;
924
+ this.index = index;
925
+ if (this.current !== before) this.fire("office-print-preview-page", { index: this.current });
926
+ }
927
+ /** The shown page is an inert copy: no tabindex, no inline style. */
928
+ willUpdate(changed) {
929
+ if (!changed.has("pages") && !changed.has("index") && this.clone !== void 0)
930
+ return;
931
+ const page = this.list[this.current];
932
+ if (!page) {
933
+ this.clone = void 0;
934
+ return;
935
+ }
936
+ const clone = this.ownerDocument.importNode(page, true);
937
+ if (clone instanceof Element) {
938
+ clone.removeAttribute("style");
939
+ clone.removeAttribute("tabindex");
940
+ clone.querySelectorAll("[tabindex]").forEach((node) => node.removeAttribute("tabindex"));
941
+ }
942
+ this.clone = clone;
943
+ }
944
+ render() {
945
+ const total = this.list.length;
946
+ const label = this.label ?? "Print preview";
947
+ return html`
948
+ <div
949
+ class="sheet"
950
+ role="img"
951
+ aria-label=${total > 1 ? `${label}, page ${this.current + 1} of ${total}` : label}
952
+ >${this.clone ?? html`<p class="empty">${this.emptyLabel ?? "Nothing to print."}</p>`}</div
953
+ >
954
+ <div class="nav" ?hidden=${total < 2}>
955
+ <button
956
+ type="button"
957
+ aria-label="Previous page"
958
+ ?disabled=${this.current === 0}
959
+ @click=${() => this.go(this.current - 1)}
960
+ >‹</button
961
+ >
962
+ <span>${this.current + 1} of ${total}</span>
963
+ <button
964
+ type="button"
965
+ aria-label="Next page"
966
+ ?disabled=${this.current >= total - 1}
967
+ @click=${() => this.go(this.current + 1)}
968
+ >›</button
969
+ >
970
+ </div>
971
+ `;
972
+ }
973
+ };
974
+ var definePrintPreview = definer("office-ui-print-preview", () => OfficeUiPrintPreview);
975
+
976
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-tabs.css?raw
977
+ var ribbon_tabs_default = ":host {\n display: block;\n min-width: 0;\n}\n.head {\n display: flex;\n align-items: stretch;\n min-width: 0;\n border-bottom: var(--office-border-width) solid var(--office-border);\n}\n.file {\n flex: none;\n min-height: var(--office-ribbon-tab-height);\n margin: 0 var(--office-space-1) 0 0;\n padding: var(--office-space-1-5) var(--office-space-4);\n border: 0;\n border-radius: 0;\n background: var(--office-accent);\n color: var(--office-accent-foreground);\n font: inherit;\n font-size: var(--office-font-size);\n cursor: pointer;\n}\n.file:hover {\n background: var(--office-accent-hover);\n}\n.file[hidden] {\n display: none;\n}\n[role='tablist'] {\n display: flex;\n flex: 0 1 auto;\n align-items: stretch;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n[role='tab'] {\n position: relative;\n flex: none;\n min-height: var(--office-ribbon-tab-height);\n padding: var(--office-space-1-5) var(--office-space-3);\n border: 0;\n border-radius: 0;\n background: transparent;\n color: var(--office-muted-foreground);\n font: inherit;\n font-size: var(--office-font-size);\n cursor: pointer;\n}\n[role='tab']:hover {\n color: var(--office-foreground);\n background: var(--office-surface);\n}\n[role='tab'][aria-selected='true'] {\n color: var(--office-accent);\n}\n[role='tab'][aria-selected='true']::after {\n content: '';\n position: absolute;\n inset: auto 0 0;\n height: var(--office-tab-indicator-width);\n background: var(--office-accent);\n}\n.file:focus-visible,\n[role='tab']:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(-1 * var(--office-focus-width));\n}\n::slotted([slot='search']) {\n align-self: center;\n}\n\n@media (max-width: 760px), (pointer: coarse) {\n .file,\n [role='tab'] {\n min-height: var(--office-target-size-touch);\n }\n .file {\n min-width: var(--office-ribbon-file-min-width);\n }\n}\n@media (forced-colors: active) {\n .file {\n border: var(--office-border-width) solid ButtonText;\n }\n [role='tab'][aria-selected='true']::after {\n background: Highlight;\n }\n}\n";
978
+
979
+ // src/ribbon/ribbon-tabs.ts
980
+ var OfficeUiRibbon = class extends OfficeElement {
981
+ static styles = controlStyles(ribbon_tabs_default);
982
+ static properties = {
983
+ // The default selection is the first panel, so `selected` has an accessor below.
984
+ selected: { type: String, noAccessor: true },
985
+ label: { type: String },
986
+ fileLabel: { attribute: "file-label", type: String },
987
+ noFile: { attribute: "no-file", ...flag },
988
+ fileExpanded: { attribute: "file-expanded", type: String },
989
+ fileKeytip: { attribute: "file-keytip", type: String },
990
+ tabs: { state: true }
991
+ };
992
+ chosen = null;
993
+ constructor() {
994
+ super();
995
+ this.label = null;
996
+ this.fileLabel = null;
997
+ this.noFile = false;
998
+ this.fileExpanded = null;
999
+ this.fileKeytip = null;
1000
+ this.tabs = [];
1001
+ }
1002
+ get selected() {
1003
+ return this.chosen ?? this.panels()[0]?.dataset.ribbonTab ?? "";
1004
+ }
1005
+ set selected(tab) {
1006
+ this.chosen = tab;
1007
+ this.reflectAttribute("selected", tab, {});
1008
+ this.requestUpdate("selected");
1009
+ }
1010
+ /** Focus the File button (for example when the backstage it opened closes). */
1011
+ focusFile() {
1012
+ this.renderRoot.querySelector(".file")?.focus();
1013
+ }
1014
+ /** Focus the selected tab. */
1015
+ focusTab() {
1016
+ this.renderRoot.querySelector('[role="tab"][aria-selected="true"]')?.focus();
1017
+ }
1018
+ panels() {
1019
+ return [...this.children].filter(
1020
+ (child) => child instanceof HTMLElement && !!child.dataset.ribbonTab
1021
+ );
1022
+ }
1023
+ /** Read the tabs off the panels (on connection and whenever the slotted panels change). */
1024
+ build() {
1025
+ this.tabs = this.panels().map((panel) => {
1026
+ const id = panel.dataset.ribbonTab;
1027
+ panel.setAttribute("role", "tabpanel");
1028
+ panel.setAttribute("aria-label", panel.dataset.label ?? id);
1029
+ return {
1030
+ id,
1031
+ label: panel.dataset.label ?? id,
1032
+ ...panel.dataset.tabKeytip ? {
1033
+ keytip: panel.dataset.tabKeytip,
1034
+ ...panel.id ? { keytipPanel: panel.id } : {}
1035
+ } : {}
1036
+ };
1037
+ });
1038
+ }
1039
+ choose(tab) {
1040
+ if (tab === this.selected) return;
1041
+ if (this.fire("office-ribbon-select", { tab }, true)) this.selected = tab;
1042
+ }
1043
+ onTabClick(event) {
1044
+ const tab = event.target.closest?.('[role="tab"]');
1045
+ if (tab) this.choose(tab.dataset.tab);
1046
+ }
1047
+ onKey(event) {
1048
+ const tabs = [...this.renderRoot.querySelectorAll('[role="tab"]')];
1049
+ const focused = event.target.closest?.('[role="tab"]');
1050
+ const at = focused ? tabs.indexOf(focused) : tabs.findIndex((tab) => tab.dataset.tab === this.selected);
1051
+ const step = { ArrowRight: 1, ArrowLeft: -1 }[event.key];
1052
+ const next = event.key === "Home" ? tabs[0] : event.key === "End" ? tabs.at(-1) : step ? tabs[(at + step + tabs.length) % tabs.length] : void 0;
1053
+ if (!next) return;
1054
+ event.preventDefault();
1055
+ this.choose(next.dataset.tab);
1056
+ this.focusTab();
1057
+ }
1058
+ connectedCallback() {
1059
+ this.build();
1060
+ super.connectedCallback();
1061
+ }
1062
+ /** Exactly one panel shows. */
1063
+ updated() {
1064
+ const selected = this.selected;
1065
+ for (const panel of this.panels()) panel.hidden = panel.dataset.ribbonTab !== selected;
1066
+ }
1067
+ render() {
1068
+ const selected = this.selected;
1069
+ return html`
1070
+ <div class="head" part="head">
1071
+ <slot name="quick-access"></slot>
1072
+ <button
1073
+ class="file"
1074
+ part="file"
1075
+ type="button"
1076
+ aria-haspopup="dialog"
1077
+ aria-expanded=${String(this.fileExpanded === "true")}
1078
+ data-keytip=${ifDefined(this.fileKeytip ?? void 0)}
1079
+ ?hidden=${present(this.noFile)}
1080
+ @click=${() => this.fire("office-ribbon-file", {})}
1081
+ >${this.fileLabel ?? "File"}</button
1082
+ >
1083
+ <div
1084
+ role="tablist"
1085
+ part="tabs"
1086
+ aria-label=${this.label ?? "Ribbon"}
1087
+ @click=${this.onTabClick}
1088
+ @keydown=${this.onKey}
1089
+ >
1090
+ ${this.tabs.map(
1091
+ (tab) => html`<button
1092
+ type="button"
1093
+ role="tab"
1094
+ id="tab-${tab.id}"
1095
+ data-tab=${tab.id}
1096
+ data-keytip=${ifDefined(tab.keytip)}
1097
+ data-keytip-panel=${ifDefined(tab.keytipPanel)}
1098
+ aria-selected=${String(tab.id === selected)}
1099
+ tabindex=${tab.id === selected ? 0 : -1}
1100
+ >${tab.label}</button
1101
+ >`
1102
+ )}
1103
+ </div>
1104
+ <slot name="search"></slot>
1105
+ <slot name="end"></slot>
1106
+ </div>
1107
+ <slot @slotchange=${this.build}></slot>
1108
+ `;
1109
+ }
1110
+ };
1111
+ var defineRibbon = definer("office-ui-ribbon", () => OfficeUiRibbon);
1112
+
1113
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-section.css?raw
1114
+ var ribbon_section_default = ":host {\n display: inline-flex;\n align-items: stretch;\n}\n::slotted(*) {\n flex: none;\n}\n";
1115
+
1116
+ // src/ribbon/ribbon-section.ts
1117
+ var COLUMN_STYLE = { display: "flex", flexDirection: "column", gap: tok("--office-space-0") };
1118
+ function setOptional(el, name, value) {
1119
+ if (value === void 0) el.removeAttribute(name);
1120
+ else el.setAttribute(name, String(value));
1121
+ }
1122
+ function syncCommand(el, command, idAttribute) {
1123
+ el.setAttribute(idAttribute, command.id);
1124
+ el.setAttribute("label", command.label);
1125
+ el.setAttribute("title", command.title ?? command.label);
1126
+ el.setAttribute("icon", command.icon);
1127
+ if (command.badge) el.setAttribute("badge", String(command.badge));
1128
+ else el.removeAttribute("badge");
1129
+ if (command.size) el.setAttribute("size", command.size);
1130
+ for (const name of ["disabled", "active", "compact", "hidden", "caret"])
1131
+ el.toggleAttribute(name, command[name] === true);
1132
+ setOptional(el, "pressed", command.pressed);
1133
+ setOptional(el, "expanded", command.expanded);
1134
+ }
1135
+ var OfficeUiRibbonSection = class extends OfficeElement {
1136
+ static styles = controlStyles(ribbon_section_default);
1137
+ static groupTag = "office-ui-ribbon-group";
1138
+ static commandTag = "office-ui-button";
1139
+ static groupIdAttribute = "data-ribbon-group";
1140
+ static commandIdAttribute = "command";
1141
+ #groups = [];
1142
+ #groupNodes = /* @__PURE__ */ new Map();
1143
+ #commandNodes = /* @__PURE__ */ new Map();
1144
+ render() {
1145
+ return html`<slot></slot>`;
1146
+ }
1147
+ get groups() {
1148
+ return this.#groups;
1149
+ }
1150
+ set groups(value) {
1151
+ this.#groups = value ?? [];
1152
+ this.#render();
1153
+ }
1154
+ connectedCallback() {
1155
+ super.connectedCallback();
1156
+ this.#render();
1157
+ }
1158
+ /**
1159
+ * The groups and commands are light-DOM children keyed by id and patched in place (so focus
1160
+ * and a product's tags and id attributes survive), which a shadow template cannot express.
1161
+ */
1162
+ #render() {
1163
+ const statics = this.constructor;
1164
+ const doc = this.ownerDocument;
1165
+ const model = this.#groups;
1166
+ const wantedGroups = new Set(model.map((group) => group.id));
1167
+ const wantedCommands = new Set(model.flatMap((g) => g.commands.map((c) => c.id)));
1168
+ for (const [id, el] of this.#commandNodes)
1169
+ if (!wantedCommands.has(id)) {
1170
+ el.remove();
1171
+ this.#commandNodes.delete(id);
1172
+ }
1173
+ for (const [id, group] of this.#groupNodes)
1174
+ if (!wantedGroups.has(id)) {
1175
+ group.el.remove();
1176
+ this.#groupNodes.delete(id);
1177
+ }
1178
+ for (const [index, group] of model.entries()) {
1179
+ let node = this.#groupNodes.get(group.id);
1180
+ if (!node) {
1181
+ node = { el: doc.createElement(statics.groupTag), columns: /* @__PURE__ */ new Map() };
1182
+ this.#groupNodes.set(group.id, node);
1183
+ }
1184
+ node.el.setAttribute(statics.groupIdAttribute, group.id);
1185
+ node.el.setAttribute("label", group.label);
1186
+ if (this.children[index] !== node.el)
1187
+ this.insertBefore(node.el, this.children[index] ?? null);
1188
+ const offsets = /* @__PURE__ */ new Map();
1189
+ for (const command of group.commands) {
1190
+ let parent = node.el;
1191
+ if (command.column !== void 0) {
1192
+ let column = node.columns.get(command.column);
1193
+ if (!column) {
1194
+ column = doc.createElement("div");
1195
+ Object.assign(column.style, COLUMN_STYLE);
1196
+ node.columns.set(command.column, column);
1197
+ node.el.append(column);
1198
+ }
1199
+ parent = column;
1200
+ }
1201
+ let el = this.#commandNodes.get(command.id);
1202
+ if (!el) {
1203
+ el = doc.createElement(statics.commandTag);
1204
+ this.#commandNodes.set(command.id, el);
1205
+ }
1206
+ syncCommand(el, command, statics.commandIdAttribute);
1207
+ const offset = offsets.get(parent) ?? 0;
1208
+ if (parent.children[offset] !== el)
1209
+ parent.insertBefore(el, parent.children[offset] ?? null);
1210
+ offsets.set(parent, offset + 1);
1211
+ }
1212
+ }
1213
+ }
1214
+ };
1215
+ var defineRibbonSection = definer("office-ui-ribbon-section", () => OfficeUiRibbonSection);
1216
+
1217
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/gallery.css?raw
1218
+ var gallery_default = "[data-office-gallery] {\n display: inline-flex;\n position: relative;\n flex: none;\n color: var(--office-foreground);\n}\n[data-office-gallery] .gallery-view {\n display: contents;\n}\n[data-office-gallery] button {\n box-sizing: border-box;\n font: inherit;\n color: inherit;\n cursor: pointer;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius);\n background: var(--office-background);\n}\n[data-office-gallery] button:hover:not(:disabled) {\n background: var(--office-selected);\n}\n[data-office-gallery] button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n[data-office-gallery] button:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n[data-office-gallery] .trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-1-5);\n min-height: var(--office-gallery-trigger-height);\n padding: 0 var(--office-space-1-5);\n font-size: var(--office-font-size-sm);\n white-space: nowrap;\n}\n[data-office-gallery]:not([mode='inline']) .trigger {\n border-color: transparent;\n background: transparent;\n}\n[data-office-gallery] .trigger svg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n[data-office-gallery][mode='inline'] .trigger,\n[data-office-gallery][chevron-only] .trigger {\n align-self: stretch;\n padding: 0 var(--office-space-1);\n}\n[data-office-gallery] .strip {\n display: flex;\n gap: var(--office-space-0);\n padding: var(--office-space-0);\n align-items: center;\n}\n[data-office-gallery] .tile {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n padding: var(--office-space-0);\n}\n[data-office-gallery] .tile[aria-pressed='true'] {\n border: var(--office-border-width-thick) solid var(--office-accent);\n background: color-mix(in srgb, var(--office-accent) 15%, var(--office-background));\n}\n[data-office-gallery] .tile svg {\n max-width: 100%;\n max-height: 100%;\n}\n[data-office-gallery][mode='inline'] .strip {\n gap: 0;\n border: var(--office-border-width) solid var(--office-border);\n border-inline-end: 0;\n border-radius: var(--office-radius-sm) 0 0 var(--office-radius-sm);\n background: var(--office-gallery-strip-background);\n}\n[data-office-gallery][mode='inline'] .trigger {\n border-radius: 0 var(--office-radius-sm) var(--office-radius-sm) 0;\n background: var(--office-gallery-strip-background);\n}\n[data-office-gallery] .trigger.command {\n border-color: transparent;\n background: transparent;\n justify-content: flex-start;\n}\n[data-office-gallery] .trigger.command:hover:not(:disabled) {\n border-color: var(--office-border);\n background: var(--office-selected);\n}\n[data-office-gallery] .trigger.command svg {\n color: var(--office-command-icon-color);\n}\n[data-office-gallery] .trigger.command-large {\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n gap: var(--office-space-0);\n min-width: var(--office-gallery-command-min-width);\n max-width: var(--office-gallery-command-max-width);\n height: var(--office-gallery-command-height);\n padding: var(--office-space-1);\n font-size: var(--office-font-size-2xs);\n line-height: var(--office-line-height-tight);\n white-space: normal;\n text-align: center;\n}\n[data-office-gallery] .trigger.command-large svg {\n width: var(--office-gallery-command-icon);\n height: var(--office-gallery-command-icon);\n}\n[data-office-gallery] .popup {\n box-sizing: border-box;\n position: fixed;\n z-index: var(--office-z-popover);\n max-width: calc(100vw - var(--office-space-4));\n max-height: var(--office-gallery-popup-max-height);\n overflow: auto;\n padding: var(--office-space-2);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n color: var(--office-popover-foreground);\n box-shadow: var(--office-shadow-lg);\n}\n[data-office-gallery] .popup[hidden] {\n display: none;\n}\n[data-office-gallery] .grid {\n display: grid;\n gap: var(--office-space-1);\n width: max-content;\n max-width: 100%;\n}\n[data-office-gallery] .heading {\n margin: var(--office-space-1) 0 var(--office-space-1-5);\n font-size: var(--office-font-size-xs);\n font-weight: var(--office-font-weight-bold);\n}\n[data-office-gallery] .section + .section {\n margin-top: var(--office-space-2-5);\n}\n@media (pointer: coarse), (max-width: 1023px) {\n [data-office-gallery] .trigger,\n [data-office-gallery] .tile {\n min-height: var(--office-target-size-touch);\n min-width: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n [data-office-gallery] button,\n [data-office-gallery] .popup {\n color: ButtonText;\n background: Canvas;\n border-color: ButtonText;\n }\n [data-office-gallery] button:disabled {\n color: GrayText;\n opacity: 1;\n }\n [data-office-gallery] button:focus-visible,\n [data-office-gallery] .tile[aria-pressed='true'] {\n outline: var(--office-focus-width) solid Highlight;\n border-color: Highlight;\n }\n}\n";
1219
+
1220
+ // src/ribbon/gallery-styles.ts
1221
+ var GALLERY_LIGHT_CSS = withTokens(gallery_default);
1222
+ var injected = /* @__PURE__ */ new WeakSet();
1223
+ function attachGalleryStyles(host) {
1224
+ const root = host.getRootNode();
1225
+ if (injected.has(root)) return;
1226
+ const doc = host.ownerDocument;
1227
+ const Sheet = doc.defaultView?.CSSStyleSheet;
1228
+ if (Sheet && "adoptedStyleSheets" in root && "replaceSync" in Sheet.prototype) {
1229
+ try {
1230
+ const sheet = new Sheet();
1231
+ sheet.replaceSync(GALLERY_LIGHT_CSS);
1232
+ root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
1233
+ injected.add(root);
1234
+ return;
1235
+ } catch {
1236
+ }
1237
+ }
1238
+ const style = doc.createElement("style");
1239
+ style.dataset.officeGalleryStyles = "";
1240
+ style.textContent = GALLERY_LIGHT_CSS;
1241
+ (root.nodeType === 9 ? doc.head : root).append(style);
1242
+ injected.add(root);
1243
+ }
1244
+
1245
+ // src/ribbon/safe-svg.ts
1246
+ var BLOCKED_ELEMENTS = /* @__PURE__ */ new Set(["script", "foreignobject", "iframe", "object", "embed"]);
1247
+ function parseSvgPreview(doc, svg2) {
1248
+ const Parser = doc.defaultView?.DOMParser ?? globalThis.DOMParser;
1249
+ if (typeof Parser !== "function") return null;
1250
+ const parsed = new Parser().parseFromString(svg2, "image/svg+xml");
1251
+ const root = parsed.documentElement;
1252
+ if (root.localName !== "svg" || parsed.getElementsByTagName("parsererror").length > 0)
1253
+ return null;
1254
+ for (const node of Array.from(root.querySelectorAll("*"))) {
1255
+ if (BLOCKED_ELEMENTS.has(node.localName.toLowerCase())) {
1256
+ node.remove();
1257
+ continue;
1258
+ }
1259
+ for (const attribute of Array.from(node.attributes))
1260
+ if (/^on/iu.test(attribute.name) || /^\s*javascript:/iu.test(attribute.value))
1261
+ node.removeAttribute(attribute.name);
1262
+ }
1263
+ for (const attribute of Array.from(root.attributes))
1264
+ if (/^on/iu.test(attribute.name) || /^\s*javascript:/iu.test(attribute.value))
1265
+ root.removeAttribute(attribute.name);
1266
+ return doc.importNode(root, true);
1267
+ }
1268
+
1269
+ // src/ribbon/gallery.ts
1270
+ var HOST_CSS = ":host { display: inline-flex; position: relative; flex: none; }";
1271
+ var OfficeUiGallery = class extends OfficeElement {
1272
+ static pickEvent = "office-gallery-pick";
1273
+ static triggerAttribute = "data-gallery";
1274
+ static popupAttribute = "data-gallery-popup";
1275
+ static itemAttribute = "data-gallery-item";
1276
+ static compactAttribute = null;
1277
+ static styles = controlStyles(HOST_CSS);
1278
+ /** `mode`, `chevron-only` and `icon` are read from the attributes, as before. */
1279
+ static get observedAttributes() {
1280
+ return [...super.observedAttributes, "mode", "chevron-only", "icon"];
1281
+ }
1282
+ model;
1283
+ locked = false;
1284
+ opened = false;
1285
+ attributeChangedCallback(name, old, value) {
1286
+ super.attributeChangedCallback(name, old, value);
1287
+ this.requestUpdate();
1288
+ }
1289
+ get state() {
1290
+ return this.model;
1291
+ }
1292
+ set state(value) {
1293
+ this.model = value ?? void 0;
1294
+ this.requestUpdate("state");
1295
+ }
1296
+ get disabled() {
1297
+ return this.locked;
1298
+ }
1299
+ set disabled(value) {
1300
+ this.locked = present(value);
1301
+ this.requestUpdate("disabled");
1302
+ }
1303
+ get open() {
1304
+ return this.opened;
1305
+ }
1306
+ set open(value) {
1307
+ const next = present(value) && !this.unavailable();
1308
+ if (next === this.opened) return;
1309
+ this.opened = next;
1310
+ this.cleanup();
1311
+ this.requestUpdate("open");
1312
+ if (next && this.isConnected) {
1313
+ const doc = this.ownerDocument;
1314
+ doc.addEventListener("pointerdown", this.onOutside, true);
1315
+ doc.addEventListener("keydown", this.onEscape);
1316
+ doc.defaultView?.addEventListener("resize", this.position);
1317
+ doc.addEventListener("scroll", this.position, true);
1318
+ this.position();
1319
+ }
1320
+ }
1321
+ close() {
1322
+ this.open = false;
1323
+ }
1324
+ get trigger() {
1325
+ this.ensureRendered();
1326
+ return this.querySelector(".trigger");
1327
+ }
1328
+ /** The open popup; while closed it is not in the DOM, so this is a detached hidden placeholder. */
1329
+ get popup() {
1330
+ this.ensureRendered();
1331
+ return this.querySelector(".popup") ?? this.closedPopup;
1332
+ }
1333
+ closedPopup = Object.assign(this.ownerDocument.createElement("div"), {
1334
+ className: "popup",
1335
+ hidden: true
1336
+ });
1337
+ /** The trigger glyph: `command.icon` for a command, else the `icon` attribute. */
1338
+ triggerIcon() {
1339
+ const name = this.model?.command?.icon ?? this.getAttribute("icon");
1340
+ if (!name) return null;
1341
+ const svg2 = createIconSvg(this.ownerDocument);
1342
+ return paintIcon(svg2, name) ? svg2 : null;
1343
+ }
1344
+ connectedCallback() {
1345
+ this.setAttribute("data-office-gallery", "");
1346
+ attachGalleryStyles(this);
1347
+ super.connectedCallback();
1348
+ this.markHost();
1349
+ }
1350
+ /** Host attributes (never written before the element is connected). */
1351
+ markHost() {
1352
+ if (!this.isConnected) return;
1353
+ this.toggleAttribute("data-command", Boolean(this.model?.command));
1354
+ this.toggleAttribute("data-command-large", Boolean(this.model?.command?.large));
1355
+ }
1356
+ disconnectedCallback() {
1357
+ super.disconnectedCallback();
1358
+ this.close();
1359
+ this.cleanup();
1360
+ }
1361
+ unavailable() {
1362
+ const state = this.model;
1363
+ return this.locked || !state || state.disabled === true || !state.sections.some((section) => section.items.length > 0);
1364
+ }
1365
+ pick(itemId) {
1366
+ const state = this.model;
1367
+ if (this.unavailable() || !state?.sections.some((section) => section.items.some((item) => item.id === itemId)))
1368
+ return;
1369
+ this.close();
1370
+ this.trigger.focus();
1371
+ this.fire(this.constructor.pickEvent, { gallery: state.id, itemId });
1372
+ }
1373
+ onTrigger() {
1374
+ if (this.model?.command) {
1375
+ this.pick(this.model.sections[0]?.items[0]?.id ?? "");
1376
+ return;
1377
+ }
1378
+ this.open = !this.open;
1379
+ }
1380
+ onKey(event) {
1381
+ const trigger = this.trigger;
1382
+ if (event.key === "Escape" && this.open) {
1383
+ event.stopPropagation();
1384
+ event.preventDefault();
1385
+ this.close();
1386
+ trigger.focus();
1387
+ }
1388
+ if (event.key === "ArrowDown" && event.target === trigger) {
1389
+ event.stopPropagation();
1390
+ event.preventDefault();
1391
+ this.open = true;
1392
+ this.popup.querySelector("button:not(:disabled)")?.focus();
1393
+ }
1394
+ if (event.key === "Enter" || event.key === " ") event.stopPropagation();
1395
+ }
1396
+ onOutside = (event) => {
1397
+ if (!event.composedPath().includes(this)) this.close();
1398
+ };
1399
+ onEscape = (event) => {
1400
+ if (event.key !== "Escape") return;
1401
+ event.stopPropagation();
1402
+ this.close();
1403
+ this.trigger.focus();
1404
+ };
1405
+ position = () => {
1406
+ const window = this.ownerDocument.defaultView;
1407
+ const popup = this.querySelector(".popup");
1408
+ if (!window || !popup) return;
1409
+ const anchor = this.trigger.getBoundingClientRect();
1410
+ const box = popup.getBoundingClientRect();
1411
+ const margin = 8;
1412
+ popup.style.left = `${Math.max(margin, Math.min(anchor.left, window.innerWidth - box.width - margin))}px`;
1413
+ popup.style.top = `${Math.max(margin, Math.min(anchor.bottom + 4, window.innerHeight - box.height - margin))}px`;
1414
+ };
1415
+ cleanup() {
1416
+ const doc = this.ownerDocument;
1417
+ doc.removeEventListener("pointerdown", this.onOutside, true);
1418
+ doc.removeEventListener("keydown", this.onEscape);
1419
+ doc.defaultView?.removeEventListener("resize", this.position);
1420
+ doc.removeEventListener("scroll", this.position, true);
1421
+ }
1422
+ // ---- Light-DOM template ---------------------------------------------------------------
1423
+ tile(item, width, height, disabled) {
1424
+ const { itemAttribute, compactAttribute } = this.constructor;
1425
+ const preview = item.preview ? parseSvgPreview(this.ownerDocument, item.preview) : null;
1426
+ return html$1`<button
1427
+ type="button"
1428
+ class="tile"
1429
+ ${unsafeStatic(compactAttribute ?? "")}
1430
+ ${unsafeStatic(itemAttribute)}=${item.id}
1431
+ ?disabled=${disabled}
1432
+ aria-pressed=${String(item.applied === true)}
1433
+ aria-label=${item.label}
1434
+ title=${item.label}
1435
+ style="width:${width + 6}px;height:${height + 6}px"
1436
+ @click=${() => this.pick(item.id)}
1437
+ >${preview}</button>`;
1438
+ }
1439
+ lightTemplate() {
1440
+ const statics = this.constructor;
1441
+ const state = this.model;
1442
+ const disabled = this.unavailable();
1443
+ const inline = this.getAttribute("mode") === "inline" && !this.hasAttribute("chevron-only");
1444
+ const chevronOnly = this.hasAttribute("chevron-only");
1445
+ const command = state?.command;
1446
+ const title = state?.label ?? "";
1447
+ const first = state?.sections[0];
1448
+ const inlineItems = state?.inline ?? first?.items ?? [];
1449
+ const triggerTitle = command ? command.hint ?? title : inline ? state?.moreLabel ?? `More ${title}` : title;
1450
+ const icon = this.triggerIcon() ?? createIconSvg(this.ownerDocument);
1451
+ const trigger = html$1`<button
1452
+ type="button"
1453
+ class="trigger${command ? " command" : ""}${command?.large ? " command-large" : ""}"
1454
+ ${unsafeStatic(statics.compactAttribute ?? "")}
1455
+ ${unsafeStatic(statics.triggerAttribute)}=${ifDefined(state?.id)}
1456
+ title=${triggerTitle}
1457
+ aria-label=${command ? title : triggerTitle}
1458
+ aria-haspopup=${ifDefined(command ? void 0 : "dialog")}
1459
+ aria-expanded=${ifDefined(command || !state ? void 0 : String(this.opened))}
1460
+ ?disabled=${!state || disabled}
1461
+ @click=${this.onTrigger}
1462
+ >${command || !inline && !chevronOnly ? html`${icon}<span>${title}</span>` : ""}${command ? "" : html`<span aria-hidden="true">⌄</span>`}</button>`;
1463
+ const popup = html$1`<div
1464
+ class="popup"
1465
+ role="dialog"
1466
+ aria-label=${title}
1467
+ ${unsafeStatic(statics.popupAttribute)}=${ifDefined(state?.id)}
1468
+ >${(state?.sections ?? []).map((section) => this.section(section, disabled))}</div>`;
1469
+ return html`<span class="gallery-view" @keydown=${this.onKey}>
1470
+ <div class="strip" ?hidden=${!inline} style=${inline ? "" : "display:none"}>
1471
+ ${inline ? repeat(
1472
+ inlineItems,
1473
+ (item) => item.id,
1474
+ (item) => this.tile(item, first?.tileWidth ?? 40, first?.tileHeight ?? 30, disabled)
1475
+ ) : ""}
1476
+ </div>
1477
+ ${trigger}${this.opened ? popup : ""}
1478
+ </span>`;
1479
+ }
1480
+ section(section, disabled) {
1481
+ return html`<section class="section">
1482
+ ${section.title ? html`<div class="heading">${section.title}</div>` : ""}
1483
+ <div class="grid" style="grid-template-columns:repeat(${section.columns}, minmax(0, 1fr))">
1484
+ ${repeat(
1485
+ section.items,
1486
+ (item) => item.id,
1487
+ (item) => this.tile(item, section.tileWidth, section.tileHeight, disabled)
1488
+ )}
1489
+ </div>
1490
+ </section>`;
1491
+ }
1492
+ /** The tiles are light-DOM children of the host, shown through this slot. */
1493
+ render() {
1494
+ return html`<slot></slot>`;
1495
+ }
1496
+ updated() {
1497
+ const state = this.model;
1498
+ if (state && this.unavailable() && this.opened) this.close();
1499
+ if (this.detachedFirstRender) return;
1500
+ render(this.lightTemplate(), this, { host: this });
1501
+ this.markHost();
1502
+ if (this.opened) this.position();
1503
+ }
1504
+ };
1505
+ var defineGallery = definer("office-ui-gallery", () => OfficeUiGallery);
1506
+
1507
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/ruler.css?raw
1508
+ var ruler_default = ":host {\n display: block;\n box-sizing: border-box;\n background: var(--office-background);\n color: var(--office-muted-foreground);\n overflow: hidden;\n font-size: var(--office-font-size-3xs);\n font-family: var(--office-font);\n}\n:host(:not([orientation='vertical'])) {\n height: var(--office-ruler-size);\n border-bottom: var(--office-border-width) solid var(--office-border);\n}\n:host([orientation='vertical']) {\n width: var(--office-ruler-size);\n border-inline-end: var(--office-border-width) solid var(--office-border);\n}\ncanvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n";
1509
+
1510
+ // src/chrome/ruler.ts
1511
+ function rulerDivisions(unitPixels) {
1512
+ return unitPixels >= 192 ? 16 : unitPixels >= 96 ? 8 : unitPixels >= 48 ? 4 : 2;
1513
+ }
1514
+ var OfficeUiRuler = class extends OfficeElement {
1515
+ static styles = controlStyles(ruler_default);
1516
+ static properties = {
1517
+ orientation: { type: String, reflect: true },
1518
+ origin: { type: Number },
1519
+ scale: { type: Number },
1520
+ direction: { type: String }
1521
+ };
1522
+ frame = 0;
1523
+ observer;
1524
+ constructor() {
1525
+ super();
1526
+ this.orientation = "";
1527
+ this.origin = 0;
1528
+ this.scale = 96;
1529
+ this.direction = "";
1530
+ }
1531
+ connectedCallback() {
1532
+ super.connectedCallback();
1533
+ const Observer = this.ownerDocument.defaultView?.ResizeObserver;
1534
+ this.observer = Observer ? new Observer(() => this.schedule()) : void 0;
1535
+ this.observer?.observe(this);
1536
+ this.schedule();
1537
+ }
1538
+ disconnectedCallback() {
1539
+ super.disconnectedCallback();
1540
+ this.observer?.disconnect();
1541
+ if (this.frame) this.ownerDocument.defaultView?.cancelAnimationFrame?.(this.frame);
1542
+ this.frame = 0;
1543
+ }
1544
+ schedule() {
1545
+ if (this.frame || !this.isConnected) return;
1546
+ const view = this.ownerDocument.defaultView;
1547
+ this.frame = view?.requestAnimationFrame?.(() => this.draw()) ?? 0;
1548
+ if (!this.frame) this.draw();
1549
+ }
1550
+ willUpdate() {
1551
+ this.setAttribute("aria-hidden", "true");
1552
+ }
1553
+ updated() {
1554
+ this.schedule();
1555
+ }
1556
+ draw() {
1557
+ this.frame = 0;
1558
+ const canvas = this.renderRoot.querySelector("canvas");
1559
+ if (!canvas) return;
1560
+ const view = this.ownerDocument.defaultView;
1561
+ const vertical = this.orientation === "vertical";
1562
+ const reverse = this.direction === "reverse";
1563
+ const box = this.getBoundingClientRect();
1564
+ const length = vertical ? box.height : box.width;
1565
+ const thickness = (vertical ? box.width : box.height) || parseFloat(OFFICE_TOKENS["--office-ruler-size"]);
1566
+ const ratio = view?.devicePixelRatio ?? 1;
1567
+ canvas.width = Math.max(1, Math.round((vertical ? thickness : length) * ratio));
1568
+ canvas.height = Math.max(1, Math.round((vertical ? length : thickness) * ratio));
1569
+ const context = canvas.getContext?.("2d");
1570
+ if (!context || !length) return;
1571
+ context.scale(ratio, ratio);
1572
+ const style = view?.getComputedStyle(this);
1573
+ const ink = style?.color || OFFICE_TOKENS["--office-muted-foreground"];
1574
+ context.strokeStyle = ink;
1575
+ context.fillStyle = ink;
1576
+ context.lineWidth = 1;
1577
+ context.font = `${style?.fontSize || OFFICE_TOKENS["--office-font-size-3xs"]} ${style?.fontFamily || OFFICE_TOKENS["--office-font"]}`;
1578
+ const origin = Number.isFinite(this.origin) ? this.origin : 0;
1579
+ const unit = Math.max(1, Number.isFinite(this.scale) ? this.scale : 96);
1580
+ const divisions = rulerDivisions(unit);
1581
+ const step = unit / divisions;
1582
+ const sign = reverse ? -1 : 1;
1583
+ const a = (0 - origin) / (sign * step);
1584
+ const b = (length - origin) / (sign * step);
1585
+ const first = Math.floor(Math.min(a, b)) - 1;
1586
+ const last = Math.ceil(Math.max(a, b)) + 1;
1587
+ context.beginPath();
1588
+ for (let index = first; index <= last; index++) {
1589
+ const at = Math.round(origin + sign * index * step) + 0.5;
1590
+ const tick = index % divisions === 0 ? thickness : index % (divisions / 2) === 0 ? thickness * 0.5 : thickness * 0.3;
1591
+ if (vertical) {
1592
+ context.moveTo(thickness, at);
1593
+ context.lineTo(thickness - tick, at);
1594
+ } else {
1595
+ context.moveTo(at, thickness);
1596
+ context.lineTo(at, thickness - tick);
1597
+ }
1598
+ if (index % divisions !== 0) continue;
1599
+ const label = String(index / divisions);
1600
+ if (!vertical) context.fillText(label, at + 2, 9);
1601
+ else {
1602
+ context.save();
1603
+ context.translate(9, at - 2);
1604
+ context.rotate(-Math.PI / 2);
1605
+ context.fillText(label, 0, 0);
1606
+ context.restore();
1607
+ }
1608
+ }
1609
+ context.stroke();
1610
+ }
1611
+ render() {
1612
+ return html`<canvas></canvas>`;
1613
+ }
1614
+ };
1615
+ var defineRuler = definer("office-ui-ruler", () => OfficeUiRuler);
1616
+
1617
+ // src/chrome/account-profile.ts
1618
+ var DEFAULT_OFFICE_PROFILE = { displayName: "", avatarColor: "#6366f1" };
1619
+ var OFFICE_AVATAR_SWATCHES = [
1620
+ "#6366f1",
1621
+ "#c2431f",
1622
+ "#0891b2",
1623
+ "#16a34a",
1624
+ "#ca8a04",
1625
+ "#db2777",
1626
+ "#64748b"
1627
+ ];
1628
+ var OFFICE_PROFILE_STORAGE_KEY = "ooxml-office-profile";
1629
+ var COLOR = /^#[0-9a-f]{6}$/i;
1630
+ function sanitizeOfficeProfile(raw) {
1631
+ const value = raw && typeof raw === "object" ? raw : {};
1632
+ const displayName = typeof value.displayName === "string" ? value.displayName.trim().slice(0, 64) : "";
1633
+ const avatarColor = typeof value.avatarColor === "string" && COLOR.test(value.avatarColor) ? value.avatarColor : DEFAULT_OFFICE_PROFILE.avatarColor;
1634
+ const initial = typeof value.initial === "string" && value.initial.trim() ? value.initial.trim().slice(0, 2) : void 0;
1635
+ return { displayName, avatarColor, ...initial ? { initial } : {} };
1636
+ }
1637
+ function profileInitials(profile) {
1638
+ return (profile.initial ?? initialsOf(profile.displayName)).toUpperCase();
1639
+ }
1640
+ var defaultStorage = () => {
1641
+ try {
1642
+ return typeof localStorage === "undefined" ? void 0 : localStorage;
1643
+ } catch {
1644
+ return void 0;
1645
+ }
1646
+ };
1647
+ function readOfficeProfile(storage = defaultStorage(), key = OFFICE_PROFILE_STORAGE_KEY) {
1648
+ try {
1649
+ const raw = storage?.getItem(key);
1650
+ return raw ? sanitizeOfficeProfile(JSON.parse(raw)) : { ...DEFAULT_OFFICE_PROFILE };
1651
+ } catch {
1652
+ return { ...DEFAULT_OFFICE_PROFILE };
1653
+ }
1654
+ }
1655
+ function writeOfficeProfile(profile, storage = defaultStorage(), key = OFFICE_PROFILE_STORAGE_KEY) {
1656
+ try {
1657
+ storage?.setItem(key, JSON.stringify(sanitizeOfficeProfile(profile)));
1658
+ } catch {
1659
+ }
1660
+ }
1661
+ function clearOfficeProfile(storage = defaultStorage(), key = OFFICE_PROFILE_STORAGE_KEY) {
1662
+ try {
1663
+ storage?.removeItem(key);
1664
+ } catch {
1665
+ }
1666
+ }
1667
+
1668
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/account.css?raw
1669
+ var account_default = ":host {\n display: block;\n font-family: var(--office-font);\n font-size: var(--office-font-size);\n}\nh2 {\n margin: 0 0 var(--office-space-3);\n font-size: var(--office-font-size-lg);\n font-weight: 600;\n}\n.who {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n margin-bottom: var(--office-space-3);\n}\n.avatar {\n display: grid;\n place-items: center;\n width: var(--office-avatar-size);\n height: var(--office-avatar-size);\n border-radius: 50%;\n color: var(--office-accent-foreground);\n font-weight: var(--office-font-weight-bold);\n font-size: var(--office-font-size-xl);\n flex: none;\n}\n.name {\n font-size: var(--office-font-size-md);\n font-weight: 600;\n}\n.note {\n margin: var(--office-space-0) 0 0;\n color: var(--office-muted-foreground);\n font-size: var(--office-font-size-sm);\n}\nlabel {\n display: grid;\n gap: var(--office-space-1);\n max-width: var(--office-account-field-width);\n margin-bottom: var(--office-space-3);\n}\ninput {\n min-height: var(--office-field-height);\n box-sizing: border-box;\n padding: var(--office-space-0) var(--office-space-1-5);\n font: inherit;\n color: inherit;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-sm);\n background: var(--office-background);\n}\n.swatches {\n display: flex;\n flex-wrap: wrap;\n gap: var(--office-space-1-5);\n margin: var(--office-space-1) 0 var(--office-space-3);\n}\n.swatches button {\n width: var(--office-target-size);\n height: var(--office-target-size);\n padding: 0;\n border: var(--office-border-width-thick) solid transparent;\n border-radius: 50%;\n cursor: pointer;\n}\n.swatches button[aria-checked='true'] {\n border-color: var(--office-foreground);\n box-shadow: inset 0 0 0 var(--office-border-width-thick) var(--office-background);\n}\n.swatches button:focus-visible,\ninput:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\n@media (forced-colors: active) {\n .swatches button {\n forced-color-adjust: none;\n }\n}\n";
1670
+
1671
+ // src/chrome/account.ts
1672
+ var OfficeUiAccount = class extends OfficeElement {
1673
+ static styles = controlStyles(account_default);
1674
+ current = { ...DEFAULT_OFFICE_PROFILE };
1675
+ get profile() {
1676
+ return { ...this.current };
1677
+ }
1678
+ set profile(value) {
1679
+ this.current = sanitizeOfficeProfile(value);
1680
+ this.requestUpdate("profile");
1681
+ }
1682
+ change(next) {
1683
+ this.profile = next;
1684
+ this.fire("office-profile-change", { profile: this.profile });
1685
+ }
1686
+ onSwatchKey(event) {
1687
+ const step = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[event.key];
1688
+ if (!step) return;
1689
+ event.preventDefault();
1690
+ const colors = OFFICE_AVATAR_SWATCHES;
1691
+ const at = Math.max(0, colors.indexOf(this.current.avatarColor));
1692
+ const color = colors[(at + step + colors.length) % colors.length];
1693
+ this.change({ ...this.current, avatarColor: color });
1694
+ this.renderRoot.querySelector(`[data-color="${color}"]`)?.focus();
1695
+ }
1696
+ render() {
1697
+ const { displayName, avatarColor } = this.current;
1698
+ const known = OFFICE_AVATAR_SWATCHES.includes(avatarColor);
1699
+ return html`
1700
+ <h2>User Information</h2>
1701
+ <div class="who">
1702
+ <div class="avatar" aria-hidden="true" style="background:${avatarColor}"
1703
+ >${profileInitials(this.current)}</div
1704
+ >
1705
+ <div>
1706
+ <div class="name">${displayName || "No display name"}</div>
1707
+ <p class="note">Stored on this device only. Never sent anywhere.</p>
1708
+ </div>
1709
+ </div>
1710
+ <label>
1711
+ Display name
1712
+ <input
1713
+ type="text"
1714
+ maxlength="64"
1715
+ autocomplete="name"
1716
+ .value=${displayName}
1717
+ @change=${(event) => this.change({
1718
+ ...this.current,
1719
+ displayName: event.target.value.trim()
1720
+ })}
1721
+ />
1722
+ </label>
1723
+ <div id="color-label">Avatar colour</div>
1724
+ <div
1725
+ class="swatches"
1726
+ role="radiogroup"
1727
+ aria-labelledby="color-label"
1728
+ @keydown=${this.onSwatchKey}
1729
+ >
1730
+ ${OFFICE_AVATAR_SWATCHES.map((color, index) => {
1731
+ const checked = color === avatarColor;
1732
+ return html`<button
1733
+ type="button"
1734
+ role="radio"
1735
+ aria-label=${color}
1736
+ aria-checked=${String(checked)}
1737
+ tabindex=${checked || !known && index === 0 ? 0 : -1}
1738
+ data-color=${color}
1739
+ style="background:${color}"
1740
+ @click=${() => this.change({ ...this.current, avatarColor: color })}
1741
+ ></button>`;
1742
+ })}
1743
+ </div>
1744
+ <slot name="sign-in"></slot>
1745
+ <slot></slot>
1746
+ `;
1747
+ }
1748
+ };
1749
+ var defineAccount = definer("office-ui-account", () => OfficeUiAccount);
1750
+
1751
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/button.css?raw
1752
+ var button_default = ":host {\n display: inline-flex;\n vertical-align: middle;\n}\nbutton {\n box-sizing: border-box;\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-1-5);\n min-width: var(--office-target-size);\n min-height: var(--office-target-size);\n padding: 0 var(--office-space-2);\n border: var(--office-border-width) solid transparent;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-foreground);\n font: inherit;\n font-size: var(--office-font-size-sm);\n cursor: pointer;\n white-space: nowrap;\n}\n:host([variant='stacked']) button {\n flex-direction: column;\n padding: var(--office-space-1) var(--office-space-2);\n}\nbutton:hover:not(:disabled) {\n background: var(--office-surface);\n}\nbutton:active:not(:disabled) {\n background: var(--office-selected);\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\nbutton[aria-pressed='true'],\nbutton[aria-expanded='true'] {\n border-color: var(--office-accent);\n background: var(--office-surface);\n}\n:host([active]) button {\n background: var(--office-command-active);\n color: var(--office-accent);\n}\nbutton:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\nsvg {\n width: var(--office-icon-size-ml);\n height: var(--office-icon-size-ml);\n flex: none;\n color: var(--office-command-icon-color);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\nsvg.glyph:not([data-painted]) {\n display: none;\n}\n.label[hidden],\n.caret[hidden],\n.badge[hidden] {\n display: none;\n}\n/* Office trails a menu chevron after the last line of the label. */\n.caret {\n display: inline-block;\n width: var(--office-icon-size-xs);\n height: var(--office-icon-size-xs);\n margin-inline-start: var(--office-space-0);\n vertical-align: middle;\n color: var(--office-muted-foreground);\n}\n.badge {\n position: absolute;\n top: 0;\n inset-inline-end: 0;\n padding: 0 var(--office-space-0);\n border-radius: var(--office-radius-full);\n background: var(--office-accent);\n color: var(--office-accent-foreground);\n font-size: var(--office-font-size-3xs);\n}\n\n/* Large ribbon command: a big glyph above a two-line label. */\n:host([size='large']) button {\n flex-direction: column;\n justify-content: flex-start;\n gap: var(--office-space-0);\n min-width: var(--office-command-large-min-width);\n max-width: var(--office-command-large-max-width);\n height: var(--office-command-large-height);\n padding: var(--office-space-0) var(--office-space-1);\n border: 0;\n white-space: normal;\n text-align: center;\n line-height: var(--office-line-height-tight);\n}\n:host([size='large']) svg.glyph {\n width: var(--office-command-large-icon);\n height: var(--office-command-large-icon);\n}\n\n/* Small ribbon command: icon and label in one row. */\n:host([size='small']) button {\n justify-content: flex-start;\n min-width: 0;\n height: var(--office-control-height);\n min-height: var(--office-control-height);\n padding: 0 var(--office-space-1-5);\n border: 0;\n text-align: start;\n}\n:host([size='small']) svg.glyph {\n width: var(--office-command-small-icon);\n height: var(--office-command-small-icon);\n}\n\n/* Tall, label-less tool tiles (pens, erasers). */\n:host([icon-only][tall]) button {\n min-width: var(--office-command-tall-min-width);\n height: var(--office-command-tall-height);\n padding: var(--office-space-1);\n}\n:host([icon-only][tall]) svg.glyph {\n width: var(--office-command-tall-icon);\n height: var(--office-command-tall-icon);\n}\n:host([icon-only][tall][active]) button {\n box-shadow: inset 0 0 0 var(--office-border-width) var(--office-accent);\n}\n\n@media (pointer: coarse), (max-width: 767px) {\n :host([size='small']) button {\n min-height: var(--office-target-size-touch);\n }\n :host([icon-only]) button {\n min-width: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n button {\n border-color: ButtonText;\n color: ButtonText;\n background: ButtonFace;\n }\n svg {\n color: ButtonText;\n }\n button:disabled {\n color: GrayText;\n border-color: GrayText;\n }\n button[aria-pressed='true'],\n button[aria-expanded='true'],\n :host([active]) button {\n background: Highlight;\n color: HighlightText;\n }\n}\n";
1753
+
1754
+ // src/ribbon/button.ts
1755
+ var asAttribute = (value) => value === void 0 ? void 0 : String(value);
1756
+ var OfficeUiButton = class extends OfficeElement {
1757
+ static requestEvent = "office-command";
1758
+ static idAttribute = "command";
1759
+ static detailKey = "command";
1760
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
1761
+ static styles = controlStyles(button_default);
1762
+ static properties = { disabled: flag, pressed: triState };
1763
+ /** Everything else is an attribute, as it always was (see `OfficeElement.watched`). */
1764
+ static watched = [
1765
+ "label",
1766
+ "icon",
1767
+ "command",
1768
+ "badge",
1769
+ "caret",
1770
+ "keyshortcuts",
1771
+ "size",
1772
+ "variant",
1773
+ "active",
1774
+ "tall",
1775
+ "icon-only",
1776
+ "expanded",
1777
+ "title"
1778
+ ];
1779
+ constructor() {
1780
+ super();
1781
+ this.disabled = false;
1782
+ }
1783
+ /** The registered icon to draw; a product subclass may map names onto its glyph set. */
1784
+ iconName() {
1785
+ return this.getAttribute("icon");
1786
+ }
1787
+ /** The tooltip: `title`, else the label of an icon-only button; a product may always show it. */
1788
+ tooltip(label, iconOnly) {
1789
+ return this.getAttribute("title") ?? (iconOnly ? label : "");
1790
+ }
1791
+ /** Whether the trailing menu chevron shows. */
1792
+ showsCaret() {
1793
+ const caret = this.getAttribute("caret");
1794
+ return caret !== null && caret !== "false";
1795
+ }
1796
+ onKeydown(event) {
1797
+ if ((event.key === " " || event.key === "Enter") && !event.ctrlKey && !event.metaKey && !event.altKey)
1798
+ event.stopPropagation();
1799
+ }
1800
+ onClick() {
1801
+ const { requestEvent, idAttribute, detailKey } = this.constructor;
1802
+ const id = this.getAttribute(idAttribute);
1803
+ if (!present(this.disabled) && id) this.fire(requestEvent, { [detailKey]: id });
1804
+ }
1805
+ render() {
1806
+ const label = this.getAttribute("label") ?? "";
1807
+ const iconOnly = this.hasAttribute("icon-only");
1808
+ const badge = this.getAttribute("badge") ?? "";
1809
+ const expanded = this.getAttribute("expanded");
1810
+ const tooltip = this.tooltip(label, iconOnly);
1811
+ return html`<button
1812
+ part="button"
1813
+ type="button"
1814
+ ?disabled=${present(this.disabled)}
1815
+ title=${tooltip || nothing}
1816
+ aria-label=${ifDefined(iconOnly && label ? label : void 0)}
1817
+ aria-keyshortcuts=${ifDefined(this.getAttribute("keyshortcuts") || void 0)}
1818
+ aria-pressed=${ifDefined(asAttribute(this.pressed))}
1819
+ aria-expanded=${ifDefined(expanded === null ? void 0 : String(expanded !== "false"))}
1820
+ @click=${this.onClick}
1821
+ @keydown=${this.onKeydown}
1822
+ >${glyph(this.iconName())}<span class="label" ?hidden=${iconOnly}
1823
+ >${label}${glyph("chevronDown", "caret", !this.showsCaret())}</span
1824
+ ><span class="badge" aria-hidden="true" ?hidden=${!badge}>${badge}</span></button
1825
+ >`;
1826
+ }
1827
+ };
1828
+ var defineButton = definer("office-ui-button", () => OfficeUiButton);
1829
+ var OfficeUiCheckable = class extends OfficeElement {
1830
+ static formAssociated = true;
1831
+ static properties = {
1832
+ checked: flag,
1833
+ disabled: flag,
1834
+ value: valueOn
1835
+ };
1836
+ internals;
1837
+ defaultChecked = false;
1838
+ constructor() {
1839
+ super();
1840
+ this.checked = false;
1841
+ this.disabled = false;
1842
+ this.value = "on";
1843
+ try {
1844
+ this.internals = this.attachInternals();
1845
+ } catch {
1846
+ this.internals = void 0;
1847
+ }
1848
+ this.addEventListener("click", (event) => {
1849
+ if (present(this.disabled)) event.preventDefault();
1850
+ else this.toggle();
1851
+ });
1852
+ this.addEventListener("keydown", (event) => {
1853
+ const toggles = event.key === " " || this.semantics === "switch" && event.key === "Enter";
1854
+ if (!toggles || present(this.disabled)) return;
1855
+ event.preventDefault();
1856
+ this.toggle();
1857
+ });
1858
+ }
1859
+ connectedCallback() {
1860
+ this.defaultChecked = present(this.checked);
1861
+ super.connectedCallback();
1862
+ }
1863
+ formResetCallback() {
1864
+ this.checked = this.defaultChecked;
1865
+ }
1866
+ formDisabledCallback(disabled) {
1867
+ this.disabled = disabled;
1868
+ }
1869
+ toggle() {
1870
+ this.checked = !present(this.checked);
1871
+ this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
1872
+ this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
1873
+ }
1874
+ /** The host carries the semantics: role, state, tab stop and form value. */
1875
+ willUpdate() {
1876
+ const checked = present(this.checked);
1877
+ const disabled = present(this.disabled);
1878
+ this.setAttribute("role", this.semantics);
1879
+ this.setAttribute("aria-checked", String(checked));
1880
+ this.setAttribute("aria-disabled", String(disabled));
1881
+ this.setAttribute("tabindex", disabled ? "-1" : "0");
1882
+ this.internals?.setFormValue?.(checked && !disabled ? this.value : null);
1883
+ }
1884
+ /** The tick, shown through CSS when `checked`. */
1885
+ renderCheck() {
1886
+ return html`<svg
1887
+ viewBox="0 0 16 16"
1888
+ fill="none"
1889
+ stroke="currentColor"
1890
+ stroke-width="2"
1891
+ stroke-linecap="round"
1892
+ stroke-linejoin="round"
1893
+ aria-hidden="true"
1894
+ >
1895
+ <path d="m3 8 3.2 3.2L13 4.5"></path>
1896
+ </svg>`;
1897
+ }
1898
+ };
1899
+
1900
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/checkbox.css?raw
1901
+ var checkbox_default = ":host {\n display: inline-grid;\n box-sizing: border-box;\n width: var(--office-checkbox-size);\n height: var(--office-checkbox-size);\n flex: none;\n place-items: center;\n border: var(--office-border-width) solid var(--office-checkbox-border);\n border-radius: var(--office-checkbox-radius);\n background: var(--office-checkbox-background);\n color: var(--office-checkbox-accent-foreground);\n cursor: pointer;\n vertical-align: middle;\n}\n:host([checked]) {\n border-color: var(--office-checkbox-accent);\n background: var(--office-checkbox-accent);\n}\n:host(:focus-visible) {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n:host([disabled]) {\n opacity: 0.5;\n cursor: not-allowed;\n}\nsvg {\n display: none;\n width: var(--office-icon-size-sm);\n height: var(--office-icon-size-sm);\n}\n:host([checked]) svg {\n display: block;\n}\n\n@media (pointer: coarse), (max-width: 767px) {\n :host {\n width: var(--office-checkbox-size-touch);\n height: var(--office-checkbox-size-touch);\n }\n svg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n }\n}\n@media (forced-colors: active) {\n :host {\n border-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n forced-color-adjust: auto;\n }\n :host([checked]) {\n border-color: Highlight;\n background: Highlight;\n color: HighlightText;\n }\n}\n";
1902
+
1903
+ // src/form/checkbox.ts
1904
+ var OfficeUiCheckbox = class extends OfficeUiCheckable {
1905
+ static styles = controlStyles(checkbox_default);
1906
+ semantics = "checkbox";
1907
+ render() {
1908
+ return this.renderCheck();
1909
+ }
1910
+ };
1911
+ var defineCheckbox = definer("office-ui-checkbox", () => OfficeUiCheckbox);
1912
+
1913
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/switch.css?raw
1914
+ var switch_default = "/* Switch metrics are tokens so a host (a title bar) can size it: --office-switch-width, -height,\n * -border-width, -knob-size, -knob-offset, -knob-travel, -track, -track-on, -thumb, -thumb-on. */\n:host {\n --_w: var(--office-switch-width);\n --_h: var(--office-switch-height);\n --_b: var(--office-switch-border-width);\n --_k: var(--office-switch-knob-size);\n --_o: var(--office-switch-knob-offset);\n display: inline-block;\n box-sizing: border-box;\n position: relative;\n width: var(--_w);\n height: var(--_h);\n flex: none;\n border: var(--_b) solid var(--office-switch-border);\n border-radius: var(--office-radius-full);\n background: var(--office-switch-track);\n cursor: pointer;\n vertical-align: middle;\n touch-action: manipulation;\n transition: background-color var(--office-duration);\n}\n.knob {\n display: block;\n position: absolute;\n top: calc((var(--_h) - 2 * var(--_b) - var(--_k)) / 2);\n left: var(--_o);\n width: var(--_k);\n height: var(--_k);\n border-radius: 50%;\n background: var(--office-switch-thumb);\n box-shadow: var(--office-shadow-sm);\n transition: transform var(--office-duration);\n}\n:host([checked]) {\n border-color: var(--office-switch-track-on);\n background: var(--office-switch-track-on);\n}\n:host([checked]) .knob {\n transform: translateX(\n var(--office-switch-knob-travel, calc(var(--_w) - var(--_k) - 2 * var(--_o) - 2 * var(--_b)))\n );\n background: var(--office-switch-thumb-on);\n}\n:host(:focus-visible) {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n:host([disabled]) {\n opacity: 0.5;\n cursor: not-allowed;\n}\nsvg {\n display: none;\n}\n\n@media (pointer: coarse), (max-width: 767px) {\n :host {\n --_w: var(--office-switch-width-touch);\n --_h: var(--office-switch-height-touch);\n --_k: var(--office-switch-knob-size-touch);\n }\n}\n@media (forced-colors: active) {\n :host {\n border-color: CanvasText;\n background: Canvas;\n forced-color-adjust: none;\n }\n .knob {\n background: CanvasText;\n }\n :host([checked]) {\n border-color: Highlight;\n background: Highlight;\n }\n :host([checked]) .knob {\n background: HighlightText;\n }\n}\n";
1915
+
1916
+ // src/form/switch.ts
1917
+ var OfficeUiSwitch = class extends OfficeUiCheckable {
1918
+ static styles = controlStyles(switch_default);
1919
+ semantics = "switch";
1920
+ render() {
1921
+ return html`${this.renderCheck()}<span class="knob" part="knob"></span>`;
1922
+ }
1923
+ };
1924
+ var defineSwitch = definer("office-ui-switch", () => OfficeUiSwitch);
1925
+
1926
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/dialog/dialog.css?raw
1927
+ var dialog_default = ":host {\n display: none;\n position: fixed;\n inset: 0;\n z-index: 1000;\n align-items: center;\n justify-content: center;\n}\n:host([open]) {\n display: flex;\n}\n.backdrop {\n position: absolute;\n inset: 0;\n background: var(--office-overlay);\n}\n.box {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n width: var(--office-dialog-width);\n min-width: var(--office-dialog-min-width);\n max-width: var(--office-dialog-max-width);\n max-height: var(--office-dialog-max-height);\n background: var(--office-background);\n color: var(--office-foreground);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-lg);\n font-family: var(--office-font);\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-2);\n padding: var(--office-space-3) var(--office-space-4);\n border-bottom: var(--office-border-width) solid var(--office-border);\n}\nh2 {\n margin: 0;\n font-size: var(--office-font-size-lg);\n font-weight: 600;\n}\n.body {\n padding: var(--office-space-4);\n overflow: auto;\n font-size: var(--office-font-size);\n}\nfooter {\n display: flex;\n justify-content: flex-end;\n gap: var(--office-space-2);\n padding: var(--office-space-3) var(--office-space-4);\n border-top: var(--office-border-width) solid var(--office-border);\n}\nfooter[hidden] {\n display: none;\n}\n.close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--office-target-size);\n min-height: var(--office-target-size);\n border: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n border-radius: var(--office-radius);\n}\n.close svg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n}\n.close:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n}\n\n@media (forced-colors: active) {\n .box {\n border-color: CanvasText;\n background: Canvas;\n }\n .backdrop {\n background: transparent;\n }\n}\n";
1928
+
1929
+ // src/dialog/dialog.ts
1930
+ var FOCUSABLE = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), office-ui-button:not([disabled]), office-ui-select:not([disabled]), office-ui-checkbox:not([disabled]), office-ui-switch:not([disabled])';
1931
+ var OfficeUiDialog = class extends OfficeElement {
1932
+ static styles = controlStyles(dialog_default);
1933
+ static properties = {
1934
+ open: flag,
1935
+ heading: { type: String },
1936
+ dismissible: { type: String }
1937
+ };
1938
+ returnFocus = null;
1939
+ constructor() {
1940
+ super();
1941
+ this.open = false;
1942
+ this.heading = "";
1943
+ this.dismissible = null;
1944
+ this.addEventListener("keydown", (event) => this.onKey(event));
1945
+ }
1946
+ show() {
1947
+ this.open = true;
1948
+ }
1949
+ /** Programmatic close; still emits `office-dialog-close` (reason `api`). */
1950
+ close() {
1951
+ this.requestClose("api");
1952
+ }
1953
+ requestClose(reason) {
1954
+ const dismissible = this.dismissible !== "false";
1955
+ if ((reason === "escape" || reason === "backdrop") && !dismissible) return;
1956
+ if (this.fire("office-dialog-close", { reason }, true)) this.open = false;
1957
+ }
1958
+ get box() {
1959
+ return this.renderRoot.querySelector(".box");
1960
+ }
1961
+ get closeButton() {
1962
+ return this.renderRoot.querySelector(".close");
1963
+ }
1964
+ focusables() {
1965
+ return [...this.querySelectorAll(FOCUSABLE)];
1966
+ }
1967
+ onOpen() {
1968
+ const active = this.ownerDocument.activeElement;
1969
+ this.returnFocus = active && active !== this.ownerDocument.body ? active : null;
1970
+ (this.focusables()[0] ?? this.box)?.focus();
1971
+ }
1972
+ onClose() {
1973
+ const target = this.returnFocus;
1974
+ this.returnFocus = null;
1975
+ target?.focus?.();
1976
+ }
1977
+ onKey(event) {
1978
+ if (!this.open) return;
1979
+ if (event.key === "Escape") {
1980
+ event.preventDefault();
1981
+ event.stopPropagation();
1982
+ this.requestClose("escape");
1983
+ } else if (event.key === "Tab") {
1984
+ const closeButton = this.closeButton;
1985
+ const items = [...this.focusables(), ...closeButton ? [closeButton] : []];
1986
+ const current = this.ownerDocument.activeElement;
1987
+ const inside = (el) => el === current || el.contains(current);
1988
+ const index = items.findIndex(inside);
1989
+ const shadowFocus = this.shadowRoot?.activeElement === closeButton;
1990
+ const at = shadowFocus ? items.length - 1 : index;
1991
+ const last = items.length - 1;
1992
+ if (at < 0 || !event.shiftKey && at === last || event.shiftKey && at === 0) {
1993
+ event.preventDefault();
1994
+ items[at < 0 || !event.shiftKey ? 0 : last]?.focus();
1995
+ }
1996
+ }
1997
+ }
1998
+ /** Focus moves in when the dialog opens (also when it connects open) and back when it closes. */
1999
+ updated(changed) {
2000
+ if (!changed.has("open")) return;
2001
+ if (this.open) this.onOpen();
2002
+ else if (changed.get("open") !== void 0) this.onClose();
2003
+ }
2004
+ render() {
2005
+ return html`
2006
+ <div class="backdrop" @click=${() => this.requestClose("backdrop")}></div>
2007
+ <div class="box" role="dialog" aria-modal="true" aria-labelledby="title" tabindex="-1">
2008
+ <header>
2009
+ <h2 id="title">${this.heading}</h2>
2010
+ <button
2011
+ class="close"
2012
+ type="button"
2013
+ aria-label="Close"
2014
+ @click=${() => this.requestClose("close-button")}
2015
+ >${glyph("close", "icon")}</button
2016
+ >
2017
+ </header>
2018
+ <div class="body"><slot></slot></div>
2019
+ <footer ?hidden=${this.querySelector('[slot="footer"]') === null}>
2020
+ <slot name="footer" @slotchange=${() => this.requestUpdate()}></slot>
2021
+ </footer>
2022
+ </div>
2023
+ `;
2024
+ }
2025
+ };
2026
+ var defineDialog = definer("office-ui-dialog", () => OfficeUiDialog);
2027
+
2028
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/icon.css?raw
2029
+ var icon_default = ":host {\n display: inline-flex;\n width: 1em;\n height: 1em;\n}\nsvg {\n width: 1em;\n height: 1em;\n fill: none;\n stroke: currentColor;\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n";
2030
+
2031
+ // src/icon.ts
2032
+ var OfficeUiIcon = class extends OfficeElement {
2033
+ static styles = controlStyles(icon_default);
2034
+ static properties = {
2035
+ name: { type: String },
2036
+ label: { type: String }
2037
+ };
2038
+ constructor() {
2039
+ super();
2040
+ this.name = "";
2041
+ this.label = "";
2042
+ }
2043
+ willUpdate() {
2044
+ if (this.label) {
2045
+ this.setAttribute("role", "img");
2046
+ this.setAttribute("aria-label", this.label);
2047
+ this.removeAttribute("aria-hidden");
2048
+ } else {
2049
+ this.removeAttribute("role");
2050
+ this.removeAttribute("aria-label");
2051
+ this.setAttribute("aria-hidden", "true");
2052
+ }
2053
+ }
2054
+ render() {
2055
+ return glyph(this.name, "icon");
2056
+ }
2057
+ };
2058
+ var defineIcon = definer("office-ui-icon", () => OfficeUiIcon);
2059
+
2060
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/menu/command-search.css?raw
2061
+ var command_search_default = ":host {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n font-family: var(--office-font);\n}\n.field {\n display: flex;\n align-items: center;\n gap: var(--office-space-1-5);\n width: 100%;\n min-width: 0;\n height: var(--office-target-size);\n padding: 0 var(--office-space-2);\n box-sizing: border-box;\n border: var(--office-border-width) solid transparent;\n border-radius: var(--office-radius);\n color: var(--office-muted-foreground);\n}\n.field:hover,\n.field:focus-within {\n border-color: var(--office-border);\n background: var(--office-background);\n}\ninput {\n flex: 1;\n min-width: 0;\n border: 0;\n outline: 0;\n background: transparent;\n color: var(--office-foreground);\n font: inherit;\n font-size: var(--office-font-size);\n}\nsvg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n flex: none;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\nul {\n position: fixed;\n inset: auto;\n margin: 0;\n padding: var(--office-space-1);\n min-width: var(--office-search-popup-min-width);\n max-height: var(--office-search-popup-max-height);\n overflow: auto;\n box-sizing: border-box;\n list-style: none;\n background: var(--office-background);\n color: var(--office-foreground);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius);\n box-shadow: var(--office-shadow);\n font-size: var(--office-font-size-sm);\n}\nul[hidden] {\n display: none;\n}\nli {\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-height: var(--office-target-size);\n padding: var(--office-space-0) var(--office-space-2-5);\n border-radius: var(--office-radius-sm);\n cursor: pointer;\n}\nli[data-active] {\n background: var(--office-surface);\n}\nli[aria-disabled='true'] {\n cursor: not-allowed;\n color: var(--office-muted-foreground);\n}\nli small {\n color: var(--office-muted-foreground);\n font-size: var(--office-font-size-xs);\n}\nli.empty {\n cursor: default;\n color: var(--office-muted-foreground);\n}\n\n@media (forced-colors: active) {\n .field,\n ul {\n border-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n li[data-active] {\n background: Highlight;\n color: HighlightText;\n }\n}\n";
2062
+
2063
+ // src/menu/command-search.ts
2064
+ var uid = 0;
2065
+ var OfficeUiCommandSearch = class extends OfficeElement {
2066
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
2067
+ static styles = controlStyles(command_search_default);
2068
+ static properties = {
2069
+ placeholder: { type: String },
2070
+ label: { type: String },
2071
+ limit: { type: String },
2072
+ found: { state: true },
2073
+ active: { state: true },
2074
+ isOpen: { state: true }
2075
+ };
2076
+ items = [];
2077
+ uid = `office-command-search-${++uid}`;
2078
+ constructor() {
2079
+ super();
2080
+ this.placeholder = null;
2081
+ this.label = null;
2082
+ this.limit = null;
2083
+ this.found = [];
2084
+ this.active = -1;
2085
+ this.isOpen = false;
2086
+ }
2087
+ get commands() {
2088
+ return this.items.map((item) => ({ ...item }));
2089
+ }
2090
+ set commands(value) {
2091
+ this.items = (Array.isArray(value) ? value : []).filter((item) => item && typeof item.id === "string").map((item) => ({ ...item, label: String(item.label ?? "") }));
2092
+ if (this.isOpen) this.search();
2093
+ }
2094
+ disconnectedCallback() {
2095
+ super.disconnectedCallback();
2096
+ this.hide();
2097
+ }
2098
+ get input() {
2099
+ return this.renderRoot.querySelector("input");
2100
+ }
2101
+ get list() {
2102
+ return this.renderRoot.querySelector("ul");
2103
+ }
2104
+ search() {
2105
+ const query = (this.input?.value ?? "").trim().toLowerCase();
2106
+ if (!query) return this.hide();
2107
+ const limit = Math.max(1, Number(this.limit) || 8);
2108
+ const rank = (item) => (item.disabled ? 2 : 0) + (item.label.toLowerCase().startsWith(query) ? 0 : 1);
2109
+ this.found = this.items.map((item, order) => ({ item, order })).filter(
2110
+ ({ item }) => `${item.label} ${item.keywords ?? ""} ${item.description ?? ""}`.toLowerCase().includes(query)
2111
+ ).sort((a, b) => rank(a.item) - rank(b.item) || a.order - b.order).map(({ item }) => item).slice(0, limit);
2112
+ this.active = this.found.findIndex((item) => !item.disabled);
2113
+ this.isOpen = true;
2114
+ }
2115
+ hide() {
2116
+ this.isOpen = false;
2117
+ }
2118
+ move(step) {
2119
+ const enabled = this.found.map((item, index) => item.disabled ? -1 : index).filter((i) => i >= 0);
2120
+ if (!enabled.length) return;
2121
+ const at = enabled.indexOf(this.active);
2122
+ this.active = enabled[(at + step + enabled.length) % enabled.length];
2123
+ }
2124
+ choose(index) {
2125
+ const item = this.found[index];
2126
+ if (!item || item.disabled) return;
2127
+ if (this.input) this.input.value = "";
2128
+ this.hide();
2129
+ this.fire("office-command", { command: item.id });
2130
+ }
2131
+ onKey(event) {
2132
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
2133
+ event.preventDefault();
2134
+ if (!this.isOpen) this.search();
2135
+ else this.move(event.key === "ArrowDown" ? 1 : -1);
2136
+ } else if (event.key === "Enter") {
2137
+ event.preventDefault();
2138
+ this.choose(this.active);
2139
+ } else if (event.key === "Escape") {
2140
+ event.preventDefault();
2141
+ event.stopPropagation();
2142
+ if (this.isOpen) this.hide();
2143
+ else if (this.input) this.input.value = "";
2144
+ }
2145
+ }
2146
+ /** The listbox is a top-layer popover below the field: show or hide it as `isOpen` changes. */
2147
+ updated(changed) {
2148
+ const list = this.list;
2149
+ if (!list || !changed.has("isOpen")) return;
2150
+ if (this.isOpen) {
2151
+ const box = this.input?.getBoundingClientRect();
2152
+ if (box) {
2153
+ list.style.left = `${Math.round(box.left)}px`;
2154
+ list.style.top = `${Math.round(box.bottom + 4)}px`;
2155
+ }
2156
+ if (list.hasAttribute("popover") && !list.matches(":popover-open")) list.showPopover?.();
2157
+ } else if (list.hasAttribute("popover")) {
2158
+ try {
2159
+ list.hidePopover?.();
2160
+ } catch {
2161
+ }
2162
+ }
2163
+ }
2164
+ optionTemplates() {
2165
+ if (!this.found.length)
2166
+ return html`<li class="empty" role="option" aria-disabled="true">No matching commands</li>`;
2167
+ return this.found.map((item, index) => {
2168
+ const detail = item.disabled ? item.title ?? "Not available" : item.description;
2169
+ return html`<li
2170
+ id="${this.uid}-${index}"
2171
+ data-index=${index}
2172
+ role="option"
2173
+ aria-selected=${String(index === this.active)}
2174
+ aria-disabled=${ifDefined(item.disabled ? "true" : void 0)}
2175
+ ?data-active=${index === this.active}
2176
+ ><span>${item.label}</span>${detail ? html`<small>${detail}</small>` : ""}</li
2177
+ >`;
2178
+ });
2179
+ }
2180
+ render() {
2181
+ const placeholder = this.placeholder ?? "Tell me what you want to do";
2182
+ const popover = typeof HTMLElement.prototype.showPopover === "function";
2183
+ const activeId = this.isOpen && this.found.length && this.active >= 0 ? `${this.uid}-${this.active}` : void 0;
2184
+ return html`
2185
+ <div class="field">
2186
+ ${glyph("search", "icon")}
2187
+ <input
2188
+ type="search"
2189
+ autocomplete="off"
2190
+ spellcheck="false"
2191
+ role="combobox"
2192
+ aria-autocomplete="list"
2193
+ aria-expanded=${String(this.isOpen)}
2194
+ aria-controls="${this.uid}-list"
2195
+ aria-label=${this.label ?? placeholder}
2196
+ aria-activedescendant=${ifDefined(activeId)}
2197
+ placeholder=${placeholder}
2198
+ @input=${this.search}
2199
+ @keydown=${this.onKey}
2200
+ @blur=${this.hide}
2201
+ />
2202
+ </div>
2203
+ <ul
2204
+ id="${this.uid}-list"
2205
+ role="listbox"
2206
+ popover=${ifDefined(popover ? "manual" : void 0)}
2207
+ ?hidden=${!popover && !this.isOpen}
2208
+ @pointerdown=${(event) => event.preventDefault()}
2209
+ @click=${(event) => {
2210
+ const item = event.target.closest("li[data-index]");
2211
+ if (item) this.choose(Number(item.dataset.index));
2212
+ }}
2213
+ >${this.isOpen ? this.optionTemplates() : ""}</ul
2214
+ >
2215
+ `;
2216
+ }
2217
+ };
2218
+ var defineCommandSearch = definer("office-ui-command-search", () => OfficeUiCommandSearch);
2219
+
2220
+ // src/menu/menu-model.ts
2221
+ var EMPTY_MENU_STATE = { x: 0, y: 0, label: "", items: [] };
2222
+ function nextEnabledIndex(items, from, step) {
2223
+ const count = items.length;
2224
+ for (let offset = 1; offset <= count; offset += 1) {
2225
+ const index = ((from + step * offset) % count + count) % count;
2226
+ if (!items[index]?.disabled) return index;
2227
+ }
2228
+ return -1;
2229
+ }
2230
+ function typeAheadIndex(items, from, query) {
2231
+ const needle = query.toLowerCase();
2232
+ const cycling = [...needle].every((char) => char === needle[0]);
2233
+ const prefix = cycling ? needle[0] : needle;
2234
+ for (let offset = cycling ? 1 : 0; offset <= items.length; offset += 1) {
2235
+ const index = (from + offset + items.length) % items.length;
2236
+ const item = items[index];
2237
+ if (item && !item.disabled && item.label.trim().toLowerCase().startsWith(prefix ?? ""))
2238
+ return index;
2239
+ }
2240
+ return -1;
2241
+ }
2242
+ function clampFlyoutPosition(input) {
2243
+ const margin = input.margin ?? 8;
2244
+ const maxLeft = input.viewportWidth - input.width - margin;
2245
+ const maxTop = input.viewportHeight - input.height - margin;
2246
+ return {
2247
+ left: Math.max(margin, input.width > 0 ? Math.min(input.x, maxLeft) : input.x),
2248
+ top: Math.max(margin, input.height > 0 ? Math.min(input.y, maxTop) : input.y)
2249
+ };
2250
+ }
2251
+
2252
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/menu/context-menu.css?raw
2253
+ var context_menu_default = ":host {\n display: contents;\n}\n:host([data-controlled]) {\n display: block;\n position: fixed;\n z-index: var(--office-z-popover);\n color: var(--office-popover-foreground);\n font: var(--office-font-size-sm) / var(--office-line-height) var(--office-font);\n}\n:host([data-controlled][hidden]) {\n display: none;\n}\n\n/* Children mode: a popover panel around the slotted menu items. */\n.panel {\n position: fixed;\n inset: auto;\n margin: 0;\n min-width: var(--office-context-menu-min-width);\n padding: var(--office-space-1);\n box-sizing: border-box;\n background: var(--office-background);\n color: var(--office-foreground);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius);\n box-shadow: var(--office-shadow);\n font-family: var(--office-font);\n}\n.panel[hidden] {\n display: none;\n}\n\n/* Controlled mode: the host is the fixed box; rows are drawn here from `state`. */\n.menu {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n min-width: var(--office-menu-min-width);\n max-height: calc(100vh - 2 * var(--office-space-2));\n overflow-y: auto;\n padding: var(--office-space-1-5) 0;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-md);\n background: var(--office-popover);\n color: inherit;\n box-shadow: var(--office-shadow-lg);\n user-select: none;\n}\n.menu:focus {\n outline: none;\n}\n.item {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--office-space-1-5);\n width: 100%;\n min-height: var(--office-field-height);\n padding: var(--office-space-1-5) var(--office-space-3);\n border: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: start;\n white-space: nowrap;\n cursor: pointer;\n}\n.item:hover:not(:disabled),\n.item:focus:not(:disabled) {\n background: var(--office-selected);\n outline: none;\n}\n.item:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(-1 * var(--office-focus-width));\n}\n.item:disabled {\n opacity: 0.45;\n cursor: default;\n}\n.item.danger {\n color: var(--office-danger);\n}\n.item svg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n flex: none;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.check {\n flex: none;\n width: var(--office-icon-size);\n text-align: center;\n}\n.separator {\n height: var(--office-border-width);\n margin: var(--office-space-1) 0;\n background: var(--office-border);\n}\n.heading {\n padding: var(--office-space-1-5) var(--office-space-3) var(--office-space-0);\n color: var(--office-muted-foreground);\n font-size: var(--office-font-size-2xs);\n font-weight: var(--office-font-weight-bold);\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n\n@media (pointer: coarse) {\n .item {\n min-height: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n .panel,\n .menu {\n border-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n box-shadow: none;\n }\n .item,\n .item.danger {\n color: CanvasText;\n }\n .item:disabled {\n color: GrayText;\n opacity: 1;\n }\n .item:hover:not(:disabled),\n .item:focus:not(:disabled) {\n background: Highlight;\n color: HighlightText;\n }\n .separator {\n background: CanvasText;\n }\n .heading {\n color: CanvasText;\n }\n}\n";
2254
+
2255
+ // src/menu/context-menu.ts
2256
+ var ITEMS = "office-ui-menu-item:not([disabled]):not([hidden])";
2257
+ var TYPE_AHEAD_MS = 700;
2258
+ var openMenus = /* @__PURE__ */ new WeakMap();
2259
+ var stateChanged = (next, old) => JSON.stringify(next) !== JSON.stringify(old);
2260
+ var OfficeUiContextMenu = class extends OfficeElement {
2261
+ static requestEvent = "office-menu-request";
2262
+ static closeEvent = "office-menu-close";
2263
+ static markerPattern = /^data-[a-z0-9-]+$/;
2264
+ static styles = controlStyles(context_menu_default);
2265
+ static properties = {
2266
+ label: { type: String },
2267
+ open: { attribute: "data-open", ...flag }
2268
+ };
2269
+ model = EMPTY_MENU_STATE;
2270
+ controlled = false;
2271
+ returnFocus = null;
2272
+ outside;
2273
+ active = -1;
2274
+ focused = false;
2275
+ query = "";
2276
+ queryTimer;
2277
+ markers = [];
2278
+ listening = false;
2279
+ opener = null;
2280
+ constructor() {
2281
+ super();
2282
+ this.label = null;
2283
+ this.open = false;
2284
+ this.addEventListener("office-command", () => this.close());
2285
+ }
2286
+ // ---- Shared -------------------------------------------------------------------------
2287
+ get panel() {
2288
+ return this.renderRoot.querySelector(".panel");
2289
+ }
2290
+ get rows() {
2291
+ return [...this.renderRoot.querySelectorAll(".item")];
2292
+ }
2293
+ config() {
2294
+ return this.constructor;
2295
+ }
2296
+ connectedCallback() {
2297
+ const reconnect = this.hasUpdated;
2298
+ if (this.controlled) this.opener = this.deepActive();
2299
+ super.connectedCallback();
2300
+ if (this.controlled && reconnect) this.settle();
2301
+ }
2302
+ disconnectedCallback() {
2303
+ super.disconnectedCallback();
2304
+ this.close();
2305
+ if (!this.controlled) return;
2306
+ this.stopListening();
2307
+ clearTimeout(this.queryTimer);
2308
+ this.focused = false;
2309
+ const opener = this.opener;
2310
+ this.opener = null;
2311
+ const active = this.ownerDocument.activeElement;
2312
+ if (opener?.isConnected && (!active || active === this.ownerDocument.body))
2313
+ opener.focus({ preventScroll: true });
2314
+ }
2315
+ typeAhead(items, from, key) {
2316
+ this.query += key;
2317
+ clearTimeout(this.queryTimer);
2318
+ this.queryTimer = setTimeout(() => this.query = "", TYPE_AHEAD_MS);
2319
+ return typeAheadIndex(items, from, this.query);
2320
+ }
2321
+ isTypeAhead(event) {
2322
+ return event.key.length === 1 && event.key !== " " && !event.ctrlKey && !event.metaKey && !event.altKey;
2323
+ }
2324
+ // ---- Children mode ------------------------------------------------------------------
2325
+ items() {
2326
+ return [...this.querySelectorAll(ITEMS)];
2327
+ }
2328
+ /** Show at client coordinates; focus the first enabled item. */
2329
+ openAt(x, y) {
2330
+ const doc = this.ownerDocument;
2331
+ const panel = this.panel;
2332
+ if (!panel) return;
2333
+ const active = doc.activeElement;
2334
+ this.returnFocus = active instanceof HTMLElement ? active : null;
2335
+ if (panel.hasAttribute("popover")) {
2336
+ if (!this.open) panel.showPopover?.();
2337
+ } else {
2338
+ this.outside ??= (event) => {
2339
+ if (!event.composedPath().includes(this)) this.close();
2340
+ };
2341
+ doc.addEventListener("pointerdown", this.outside, true);
2342
+ }
2343
+ this.open = true;
2344
+ const view = doc.defaultView;
2345
+ const { left, top } = clampFlyoutPosition({
2346
+ x,
2347
+ y,
2348
+ width: panel.offsetWidth,
2349
+ height: panel.offsetHeight,
2350
+ viewportWidth: view?.innerWidth ?? x,
2351
+ viewportHeight: view?.innerHeight ?? y,
2352
+ margin: 4
2353
+ });
2354
+ panel.style.left = `${Math.round(left)}px`;
2355
+ panel.style.top = `${Math.round(top)}px`;
2356
+ this.items()[0]?.focus();
2357
+ }
2358
+ close() {
2359
+ if (!this.open) return;
2360
+ const panel = this.panel;
2361
+ if (panel?.hasAttribute("popover")) {
2362
+ try {
2363
+ panel.hidePopover?.();
2364
+ } catch {
2365
+ }
2366
+ }
2367
+ this.closed(true);
2368
+ }
2369
+ closed(restoreFocus) {
2370
+ if (!this.open) return;
2371
+ this.open = false;
2372
+ if (this.outside) this.ownerDocument.removeEventListener("pointerdown", this.outside, true);
2373
+ if (restoreFocus) this.returnFocus?.focus?.();
2374
+ this.returnFocus = null;
2375
+ }
2376
+ onChildKey(event) {
2377
+ const items = this.items();
2378
+ const from = items.findIndex(
2379
+ (item) => item === event.target || item.contains(event.target)
2380
+ );
2381
+ const move = (index) => {
2382
+ event.preventDefault();
2383
+ items[(index + items.length) % items.length]?.focus();
2384
+ };
2385
+ if (event.key === "ArrowDown") move(from + 1);
2386
+ else if (event.key === "ArrowUp") move(from - 1);
2387
+ else if (event.key === "Home") move(0);
2388
+ else if (event.key === "End") move(items.length - 1);
2389
+ else if (event.key === "Escape" || event.key === "Tab") {
2390
+ if (event.key === "Escape") event.preventDefault();
2391
+ this.close();
2392
+ } else if (this.isTypeAhead(event)) {
2393
+ const labels = items.map((item) => ({
2394
+ label: item.getAttribute("label") ?? item.textContent ?? ""
2395
+ }));
2396
+ const target = this.typeAhead(labels, from, event.key);
2397
+ if (target >= 0) move(target);
2398
+ }
2399
+ }
2400
+ // ---- Controlled mode ----------------------------------------------------------------
2401
+ get state() {
2402
+ return this.model;
2403
+ }
2404
+ set state(value) {
2405
+ const old = this.model;
2406
+ this.model = value;
2407
+ if (!this.controlled) {
2408
+ this.controlled = true;
2409
+ this.toggleAttribute("data-controlled", true);
2410
+ if (this.isConnected) this.opener = this.deepActive();
2411
+ }
2412
+ if (stateChanged(value, old)) this.requestUpdate("state", old);
2413
+ }
2414
+ deepActive() {
2415
+ let node = this.ownerDocument.activeElement;
2416
+ while (node?.shadowRoot?.activeElement) node = node.shadowRoot.activeElement;
2417
+ return node instanceof HTMLElement && node !== this.ownerDocument.body ? node : null;
2418
+ }
2419
+ willUpdate(changed) {
2420
+ if (!this.controlled) return;
2421
+ if (changed.has("state")) {
2422
+ const { items, markers, zIndex } = this.model;
2423
+ this.toggleAttribute("hidden", items.length === 0);
2424
+ this.applyMarkers(markers ?? []);
2425
+ this.hostWrite(() => {
2426
+ if (zIndex !== void 0) this.style.zIndex = String(zIndex);
2427
+ else this.style.removeProperty("z-index");
2428
+ });
2429
+ const same = items[this.active];
2430
+ this.active = same && !same.disabled ? this.active : nextEnabledIndex(items, -1, 1);
2431
+ }
2432
+ }
2433
+ updated(changed) {
2434
+ if (this.controlled && changed.has("state")) this.settle();
2435
+ }
2436
+ /** Position the open menu, listen for dismissal and keep focus on the active row. */
2437
+ settle() {
2438
+ const { items, autoFocus } = this.model;
2439
+ if (items.length === 0) {
2440
+ this.stopListening();
2441
+ this.focused = false;
2442
+ return;
2443
+ }
2444
+ if (!this.isConnected) return;
2445
+ this.place();
2446
+ this.listen();
2447
+ if (!this.focused && autoFocus !== false) {
2448
+ this.focused = true;
2449
+ this.focusRow(nextEnabledIndex(items, -1, 1));
2450
+ } else if (this.focused && this.active >= 0)
2451
+ this.rows[this.active]?.focus({ preventScroll: true });
2452
+ }
2453
+ applyMarkers(next) {
2454
+ for (const name of this.markers) this.removeAttribute(name);
2455
+ const pattern = this.config().markerPattern;
2456
+ this.markers = next.filter((name) => pattern.test(name));
2457
+ for (const name of this.markers) this.setAttribute(name, "true");
2458
+ }
2459
+ place() {
2460
+ const view = this.ownerDocument.defaultView;
2461
+ this.style.left = `${this.model.x}px`;
2462
+ this.style.top = `${this.model.y}px`;
2463
+ const box = this.getBoundingClientRect();
2464
+ const { left, top } = clampFlyoutPosition({
2465
+ x: this.model.x,
2466
+ y: this.model.y,
2467
+ width: box.width,
2468
+ height: box.height,
2469
+ viewportWidth: view?.innerWidth ?? box.right,
2470
+ viewportHeight: view?.innerHeight ?? box.bottom
2471
+ });
2472
+ this.style.left = `${left}px`;
2473
+ this.style.top = `${top}px`;
2474
+ }
2475
+ focusRow(index) {
2476
+ if (index < 0) return;
2477
+ this.active = index;
2478
+ const rows = this.rows;
2479
+ rows.forEach((row, at) => row.tabIndex = at === index ? 0 : -1);
2480
+ rows[index]?.focus({ preventScroll: true });
2481
+ }
2482
+ onRowKey(event) {
2483
+ const items = this.model.items;
2484
+ let target = -2;
2485
+ if (event.key === "ArrowDown") target = nextEnabledIndex(items, this.active, 1);
2486
+ else if (event.key === "ArrowUp") target = nextEnabledIndex(items, this.active, -1);
2487
+ else if (event.key === "Home") target = nextEnabledIndex(items, -1, 1);
2488
+ else if (event.key === "End") target = nextEnabledIndex(items, items.length, -1);
2489
+ else if (this.isTypeAhead(event)) target = this.typeAhead(items, this.active, event.key);
2490
+ if (target === -2) return;
2491
+ event.preventDefault();
2492
+ this.focusRow(target);
2493
+ }
2494
+ onRowOver(event) {
2495
+ const row = event.target?.closest("button");
2496
+ const index = row ? this.rows.indexOf(row) : -1;
2497
+ if (index >= 0 && !row?.disabled && index !== this.active) this.focusRow(index);
2498
+ }
2499
+ dismiss(reason) {
2500
+ this.fire(this.config().closeEvent, { reason });
2501
+ }
2502
+ onDocumentKey = (event) => {
2503
+ if (openMenus.get(this.ownerDocument)?.at(-1) !== this) return;
2504
+ if (event.key === "Escape") {
2505
+ event.preventDefault();
2506
+ event.stopPropagation();
2507
+ this.dismiss("escape");
2508
+ } else if (event.key === "Tab") this.dismiss("tab");
2509
+ };
2510
+ onDocumentPointer = (event) => {
2511
+ if (!event.composedPath().includes(this)) this.dismiss("outside");
2512
+ };
2513
+ listen() {
2514
+ if (this.listening) return;
2515
+ this.listening = true;
2516
+ const doc = this.ownerDocument;
2517
+ openMenus.set(doc, [...openMenus.get(doc) ?? [], this]);
2518
+ doc.addEventListener("keydown", this.onDocumentKey, true);
2519
+ doc.addEventListener("pointerdown", this.onDocumentPointer, true);
2520
+ }
2521
+ stopListening() {
2522
+ if (!this.listening) return;
2523
+ this.listening = false;
2524
+ const doc = this.ownerDocument;
2525
+ openMenus.set(
2526
+ doc,
2527
+ (openMenus.get(doc) ?? []).filter((menu) => menu !== this)
2528
+ );
2529
+ doc.removeEventListener("keydown", this.onDocumentKey, true);
2530
+ doc.removeEventListener("pointerdown", this.onDocumentPointer, true);
2531
+ }
2532
+ // ---- Templates ----------------------------------------------------------------------
2533
+ row(item, index) {
2534
+ const checkable = item.checked !== void 0;
2535
+ return html`<button
2536
+ type="button"
2537
+ class=${item.danger ? "item danger" : "item"}
2538
+ data-item-id=${item.id}
2539
+ title=${ifDefined(item.title)}
2540
+ role=${checkable ? "menuitemcheckbox" : "menuitem"}
2541
+ aria-checked=${ifDefined(checkable ? String(item.checked) : void 0)}
2542
+ aria-disabled=${ifDefined(item.disabled ? "true" : void 0)}
2543
+ tabindex=${index === this.active ? 0 : -1}
2544
+ ?disabled=${Boolean(item.disabled)}
2545
+ @click=${() => this.fire(this.config().requestEvent, { id: item.id })}
2546
+ >${checkable ? html`<span class="check" aria-hidden="true">${item.checked ? "\u2713" : ""}</span>` : ""}${item.icon ? glyph(item.icon, "icon") : ""}${item.label}</button
2547
+ >`;
2548
+ }
2549
+ /** Rows with their rules and headings; a heading opens a labelled group for the rows after it. */
2550
+ rowTemplates() {
2551
+ const out = [];
2552
+ let group;
2553
+ const closeGroup = () => {
2554
+ if (!group) return;
2555
+ out.push(html`<div role="group" aria-label=${group.heading}>
2556
+ <div class="heading" aria-hidden="true">${group.heading}</div>${group.rows}</div
2557
+ >`);
2558
+ group = void 0;
2559
+ };
2560
+ this.model.items.forEach((item, index) => {
2561
+ if (item.separatorBefore || item.heading) closeGroup();
2562
+ if (item.separatorBefore) out.push(html`<div class="separator" role="separator"></div>`);
2563
+ if (item.heading) group = { heading: item.heading, rows: [] };
2564
+ (group ? group.rows : out).push(this.row(item, index));
2565
+ });
2566
+ closeGroup();
2567
+ return out;
2568
+ }
2569
+ render() {
2570
+ if (this.controlled)
2571
+ return html`
2572
+ <div
2573
+ class="menu"
2574
+ role="menu"
2575
+ aria-orientation="vertical"
2576
+ aria-label=${this.model.label}
2577
+ tabindex="-1"
2578
+ ?hidden=${this.model.items.length === 0}
2579
+ @keydown=${this.onRowKey}
2580
+ @pointerover=${this.onRowOver}
2581
+ @contextmenu=${(event) => event.preventDefault()}
2582
+ >${this.rowTemplates()}</div
2583
+ >
2584
+ `;
2585
+ const popover = typeof HTMLElement.prototype.showPopover === "function";
2586
+ return html`
2587
+ <div
2588
+ class="panel"
2589
+ role="menu"
2590
+ aria-label=${this.label ?? "Context menu"}
2591
+ popover=${ifDefined(popover ? "auto" : void 0)}
2592
+ ?hidden=${!popover && !present(this.open)}
2593
+ @toggle=${(event) => event.newState === "closed" && this.closed(false)}
2594
+ @keydown=${this.onChildKey}
2595
+ >
2596
+ <slot></slot>
2597
+ </div>
2598
+ `;
2599
+ }
2600
+ };
2601
+ var defineContextMenu = definer("office-ui-context-menu", () => OfficeUiContextMenu);
2602
+
2603
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/menu/menu-separator.css?raw
2604
+ var menu_separator_default = ":host {\n display: block;\n height: var(--office-border-width);\n margin: var(--office-space-1) var(--office-space-1-5);\n background: var(--office-border);\n}\n\n@media (forced-colors: active) {\n :host {\n background: CanvasText;\n }\n}\n";
2605
+
2606
+ // src/menu/menu-separator.ts
2607
+ var OfficeUiMenuSeparator = class extends OfficeElement {
2608
+ static styles = controlStyles(menu_separator_default);
2609
+ willUpdate() {
2610
+ this.setAttribute("role", "separator");
2611
+ }
2612
+ };
2613
+ var defineMenuSeparator = definer("office-ui-menu-separator", () => OfficeUiMenuSeparator);
2614
+
2615
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/menu/menu-button.css?raw
2616
+ var menu_button_default = ":host {\n display: inline-flex;\n vertical-align: middle;\n}\n.wrap {\n display: inline-flex;\n align-items: stretch;\n border: var(--office-border-width) solid transparent;\n border-radius: var(--office-radius);\n}\n:host([variant='stacked']) .wrap {\n flex-direction: column;\n}\nbutton {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-1);\n min-width: var(--office-target-size);\n min-height: var(--office-target-size);\n padding: 0 var(--office-space-1-5);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-foreground);\n font: inherit;\n font-size: var(--office-font-size-sm);\n cursor: pointer;\n white-space: nowrap;\n}\n:host([variant='stacked']) .main {\n flex-direction: column;\n padding: var(--office-space-1) var(--office-space-1-5) 0;\n}\n:host([variant='stacked']) .caret {\n min-height: var(--office-icon-size-md);\n padding: 0 var(--office-space-1-5) var(--office-space-0);\n}\n:host([variant='stacked']:not([command])) .main {\n padding-bottom: 0;\n}\n.wrap:hover:not(.disabled) {\n border-color: var(--office-border);\n}\nbutton:hover:not(:disabled) {\n background: var(--office-surface);\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(-1 * var(--office-focus-width));\n}\nbutton:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\nbutton[aria-expanded='true'] {\n background: var(--office-surface);\n}\n.caret svg {\n width: var(--office-icon-size-sm);\n height: var(--office-icon-size-sm);\n}\nsvg {\n width: var(--office-icon-size-ml);\n height: var(--office-icon-size-ml);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\nsvg:not([data-painted]) {\n display: none;\n}\n.panel {\n position: fixed;\n inset: auto;\n margin: 0;\n min-width: var(--office-menu-min-width);\n padding: var(--office-space-1);\n box-sizing: border-box;\n background: var(--office-background);\n color: var(--office-foreground);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius);\n box-shadow: var(--office-shadow);\n}\n.panel[hidden] {\n display: none;\n}\n\n@media (forced-colors: active) {\n button {\n color: ButtonText;\n }\n button:disabled {\n color: GrayText;\n }\n .panel {\n border-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n}\n";
2617
+
2618
+ // src/menu/menu-button.ts
2619
+ var ENABLED_ITEMS = "office-ui-menu-item:not([disabled]):not([hidden])";
2620
+ var OfficeUiMenuButton = class extends OfficeElement {
2621
+ static styles = controlStyles(menu_button_default);
2622
+ static properties = {
2623
+ label: { type: String },
2624
+ icon: { type: String },
2625
+ command: { type: String, reflect: true },
2626
+ keyshortcuts: { type: String },
2627
+ disabled: flag,
2628
+ iconOnly: { attribute: "icon-only", ...flag },
2629
+ open: { state: true }
2630
+ };
2631
+ outside;
2632
+ /** `title` is a native attribute, so it is watched rather than declared as a property. */
2633
+ static get observedAttributes() {
2634
+ return [...super.observedAttributes, "title"];
2635
+ }
2636
+ constructor() {
2637
+ super();
2638
+ this.label = "";
2639
+ this.icon = null;
2640
+ this.command = null;
2641
+ this.keyshortcuts = null;
2642
+ this.disabled = false;
2643
+ this.iconOnly = false;
2644
+ this.open = false;
2645
+ this.addEventListener("office-command", (event) => {
2646
+ if (event.target !== this) this.hide(true);
2647
+ });
2648
+ }
2649
+ attributeChangedCallback(name, old, value) {
2650
+ super.attributeChangedCallback(name, old, value);
2651
+ if (name === "title") this.requestUpdate();
2652
+ }
2653
+ disconnectedCallback() {
2654
+ super.disconnectedCallback();
2655
+ this.hide(false);
2656
+ }
2657
+ get panel() {
2658
+ return this.renderRoot.querySelector(".panel");
2659
+ }
2660
+ get split() {
2661
+ return this.command !== null;
2662
+ }
2663
+ get nativePopover() {
2664
+ return typeof (this.panel ?? HTMLElement.prototype).showPopover === "function";
2665
+ }
2666
+ items() {
2667
+ return [...this.querySelectorAll(ENABLED_ITEMS)];
2668
+ }
2669
+ toggle() {
2670
+ if (this.open) this.hide(true);
2671
+ else this.show(0);
2672
+ }
2673
+ show(focusIndex) {
2674
+ const panel = this.panel;
2675
+ const wrap = this.renderRoot.querySelector(".wrap");
2676
+ if (!panel || !wrap) return;
2677
+ const anchor = wrap.getBoundingClientRect();
2678
+ panel.style.left = `${Math.round(anchor.left)}px`;
2679
+ panel.style.top = `${Math.round(anchor.bottom + 2)}px`;
2680
+ if (panel.hasAttribute("popover")) panel.showPopover?.();
2681
+ else {
2682
+ this.outside = (event) => {
2683
+ if (!event.composedPath().includes(this)) this.hide(false);
2684
+ };
2685
+ this.ownerDocument.addEventListener("pointerdown", this.outside, true);
2686
+ }
2687
+ this.open = true;
2688
+ this.place(panel, anchor);
2689
+ this.items()[focusIndex]?.focus();
2690
+ }
2691
+ /** Keep the open menu inside the viewport: flip left of the trigger's right edge if needed. */
2692
+ place(panel, anchor) {
2693
+ const view = this.ownerDocument.defaultView;
2694
+ const width = panel.offsetWidth;
2695
+ const height = panel.offsetHeight;
2696
+ if (!view || !width) return;
2697
+ const maxLeft = view.innerWidth - width - 4;
2698
+ const left = anchor.left > maxLeft ? Math.max(4, anchor.right - width) : anchor.left;
2699
+ const top = anchor.bottom + 2 + height > view.innerHeight && anchor.top - height - 2 > 0 ? anchor.top - height - 2 : anchor.bottom + 2;
2700
+ panel.style.left = `${Math.round(Math.min(left, Math.max(4, maxLeft)))}px`;
2701
+ panel.style.top = `${Math.round(top)}px`;
2702
+ }
2703
+ hide(restoreFocus) {
2704
+ if (!this.open) return;
2705
+ const panel = this.panel;
2706
+ if (panel?.hasAttribute("popover")) {
2707
+ try {
2708
+ panel.hidePopover?.();
2709
+ } catch {
2710
+ }
2711
+ }
2712
+ this.closed();
2713
+ if (restoreFocus)
2714
+ this.renderRoot.querySelector(this.split ? ".caret" : ".main")?.focus();
2715
+ }
2716
+ closed() {
2717
+ if (this.outside) this.ownerDocument.removeEventListener("pointerdown", this.outside, true);
2718
+ this.outside = void 0;
2719
+ this.open = false;
2720
+ }
2721
+ onMain() {
2722
+ if (present(this.disabled)) return;
2723
+ if (this.command) this.fire("office-command", { command: this.command });
2724
+ else this.toggle();
2725
+ }
2726
+ onTriggerKey(event) {
2727
+ if (event.key === "ArrowDown" && !present(this.disabled)) {
2728
+ event.preventDefault();
2729
+ this.show(0);
2730
+ }
2731
+ }
2732
+ onMenuKey(event) {
2733
+ const items = this.items();
2734
+ const from = items.findIndex(
2735
+ (item) => item === event.target || item.contains(event.target)
2736
+ );
2737
+ const move = (index) => {
2738
+ event.preventDefault();
2739
+ items[(index + items.length) % items.length]?.focus();
2740
+ };
2741
+ if (event.key === "ArrowDown") move(from + 1);
2742
+ else if (event.key === "ArrowUp") move(from - 1);
2743
+ else if (event.key === "Home") move(0);
2744
+ else if (event.key === "End") move(items.length - 1);
2745
+ else if (event.key === "Escape") {
2746
+ event.preventDefault();
2747
+ this.hide(true);
2748
+ } else if (event.key === "Tab") this.hide(false);
2749
+ }
2750
+ willUpdate() {
2751
+ if (present(this.disabled)) this.hide(false);
2752
+ }
2753
+ render() {
2754
+ const { label, split, open } = this;
2755
+ const iconOnly = present(this.iconOnly);
2756
+ const disabled = present(this.disabled);
2757
+ const title = this.getAttribute("title") ?? label;
2758
+ const popover = this.nativePopover;
2759
+ return html`
2760
+ <div class="wrap ${disabled ? "disabled" : ""}">
2761
+ <button
2762
+ class="main"
2763
+ part="button"
2764
+ type="button"
2765
+ title=${title}
2766
+ aria-label=${ifDefined(iconOnly ? label : void 0)}
2767
+ aria-keyshortcuts=${ifDefined(this.keyshortcuts || void 0)}
2768
+ aria-haspopup=${ifDefined(split ? void 0 : "menu")}
2769
+ aria-expanded=${ifDefined(split ? void 0 : String(open))}
2770
+ ?disabled=${disabled}
2771
+ @click=${this.onMain}
2772
+ @keydown=${this.onTriggerKey}
2773
+ >${glyph(this.icon)}<span class="text" ?hidden=${iconOnly}>${label}</span></button
2774
+ >
2775
+ <!-- Without a split command the caret is decoration on one control, not a second button. -->
2776
+ <button
2777
+ class="caret"
2778
+ type="button"
2779
+ title=${split ? `${label} options` : title}
2780
+ aria-label=${ifDefined(split ? `${label} options` : void 0)}
2781
+ aria-hidden=${ifDefined(split ? void 0 : "true")}
2782
+ aria-haspopup="menu"
2783
+ aria-expanded=${String(open)}
2784
+ tabindex=${split ? 0 : -1}
2785
+ ?disabled=${disabled}
2786
+ @click=${() => !disabled && this.toggle()}
2787
+ @keydown=${this.onTriggerKey}
2788
+ >${glyph("chevronDown", "chevron")}</button
2789
+ >
2790
+ </div>
2791
+ <div
2792
+ class="panel"
2793
+ role="menu"
2794
+ popover=${ifDefined(popover ? "auto" : void 0)}
2795
+ ?hidden=${!popover && !open}
2796
+ @toggle=${(event) => event.newState === "closed" && this.closed()}
2797
+ @keydown=${this.onMenuKey}
2798
+ >
2799
+ <slot></slot>
2800
+ </div>
2801
+ `;
2802
+ }
2803
+ };
2804
+ var defineMenuButton = definer("office-ui-menu-button", () => OfficeUiMenuButton);
2805
+
2806
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/menu/menu-item.css?raw
2807
+ var menu_item_default = ":host {\n display: block;\n}\n:host([hidden]) {\n display: none;\n}\nbutton {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n width: 100%;\n min-height: var(--office-target-size);\n padding: 0 var(--office-space-2-5) 0 var(--office-space-2);\n border: 0;\n border-radius: var(--office-radius-sm);\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: var(--office-font-size-sm);\n text-align: start;\n cursor: pointer;\n}\nbutton:hover:not(:disabled),\nbutton:focus-visible {\n background: var(--office-surface);\n outline: none;\n}\nbutton:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.check {\n width: var(--office-icon-size);\n text-align: center;\n}\nsvg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n flex: none;\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\nsvg:not([data-painted]) {\n visibility: hidden;\n}\n\n@media (forced-colors: active) {\n button:focus-visible {\n background: Highlight;\n color: HighlightText;\n }\n}\n";
2808
+
2809
+ // src/menu/menu-item.ts
2810
+ var OfficeUiMenuItem = class extends OfficeElement {
2811
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
2812
+ static styles = controlStyles(menu_item_default);
2813
+ static properties = {
2814
+ label: { type: String },
2815
+ icon: { type: String },
2816
+ command: { type: String },
2817
+ checked: { type: String },
2818
+ disabled: flag
2819
+ };
2820
+ /** `title` is a native attribute, so it is watched rather than declared as a property. */
2821
+ static get observedAttributes() {
2822
+ return [...super.observedAttributes, "title"];
2823
+ }
2824
+ constructor() {
2825
+ super();
2826
+ this.label = "";
2827
+ this.icon = null;
2828
+ this.command = null;
2829
+ this.checked = null;
2830
+ this.disabled = false;
2831
+ }
2832
+ attributeChangedCallback(name, old, value) {
2833
+ super.attributeChangedCallback(name, old, value);
2834
+ if (name === "title") this.requestUpdate();
2835
+ }
2836
+ /** Focus the menuitem itself, so arrow navigation never depends on delegatesFocus. */
2837
+ focus(options) {
2838
+ this.renderRoot.querySelector("button")?.focus(options);
2839
+ }
2840
+ onClick() {
2841
+ if (!present(this.disabled) && this.command)
2842
+ this.fire("office-command", { command: this.command });
2843
+ }
2844
+ render() {
2845
+ const checkable = this.checked !== null;
2846
+ const on = checkable && this.checked !== "false";
2847
+ return html`
2848
+ <button
2849
+ type="button"
2850
+ tabindex="-1"
2851
+ role=${checkable ? "menuitemcheckbox" : "menuitem"}
2852
+ aria-checked=${ifDefined(checkable ? String(on) : void 0)}
2853
+ title=${this.getAttribute("title") ?? ""}
2854
+ ?disabled=${present(this.disabled)}
2855
+ @click=${this.onClick}
2856
+ >
2857
+ <span class="check" aria-hidden="true">${on ? "\u2713" : ""}</span>${glyph(this.icon)}<span
2858
+ >${this.label}</span
2859
+ >
2860
+ </button>
2861
+ `;
2862
+ }
2863
+ };
2864
+ var defineMenuItem = definer("office-ui-menu-item", () => OfficeUiMenuItem);
2865
+
2866
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/dialog/options-dialog.css?raw
2867
+ var options_dialog_default = ":host {\n display: contents;\n}\noffice-ui-dialog {\n --office-dialog-width: var(--office-options-width);\n --office-dialog-max-width: var(--office-options-width);\n}\n.layout {\n display: grid;\n grid-template-columns: var(--office-options-nav-width) minmax(0, 1fr);\n gap: 0;\n min-height: var(--office-options-min-height);\n margin: calc(-1 * var(--office-space-4));\n}\nnav {\n display: flex;\n flex-direction: column;\n padding: var(--office-space-2) 0;\n border-inline-end: var(--office-border-width) solid var(--office-border);\n background: var(--office-surface);\n overflow: auto;\n}\nnav button {\n min-height: var(--office-options-row-height);\n padding: var(--office-space-1) var(--office-space-3-5);\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\nnav button[aria-selected='true'] {\n background: var(--office-selected);\n font-weight: 600;\n box-shadow: inset var(--office-selection-bar-width) 0 0 var(--office-accent);\n}\nnav button:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(-1 * var(--office-focus-width));\n}\n[role='tabpanel'] {\n padding: var(--office-space-4) var(--office-space-5);\n overflow: auto;\n}\n.lead {\n margin: 0 0 var(--office-space-3);\n font-size: var(--office-font-size-md);\n}\n.unavailable {\n margin: 0 0 var(--office-space-3);\n color: var(--office-muted-foreground);\n}\nsection + section {\n margin-top: var(--office-space-4);\n}\nh3 {\n margin: 0 0 var(--office-space-2);\n padding-bottom: var(--office-space-1);\n border-bottom: var(--office-border-width) solid var(--office-border);\n font-size: var(--office-font-size);\n font-weight: 600;\n}\n.desc {\n margin: 0 0 var(--office-space-2);\n color: var(--office-muted-foreground);\n}\n.row {\n display: flex;\n align-items: center;\n gap: var(--office-space-2);\n min-height: var(--office-options-row-height);\n}\n.row.indent {\n padding-inline-start: var(--office-space-6);\n}\n.row.field > span:first-child {\n min-width: var(--office-options-label-width);\n}\n.row[data-disabled] {\n color: var(--office-muted-foreground);\n}\ninput[type='text'],\ninput[type='number'],\nselect {\n min-height: var(--office-input-height);\n box-sizing: border-box;\n font: inherit;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-sm);\n background: var(--office-background);\n color: inherit;\n}\ninput[type='number'] {\n width: var(--office-options-number-width);\n}\n.info {\n display: inline-grid;\n place-items: center;\n width: var(--office-info-size);\n height: var(--office-info-size);\n border: var(--office-border-width) solid currentColor;\n border-radius: 50%;\n font-size: var(--office-font-size-2xs);\n color: var(--office-muted-foreground);\n cursor: help;\n}\n.actions {\n display: flex;\n gap: var(--office-space-2);\n}\n.actions button {\n min-width: var(--office-dialog-button-min-width);\n min-height: var(--office-control-height-md);\n font: inherit;\n cursor: pointer;\n}\n@media (max-width: 600px) {\n .layout {\n grid-template-columns: 1fr;\n }\n nav {\n flex-direction: row;\n border-inline-end: 0;\n border-bottom: var(--office-border-width) solid var(--office-border);\n }\n nav button {\n white-space: nowrap;\n }\n .row.field {\n flex-wrap: wrap;\n }\n}\n";
2868
+
2869
+ // src/dialog/options-dialog.ts
2870
+ function clampOptionNumber(raw, min, max) {
2871
+ const parsed = Number(raw);
2872
+ return Number.isFinite(parsed) && raw.trim() !== "" ? Math.min(max, Math.max(min, parsed)) : void 0;
2873
+ }
2874
+ var OfficeUiOptionsDialog = class extends OfficeElement {
2875
+ static styles = controlStyles(options_dialog_default);
2876
+ static properties = {
2877
+ open: flag,
2878
+ heading: { type: String },
2879
+ categories: { attribute: false },
2880
+ // `values` keeps a private copy and `category` validates its id, so both have accessors.
2881
+ active: { state: true }
2882
+ };
2883
+ committed = {};
2884
+ draft = {};
2885
+ constructor() {
2886
+ super();
2887
+ this.open = false;
2888
+ this.heading = null;
2889
+ this.categories = [];
2890
+ this.active = "";
2891
+ }
2892
+ get values() {
2893
+ return { ...this.committed };
2894
+ }
2895
+ set values(value) {
2896
+ this.committed = { ...value };
2897
+ if (!present(this.open)) this.draft = { ...value };
2898
+ this.requestUpdate("values");
2899
+ }
2900
+ /** The selected category id. */
2901
+ get category() {
2902
+ return this.active;
2903
+ }
2904
+ set category(id) {
2905
+ if (this.categories.some((category) => category.id === id)) this.active = id;
2906
+ }
2907
+ show() {
2908
+ this.open = true;
2909
+ }
2910
+ close() {
2911
+ this.open = false;
2912
+ }
2913
+ commit() {
2914
+ const changed = Object.keys(this.draft).filter(
2915
+ (key) => this.draft[key] !== this.committed[key]
2916
+ );
2917
+ this.committed = { ...this.draft };
2918
+ this.fire("office-options-change", { values: { ...this.committed }, changed });
2919
+ this.close();
2920
+ }
2921
+ onNavKey(event) {
2922
+ const tabs = [...this.renderRoot.querySelectorAll('nav [role="tab"]')];
2923
+ const at = tabs.findIndex((tab) => tab.getAttribute("aria-selected") === "true");
2924
+ const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key];
2925
+ const target = event.key === "Home" ? tabs[0] : event.key === "End" ? tabs.at(-1) : step ? tabs[(at + step + tabs.length) % tabs.length] : void 0;
2926
+ if (!target) return;
2927
+ event.preventDefault();
2928
+ this.category = target.dataset.category;
2929
+ this.renderRoot.querySelector('nav [aria-selected="true"]')?.focus();
2930
+ }
2931
+ willUpdate(changed) {
2932
+ if (!this.categories.some((category) => category.id === this.active))
2933
+ this.active = this.categories[0]?.id ?? "";
2934
+ if (changed.has("open") && present(this.open)) this.draft = { ...this.committed };
2935
+ }
2936
+ set(key, next) {
2937
+ this.draft[key] = next;
2938
+ }
2939
+ control(control) {
2940
+ const value = this.draft[control.key];
2941
+ const disabled = Boolean(control.disabled);
2942
+ const info = control.info ? html`<span class="info" title=${control.info} role="img" aria-label=${control.info}
2943
+ >i</span
2944
+ >` : "";
2945
+ let body;
2946
+ if (control.kind === "toggle")
2947
+ body = html`<input
2948
+ type="checkbox"
2949
+ .checked=${value === true}
2950
+ ?disabled=${disabled}
2951
+ @change=${(event) => this.set(control.key, event.target.checked)}
2952
+ /><span>${control.label}</span>`;
2953
+ else if (control.kind === "select") {
2954
+ const current = String(value ?? control.choices[0]?.value ?? "");
2955
+ body = html`<span>${control.label}</span
2956
+ ><select
2957
+ ?disabled=${disabled}
2958
+ @change=${(event) => this.set(control.key, event.target.value)}
2959
+ >
2960
+ ${control.choices.map(
2961
+ (choice) => html`<option value=${choice.value} ?selected=${choice.value === current}
2962
+ >${choice.label}</option
2963
+ >`
2964
+ )}
2965
+ </select>`;
2966
+ } else if (control.kind === "number")
2967
+ body = html`<span>${control.label}</span
2968
+ ><input
2969
+ type="number"
2970
+ min=${control.min}
2971
+ max=${control.max}
2972
+ step=${ifDefined(control.step ? String(control.step) : void 0)}
2973
+ .value=${value === void 0 ? "" : String(value)}
2974
+ ?disabled=${disabled}
2975
+ @change=${(event) => {
2976
+ const field = event.target;
2977
+ const next = clampOptionNumber(field.value, control.min, control.max);
2978
+ if (next === void 0) field.value = String(this.draft[control.key] ?? "");
2979
+ else {
2980
+ field.value = String(next);
2981
+ this.set(control.key, next);
2982
+ }
2983
+ }}
2984
+ />${control.unit ? html`<span>${control.unit}</span>` : ""}`;
2985
+ else
2986
+ body = html`<span>${control.label}</span
2987
+ ><input
2988
+ type="text"
2989
+ maxlength=${ifDefined(control.maxLength ? String(control.maxLength) : void 0)}
2990
+ .value=${value === void 0 ? "" : String(value)}
2991
+ ?disabled=${disabled}
2992
+ @input=${(event) => this.set(control.key, event.target.value)}
2993
+ />`;
2994
+ return html`<label
2995
+ class="row${control.indent ? " indent" : ""}${control.kind === "toggle" ? "" : " field"}"
2996
+ data-key=${control.key}
2997
+ ?data-disabled=${disabled}
2998
+ title=${ifDefined(control.disabled)}
2999
+ >${body}${info}</label
3000
+ >`;
3001
+ }
3002
+ pane(category) {
3003
+ if (!category) return "";
3004
+ return html`
3005
+ ${category.description ? html`<p class="lead">${category.description}</p>` : ""}
3006
+ ${category.disabled ? html`<p class="unavailable">${category.disabled}</p>` : ""}
3007
+ ${category.sections.map(
3008
+ (section) => html`<section>
3009
+ <h3>${section.title}</h3>
3010
+ ${section.description ? html`<p class="desc">${section.description}</p>` : ""}
3011
+ ${section.controls.map((control) => this.control(control))}
3012
+ </section>`
3013
+ )}
3014
+ `;
3015
+ }
3016
+ render() {
3017
+ const category = this.categories.find((entry) => entry.id === this.active);
3018
+ return html`
3019
+ <office-ui-dialog
3020
+ heading=${this.heading ?? "Options"}
3021
+ ?open=${present(this.open)}
3022
+ @office-dialog-close=${(event) => {
3023
+ if (!event.defaultPrevented) this.close();
3024
+ }}
3025
+ >
3026
+ <div class="layout">
3027
+ <nav
3028
+ role="tablist"
3029
+ aria-orientation="vertical"
3030
+ aria-label="Categories"
3031
+ @keydown=${this.onNavKey}
3032
+ >
3033
+ ${this.categories.map(
3034
+ (entry) => html`<button
3035
+ type="button"
3036
+ role="tab"
3037
+ id="tab-${entry.id}"
3038
+ data-category=${entry.id}
3039
+ aria-controls="pane"
3040
+ aria-selected=${String(entry.id === this.active)}
3041
+ tabindex=${entry.id === this.active ? 0 : -1}
3042
+ @click=${() => this.category = entry.id}
3043
+ >${entry.label}</button
3044
+ >`
3045
+ )}
3046
+ </nav>
3047
+ <div
3048
+ role="tabpanel"
3049
+ id="pane"
3050
+ aria-labelledby=${ifDefined(category ? `tab-${category.id}` : void 0)}
3051
+ >${this.pane(category)}</div
3052
+ >
3053
+ </div>
3054
+ <div slot="footer" class="actions">
3055
+ <button type="button" data-action="ok" @click=${this.commit}>OK</button>
3056
+ <button type="button" data-action="cancel" @click=${this.close}>Cancel</button>
3057
+ </div>
3058
+ </office-ui-dialog>
3059
+ `;
3060
+ }
3061
+ };
3062
+ var defineOptionsDialog = definer("office-ui-options-dialog", () => OfficeUiOptionsDialog);
3063
+
3064
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-group.css?raw
3065
+ var ribbon_group_default = ":host {\n display: inline-flex;\n flex: none;\n align-self: stretch;\n}\n.group {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n min-height: var(--office-ribbon-group-height);\n padding: var(--office-ribbon-group-padding);\n}\n/* Office draws a hairline between groups; a product may stop it short of the top and bottom. */\n.group::after {\n content: '';\n position: absolute;\n top: var(--office-ribbon-separator-inset);\n bottom: var(--office-ribbon-separator-inset);\n inset-inline-end: 0;\n width: var(--office-border-width);\n background: var(--office-ribbon-separator);\n}\n:host(:last-child) .group::after {\n content: none;\n}\n.row {\n display: flex;\n align-items: flex-start;\n gap: var(--office-space-1);\n flex: 1;\n}\n/* One-line groups centre their commands on the whole group box. */\n:host([data-compact-row]) .row {\n align-items: center;\n}\n/* Several small drop-down galleries stack in columns of three. */\n:host([data-stack]) .row {\n flex-direction: column;\n align-items: stretch;\n align-content: flex-start;\n flex-wrap: wrap;\n max-height: var(--office-ribbon-stack-max-height);\n gap: var(--office-space-0);\n}\n::slotted(*) {\n flex-shrink: 0;\n}\n.foot {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--office-ribbon-caption-padding);\n}\n.caption {\n color: var(--office-muted-foreground);\n font-size: var(--office-ribbon-caption-size);\n line-height: var(--office-ribbon-caption-line-height);\n text-align: center;\n white-space: nowrap;\n}\n.launcher {\n position: absolute;\n inset-inline-end: var(--office-launcher-inset-end);\n bottom: var(--office-launcher-inset-bottom);\n display: inline-grid;\n place-items: center;\n width: var(--office-launcher-width);\n height: var(--office-launcher-height);\n padding: 0;\n border: 0;\n border-radius: var(--office-radius-xs);\n background: transparent;\n color: var(--office-muted-foreground);\n cursor: pointer;\n}\n.launcher:hover:not(:disabled) {\n background: var(--office-surface);\n color: var(--office-foreground);\n}\n.launcher:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n}\n.launcher:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.launcher[hidden] {\n display: none;\n}\n.launcher svg {\n width: var(--office-icon-size-xs);\n height: var(--office-icon-size-xs);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n/* Collapsed (narrow window): the group is one button; its commands open in a popup. */\n.face {\n display: none;\n}\n:host([data-collapsed]) .face {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-start;\n gap: var(--office-space-0);\n min-width: var(--office-collapsed-group-min-width);\n height: var(--office-command-large-height);\n padding: var(--office-space-0) var(--office-space-1-5);\n border: 0;\n border-radius: var(--office-radius);\n background: transparent;\n color: var(--office-foreground);\n font: inherit;\n font-size: var(--office-font-size-sm);\n cursor: pointer;\n}\n:host([data-collapsed]) .face:hover,\n:host([data-open]) .face {\n background: var(--office-selected);\n}\n.face:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n.face svg {\n width: var(--office-command-large-icon);\n height: var(--office-command-large-icon);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n color: var(--office-command-icon-color);\n}\n.face svg.chev {\n width: var(--office-icon-size-xs);\n height: var(--office-icon-size-xs);\n color: currentColor;\n}\n:host([data-collapsed]) .foot,\n:host([data-collapsed]) .row {\n display: none;\n}\n:host([data-collapsed][data-open]) .row {\n display: flex;\n flex-wrap: wrap;\n position: fixed;\n top: var(--office-ribbon-collapse-y);\n left: var(--office-ribbon-collapse-x);\n z-index: var(--office-z-popover);\n box-sizing: border-box;\n max-width: calc(100vw - 2 * var(--office-space-2));\n padding: var(--office-space-2);\n background: var(--office-popover);\n color: var(--office-popover-foreground);\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-md);\n box-shadow: var(--office-shadow);\n}\n\n@media (forced-colors: active) {\n .group::after {\n background: CanvasText;\n }\n .caption,\n .launcher {\n color: CanvasText;\n }\n :host([data-collapsed][data-open]) .row {\n border-color: CanvasText;\n }\n}\n";
3066
+
3067
+ // src/ribbon/ribbon-group.ts
3068
+ function tokenPx(el, name, fallback) {
3069
+ const value = el.ownerDocument.defaultView?.getComputedStyle(el).getPropertyValue(name).trim();
3070
+ const px = value?.endsWith("px") ? parseFloat(value) : NaN;
3071
+ return Number.isFinite(px) ? px : fallback;
3072
+ }
3073
+ var OfficeUiRibbonGroup = class extends OfficeElement {
3074
+ static launcherEvent = "office-command";
3075
+ static collapseEvent = "office-ribbon-collapse-toggle";
3076
+ static styles = controlStyles(ribbon_group_default);
3077
+ /** The group is configured by attributes, as it always was (see `OfficeElement.watched`). */
3078
+ static watched = [
3079
+ "label",
3080
+ "launcher",
3081
+ "launcher-label",
3082
+ "launcher-disabled",
3083
+ "icon",
3084
+ "data-open"
3085
+ ];
3086
+ observer;
3087
+ /** The launcher's event detail; null emits nothing. */
3088
+ launcherDetail() {
3089
+ const command = this.getAttribute("launcher");
3090
+ return command ? { command } : null;
3091
+ }
3092
+ /** The registered icon on the collapsed face; a product subclass may map names. */
3093
+ iconName() {
3094
+ return this.getAttribute("icon") ?? "grid";
3095
+ }
3096
+ connectedCallback() {
3097
+ super.connectedCallback();
3098
+ this.observe();
3099
+ }
3100
+ disconnectedCallback() {
3101
+ super.disconnectedCallback();
3102
+ this.observer?.disconnect();
3103
+ }
3104
+ observe() {
3105
+ this.observer?.disconnect();
3106
+ if (!this.isConnected) return;
3107
+ const Observer = this.ownerDocument.defaultView?.ResizeObserver;
3108
+ if (Observer) this.observer ??= new Observer(() => this.measure());
3109
+ const slot = this.renderRoot.querySelector("slot");
3110
+ for (const el of slot?.assignedElements() ?? []) this.observer?.observe(el);
3111
+ this.measure();
3112
+ }
3113
+ measure() {
3114
+ const children = [...this.children];
3115
+ const stackable = children.filter((el) => {
3116
+ const gallery = el.matches('[mode="dropdown"]') ? el : el.querySelector('[mode="dropdown"]');
3117
+ return gallery !== null && !gallery.hasAttribute("data-command-large");
3118
+ });
3119
+ this.toggleAttribute(
3120
+ "data-stack",
3121
+ stackable.length > 1 && stackable.length === children.length
3122
+ );
3123
+ const tallest = Math.max(0, ...children.map((el) => el.getBoundingClientRect().height));
3124
+ if (tallest > 0)
3125
+ this.toggleAttribute(
3126
+ "data-compact-row",
3127
+ tallest <= tokenPx(this, "--office-ribbon-compact-row-max", 36)
3128
+ );
3129
+ }
3130
+ onLauncher() {
3131
+ if (this.hasAttribute("launcher-disabled")) return;
3132
+ const detail = this.launcherDetail();
3133
+ if (detail) this.fire(this.constructor.launcherEvent, detail);
3134
+ }
3135
+ /** The overflow controller listens for this and owns which popup is open. */
3136
+ onFace() {
3137
+ this.fire(this.constructor.collapseEvent, void 0);
3138
+ }
3139
+ willUpdate() {
3140
+ this.setAttribute("role", "group");
3141
+ this.setAttribute("aria-label", this.getAttribute("label") ?? "");
3142
+ }
3143
+ render() {
3144
+ const label = this.getAttribute("label") ?? "";
3145
+ const launcherLabel = this.getAttribute("launcher-label") ?? `${label} options`;
3146
+ return html`
3147
+ <div class="group">
3148
+ <button
3149
+ class="face"
3150
+ type="button"
3151
+ aria-haspopup="true"
3152
+ aria-label=${label}
3153
+ aria-expanded=${String(this.hasAttribute("data-open"))}
3154
+ title=${label}
3155
+ @click=${this.onFace}
3156
+ >${glyph(this.iconName(), "face-icon")}<span>${label}</span>${glyph("chevronDown", "chev")}</button
3157
+ >
3158
+ <div class="row">
3159
+ <slot @slotchange=${this.observe}></slot>
3160
+ </div>
3161
+ <div class="foot">
3162
+ <span class="caption">${label}</span>
3163
+ <button
3164
+ class="launcher"
3165
+ type="button"
3166
+ aria-label=${launcherLabel}
3167
+ title=${launcherLabel}
3168
+ ?hidden=${!this.hasAttribute("launcher")}
3169
+ ?disabled=${this.hasAttribute("launcher-disabled")}
3170
+ @click=${this.onLauncher}
3171
+ >${glyph("launcher", "launcher-icon")}</button
3172
+ >
3173
+ </div>
3174
+ </div>
3175
+ `;
3176
+ }
3177
+ };
3178
+ var defineRibbonGroup = definer("office-ui-ribbon-group", () => OfficeUiRibbonGroup);
3179
+
3180
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-stack.css?raw
3181
+ var ribbon_stack_default = ":host {\n display: inline-flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n gap: var(--office-space-px);\n align-self: stretch;\n}\n:host([orientation='horizontal']) {\n flex-direction: row;\n align-items: center;\n gap: var(--office-space-0);\n align-self: auto;\n}\n::slotted(*) {\n flex-shrink: 0;\n}\n";
3182
+
3183
+ // src/ribbon/ribbon-stack.ts
3184
+ var OfficeUiRibbonStack = class extends OfficeElement {
3185
+ static styles = controlStyles(ribbon_stack_default);
3186
+ render() {
3187
+ return html`<slot></slot>`;
3188
+ }
3189
+ };
3190
+ var defineRibbonStack = definer("office-ui-ribbon-stack", () => OfficeUiRibbonStack);
3191
+
3192
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-toolbar.css?raw
3193
+ var ribbon_toolbar_default = ":host {\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n gap: var(--office-space-0);\n padding: var(--office-space-0) var(--office-space-1);\n background: var(--office-surface);\n border-bottom: var(--office-border-width) solid var(--office-border);\n}\n\n@media (forced-colors: active) {\n :host {\n border-bottom-color: CanvasText;\n background: Canvas;\n }\n}\n";
3194
+
3195
+ // src/ribbon/ribbon-toolbar.ts
3196
+ var ITEM = "office-ui-button, office-ui-menu-button, office-ui-select, office-ui-checkbox, office-ui-switch";
3197
+ var OfficeUiToolbar = class extends OfficeElement {
3198
+ static styles = controlStyles(ribbon_toolbar_default);
3199
+ constructor() {
3200
+ super();
3201
+ this.addEventListener("keydown", (event) => this.onKey(event));
3202
+ }
3203
+ items() {
3204
+ return [...this.querySelectorAll(ITEM)].filter(
3205
+ (el) => !el.hasAttribute("disabled")
3206
+ );
3207
+ }
3208
+ onKey(event) {
3209
+ const vertical = this.getAttribute("aria-orientation") === "vertical";
3210
+ const next = vertical ? "ArrowDown" : "ArrowRight";
3211
+ const prev = vertical ? "ArrowUp" : "ArrowLeft";
3212
+ if (![next, prev, "Home", "End"].includes(event.key)) return;
3213
+ const items = this.items();
3214
+ const from = items.findIndex((el) => el === event.target || el.contains(event.target));
3215
+ if (from < 0 || items.length === 0) return;
3216
+ event.preventDefault();
3217
+ const index = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : (from + (event.key === next ? 1 : -1) + items.length) % items.length;
3218
+ items[index]?.focus();
3219
+ }
3220
+ willUpdate() {
3221
+ this.setAttribute("role", "toolbar");
3222
+ }
3223
+ render() {
3224
+ return html`<slot></slot>`;
3225
+ }
3226
+ };
3227
+ var defineToolbar = definer("office-ui-toolbar", () => OfficeUiToolbar);
3228
+
3229
+ // src/form/select-menu.ts
3230
+ var SELECT_OPTION_ATTRIBUTES = [
3231
+ "value",
3232
+ "disabled",
3233
+ "label",
3234
+ "selected",
3235
+ "hidden",
3236
+ "data-display-label",
3237
+ "data-description",
3238
+ "style"
3239
+ ];
3240
+ function collectSelectChoices(options) {
3241
+ return options.map((option) => {
3242
+ const parent = option.parentElement;
3243
+ const group = parent?.localName === "optgroup" ? parent : null;
3244
+ return {
3245
+ value: option.value,
3246
+ label: option.label || option.textContent?.trim() || "",
3247
+ disabled: option.disabled || Boolean(group?.disabled),
3248
+ hidden: Boolean(option.hidden) || Boolean(group?.hidden),
3249
+ group: group?.label ?? "",
3250
+ ...option.dataset.displayLabel ? { displayLabel: option.dataset.displayLabel } : {},
3251
+ ...option.dataset.description ? { description: option.dataset.description } : {},
3252
+ ...option.style.fontFamily ? { fontFamily: option.style.fontFamily } : {}
3253
+ };
3254
+ });
3255
+ }
3256
+ function tokenPx2(el, name, fallback) {
3257
+ const value = el.ownerDocument.defaultView?.getComputedStyle(el).getPropertyValue(name).trim();
3258
+ const px = value?.endsWith("px") ? parseFloat(value) : NaN;
3259
+ return Number.isFinite(px) ? px : fallback;
3260
+ }
3261
+ function positionSelectMenu(menu, trigger, host) {
3262
+ const view = menu.ownerDocument.defaultView;
3263
+ const rect = trigger.getBoundingClientRect();
3264
+ const margin = tokenPx2(host, "--office-space-2", 8);
3265
+ const gap = tokenPx2(host, "--office-space-1", 4);
3266
+ const width = Math.max(0, view.innerWidth - margin * 2);
3267
+ const below = Math.max(0, view.innerHeight - rect.bottom - gap - margin);
3268
+ const above = Math.max(0, rect.top - gap - margin);
3269
+ menu.style.minWidth = `${Math.min(rect.width, width)}px`;
3270
+ menu.style.maxWidth = `${width}px`;
3271
+ const cap = host.getAttribute("data-font-picker") === "family" ? tokenPx2(host, "--office-font-picker-max-height", 320) : tokenPx2(host, "--office-select-max-height", 240);
3272
+ menu.style.maxHeight = `${cap}px`;
3273
+ const preferred = menu.getBoundingClientRect().height;
3274
+ const flip = below < preferred && above > below;
3275
+ menu.style.maxHeight = `${Math.min(cap, flip ? above : below)}px`;
3276
+ const { height, width: menuWidth } = menu.getBoundingClientRect();
3277
+ menu.style.top = `${Math.max(margin, flip ? rect.top - gap - height : rect.bottom + gap)}px`;
3278
+ menu.style.left = `${Math.max(margin, Math.min(rect.left, view.innerWidth - menuWidth - margin))}px`;
3279
+ }
3280
+ function nextSelectActive(choices, active, step) {
3281
+ for (let i = 0; i < choices.length; i++) {
3282
+ active = (active + step + choices.length) % choices.length;
3283
+ if (!choices[active].disabled && !choices[active].hidden) return active;
3284
+ }
3285
+ return active;
3286
+ }
3287
+ var SELECT_PAGE_SIZE = 8;
3288
+ function pageSelectActive(choices, active, direction) {
3289
+ const usable = (index) => !choices[index]?.disabled && !choices[index]?.hidden;
3290
+ const last = choices.length - 1;
3291
+ const target = Math.max(0, Math.min(last, Math.max(active, 0) + direction * SELECT_PAGE_SIZE));
3292
+ for (let index = target; index >= 0 && index <= last; index -= direction)
3293
+ if (usable(index)) return index;
3294
+ return active;
3295
+ }
3296
+
3297
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/select.css?raw
3298
+ var select_default = "/*\n * `variant=\"ribbon-font\"` and `variant=\"ribbon-icon\"` are the compact ribbon combo and icon\n * picker; `data-font-picker=\"family\"` widens and lengthens the popup. Moved from pptx-viewer\n * (`select-styles.ts`, `select-ribbon-styles.ts`) and expressed in tokens.\n */\n:host {\n display: inline-block;\n box-sizing: border-box;\n min-width: 0;\n max-width: 100%;\n color: var(--office-foreground);\n font: inherit;\n font-size: var(--office-font-size-sm);\n vertical-align: middle;\n}\n:host([disabled]) {\n opacity: 0.5;\n cursor: not-allowed;\n}\nbutton {\n box-sizing: border-box;\n width: 100%;\n min-height: var(--office-field-height);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--office-space-2-5);\n padding: var(--office-space-1) var(--office-space-1-5);\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-field-radius);\n background: var(--office-select-background);\n color: inherit;\n font: inherit;\n font-size: inherit;\n text-align: start;\n cursor: pointer;\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\nbutton:disabled {\n cursor: not-allowed;\n}\n.value {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.chevron svg {\n display: none;\n}\n.chevron {\n flex: none;\n width: var(--office-chevron-size);\n height: var(--office-chevron-size);\n margin: 0 var(--office-space-0) var(--office-space-0) 0;\n border-right: calc(var(--office-border-width) * 1.5) solid currentColor;\n border-bottom: calc(var(--office-border-width) * 1.5) solid currentColor;\n transform: rotate(45deg);\n}\n:host([open]) .chevron {\n margin: var(--office-space-0) var(--office-space-0) 0 0;\n transform: rotate(225deg);\n}\n\n.menu {\n box-sizing: border-box;\n position: fixed;\n margin: 0;\n padding: var(--office-space-1);\n min-width: var(--office-select-menu-min-width);\n max-height: min(var(--office-select-max-height), 50vh);\n overflow: auto;\n border: var(--office-border-width) solid var(--office-field-border);\n border-radius: var(--office-field-radius);\n background: var(--office-popover);\n color: var(--office-popover-foreground);\n box-shadow: var(--office-shadow);\n font: inherit;\n font-size: inherit;\n}\n.menu:not(:popover-open):not([data-fallback-open]) {\n display: none;\n}\n.option {\n display: block;\n box-sizing: border-box;\n width: 100%;\n padding: var(--office-space-1-5) var(--office-space-2);\n border-radius: var(--office-radius-sm);\n cursor: pointer;\n white-space: nowrap;\n}\n.group {\n padding: var(--office-space-1-5) var(--office-space-2) var(--office-space-0);\n color: var(--office-muted-foreground);\n font-size: 0.9em;\n font-weight: var(--office-font-weight-bold);\n}\n.option:not([aria-disabled='true']):hover,\n.option[data-active]:not([aria-selected='true']) {\n background: var(--office-select-option-active);\n}\n.option[aria-selected='true'] {\n background: var(--office-accent);\n color: var(--office-accent-foreground);\n}\n.option[aria-selected='true']:hover {\n filter: brightness(1.08);\n}\n.option[aria-disabled='true'] {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.description {\n flex: none;\n color: var(--office-muted-foreground);\n font-size: var(--office-font-size-2xs);\n}\n\n/* Ribbon variants */\n:host([variant^='ribbon-'][disabled]) {\n opacity: 0.4;\n}\n:host([variant^='ribbon-']) button {\n height: var(--office-ribbon-combo-height);\n min-height: var(--office-ribbon-combo-height);\n padding: var(--office-space-1) var(--office-space-2);\n gap: var(--office-space-1);\n border: var(--office-border-width) solid\n color-mix(in oklab, var(--office-border) 60%, transparent);\n border-radius: var(--office-radius-sm);\n font-size: var(--office-font-size-sm);\n background: color-mix(in oklab, var(--office-background) 60%, transparent);\n color: var(--office-foreground);\n}\n:host([variant^='ribbon-']) .value {\n flex: 1;\n min-width: 0;\n}\n:host([variant^='ribbon-']) .chevron {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n margin: 0;\n border: 0;\n transform: none;\n}\n:host([variant^='ribbon-']) .chevron svg {\n display: block;\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n}\n:host([variant='ribbon-font']) .chevron {\n color: var(--office-muted-foreground);\n}\n:host([variant='ribbon-font'][data-font-picker='size']) button {\n padding-inline: var(--office-space-1-5);\n}\n:host([variant='ribbon-icon']) button {\n width: var(--office-ribbon-icon-select-width);\n gap: var(--office-space-1-5);\n padding: var(--office-space-1-5) var(--office-space-2-5);\n border: 0;\n background: var(--office-surface);\n justify-content: center;\n}\n:host([variant='ribbon-icon']) :is(.value, .chevron) {\n display: none;\n}\n::slotted(svg[slot='icon']) {\n display: block;\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n flex: none;\n}\n:host([variant^='ribbon-']) button:hover:not(:disabled) {\n background: var(--office-selected);\n}\n:host([variant^='ribbon-']) .menu {\n width: var(--office-select-ribbon-menu-width);\n padding: var(--office-space-1) 0;\n border-radius: var(--office-radius-lg);\n font-size: var(--office-font-size-sm);\n}\n:host([data-font-picker='family']) .menu {\n width: var(--office-font-picker-width);\n}\n:host([variant^='ribbon-']) .option {\n display: flex;\n justify-content: space-between;\n gap: var(--office-space-3);\n padding: var(--office-space-1-5) var(--office-space-3);\n border-radius: 0;\n}\n:host([variant^='ribbon-']) .option[aria-selected='true'] {\n background: transparent;\n color: var(--office-foreground);\n}\n:host([variant^='ribbon-']) .option:hover,\n:host([variant^='ribbon-']) .option[data-active] {\n background: var(--office-surface);\n filter: none;\n}\n:host([variant^='ribbon-']) .group {\n padding: var(--office-space-2) var(--office-space-3) var(--office-space-1);\n text-transform: uppercase;\n letter-spacing: 0.02em;\n font-size: var(--office-font-size-2xs);\n}\n\n@media (pointer: coarse), (max-width: 767px) {\n button,\n :host([variant^='ribbon-']) button {\n min-height: var(--office-target-size-touch);\n }\n :host([variant^='ribbon-']) button {\n height: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n button,\n .menu {\n border-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n .option[aria-selected='true'],\n .option[data-active],\n .option:not([aria-disabled='true']):hover {\n background: Highlight;\n color: HighlightText;\n }\n}\n";
3299
+
3300
+ // src/form/select.ts
3301
+ var uid2 = 0;
3302
+ var OfficeUiSelect = class extends OfficeElement {
3303
+ static formAssociated = true;
3304
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
3305
+ static styles = controlStyles(select_default);
3306
+ static properties = {
3307
+ // `value` and `options` are derived from the `<option>` children, so they have accessors.
3308
+ value: { type: String, reflect: true, noAccessor: true },
3309
+ disabled: flag,
3310
+ open: flag,
3311
+ active: { state: true },
3312
+ choices: { state: true },
3313
+ opened: { state: true }
3314
+ };
3315
+ static get observedAttributes() {
3316
+ return [...super.observedAttributes, "aria-label", "variant", "data-font-picker"];
3317
+ }
3318
+ internals;
3319
+ observer;
3320
+ menuId = `office-ui-select-options-${++uid2}`;
3321
+ requested = null;
3322
+ data = [];
3323
+ refreshFrame = 0;
3324
+ search = "";
3325
+ searchTimer = 0;
3326
+ defaultValue = "";
3327
+ constructor() {
3328
+ super();
3329
+ this.disabled = false;
3330
+ this.open = false;
3331
+ this.active = -1;
3332
+ this.choices = [];
3333
+ this.opened = false;
3334
+ try {
3335
+ this.internals = this.attachInternals();
3336
+ } catch {
3337
+ this.internals = void 0;
3338
+ }
3339
+ const Observer = this.ownerDocument.defaultView?.MutationObserver;
3340
+ this.observer = Observer ? new Observer(() => {
3341
+ const view = this.ownerDocument.defaultView;
3342
+ if (this.refreshFrame || !view) return;
3343
+ this.refreshFrame = view.requestAnimationFrame(() => {
3344
+ this.refreshFrame = 0;
3345
+ this.refresh();
3346
+ });
3347
+ }) : void 0;
3348
+ }
3349
+ /** The `<option>` children, or the programmatic options when there are none. */
3350
+ get options() {
3351
+ const children = [...this.querySelectorAll("option")];
3352
+ return children.length ? children : this.data;
3353
+ }
3354
+ set options(next) {
3355
+ this.data = [...next];
3356
+ this.refresh();
3357
+ }
3358
+ get value() {
3359
+ const requested = this.requested;
3360
+ const choices = this.choices.length ? this.choices : this.collect();
3361
+ if (requested !== null && this.getAttribute("variant") === "ribbon-font") return requested;
3362
+ if (requested !== null && (!choices.length || choices.some((c) => c.value === requested)))
3363
+ return requested;
3364
+ return [...this.querySelectorAll("option")].find((option) => option.selected)?.value ?? choices.find((choice) => !choice.disabled && !choice.hidden)?.value ?? choices[0]?.value ?? requested ?? "";
3365
+ }
3366
+ set value(next) {
3367
+ this.requested = String(next ?? "");
3368
+ this.requestUpdate("value", void 0);
3369
+ }
3370
+ get selectedIndex() {
3371
+ return this.options.findIndex((option) => option.value === this.value);
3372
+ }
3373
+ set selectedIndex(index) {
3374
+ this.value = this.options[index]?.value ?? "";
3375
+ }
3376
+ get menu() {
3377
+ return this.renderRoot.querySelector(".menu");
3378
+ }
3379
+ get trigger() {
3380
+ return this.renderRoot.querySelector("button");
3381
+ }
3382
+ focus(options) {
3383
+ this.trigger?.focus(options);
3384
+ }
3385
+ formResetCallback() {
3386
+ this.value = this.defaultValue;
3387
+ }
3388
+ formDisabledCallback(disabled) {
3389
+ this.disabled = disabled;
3390
+ }
3391
+ connectedCallback() {
3392
+ this.defaultValue = this.getAttribute("value") ?? "";
3393
+ this.observer?.observe(this, {
3394
+ childList: true,
3395
+ subtree: true,
3396
+ attributes: true,
3397
+ characterData: true,
3398
+ attributeFilter: SELECT_OPTION_ATTRIBUTES
3399
+ });
3400
+ this.choices = this.collect();
3401
+ super.connectedCallback();
3402
+ }
3403
+ disconnectedCallback() {
3404
+ super.disconnectedCallback();
3405
+ this.open = false;
3406
+ this.closePopup();
3407
+ const view = this.ownerDocument.defaultView;
3408
+ view?.clearTimeout(this.searchTimer);
3409
+ this.searchTimer = 0;
3410
+ this.search = "";
3411
+ this.observer?.disconnect();
3412
+ if (this.refreshFrame) view?.cancelAnimationFrame(this.refreshFrame);
3413
+ this.refreshFrame = 0;
3414
+ }
3415
+ collect() {
3416
+ const children = [...this.querySelectorAll("option")];
3417
+ if (children.length) return collectSelectChoices(children);
3418
+ return this.data.map((option) => ({
3419
+ value: option.value,
3420
+ label: option.label,
3421
+ disabled: option.disabled === true,
3422
+ hidden: false,
3423
+ group: ""
3424
+ }));
3425
+ }
3426
+ /** Re-read the choices after the `<option>` children or the `options` data changed. */
3427
+ refresh() {
3428
+ this.choices = this.collect();
3429
+ this.requestUpdate();
3430
+ }
3431
+ isUsable(choice) {
3432
+ return Boolean(choice) && !choice.disabled && !choice.hidden;
3433
+ }
3434
+ show() {
3435
+ this.choices = this.collect();
3436
+ if (present(this.disabled) || this.open || !this.choices.some((c) => this.isUsable(c))) return;
3437
+ const selected = this.choices.findIndex((c) => c.value === this.value && this.isUsable(c));
3438
+ this.active = selected >= 0 ? selected : this.choices.findIndex((c) => this.isUsable(c));
3439
+ this.opened = true;
3440
+ this.open = true;
3441
+ }
3442
+ close() {
3443
+ this.open = false;
3444
+ }
3445
+ /** The popup is a top-layer popover: show or hide it as `open` changes. */
3446
+ openPopup() {
3447
+ const menu = this.menu;
3448
+ if (!menu) return;
3449
+ if (typeof menu.showPopover === "function") menu.showPopover();
3450
+ this.position();
3451
+ const view = this.ownerDocument.defaultView;
3452
+ this.ownerDocument.addEventListener("pointerdown", this.onOutside, true);
3453
+ view?.addEventListener("resize", this.position);
3454
+ view?.addEventListener("scroll", this.position, true);
3455
+ }
3456
+ closePopup() {
3457
+ const view = this.ownerDocument.defaultView;
3458
+ view?.clearTimeout(this.searchTimer);
3459
+ this.search = "";
3460
+ try {
3461
+ if (this.menu?.matches(":popover-open")) this.menu.hidePopover();
3462
+ } catch {
3463
+ }
3464
+ this.ownerDocument.removeEventListener("pointerdown", this.onOutside, true);
3465
+ view?.removeEventListener("resize", this.position);
3466
+ view?.removeEventListener("scroll", this.position, true);
3467
+ }
3468
+ onOutside = (event) => {
3469
+ if (!event.composedPath().includes(this)) this.close();
3470
+ };
3471
+ position = () => {
3472
+ const { menu, trigger } = this;
3473
+ if (this.open && menu && trigger) positionSelectMenu(menu, trigger, this);
3474
+ };
3475
+ move(step) {
3476
+ if (!this.open) this.show();
3477
+ if (this.open) this.active = nextSelectActive(this.choices, this.active, step);
3478
+ }
3479
+ commit(index) {
3480
+ const choice = this.choices[index];
3481
+ if (!choice || !this.isUsable(choice)) return;
3482
+ const changed = this.value !== choice.value;
3483
+ this.value = choice.value;
3484
+ this.close();
3485
+ this.trigger?.focus();
3486
+ if (changed) {
3487
+ this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
3488
+ this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
3489
+ }
3490
+ }
3491
+ onKeyDown(event) {
3492
+ if (present(this.disabled)) return;
3493
+ const stop = () => {
3494
+ event.preventDefault();
3495
+ event.stopPropagation();
3496
+ };
3497
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
3498
+ stop();
3499
+ this.move(event.key === "ArrowDown" ? 1 : -1);
3500
+ } else if (event.key === "Home" || event.key === "End") {
3501
+ stop();
3502
+ this.show();
3503
+ this.active = event.key === "Home" ? nextSelectActive(this.choices, -1, 1) : nextSelectActive(this.choices, 0, -1);
3504
+ } else if (event.key === "PageUp" || event.key === "PageDown") {
3505
+ stop();
3506
+ this.show();
3507
+ this.active = pageSelectActive(this.choices, this.active, event.key === "PageDown" ? 1 : -1);
3508
+ } else if (event.key === "Enter" || event.key === " ") {
3509
+ stop();
3510
+ if (this.open) this.commit(this.active);
3511
+ else this.show();
3512
+ } else if (event.key === "Escape" && this.open) {
3513
+ stop();
3514
+ this.close();
3515
+ this.trigger?.focus();
3516
+ } else if (event.key === "Tab") this.close();
3517
+ else if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
3518
+ event.stopPropagation();
3519
+ this.typeAhead(event.key);
3520
+ }
3521
+ }
3522
+ typeAhead(key) {
3523
+ const view = this.ownerDocument.defaultView;
3524
+ this.search += key.toLocaleLowerCase();
3525
+ view?.clearTimeout(this.searchTimer);
3526
+ this.searchTimer = view?.setTimeout(() => {
3527
+ this.search = "";
3528
+ }, 700) ?? 0;
3529
+ this.show();
3530
+ const index = this.choices.findIndex(
3531
+ (choice) => this.isUsable(choice) && choice.label.toLocaleLowerCase().startsWith(this.search)
3532
+ );
3533
+ if (index >= 0) this.active = index;
3534
+ }
3535
+ willUpdate() {
3536
+ this.choices = this.collect();
3537
+ if (this.requested === null && this.choices.length) {
3538
+ this.requested = this.value;
3539
+ this.requestUpdate("value", void 0);
3540
+ }
3541
+ this.internals?.setFormValue?.(present(this.disabled) ? null : this.value);
3542
+ if (present(this.disabled) && this.open) this.open = false;
3543
+ }
3544
+ updated(changed) {
3545
+ if (changed.has("open")) {
3546
+ if (this.open) this.openPopup();
3547
+ else if (changed.get("open") !== void 0) this.closePopup();
3548
+ }
3549
+ if (this.open) {
3550
+ this.position();
3551
+ this.menu?.querySelector("[data-active]")?.scrollIntoView?.({ block: "nearest" });
3552
+ }
3553
+ }
3554
+ optionTemplates() {
3555
+ let previousGroup = "";
3556
+ return this.choices.map((choice, index) => {
3557
+ if (choice.hidden) return "";
3558
+ const heading = choice.group && choice.group !== previousGroup ? html`<div class="group" role="presentation">${choice.group}</div>` : "";
3559
+ previousGroup = choice.group;
3560
+ const text = choice.displayLabel ?? choice.label;
3561
+ const note = choice.description ? html`<span class="description">${choice.description}</span>` : "";
3562
+ return html`${heading}<div
3563
+ class="option"
3564
+ role="option"
3565
+ id="${this.menuId}-${index}"
3566
+ data-index=${index}
3567
+ ?data-active=${this.open && index === this.active}
3568
+ aria-selected=${String(choice.value === this.value)}
3569
+ aria-disabled=${String(choice.disabled)}
3570
+ style=${ifDefined(choice.fontFamily ? `font-family:${choice.fontFamily}` : void 0)}
3571
+ >${text}${note}</div
3572
+ >`;
3573
+ });
3574
+ }
3575
+ render() {
3576
+ const value = this.value;
3577
+ const shown = this.choices.find((choice) => choice.value === value);
3578
+ const name = this.getAttribute("aria-label");
3579
+ const open = present(this.open);
3580
+ const native = typeof HTMLElement.prototype.showPopover === "function";
3581
+ return html`
3582
+ <button
3583
+ part="trigger"
3584
+ type="button"
3585
+ role="combobox"
3586
+ aria-haspopup="listbox"
3587
+ aria-expanded=${String(open)}
3588
+ aria-controls=${this.menuId}
3589
+ aria-label=${ifDefined(name ?? void 0)}
3590
+ aria-activedescendant=${ifDefined(
3591
+ open && this.active >= 0 ? `${this.menuId}-${this.active}` : void 0
3592
+ )}
3593
+ ?disabled=${present(this.disabled)}
3594
+ @click=${() => this.open ? this.close() : this.show()}
3595
+ @keydown=${this.onKeyDown}
3596
+ >
3597
+ <slot name="icon"></slot>
3598
+ <span class="value" part="value">${shown?.displayLabel ?? shown?.label ?? value}</span>
3599
+ <span class="chevron" part="indicator" aria-hidden="true">
3600
+ <svg
3601
+ viewBox="0 0 24 24"
3602
+ fill="none"
3603
+ stroke="currentColor"
3604
+ stroke-width="2"
3605
+ stroke-linecap="round"
3606
+ stroke-linejoin="round"
3607
+ >
3608
+ <path d="m6 9 6 6 6-6"></path>
3609
+ </svg>
3610
+ </span>
3611
+ </button>
3612
+ <div
3613
+ class="menu"
3614
+ id=${this.menuId}
3615
+ role="listbox"
3616
+ popover="manual"
3617
+ part="popup"
3618
+ ?data-fallback-open=${open && !native}
3619
+ @pointerdown=${(event) => {
3620
+ if (event.target.closest("[data-index]")) event.preventDefault();
3621
+ }}
3622
+ @click=${(event) => {
3623
+ const target = event.target.closest("[data-index]");
3624
+ if (target) this.commit(Number(target.dataset.index));
3625
+ }}
3626
+ >
3627
+ ${this.querySelector('[slot="custom"]') ? html`<slot name="custom"></slot>` : ""}
3628
+ ${this.opened ? keyed(value, this.optionTemplates()) : ""}
3629
+ </div>
3630
+ `;
3631
+ }
3632
+ };
3633
+ var defineSelect = definer("office-ui-select", () => OfficeUiSelect);
3634
+
3635
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/status-bar.css?raw
3636
+ var status_bar_default = ":host {\n display: flex;\n align-items: center;\n gap: var(--office-space-3);\n min-height: var(--office-status-bar-height);\n padding: 0 var(--office-space-2);\n font-size: var(--office-font-size-xs);\n background: var(--office-surface);\n color: var(--office-foreground);\n border-top: var(--office-border-width) solid var(--office-border);\n font-family: var(--office-font);\n}\n::slotted([slot='end']) {\n margin-inline-start: auto;\n}\n.bar {\n display: contents;\n}\n[hidden] {\n display: none !important;\n}\n:host([data-controlled]) {\n display: block;\n flex: none;\n padding: 0;\n gap: 0;\n}\n:host([data-controlled]) .bar {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--office-space-1);\n width: 100%;\n min-height: var(--office-status-bar-height);\n padding: var(--office-space-0) var(--office-space-2);\n color: var(--office-muted-foreground);\n}\n:host([data-controlled]) ::slotted([slot='end']) {\n margin-inline-start: 0;\n}\n.items {\n display: flex;\n align-items: center;\n min-width: 0;\n}\n.item {\n flex: none;\n white-space: nowrap;\n}\n.item + .item::before {\n content: '';\n display: inline-block;\n width: var(--office-border-width);\n height: var(--office-space-3);\n margin: 0 var(--office-space-2);\n vertical-align: middle;\n background: var(--office-border);\n}\n.item.saving {\n color: var(--office-warning);\n}\n.item.error {\n color: var(--office-danger);\n}\n.spacer {\n flex: 1;\n}\n.toggles,\n.group {\n display: flex;\n align-items: center;\n gap: var(--office-space-0);\n}\n.sep {\n flex: none;\n width: var(--office-border-width);\n height: var(--office-space-3);\n margin: 0 var(--office-space-0);\n background: var(--office-border);\n}\n.bar button {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-1);\n min-width: var(--office-control-height-xs);\n min-height: var(--office-control-height-xs);\n padding: var(--office-space-1);\n border: 0;\n border-radius: var(--office-radius-sm);\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n touch-action: manipulation;\n}\n.bar button:hover {\n background: var(--office-selected);\n color: var(--office-foreground);\n}\n.bar button[aria-pressed='true'] {\n color: var(--office-accent);\n}\n.bar button:active {\n opacity: 0.8;\n}\n.bar button:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n.bar svg {\n flex: none;\n width: var(--office-icon-size);\n height: var(--office-icon-size);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.toggle svg,\n.zoom-step svg {\n width: var(--office-icon-size-sm);\n height: var(--office-icon-size-sm);\n}\n.zoom-fit {\n min-width: var(--office-zoom-value-width);\n font-variant-numeric: tabular-nums;\n}\n@media (max-width: 767px) {\n .narrow-hide,\n .toggle .label {\n display: none !important;\n }\n}\n@media (pointer: coarse) {\n .bar button {\n min-width: var(--office-target-size-touch);\n min-height: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n :host {\n background: Canvas;\n color: CanvasText;\n border-top-color: CanvasText;\n }\n .bar button {\n color: ButtonText;\n }\n .bar button:hover {\n background: Highlight;\n color: HighlightText;\n }\n .bar button[aria-pressed='true'] {\n border: var(--office-border-width) solid Highlight;\n }\n .bar button:focus-visible {\n outline-color: Highlight;\n }\n .sep,\n .item + .item::before {\n background: CanvasText;\n }\n}\n";
3637
+
3638
+ // src/chrome/status-bar.ts
3639
+ var OfficeUiStatusBar = class extends OfficeElement {
3640
+ static activateEvent = "office-status-activate";
3641
+ static styles = controlStyles(status_bar_default);
3642
+ static properties = {
3643
+ // Setting `state` marks the bar controlled, so it has an accessor below.
3644
+ label: { type: String },
3645
+ hasCollaboration: { state: true }
3646
+ };
3647
+ model;
3648
+ constructor() {
3649
+ super();
3650
+ this.label = null;
3651
+ this.hasCollaboration = false;
3652
+ }
3653
+ get state() {
3654
+ return this.model;
3655
+ }
3656
+ set state(value) {
3657
+ this.model = value ?? void 0;
3658
+ this.toggleAttribute("data-controlled", this.model !== void 0);
3659
+ this.requestUpdate("state");
3660
+ }
3661
+ activate(id) {
3662
+ this.fire(this.constructor.activateEvent, { id });
3663
+ }
3664
+ /** Keep native activation out of the host document's navigation shortcuts. */
3665
+ onKeydown(event) {
3666
+ const { key, ctrlKey, metaKey, altKey } = event;
3667
+ if ((key === " " || key === "Enter") && !ctrlKey && !metaKey && !altKey)
3668
+ event.stopPropagation();
3669
+ }
3670
+ willUpdate() {
3671
+ this.setAttribute("role", "group");
3672
+ if (!this.hasAttribute("aria-label")) this.setAttribute("aria-label", this.label ?? "Status");
3673
+ }
3674
+ button(className, spec) {
3675
+ return html`<button
3676
+ type="button"
3677
+ class=${className}
3678
+ data-id=${spec.id}
3679
+ title=${spec.label}
3680
+ aria-label=${spec.label}
3681
+ aria-pressed=${ifDefined(spec.pressed === void 0 ? void 0 : String(spec.pressed))}
3682
+ ?hidden=${spec.hidden === true}
3683
+ @click=${() => this.activate(spec.id)}
3684
+ @keydown=${this.onKeydown}
3685
+ >${glyph(spec.icon, "icon")}${spec.text ? html`<span class="label">${spec.text}</span>` : ""}</button
3686
+ >`;
3687
+ }
3688
+ zoomStep(id, label, icon) {
3689
+ return html`<button
3690
+ type="button"
3691
+ class="zoom-step"
3692
+ data-id=${id}
3693
+ title=${label}
3694
+ aria-label=${label}
3695
+ @click=${() => this.activate(id)}
3696
+ @keydown=${this.onKeydown}
3697
+ >${glyph(icon, "icon")}</button
3698
+ >`;
3699
+ }
3700
+ render() {
3701
+ const state = this.model;
3702
+ const items = state?.items ?? [];
3703
+ const toggles = state?.toggles ?? [];
3704
+ const views = state?.views || [];
3705
+ const zoom = state?.zoom;
3706
+ const viewsHidden = views.every((spec) => spec.hidden === true);
3707
+ const togglesHidden = toggles.every((spec) => spec.hidden === true);
3708
+ const zoomHidden = !zoom || zoom.hidden === true;
3709
+ return html`
3710
+ <div class="bar" part="bar">
3711
+ <span class="items" ?hidden=${items.length === 0}>
3712
+ ${repeat(
3713
+ items,
3714
+ (spec) => spec.id,
3715
+ (spec) => html`<span
3716
+ class="item${spec.tone === "saving" ? " saving" : ""}${spec.tone === "error" ? " error" : ""}${spec.narrowHide ? " narrow-hide" : ""}"
3717
+ data-item=${spec.id}
3718
+ title=${ifDefined(spec.title)}
3719
+ aria-live=${ifDefined(spec.live ? "polite" : void 0)}
3720
+ >${spec.text}</span
3721
+ >`
3722
+ )}
3723
+ </span>
3724
+ <slot></slot>
3725
+ <span class="spacer" ?hidden=${!state}></span>
3726
+ <span class="toggles" ?hidden=${togglesHidden}>
3727
+ ${repeat(
3728
+ toggles,
3729
+ (spec) => spec.id,
3730
+ (spec) => this.button("toggle", spec)
3731
+ )}
3732
+ </span>
3733
+ <i class="sep tight" aria-hidden="true" ?hidden=${viewsHidden || togglesHidden}></i>
3734
+ <div class="group views" ?hidden=${viewsHidden}>
3735
+ ${repeat(
3736
+ views,
3737
+ (spec) => spec.id,
3738
+ (spec) => this.button("view", spec)
3739
+ )}
3740
+ </div>
3741
+ <i class="sep tight" aria-hidden="true" ?hidden=${!this.hasCollaboration}></i>
3742
+ <slot
3743
+ name="collaboration"
3744
+ @slotchange=${(event) => this.hasCollaboration = event.target.assignedNodes().length > 0}
3745
+ ></slot>
3746
+ <slot name="end"></slot>
3747
+ <i class="sep tight" aria-hidden="true" ?hidden=${zoomHidden}></i>
3748
+ <div class="group zoom" ?hidden=${zoomHidden}>
3749
+ ${this.zoomStep("zoomOut", zoom?.outLabel ?? "", zoom?.outIcon ?? "minus")}
3750
+ <button
3751
+ type="button"
3752
+ class="zoom-fit"
3753
+ data-id="zoomFit"
3754
+ title=${zoom?.fitLabel ?? ""}
3755
+ aria-label=${zoom?.fitLabel ?? ""}
3756
+ @click=${() => this.activate("zoomFit")}
3757
+ @keydown=${this.onKeydown}
3758
+ >${zoom ? `${Math.round(zoom.percent)}%` : ""}</button
3759
+ >${this.zoomStep("zoomIn", zoom?.inLabel ?? "", zoom?.inIcon ?? "plus")}</div
3760
+ >
3761
+ </div>
3762
+ `;
3763
+ }
3764
+ };
3765
+ var defineStatusBar = definer("office-ui-status-bar", () => OfficeUiStatusBar);
3766
+
3767
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/status-item.css?raw
3768
+ var status_item_default = ":host {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-1);\n white-space: nowrap;\n}\n.label {\n color: var(--office-muted-foreground);\n}\nbutton {\n all: unset;\n display: inline-flex;\n gap: var(--office-space-1);\n align-items: center;\n cursor: pointer;\n padding: 0 var(--office-space-1);\n min-height: var(--office-target-size);\n border-radius: var(--office-radius-sm);\n}\nbutton:hover {\n background: var(--office-background);\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n}\n@media (forced-colors: active) {\n .label {\n color: GrayText;\n }\n button:focus-visible {\n outline-color: Highlight;\n }\n}\nspan.wrap {\n display: inline-flex;\n gap: var(--office-space-1);\n}\n";
3769
+
3770
+ // src/chrome/status-item.ts
3771
+ var OfficeUiStatusItem = class extends OfficeElement {
3772
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
3773
+ static styles = controlStyles(status_item_default);
3774
+ static properties = {
3775
+ label: { type: String },
3776
+ value: { type: String, reflect: true },
3777
+ interactive: flag
3778
+ };
3779
+ constructor() {
3780
+ super();
3781
+ this.label = "";
3782
+ this.value = "";
3783
+ this.interactive = false;
3784
+ }
3785
+ render() {
3786
+ const content = html`${this.label ? html`<span class="label">${this.label}</span>` : ""}<span
3787
+ class="value"
3788
+ >${this.value}</span
3789
+ >`;
3790
+ return this.interactive ? html`<button
3791
+ type="button"
3792
+ class="wrap"
3793
+ @click=${() => this.fire("office-status-activate", { id: this.getAttribute("id") ?? "" })}
3794
+ >${content}</button
3795
+ >` : html`<span class="wrap">${content}</span>`;
3796
+ }
3797
+ };
3798
+ var defineStatusItem = definer("office-ui-status-item", () => OfficeUiStatusItem);
3799
+
3800
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/tab-strip.css?raw
3801
+ var tab_strip_default = ":host {\n display: flex;\n align-items: stretch;\n min-width: 0;\n min-height: var(--office-tab-height);\n gap: var(--office-space-0);\n padding: 0 var(--office-space-1-5);\n background: var(--office-surface);\n color: var(--office-muted-foreground);\n border-top: var(--office-border-width) solid var(--office-border);\n font-size: var(--office-font-size-sm);\n font-family: var(--office-font);\n}\n.step {\n flex: none;\n display: inline-grid;\n place-items: center;\n width: var(--office-target-size);\n min-height: var(--office-target-size);\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n}\n.step:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.add {\n font-size: var(--office-font-size-xl);\n line-height: 1;\n}\n.add[hidden] {\n display: none;\n}\n.list {\n display: flex;\n align-items: stretch;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: thin;\n}\n.tab {\n flex: none;\n max-width: var(--office-tab-max-width);\n min-height: var(--office-target-size);\n margin-top: calc(-1 * var(--office-space-px));\n padding: var(--office-space-0) var(--office-space-3-5);\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n border: var(--office-border-width) solid transparent;\n border-top: 0;\n border-radius: 0 0 var(--office-radius) var(--office-radius);\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n.tab:hover {\n color: var(--office-foreground);\n}\n.tab[aria-selected='true'] {\n border-color: var(--office-border);\n background: var(--office-background);\n color: var(--office-accent);\n font-weight: 600;\n box-shadow: inset 0 var(--office-border-width-thick) 0 var(--office-accent);\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(-1 * var(--office-focus-width));\n}\nsvg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n@media (forced-colors: active) {\n :host {\n border-top-color: CanvasText;\n }\n .tab[aria-selected='true'] {\n background: Highlight;\n color: HighlightText;\n forced-color-adjust: none;\n }\n}\n";
3802
+
3803
+ // src/chrome/tab-strip.ts
3804
+ var KEY_STEPS = {
3805
+ ArrowLeft: -1,
3806
+ ArrowRight: 1,
3807
+ ArrowUp: -1,
3808
+ ArrowDown: 1
3809
+ };
3810
+ var OfficeUiTabStrip = class extends OfficeElement {
3811
+ static styles = controlStyles(tab_strip_default);
3812
+ static properties = {
3813
+ // `tabs` is sanitised and copied, `selected` reads as '' when unset: both have accessors.
3814
+ selected: { type: String, noAccessor: true },
3815
+ label: { type: String },
3816
+ previousLabel: { attribute: "previous-label", type: String },
3817
+ nextLabel: { attribute: "next-label", type: String },
3818
+ addLabel: { attribute: "add-label", type: String },
3819
+ addTitle: { attribute: "add-title", type: String },
3820
+ addDisabled: { attribute: "add-disabled", ...flag },
3821
+ disabled: flag
3822
+ };
3823
+ items = [];
3824
+ picked = null;
3825
+ constructor() {
3826
+ super();
3827
+ this.label = null;
3828
+ this.previousLabel = null;
3829
+ this.nextLabel = null;
3830
+ this.addLabel = null;
3831
+ this.addTitle = null;
3832
+ this.addDisabled = false;
3833
+ this.disabled = false;
3834
+ }
3835
+ get tabs() {
3836
+ return this.items.map((tab) => ({ ...tab }));
3837
+ }
3838
+ set tabs(value) {
3839
+ this.items = (Array.isArray(value) ? value : []).filter((tab) => tab && typeof tab.id === "string").map((tab) => ({
3840
+ id: tab.id,
3841
+ label: String(tab.label ?? ""),
3842
+ ...tab.title === void 0 ? {} : { title: String(tab.title) }
3843
+ }));
3844
+ this.requestUpdate("tabs");
3845
+ }
3846
+ get selected() {
3847
+ return this.picked ?? "";
3848
+ }
3849
+ set selected(id) {
3850
+ this.picked = id === null ? null : String(id);
3851
+ this.reflectAttribute("selected", this.picked, {});
3852
+ this.requestUpdate("selected");
3853
+ }
3854
+ index() {
3855
+ return this.items.findIndex((tab) => tab.id === this.selected);
3856
+ }
3857
+ move(delta, focus) {
3858
+ const target = this.items[Math.max(0, Math.min(this.items.length - 1, this.index() + delta))];
3859
+ if (target) this.choose(target.id, focus);
3860
+ }
3861
+ choose(id, focus) {
3862
+ if (present(this.disabled)) return;
3863
+ if (id !== this.selected && this.fire("office-tab-select", { id }, true)) this.selected = id;
3864
+ if (focus)
3865
+ [...this.renderRoot.querySelectorAll(".tab")].find((tab) => tab.dataset.id === this.selected)?.focus();
3866
+ }
3867
+ onListClick(event) {
3868
+ const tab = event.target.closest(".tab");
3869
+ if (tab?.dataset.id !== void 0) this.choose(tab.dataset.id, false);
3870
+ }
3871
+ onKey(event) {
3872
+ if (event.key === "Home" || event.key === "End") {
3873
+ event.preventDefault();
3874
+ const target = event.key === "Home" ? this.items[0] : this.items[this.items.length - 1];
3875
+ if (target) this.choose(target.id, true);
3876
+ } else if (KEY_STEPS[event.key] !== void 0) {
3877
+ event.preventDefault();
3878
+ this.move(KEY_STEPS[event.key], true);
3879
+ }
3880
+ }
3881
+ updated(_changed) {
3882
+ this.renderRoot.querySelector('.tab[aria-selected="true"]')?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
3883
+ }
3884
+ render() {
3885
+ const disabled = present(this.disabled);
3886
+ const index = this.index();
3887
+ const previous = this.previousLabel || "Previous";
3888
+ const next = this.nextLabel || "Next";
3889
+ return html`
3890
+ <button
3891
+ type="button"
3892
+ class="step"
3893
+ aria-label=${previous}
3894
+ title=${previous}
3895
+ ?disabled=${disabled || index <= 0}
3896
+ @click=${() => this.move(-1, false)}
3897
+ >${glyph("chevronLeft", "icon")}</button
3898
+ >
3899
+ <button
3900
+ type="button"
3901
+ class="step"
3902
+ aria-label=${next}
3903
+ title=${next}
3904
+ ?disabled=${disabled || index < 0 || index >= this.items.length - 1}
3905
+ @click=${() => this.move(1, false)}
3906
+ >${glyph("chevronRight", "icon")}</button
3907
+ >
3908
+ <div
3909
+ class="list"
3910
+ role="tablist"
3911
+ aria-label=${this.label ?? "Tabs"}
3912
+ @click=${this.onListClick}
3913
+ @keydown=${this.onKey}
3914
+ >
3915
+ ${this.items.map((item, at) => {
3916
+ const selected = item.id === this.selected;
3917
+ return html`<button
3918
+ type="button"
3919
+ class="tab"
3920
+ role="tab"
3921
+ data-id=${item.id}
3922
+ title=${item.title ?? item.label}
3923
+ aria-selected=${String(selected)}
3924
+ tabindex=${selected || index < 0 && at === 0 ? 0 : -1}
3925
+ ?disabled=${disabled}
3926
+ >${item.label}</button
3927
+ >`;
3928
+ })}
3929
+ </div>
3930
+ <button
3931
+ type="button"
3932
+ class="step add"
3933
+ aria-label=${this.addLabel ?? "Add"}
3934
+ title=${this.addTitle ?? this.addLabel ?? ""}
3935
+ ?hidden=${!this.addLabel}
3936
+ ?disabled=${disabled || present(this.addDisabled)}
3937
+ @click=${() => !present(this.addDisabled) && this.fire("office-command", { command: "tab-add" })}
3938
+ >+</button
3939
+ >
3940
+ `;
3941
+ }
3942
+ };
3943
+ var defineTabStrip = definer("office-ui-tab-strip", () => OfficeUiTabStrip);
3944
+
3945
+ // src/chrome/title-bar-types.ts
3946
+ var OFFICE_TITLE_BAR_SEARCH_LIMIT = 8;
3947
+ function matchTitleBarCommands(search, query) {
3948
+ if (search.match) return search.match(query);
3949
+ const needle = query.trim().toLowerCase();
3950
+ return (search.commands ?? []).filter((entry) => entry.label.toLowerCase().includes(needle));
3951
+ }
3952
+
3953
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/title-bar.css?raw
3954
+ var title_bar_default = ":host {\n display: block;\n flex: none;\n}\n:host([data-empty]) {\n display: none !important;\n}\n[hidden] {\n display: none !important;\n}\n.bar {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--office-space-1);\n width: 100%;\n height: var(--office-title-bar-height);\n padding: 0 var(--office-space-2);\n border-bottom: var(--office-border-width) solid var(--office-title-bar-border);\n background: var(--office-title-bar-background);\n color: var(--office-title-bar-foreground);\n font: var(--office-font-size-xs)/var(--office-line-height-tight) var(--office-font);\n user-select: none;\n}\n:host([placement='belowRibbon']) .bar {\n height: auto;\n min-height: var(--office-title-bar-below-height);\n padding-block: var(--office-space-0);\n}\n.mark {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--office-title-bar-mark-size);\n height: var(--office-title-bar-mark-size);\n border-radius: var(--office-radius-sm);\n background: var(--office-title-bar-mark-background);\n color: var(--office-title-bar-mark-foreground);\n font-size: var(--office-font-size-2xs);\n font-weight: var(--office-font-weight-bold);\n}\n.autosave {\n flex: none;\n display: flex;\n align-items: center;\n gap: var(--office-space-1-5);\n padding: 0 var(--office-space-0) 0 var(--office-space-1-5);\n white-space: nowrap;\n}\n.label,\n.dot,\n.status {\n color: var(--office-muted-foreground);\n white-space: nowrap;\n}\n.sep {\n flex: none;\n width: var(--office-border-width);\n height: var(--office-space-4);\n margin: 0 var(--office-space-1);\n background: var(--office-title-bar-border);\n}\n.qat {\n display: flex;\n align-items: center;\n gap: var(--office-space-0);\n min-width: 0;\n}\n.qat button,\n.results button {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--office-space-1);\n min-width: var(--office-control-height-md);\n min-height: var(--office-control-height-md);\n padding: var(--office-space-1);\n border: 0;\n border-radius: var(--office-radius-sm);\n background: transparent;\n color: var(--office-muted-foreground);\n font: inherit;\n cursor: pointer;\n touch-action: manipulation;\n}\n.qat button:hover:not(:disabled) {\n background: var(--office-selected);\n}\n.qat button:active:not(:disabled) {\n opacity: 0.7;\n}\n.qat button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.qat small {\n font-size: var(--office-font-size-xs);\n white-space: nowrap;\n}\nsvg {\n flex: none;\n width: var(--office-icon-size);\n height: var(--office-icon-size);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.file {\n display: flex;\n align-items: baseline;\n gap: var(--office-space-1-5);\n min-width: 0;\n padding: 0 var(--office-space-1);\n flex: 0 1 auto;\n}\n.name {\n max-width: var(--office-title-bar-name-max-width);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--office-title-bar-foreground);\n font-size: var(--office-font-size-sm);\n font-weight: var(--office-font-weight-medium);\n}\n.status.saving {\n color: var(--office-warning);\n}\n.status.error {\n color: var(--office-danger);\n}\n.search {\n position: relative;\n flex: 1;\n display: flex;\n justify-content: center;\n min-width: 0;\n padding: 0 var(--office-space-2);\n}\n.box {\n position: relative;\n width: 100%;\n max-width: var(--office-title-bar-search-max-width);\n min-width: 0;\n}\n.box > [part='search'] {\n width: 100%;\n}\n.results {\n position: absolute;\n z-index: var(--office-z-popover);\n top: calc(100% + var(--office-space-1));\n right: 0;\n left: 0;\n max-height: var(--office-title-bar-results-max-height);\n overflow-y: auto;\n border: var(--office-border-width) solid var(--office-border);\n border-radius: var(--office-radius-lg);\n background: var(--office-popover);\n color: var(--office-popover-foreground);\n box-shadow: var(--office-shadow-lg);\n}\n.heading {\n padding: var(--office-space-1-5) var(--office-space-3);\n color: var(--office-muted-foreground);\n font-size: var(--office-font-size-2xs);\n font-weight: var(--office-font-weight-bold);\n letter-spacing: 0.05em;\n text-transform: uppercase;\n}\n.empty {\n padding: var(--office-space-2) var(--office-space-3);\n color: var(--office-muted-foreground);\n}\n.results button {\n display: flex;\n width: 100%;\n justify-content: flex-start;\n gap: var(--office-space-2);\n padding: var(--office-space-1-5) var(--office-space-3);\n border-radius: 0;\n color: inherit;\n text-align: start;\n}\n.results button:hover,\n.results button[aria-selected='true'] {\n background: var(--office-selected);\n}\n.results .cat {\n margin-inline-start: auto;\n color: var(--office-muted-foreground);\n font-size: var(--office-font-size-2xs);\n text-transform: capitalize;\n}\n.results .content {\n border-top: var(--office-border-width) solid var(--office-border);\n}\n.end {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--office-space-1);\n}\n.sr {\n position: absolute;\n width: var(--office-space-px);\n height: var(--office-space-px);\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\nbutton:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: var(--office-focus-offset);\n}\n@media (max-width: 767px), (max-width: 1023px) and (max-height: 520px) {\n :host(:not([placement='belowRibbon'])) {\n display: none !important;\n }\n}\n@media (max-width: 1100px) {\n .qat small {\n display: none;\n }\n .name {\n max-width: var(--office-title-bar-name-max-width-narrow);\n }\n}\n@media (pointer: coarse) {\n .qat button,\n .results button {\n min-width: var(--office-target-size-touch);\n min-height: var(--office-target-size-touch);\n }\n .bar {\n height: auto;\n min-height: var(--office-target-size-touch);\n }\n}\n@media (forced-colors: active) {\n .bar {\n border-bottom-color: CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n .mark {\n border: var(--office-border-width) solid CanvasText;\n background: Canvas;\n color: CanvasText;\n }\n .label,\n .dot,\n .status,\n .name,\n .qat button,\n .results button {\n color: CanvasText;\n }\n .sep {\n background: CanvasText;\n }\n .qat button:hover:not(:disabled),\n .results button:hover,\n .results button[aria-selected='true'] {\n background: Highlight;\n color: HighlightText;\n }\n .qat button:disabled {\n color: GrayText;\n opacity: 1;\n }\n .results {\n border-color: CanvasText;\n background: Canvas;\n }\n button:focus-visible {\n outline-color: Highlight;\n }\n}\n";
3955
+
3956
+ // src/chrome/title-bar.ts
3957
+ var EMPTY = { fileName: "" };
3958
+ var OfficeUiTitleBar = class extends OfficeElement {
3959
+ static autosaveEvent = "office-autosave-toggle";
3960
+ static commandEvent = "office-command";
3961
+ static searchEvent = "office-command-search";
3962
+ /** Inner controls; a product subclass may use its own aliased tags. */
3963
+ static switchTag = "office-ui-switch";
3964
+ static searchTag = "office-ui-search";
3965
+ static styles = controlStyles(title_bar_default);
3966
+ static properties = {
3967
+ placement: { type: String, noAccessor: true },
3968
+ stop: { state: true },
3969
+ found: { state: true },
3970
+ active: { state: true },
3971
+ resultsOpen: { state: true },
3972
+ live: { state: true }
3973
+ };
3974
+ model = EMPTY;
3975
+ constructor() {
3976
+ super();
3977
+ this.stop = "";
3978
+ this.found = [];
3979
+ this.active = 0;
3980
+ this.resultsOpen = false;
3981
+ this.live = "";
3982
+ }
3983
+ get state() {
3984
+ return this.model;
3985
+ }
3986
+ set state(value) {
3987
+ this.model = value ?? EMPTY;
3988
+ this.requestUpdate("state");
3989
+ }
3990
+ /** Read from the attribute, so a subclass that intercepts attribute changes still sees it. */
3991
+ get placement() {
3992
+ return this.getAttribute("placement") === "belowRibbon" ? "belowRibbon" : "titleBar";
3993
+ }
3994
+ set placement(value) {
3995
+ if (this.getAttribute("placement") !== value) this.setAttribute("placement", value);
3996
+ this.requestUpdate("placement");
3997
+ }
3998
+ /** The search field inside the bar, for hosts that move focus to it (Alt+Q). */
3999
+ get searchField() {
4000
+ this.ensureRendered();
4001
+ return this.renderRoot.querySelector('[part="search"]');
4002
+ }
4003
+ /** A Quick Access button was pressed; subclasses may route by id. */
4004
+ activate(id) {
4005
+ this.fire(this.constructor.commandEvent, { command: id });
4006
+ }
4007
+ /** Called after every render; subclasses mirror state onto the host here. */
4008
+ rendered() {
4009
+ }
4010
+ // ---- Quick Access Toolbar -------------------------------------------------------------
4011
+ enabledButtons() {
4012
+ return [...this.renderRoot.querySelectorAll(".qat button:not(:disabled)")];
4013
+ }
4014
+ onQatFocus(event) {
4015
+ const target = event.target.closest("button");
4016
+ if (target?.dataset.command) this.stop = target.dataset.command;
4017
+ }
4018
+ onQatKey(event) {
4019
+ if (event.ctrlKey || event.metaKey || event.altKey) return;
4020
+ const list = this.enabledButtons();
4021
+ const index = list.findIndex((b) => b === event.target.closest("button"));
4022
+ const last = list.length - 1;
4023
+ const moves = {
4024
+ ArrowRight: (index + 1) % list.length,
4025
+ ArrowLeft: (index + last) % list.length,
4026
+ Home: 0,
4027
+ End: last
4028
+ };
4029
+ const next = moves[event.key] ?? -1;
4030
+ if (next >= 0 && index >= 0) {
4031
+ event.preventDefault();
4032
+ list[next]?.focus();
4033
+ }
4034
+ if (next >= 0 || event.key === " " || event.key === "Enter") event.stopPropagation();
4035
+ }
4036
+ quickAccess() {
4037
+ const qa = this.model.quickAccess;
4038
+ const items = qa?.items ?? [];
4039
+ const enabled = items.filter((item) => item.disabled !== true);
4040
+ const current = enabled.find((item) => item.id === this.stop) ?? enabled[0];
4041
+ return html`<div
4042
+ class="qat"
4043
+ role="toolbar"
4044
+ part="quick-access"
4045
+ aria-label=${qa?.label ?? ""}
4046
+ ?hidden=${items.length === 0}
4047
+ @focusin=${this.onQatFocus}
4048
+ @keydown=${this.onQatKey}
4049
+ >
4050
+ ${repeat(
4051
+ items,
4052
+ (item) => item.id,
4053
+ (item) => html`<button
4054
+ type="button"
4055
+ data-command=${item.id}
4056
+ aria-label=${item.label}
4057
+ title=${ifDefined(item.title)}
4058
+ tabindex=${item === current ? 0 : -1}
4059
+ ?disabled=${item.disabled === true}
4060
+ @click=${() => this.activate(item.id)}
4061
+ >${glyph(item.icon, "icon")}${qa?.showLabels === true ? html`<small>${item.label}</small>` : ""}</button
4062
+ >`
4063
+ )}
4064
+ </div>`;
4065
+ }
4066
+ // ---- Command search -------------------------------------------------------------------
4067
+ get query() {
4068
+ return this.searchField?.value ?? "";
4069
+ }
4070
+ openResults() {
4071
+ const search = this.model.search;
4072
+ const query = this.query;
4073
+ if (!search || !query.trim()) return this.closeResults();
4074
+ this.found = [...matchTitleBarCommands(search, query)].slice(0, OFFICE_TITLE_BAR_SEARCH_LIMIT);
4075
+ this.active = Math.min(this.active, Math.max(this.found.length - 1, 0));
4076
+ this.resultsOpen = true;
4077
+ this.live = this.found.length > 0 ? search.heading : search.empty;
4078
+ }
4079
+ closeResults() {
4080
+ this.resultsOpen = false;
4081
+ this.live = "";
4082
+ this.found = [];
4083
+ }
4084
+ commit(entry) {
4085
+ const query = this.query;
4086
+ this.searchField.value = "";
4087
+ this.closeResults();
4088
+ this.fire(
4089
+ this.constructor.searchEvent,
4090
+ entry ? { query, command: entry.id } : { query }
4091
+ );
4092
+ }
4093
+ onSearchKey(event) {
4094
+ const { key, ctrlKey, metaKey } = event;
4095
+ const field = this.searchField;
4096
+ if (key === "Enter" && field.value.trim()) this.commit(this.found[this.active]);
4097
+ else if (key === "Escape" && field.value) {
4098
+ field.value = "";
4099
+ this.closeResults();
4100
+ } else if ((key === "ArrowDown" || key === "ArrowUp") && this.found.length > 0) {
4101
+ event.preventDefault();
4102
+ this.active = (this.active + (key === "ArrowDown" ? 1 : this.found.length - 1)) % this.found.length;
4103
+ }
4104
+ if (!ctrlKey && !metaKey) event.stopPropagation();
4105
+ }
4106
+ onSearchBlur(event) {
4107
+ const next = event.relatedTarget;
4108
+ const box = this.renderRoot.querySelector(".box");
4109
+ if (!next || !box?.contains(next)) this.closeResults();
4110
+ }
4111
+ results(search) {
4112
+ if (!search || !this.resultsOpen)
4113
+ return html`<div class="results" role="listbox" hidden></div>
4114
+ <div class="sr" role="status"></div>`;
4115
+ const query = this.query;
4116
+ const pick = (event, run) => {
4117
+ event.preventDefault();
4118
+ run();
4119
+ };
4120
+ return html`
4121
+ <div class="results" role="listbox" ?hidden=${!this.resultsOpen}>
4122
+ ${this.found.length > 0 ? html`<div class="heading">${search.heading}</div> ${this.found.map(
4123
+ (entry, index) => html`<button
4124
+ type="button"
4125
+ role="option"
4126
+ tabindex="-1"
4127
+ aria-selected=${String(index === this.active)}
4128
+ @mousedown=${(event) => pick(event, () => this.commit(entry))}
4129
+ @mouseenter=${() => this.active = index}
4130
+ ><span>${entry.label}</span
4131
+ ><span class="cat">${entry.category ?? ""}</span></button
4132
+ >`
4133
+ )}` : html`<div class="empty">${search.empty}</div>`}
4134
+ ${search.content ? html`<button
4135
+ type="button"
4136
+ class="content"
4137
+ tabindex="-1"
4138
+ @mousedown=${(event) => pick(event, () => this.commit())}
4139
+ >${glyph(search.contentIcon ?? "search", "icon")}<span
4140
+ >${search.content(query)}</span
4141
+ ></button
4142
+ >` : ""}
4143
+ </div>
4144
+ <div class="sr" role="status">${this.live}</div>
4145
+ `;
4146
+ }
4147
+ searchBox() {
4148
+ const search = this.model.search;
4149
+ const tag = unsafeStatic(this.constructor.searchTag);
4150
+ return html`<span class="search" ?hidden=${!search}>
4151
+ <div class="box" ?hidden=${!search} @focusout=${this.onSearchBlur}>
4152
+ ${html$1`<${tag}
4153
+ variant="titlebar"
4154
+ part="search"
4155
+ placeholder=${ifDefined(search?.placeholder)}
4156
+ aria-label=${ifDefined(search?.label)}
4157
+ @input=${() => {
4158
+ this.active = 0;
4159
+ this.openResults();
4160
+ }}
4161
+ @focusin=${this.openResults}
4162
+ @keydown=${this.onSearchKey}
4163
+ ></${tag}>`}
4164
+ ${this.results(search)}
4165
+ </div>
4166
+ </span>`;
4167
+ }
4168
+ // ---- AutoSave, mark and file name -----------------------------------------------------
4169
+ autosave() {
4170
+ const autosave = this.model.autosave;
4171
+ const tag = unsafeStatic(this.constructor.switchTag);
4172
+ return html`<span class="autosave" ?hidden=${!autosave}>
4173
+ <span class="label">${autosave?.label ?? ""}</span>
4174
+ ${html$1`<${tag}
4175
+ class="switch"
4176
+ part="autosave-switch"
4177
+ aria-label=${autosave?.toggleLabel ?? ""}
4178
+ title=${autosave ? autosave.title ?? autosave.toggleLabel : ""}
4179
+ .checked=${autosave?.enabled === true}
4180
+ ?disabled=${autosave?.available === false}
4181
+ @change=${(event) => {
4182
+ this.fire(this.constructor.autosaveEvent, null);
4183
+ event.target.checked = this.model.autosave?.enabled === true;
4184
+ }}
4185
+ ></${tag}>`}
4186
+ <span class="label">${autosave?.stateLabel ?? ""}</span>
4187
+ </span>
4188
+ <i class="sep" aria-hidden="true" ?hidden=${!autosave}></i>`;
4189
+ }
4190
+ willUpdate(changed) {
4191
+ const items = this.model.quickAccess?.items ?? [];
4192
+ this.toggleAttribute("data-empty", this.placement === "belowRibbon" && items.length === 0);
4193
+ if (changed.has("state") && this.resultsOpen) this.openResults();
4194
+ }
4195
+ updated() {
4196
+ this.rendered();
4197
+ }
4198
+ render() {
4199
+ const state = this.model;
4200
+ const below = this.placement === "belowRibbon";
4201
+ const items = state.quickAccess?.items ?? [];
4202
+ if (below) return html`<div class="bar" part="bar">${this.quickAccess()}</div>`;
4203
+ return html`
4204
+ <div class="bar" part="bar">
4205
+ <span class="mark" aria-hidden="true" ?hidden=${!state.appMark}
4206
+ >${state.appMark ?? ""}</span
4207
+ >
4208
+ ${this.autosave()} ${this.quickAccess()}
4209
+ <i class="sep" aria-hidden="true" ?hidden=${items.length === 0}></i>
4210
+ <span class="file">
4211
+ <span class="name" part="file-name">${state.fileName}</span>
4212
+ <span class="dot" aria-hidden="true" ?hidden=${!state.status}>•</span>
4213
+ <span
4214
+ class="status${state.tone === "saving" ? " saving" : ""}${state.tone === "error" ? " error" : ""}"
4215
+ ?hidden=${!state.status}
4216
+ >${state.status ?? ""}</span
4217
+ >
4218
+ </span>
4219
+ ${this.searchBox()}
4220
+ <div class="end">
4221
+ <slot name="collaboration"></slot>
4222
+ <slot name="account"></slot>
4223
+ </div>
4224
+ </div>
4225
+ `;
4226
+ }
4227
+ };
4228
+ var defineTitleBar = definer("office-ui-title-bar", () => OfficeUiTitleBar);
4229
+
4230
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/zoom-slider.css?raw
4231
+ var zoom_slider_default = ":host {\n display: inline-flex;\n align-items: center;\n gap: var(--office-space-0);\n font-size: var(--office-font-size-xs);\n color: var(--office-foreground);\n font-family: var(--office-font);\n}\nbutton {\n box-sizing: border-box;\n display: inline-grid;\n place-items: center;\n min-width: var(--office-target-size);\n min-height: var(--office-target-size);\n padding: 0;\n border: var(--office-border-width) solid transparent;\n border-radius: var(--office-radius);\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: var(--office-font-size-lg);\n line-height: 1;\n cursor: pointer;\n}\nbutton:hover:not(:disabled) {\n background: var(--office-background);\n}\nbutton:focus-visible,\ninput:focus-visible {\n outline: var(--office-focus-width) solid var(--office-ring);\n outline-offset: calc(var(--office-focus-offset) / 2);\n}\nbutton:disabled,\ninput:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\ninput {\n width: var(--office-zoom-slider-width);\n min-width: var(--office-zoom-slider-min-width);\n margin: 0 var(--office-space-0);\n accent-color: var(--office-accent);\n}\noutput {\n min-width: var(--office-zoom-value-width);\n text-align: center;\n font-variant-numeric: tabular-nums;\n}\nsvg {\n width: var(--office-icon-size-md);\n height: var(--office-icon-size-md);\n fill: none;\n stroke: currentColor;\n stroke-width: var(--office-icon-stroke);\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n:host(:not([fit])) .fit {\n display: none;\n}\n\n@media (pointer: coarse), (max-width: 767px) {\n input {\n flex: 1;\n }\n}\n@media (forced-colors: active) {\n button {\n color: ButtonText;\n }\n button:disabled {\n color: GrayText;\n }\n}\n";
4232
+
4233
+ // src/form/zoom-slider.ts
4234
+ var clampTo = (value, min, max) => Math.min(max, Math.max(min, Math.round(value)));
4235
+ var finite = (value, fallback) => Number.isFinite(value) ? value : fallback;
4236
+ var OfficeUiZoomSlider = class extends OfficeElement {
4237
+ static styles = controlStyles(zoom_slider_default);
4238
+ static properties = {
4239
+ // `value` clamps to the range, so it has a hand-written accessor below.
4240
+ value: { type: Number, reflect: true, noAccessor: true },
4241
+ min: { type: Number },
4242
+ max: { type: Number },
4243
+ step: { type: Number },
4244
+ disabled: flag,
4245
+ fit: { type: String, reflect: true },
4246
+ label: { type: String },
4247
+ outLabel: { type: String, attribute: "out-label" },
4248
+ inLabel: { type: String, attribute: "in-label" },
4249
+ sliderLabel: { type: String, attribute: "slider-label" }
4250
+ };
4251
+ requested = 100;
4252
+ constructor() {
4253
+ super();
4254
+ this.min = 10;
4255
+ this.max = 400;
4256
+ this.step = 10;
4257
+ this.disabled = false;
4258
+ this.fit = null;
4259
+ this.label = null;
4260
+ this.outLabel = null;
4261
+ this.inLabel = null;
4262
+ this.sliderLabel = null;
4263
+ }
4264
+ get value() {
4265
+ return clampTo(this.requested, this.low, this.high);
4266
+ }
4267
+ set value(next) {
4268
+ const old = this.value;
4269
+ this.requested = finite(Number(next), 100);
4270
+ this.requestUpdate("value", old);
4271
+ }
4272
+ get low() {
4273
+ return finite(this.min, 10);
4274
+ }
4275
+ get high() {
4276
+ return Math.max(this.low, finite(this.max, 400));
4277
+ }
4278
+ /** Office buttons snap to the next multiple of `step`, so 67% steps to 70%, not 77%. */
4279
+ stepBy(direction) {
4280
+ const step = Math.max(1, finite(this.step, 10));
4281
+ const current = this.value;
4282
+ const next = direction > 0 ? Math.floor(current / step) * step + step : Math.ceil(current / step) * step - step;
4283
+ this.commit(next, "change");
4284
+ }
4285
+ commit(next, type) {
4286
+ if (present(this.disabled)) return;
4287
+ const value = clampTo(next, this.low, this.high);
4288
+ if (value === this.value && type === "change") return;
4289
+ this.value = value;
4290
+ this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
4291
+ if (type === "change")
4292
+ this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
4293
+ }
4294
+ willUpdate() {
4295
+ this.setAttribute("role", "group");
4296
+ this.setAttribute("aria-label", this.label ?? "Zoom");
4297
+ }
4298
+ render() {
4299
+ const { value } = this;
4300
+ const disabled = present(this.disabled);
4301
+ const fit = this.fit || "Fit";
4302
+ const out = this.outLabel || "Zoom out";
4303
+ const inn = this.inLabel || "Zoom in";
4304
+ return html`
4305
+ <button
4306
+ type="button"
4307
+ aria-label=${out}
4308
+ title=${out}
4309
+ ?disabled=${disabled || value <= this.low}
4310
+ @click=${() => this.stepBy(-1)}
4311
+ >−</button
4312
+ >
4313
+ <input
4314
+ type="range"
4315
+ aria-label=${this.sliderLabel || "Zoom"}
4316
+ aria-valuetext="${value}%"
4317
+ min=${this.low}
4318
+ max=${this.high}
4319
+ .value=${String(value)}
4320
+ ?disabled=${disabled}
4321
+ @input=${(event) => this.commit(event.target.valueAsNumber, "input")}
4322
+ @change=${() => this.dispatchEvent(new Event("change", { bubbles: true, composed: true }))}
4323
+ />
4324
+ <button
4325
+ type="button"
4326
+ aria-label=${inn}
4327
+ title=${inn}
4328
+ ?disabled=${disabled || value >= this.high}
4329
+ @click=${() => this.stepBy(1)}
4330
+ >+</button
4331
+ >
4332
+ <output>${value}%</output>
4333
+ <button
4334
+ class="fit"
4335
+ type="button"
4336
+ aria-label=${fit}
4337
+ title=${fit}
4338
+ ?disabled=${disabled}
4339
+ @click=${() => this.fire("office-command", { command: "zoom-fit" })}
4340
+ >${glyph("fitPage", "fit-icon")}</button
4341
+ >
4342
+ `;
4343
+ }
4344
+ };
4345
+ var defineZoomSlider = definer("office-ui-zoom-slider", () => OfficeUiZoomSlider);
4346
+
4347
+ // src/keytips.ts
4348
+ var BADGE_CSS = "position:fixed;inset:auto;margin:0;padding:0;border:0;background:transparent;overflow:visible;pointer-events:none";
4349
+ var TIP_CSS = `position:fixed;transform:translate(-50%,-30%);min-width:${tok("--office-icon-size")};padding:${tok("--office-space-px")} ${tok("--office-space-1")};border:${tok("--office-border-width")} solid ${tok("--office-keytip-border")};border-radius:${tok("--office-radius-xs")};background:${tok("--office-keytip-background")};color:${tok("--office-keytip-foreground")};font:${tok("--office-font-weight-bold")} ${tok("--office-font-size-xs")}/${tok("--office-icon-size")} ${tok("--office-font")};text-align:center;box-shadow:${tok("--office-shadow-sm")}`;
4350
+ var isDisabled = (el) => el.hasAttribute("disabled") || el.getAttribute("aria-disabled") === "true";
4351
+ var visible = (el) => {
4352
+ const box = el.getBoundingClientRect();
4353
+ return box.width > 0 && box.height > 0;
4354
+ };
4355
+ function deepTips(root) {
4356
+ const found = [...root.querySelectorAll("[data-keytip]")];
4357
+ for (const host of root.querySelectorAll("*")) {
4358
+ const shadow = host.shadowRoot;
4359
+ if (!shadow) continue;
4360
+ const inner = deepTips(shadow);
4361
+ if (!inner.length) continue;
4362
+ for (const el of inner) hostOf.set(el, host);
4363
+ found.push(...inner);
4364
+ }
4365
+ return found;
4366
+ }
4367
+ var hostOf = /* @__PURE__ */ new WeakMap();
4368
+ function activate(el) {
4369
+ const inner = el.shadowRoot?.querySelector(".main, button");
4370
+ (inner ?? el).click();
4371
+ }
4372
+ function attachKeyTips(scope) {
4373
+ const doc = (scope instanceof ShadowRoot ? scope.host : scope).ownerDocument;
4374
+ const view = doc.defaultView;
4375
+ const layer = doc.createElement("div");
4376
+ layer.setAttribute("aria-hidden", "true");
4377
+ layer.style.cssText = BADGE_CSS;
4378
+ const topLayer = typeof layer.showPopover === "function";
4379
+ if (topLayer) layer.setAttribute("popover", "manual");
4380
+ else layer.hidden = true;
4381
+ scope.append(layer);
4382
+ let levels = [];
4383
+ let typed = "";
4384
+ let altAlone = false;
4385
+ const tipsAt = (container) => deepTips(container ?? scope).filter((el) => {
4386
+ const owner = (hostOf.get(el) ?? el.parentElement)?.closest("[data-keytip-level]") ?? null;
4387
+ return owner === container && visible(el);
4388
+ });
4389
+ const current = () => tipsAt(levels.at(-1) ?? null);
4390
+ const render2 = () => {
4391
+ if (!handle.active) return;
4392
+ layer.replaceChildren(
4393
+ ...current().map((el) => {
4394
+ const box = el.getBoundingClientRect();
4395
+ const tip = doc.createElement("span");
4396
+ tip.textContent = el.dataset.keytip;
4397
+ tip.style.cssText = `${TIP_CSS};left:${Math.round(box.left + box.width / 2)}px;top:${Math.round(box.bottom - 4)}px;opacity:${isDisabled(el) || typed && !el.dataset.keytip.startsWith(typed) ? "0.45" : "1"}`;
4398
+ return tip;
4399
+ })
4400
+ );
4401
+ };
4402
+ const show = () => {
4403
+ if (topLayer && !layer.matches(":popover-open")) layer.showPopover?.();
4404
+ else layer.hidden = false;
4405
+ render2();
4406
+ };
4407
+ const stop = () => {
4408
+ levels = [];
4409
+ typed = "";
4410
+ if (topLayer) {
4411
+ try {
4412
+ layer.hidePopover?.();
4413
+ } catch {
4414
+ }
4415
+ } else layer.hidden = true;
4416
+ layer.replaceChildren();
4417
+ handle.active = false;
4418
+ };
4419
+ const start = () => {
4420
+ levels = [];
4421
+ typed = "";
4422
+ handle.active = true;
4423
+ show();
4424
+ };
4425
+ const choose = (el) => {
4426
+ typed = "";
4427
+ if (isDisabled(el)) return render2();
4428
+ const panel = el.dataset.keytipPanel;
4429
+ activate(el);
4430
+ const next = panel ? [...scope.querySelectorAll("[data-keytip-level]")].find((el2) => el2.id === panel) ?? null : null;
4431
+ if (next) {
4432
+ levels.push(next);
4433
+ view?.requestAnimationFrame ? view.requestAnimationFrame(render2) : render2();
4434
+ } else stop();
4435
+ };
4436
+ const onKeyDown = (event) => {
4437
+ const key = event;
4438
+ if (key.key === "Alt") {
4439
+ altAlone = !key.ctrlKey && !key.metaKey && !key.shiftKey;
4440
+ return;
4441
+ }
4442
+ altAlone = false;
4443
+ if (!handle.active) return;
4444
+ if (key.key === "Escape") {
4445
+ key.preventDefault();
4446
+ key.stopPropagation();
4447
+ typed = "";
4448
+ if (levels.length) {
4449
+ levels.pop();
4450
+ render2();
4451
+ } else stop();
4452
+ return;
4453
+ }
4454
+ if (key.key.length !== 1 || key.ctrlKey || key.metaKey) return stop();
4455
+ key.preventDefault();
4456
+ key.stopPropagation();
4457
+ typed += key.key.toUpperCase();
4458
+ const candidates = current().filter((el) => el.dataset.keytip.toUpperCase().startsWith(typed));
4459
+ const exact = candidates.find((el) => el.dataset.keytip.toUpperCase() === typed);
4460
+ if (exact) choose(exact);
4461
+ else if (!candidates.length) typed = "";
4462
+ render2();
4463
+ };
4464
+ const onKeyUp = (event) => {
4465
+ const key = event;
4466
+ if (key.key !== "Alt" || !altAlone) return;
4467
+ altAlone = false;
4468
+ key.preventDefault();
4469
+ if (handle.active) stop();
4470
+ else start();
4471
+ };
4472
+ const onPointer = () => {
4473
+ if (handle.active) stop();
4474
+ };
4475
+ const target = scope;
4476
+ target.addEventListener("keydown", onKeyDown, true);
4477
+ target.addEventListener("keyup", onKeyUp, true);
4478
+ target.addEventListener("pointerdown", onPointer, true);
4479
+ view?.addEventListener("blur", onPointer);
4480
+ const handle = {
4481
+ active: false,
4482
+ dispose() {
4483
+ stop();
4484
+ target.removeEventListener("keydown", onKeyDown, true);
4485
+ target.removeEventListener("keyup", onKeyUp, true);
4486
+ target.removeEventListener("pointerdown", onPointer, true);
4487
+ view?.removeEventListener("blur", onPointer);
4488
+ layer.remove();
4489
+ }
4490
+ };
4491
+ return handle;
4492
+ }
4493
+
4494
+ // src/controls.ts
4495
+ var CONTROL_DEFINERS = [
4496
+ defineIcon,
4497
+ defineButton,
4498
+ defineCheckbox,
4499
+ defineSwitch,
4500
+ defineSelect,
4501
+ defineRibbonGroup,
4502
+ defineRibbonStack,
4503
+ defineMenuItem,
4504
+ defineMenuSeparator,
4505
+ defineMenuButton,
4506
+ defineContextMenu,
4507
+ defineCommandSearch,
4508
+ defineToolbar,
4509
+ defineDialog,
4510
+ defineStatusBar,
4511
+ defineStatusItem,
4512
+ defineZoomSlider,
4513
+ defineTabStrip,
4514
+ defineOptionsDialog,
4515
+ defineAccount,
4516
+ defineRibbon,
4517
+ defineFindBar,
4518
+ defineRuler,
4519
+ defineBackstage,
4520
+ definePrintPreview,
4521
+ defineRadio,
4522
+ defineSearchField,
4523
+ defineRibbonToggle,
4524
+ defineDialogFooter,
4525
+ defineToasts,
4526
+ defineReadOnlyBanner,
4527
+ definePasteOptions,
4528
+ defineTitleBar,
4529
+ defineRibbonSection,
4530
+ defineGallery
4531
+ ];
4532
+ function registerControls(registry) {
4533
+ for (const define of CONTROL_DEFINERS) define(registry);
4534
+ }
4535
+
4536
+ export { CONTROL_DEFINERS, DEFAULT_OFFICE_PROFILE, EMPTY_MENU_STATE, OFFICE_AVATAR_SWATCHES, OFFICE_PROFILE_STORAGE_KEY, OFFICE_TITLE_BAR_SEARCH_LIMIT, OFFICE_TOAST_VISIBLE_LIMIT, attachKeyTips, clampFlyoutPosition, clampOptionNumber, clearOfficeProfile, defineAccount, defineBackstage, defineButton, defineCheckbox, defineCommandSearch, defineContextMenu, defineDialog, defineDialogFooter, defineFindBar, defineGallery, defineIcon, defineMenuButton, defineMenuItem, defineMenuSeparator, defineOptionsDialog, definePasteOptions, definePrintPreview, defineRadio, defineReadOnlyBanner, defineRibbon, defineRibbonGroup, defineRibbonSection, defineRibbonStack, defineRibbonToggle, defineRuler, defineSearchField, defineSelect, defineStatusBar, defineStatusItem, defineSwitch, defineTabStrip, defineTitleBar, defineToasts, defineToolbar, defineZoomSlider, glyph, nextEnabledIndex, parseSvgPreview, profileInitials, readOfficeProfile, registerControls, rulerDivisions, sanitizeOfficeProfile, typeAheadIndex, writeOfficeProfile };