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.
- package/README.md +47 -2
- package/dist/base.d.ts +118 -0
- package/dist/{account.d.ts → chrome/account-profile.d.ts} +0 -6
- package/dist/chrome/account.d.ts +17 -0
- package/dist/chrome/backstage.d.ts +78 -0
- package/dist/chrome/find-bar.d.ts +111 -0
- package/dist/chrome/print-preview.d.ts +39 -0
- package/dist/chrome/ruler.d.ts +43 -0
- package/dist/chrome/status-bar.d.ts +80 -0
- package/dist/chrome/status-item.d.ts +40 -0
- package/dist/chrome/tab-strip.d.ts +88 -0
- package/dist/chrome/title-bar-types.d.ts +75 -0
- package/dist/chrome/title-bar.d.ts +81 -0
- package/dist/{chunk-7XBKSYGM.js → chunk-2X6QYQ3Q.js} +10 -1
- package/dist/{chunk-4JQLQROF.js → chunk-3MDKXKKQ.js} +53 -65
- package/dist/chunk-5A4REH6T.js +259 -0
- package/dist/chunk-E47762NY.js +90 -0
- package/dist/chunk-HNGJI5EZ.js +834 -0
- package/dist/{chunk-G3LAGZEW.js → chunk-HSJH6SUW.js} +33 -1
- package/dist/{chunk-W5PYFBEZ.js → chunk-HZ422LTR.js} +1 -49
- package/dist/chunk-LKLZOPTO.js +4536 -0
- package/dist/controls.d.ts +56 -46
- package/dist/controls.js +5 -5
- package/dist/dialog/dialog-footer.d.ts +40 -0
- package/dist/dialog/dialog.d.ts +52 -0
- package/dist/{options.d.ts → dialog/options-dialog.d.ts} +46 -1
- package/dist/form/checkable.d.ts +49 -0
- package/dist/form/checkbox.d.ts +8 -0
- package/dist/form/radio.d.ts +68 -0
- package/dist/form/search-field.d.ts +54 -0
- package/dist/{select-menu.d.ts → form/select-menu.d.ts} +3 -13
- package/dist/form/select.d.ts +115 -0
- package/dist/form/switch.d.ts +11 -0
- package/dist/form/zoom-slider.d.ts +78 -0
- package/dist/glyph.d.ts +2 -0
- package/dist/icon.d.ts +19 -0
- package/dist/icons.d.ts +0 -3
- package/dist/icons.js +1 -3
- package/dist/index.d.ts +4 -1
- package/dist/index.js +16 -12
- package/dist/menu/command-search.d.ts +74 -0
- package/dist/menu/context-menu.d.ts +89 -0
- package/dist/menu/menu-button.d.ts +77 -0
- package/dist/menu/menu-item.d.ts +52 -0
- package/dist/{menu-model.d.ts → menu/menu-model.d.ts} +2 -0
- package/dist/menu/menu-separator.d.ts +7 -0
- package/dist/notices/paste-options.d.ts +41 -0
- package/dist/notices/read-only-banner.d.ts +53 -0
- package/dist/notices/toasts.d.ts +40 -0
- package/dist/presence/presence.d.ts +53 -0
- package/dist/presence.d.ts +1 -31
- package/dist/presence.js +3 -3
- package/dist/ribbon/button.d.ts +60 -0
- package/dist/ribbon/gallery-styles.d.ts +2 -0
- package/dist/ribbon/gallery.d.ts +97 -0
- package/dist/ribbon/ribbon-group.d.ts +35 -0
- package/dist/{ribbon-section.d.ts → ribbon/ribbon-section.d.ts} +14 -1
- package/dist/ribbon/ribbon-stack.d.ts +10 -0
- package/dist/ribbon/ribbon-tabs.d.ts +82 -0
- package/dist/ribbon/ribbon-toggle.d.ts +49 -0
- package/dist/ribbon/ribbon-toolbar.d.ts +15 -0
- package/dist/smartart/smartart.d.ts +43 -0
- package/dist/smartart.d.ts +1 -14
- package/dist/smartart.js +3 -3
- package/dist/styles.d.ts +1 -4
- package/dist/teams/app-rail.d.ts +29 -0
- package/dist/teams/avatar.d.ts +39 -0
- package/dist/teams/base.d.ts +17 -0
- package/dist/teams/call-controls.d.ts +61 -0
- package/dist/teams/call-grid.d.ts +33 -0
- package/dist/teams/channel-list.d.ts +40 -0
- package/dist/teams/chat-composer.d.ts +60 -0
- package/dist/teams/chat-list.d.ts +51 -0
- package/dist/teams/icons.d.ts +3 -0
- package/dist/teams/index.d.ts +14 -0
- package/dist/teams/prejoin.d.ts +40 -0
- package/dist/teams.js +5 -0
- package/dist/theme.js +2 -2
- package/dist/tokens.d.ts +245 -214
- package/package.json +12 -6
- package/dist/backstage.d.ts +0 -28
- package/dist/button.d.ts +0 -14
- package/dist/checkable.d.ts +0 -4
- package/dist/chrome-controls.d.ts +0 -52
- package/dist/chunk-24MMSCKW.js +0 -119
- package/dist/chunk-UEJ4F33J.js +0 -78
- package/dist/chunk-W7YI5DSJ.js +0 -5970
- package/dist/command-search.d.ts +0 -18
- package/dist/context-menu.d.ts +0 -20
- package/dist/dialog.d.ts +0 -13
- package/dist/document-notices.d.ts +0 -55
- package/dist/find-bar.d.ts +0 -15
- package/dist/gallery-styles.d.ts +0 -7
- package/dist/gallery-view.d.ts +0 -53
- package/dist/gallery.d.ts +0 -20
- package/dist/menu.d.ts +0 -15
- package/dist/print-preview.d.ts +0 -11
- package/dist/radio.d.ts +0 -1
- package/dist/ribbon-tabs.d.ts +0 -14
- package/dist/ribbon-toggle.d.ts +0 -1
- package/dist/ribbon.d.ts +0 -24
- package/dist/ruler.d.ts +0 -10
- package/dist/search-field.d.ts +0 -1
- package/dist/select-styles.d.ts +0 -6
- package/dist/select.d.ts +0 -19
- package/dist/status-bar-view.d.ts +0 -50
- package/dist/status-bar.d.ts +0 -19
- package/dist/tab-strip.d.ts +0 -20
- package/dist/title-bar-search.d.ts +0 -43
- package/dist/title-bar-strip.d.ts +0 -20
- package/dist/title-bar.d.ts +0 -49
- package/dist/zoom-slider.d.ts +0 -9
- /package/dist/{safe-svg.d.ts → ribbon/safe-svg.d.ts} +0 -0
- /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 };
|