ooxml-ui 0.23.0 → 0.25.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.
@@ -23,6 +23,8 @@ export interface OfficeStatusButton {
23
23
  text?: string | undefined;
24
24
  pressed?: boolean | undefined;
25
25
  hidden?: boolean | undefined;
26
+ /** Offered but unavailable (a view the product does not provide yet). */
27
+ disabled?: boolean | undefined;
26
28
  }
27
29
  /** Everything the controlled bar shows, already translated. Absent parts are not rendered. */
28
30
  export interface OfficeStatusBarState {
@@ -4,8 +4,8 @@ import { type OfficeTitleBarCommand, type OfficeTitleBarPlacement, type OfficeTi
4
4
  export * from './title-bar-types.js';
5
5
  /**
6
6
  * `<office-ui-title-bar>`: Office's title bar above the ribbon (app mark, AutoSave, Quick
7
- * Access Toolbar, file name and status, centred command search, `collaboration` and `account`
8
- * slots). Controlled: set `state` (`OfficeTitleBarState`, every string translated); the bar
7
+ * Access Toolbar, file name and status, centred command search, `actions`, `collaboration` and
8
+ * `account` slots). Controlled: set `state` (`OfficeTitleBarState`, every string translated); the bar
9
9
  * owns no effects. `placement="belowRibbon"` renders only the Quick Access row and hides
10
10
  * itself (`data-empty`) while that row is empty. Events (bubbling, composed; static names let a
11
11
  * product subclass keep its published ones): `office-autosave-toggle`, `office-command`
@@ -1,5 +1,39 @@
1
1
  import { OFFICE_TOKENS, TOUCH } from './chunk-HSJH6SUW.js';
2
2
 
3
+ // src/theme-bridge.ts
4
+ var SHADCN_NAMES = [
5
+ ["--office-foreground", "foreground"],
6
+ ["--office-muted-foreground", "muted-foreground"],
7
+ ["--office-background", "background"],
8
+ ["--office-surface", "card"],
9
+ ["--office-selected", "accent"],
10
+ ["--office-border", "border"],
11
+ ["--office-accent", "primary"],
12
+ ["--office-accent-foreground", "primary-foreground"],
13
+ ["--office-ring", "ring"],
14
+ ["--office-popover", "popover"],
15
+ // Popover text falls back to the plain foreground, as it does in the shadcn themes.
16
+ ["--office-popover-foreground", "popover-foreground", "foreground"],
17
+ ["--office-danger", "destructive"],
18
+ ["--office-danger-foreground", "destructive-foreground"]
19
+ ];
20
+ function themeBridge(selector, map) {
21
+ const lines = Object.entries(map).filter(([, value]) => value).map(([name, value]) => ` ${name}: ${value};`);
22
+ return `${selector} {
23
+ ${lines.join("\n")}
24
+ }
25
+ `;
26
+ }
27
+ function shadcnBridge(selector, prefix, options = {}) {
28
+ const map = {};
29
+ for (const [token, name, fallbackName] of SHADCN_NAMES) {
30
+ const fallback = options.fallbacks?.[name];
31
+ const inner = fallbackName ? `var(${prefix}${fallbackName})` : fallback;
32
+ map[token] = `var(${prefix}${name}${inner ? `, ${inner}` : ""})`;
33
+ }
34
+ return themeBridge(selector, { ...map, ...options.extra });
35
+ }
36
+
3
37
  // src/theme.ts
4
38
  var THEME_STYLE_ID = "office-ui-theme";
5
39
  var DARK = `
@@ -67,4 +101,4 @@ function installOfficeUiTheme(doc = globalThis.document) {
67
101
  return true;
68
102
  }
69
103
 
70
- export { THEME_CSS, THEME_STYLE_ID, installOfficeUiTheme };
104
+ export { THEME_CSS, THEME_STYLE_ID, installOfficeUiTheme, shadcnBridge, themeBridge };
@@ -22,9 +22,11 @@ function defineOnce(registry, tag, make) {
22
22
  function browserRegistry() {
23
23
  return typeof window === "undefined" ? void 0 : window.customElements;
24
24
  }
25
- function definer(tag, make) {
25
+ function definer(tag, make, requires = []) {
26
26
  const define = (registry = browserRegistry()) => {
27
- if (registry) defineOnce(registry, tag, make);
27
+ if (!registry) return;
28
+ for (const dependency of requires) dependency(registry);
29
+ defineOnce(registry, tag, make);
28
30
  };
29
31
  return Object.assign(define, { tag });
30
32
  }
@@ -1,4 +1,4 @@
1
- import { OfficeElement, controlStyles, definer } from './chunk-5A4REH6T.js';
1
+ import { OfficeElement, controlStyles, definer } from './chunk-PCZJV6HQ.js';
2
2
  import { html } from 'lit';
3
3
  import { normalizeHexColor } from 'ooxml-core/color';
4
4
 
@@ -1,8 +1,8 @@
1
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';
2
+ import { initialsOf } from './chunk-XQWRKHC2.js';
3
+ import { OfficeElement, controlStyles, definer, valueOn, flag, present, triState, withTokens } from './chunk-PCZJV6HQ.js';
4
4
  import { tok, OFFICE_TOKENS } from './chunk-HSJH6SUW.js';
5
- import { html, LitElement, render, nothing, svg } from 'lit';
5
+ import { html, LitElement, nothing, render, svg } from 'lit';
6
6
  import { ifDefined } from 'lit/directives/if-defined.js';
7
7
  import { repeat } from 'lit/directives/repeat.js';
8
8
  import { unsafeStatic, html as html$1 } from 'lit/static-html.js';
@@ -489,6 +489,89 @@ var OfficeUiRadio = class extends OfficeElement {
489
489
  }
490
490
  };
491
491
  var defineRadio = definer("office-ui-radio", () => OfficeUiRadio);
492
+ var OfficeUiCheckable = class extends OfficeElement {
493
+ static formAssociated = true;
494
+ static properties = {
495
+ checked: flag,
496
+ disabled: flag,
497
+ value: valueOn
498
+ };
499
+ internals;
500
+ defaultChecked = false;
501
+ constructor() {
502
+ super();
503
+ this.checked = false;
504
+ this.disabled = false;
505
+ this.value = "on";
506
+ try {
507
+ this.internals = this.attachInternals();
508
+ } catch {
509
+ this.internals = void 0;
510
+ }
511
+ this.addEventListener("click", (event) => {
512
+ if (present(this.disabled)) event.preventDefault();
513
+ else this.toggle();
514
+ });
515
+ this.addEventListener("keydown", (event) => {
516
+ const toggles = event.key === " " || this.semantics === "switch" && event.key === "Enter";
517
+ if (!toggles || present(this.disabled)) return;
518
+ event.preventDefault();
519
+ this.toggle();
520
+ });
521
+ }
522
+ connectedCallback() {
523
+ this.defaultChecked = present(this.checked);
524
+ super.connectedCallback();
525
+ }
526
+ formResetCallback() {
527
+ this.checked = this.defaultChecked;
528
+ }
529
+ formDisabledCallback(disabled) {
530
+ this.disabled = disabled;
531
+ }
532
+ toggle() {
533
+ this.checked = !present(this.checked);
534
+ this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
535
+ this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
536
+ }
537
+ /** The host carries the semantics: role, state, tab stop and form value. */
538
+ willUpdate() {
539
+ const checked = present(this.checked);
540
+ const disabled = present(this.disabled);
541
+ this.setAttribute("role", this.semantics);
542
+ this.setAttribute("aria-checked", String(checked));
543
+ this.setAttribute("aria-disabled", String(disabled));
544
+ this.setAttribute("tabindex", disabled ? "-1" : "0");
545
+ this.internals?.setFormValue?.(checked && !disabled ? this.value : null);
546
+ }
547
+ /** The tick, shown through CSS when `checked`. */
548
+ renderCheck() {
549
+ return html`<svg
550
+ viewBox="0 0 16 16"
551
+ fill="none"
552
+ stroke="currentColor"
553
+ stroke-width="2"
554
+ stroke-linecap="round"
555
+ stroke-linejoin="round"
556
+ aria-hidden="true"
557
+ >
558
+ <path d="m3 8 3.2 3.2L13 4.5"></path>
559
+ </svg>`;
560
+ }
561
+ };
562
+
563
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/checkbox.css?raw
564
+ 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";
565
+
566
+ // src/form/checkbox.ts
567
+ var OfficeUiCheckbox = class extends OfficeUiCheckable {
568
+ static styles = controlStyles(checkbox_default);
569
+ semantics = "checkbox";
570
+ render() {
571
+ return this.renderCheck();
572
+ }
573
+ };
574
+ var defineCheckbox = definer("office-ui-checkbox", () => OfficeUiCheckbox);
492
575
 
493
576
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-toggle.css?raw
494
577
  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";
@@ -552,7 +635,9 @@ var OfficeUiRibbonToggle = class extends OfficeElement {
552
635
  `;
553
636
  }
554
637
  };
555
- var defineRibbonToggle = definer("office-ui-ribbon-toggle", () => OfficeUiRibbonToggle);
638
+ var defineRibbonToggle = definer("office-ui-ribbon-toggle", () => OfficeUiRibbonToggle, [
639
+ defineCheckbox
640
+ ]);
556
641
 
557
642
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/search-field.css?raw
558
643
  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";
@@ -1000,7 +1085,9 @@ var OfficeUiRibbon = class extends OfficeElement {
1000
1085
  this.tabs = [];
1001
1086
  }
1002
1087
  get selected() {
1003
- return this.chosen ?? this.panels()[0]?.dataset.ribbonTab ?? "";
1088
+ const shown = this.shown();
1089
+ const chosen = shown.find((panel) => panel.dataset.ribbonTab === this.chosen);
1090
+ return chosen?.dataset.ribbonTab ?? shown[0]?.dataset.ribbonTab ?? "";
1004
1091
  }
1005
1092
  set selected(tab) {
1006
1093
  this.chosen = tab;
@@ -1020,9 +1107,13 @@ var OfficeUiRibbon = class extends OfficeElement {
1020
1107
  (child) => child instanceof HTMLElement && !!child.dataset.ribbonTab
1021
1108
  );
1022
1109
  }
1110
+ /** The panels that have a tab: those not marked `data-tab-hidden`. */
1111
+ shown() {
1112
+ return this.panels().filter((panel) => panel.dataset.tabHidden === void 0);
1113
+ }
1023
1114
  /** Read the tabs off the panels (on connection and whenever the slotted panels change). */
1024
1115
  build() {
1025
- this.tabs = this.panels().map((panel) => {
1116
+ this.tabs = this.shown().map((panel) => {
1026
1117
  const id = panel.dataset.ribbonTab;
1027
1118
  panel.setAttribute("role", "tabpanel");
1028
1119
  panel.setAttribute("aria-label", panel.dataset.label ?? id);
@@ -1055,9 +1146,23 @@ var OfficeUiRibbon = class extends OfficeElement {
1055
1146
  this.choose(next.dataset.tab);
1056
1147
  this.focusTab();
1057
1148
  }
1149
+ /** Rebuild the tab row when a panel's label or visibility flag changes. */
1150
+ watcher;
1058
1151
  connectedCallback() {
1059
1152
  this.build();
1060
1153
  super.connectedCallback();
1154
+ const Observer = this.ownerDocument.defaultView?.MutationObserver;
1155
+ this.watcher = Observer ? new Observer(() => this.build()) : void 0;
1156
+ this.watcher?.observe(this, {
1157
+ attributes: true,
1158
+ subtree: true,
1159
+ attributeFilter: ["data-tab-hidden", "data-label"]
1160
+ });
1161
+ }
1162
+ disconnectedCallback() {
1163
+ super.disconnectedCallback();
1164
+ this.watcher?.disconnect();
1165
+ this.watcher = void 0;
1061
1166
  }
1062
1167
  /** Exactly one panel shows. */
1063
1168
  updated() {
@@ -1110,6 +1215,201 @@ var OfficeUiRibbon = class extends OfficeElement {
1110
1215
  };
1111
1216
  var defineRibbon = definer("office-ui-ribbon", () => OfficeUiRibbon);
1112
1217
 
1218
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/button.css?raw
1219
+ 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";
1220
+
1221
+ // src/ribbon/button.ts
1222
+ var asAttribute = (value) => value === void 0 ? void 0 : String(value);
1223
+ var OfficeUiButton = class extends OfficeElement {
1224
+ static requestEvent = "office-command";
1225
+ static idAttribute = "command";
1226
+ static detailKey = "command";
1227
+ static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
1228
+ static styles = controlStyles(button_default);
1229
+ static properties = { disabled: flag, pressed: triState };
1230
+ /** Everything else is an attribute, as it always was (see `OfficeElement.watched`). */
1231
+ static watched = [
1232
+ "label",
1233
+ "icon",
1234
+ "command",
1235
+ "badge",
1236
+ "caret",
1237
+ "keyshortcuts",
1238
+ "size",
1239
+ "variant",
1240
+ "active",
1241
+ "tall",
1242
+ "icon-only",
1243
+ "expanded",
1244
+ "title"
1245
+ ];
1246
+ constructor() {
1247
+ super();
1248
+ this.disabled = false;
1249
+ }
1250
+ /** The registered icon to draw; a product subclass may map names onto its glyph set. */
1251
+ iconName() {
1252
+ return this.getAttribute("icon");
1253
+ }
1254
+ /** The tooltip: `title`, else the label of an icon-only button; a product may always show it. */
1255
+ tooltip(label, iconOnly) {
1256
+ return this.getAttribute("title") ?? (iconOnly ? label : "");
1257
+ }
1258
+ /** Whether the trailing menu chevron shows. */
1259
+ showsCaret() {
1260
+ const caret = this.getAttribute("caret");
1261
+ return caret !== null && caret !== "false";
1262
+ }
1263
+ onKeydown(event) {
1264
+ if ((event.key === " " || event.key === "Enter") && !event.ctrlKey && !event.metaKey && !event.altKey)
1265
+ event.stopPropagation();
1266
+ }
1267
+ onClick() {
1268
+ const { requestEvent, idAttribute, detailKey } = this.constructor;
1269
+ const id = this.getAttribute(idAttribute);
1270
+ if (!present(this.disabled) && id) this.fire(requestEvent, { [detailKey]: id });
1271
+ }
1272
+ render() {
1273
+ const label = this.getAttribute("label") ?? "";
1274
+ const iconOnly = this.hasAttribute("icon-only");
1275
+ const badge = this.getAttribute("badge") ?? "";
1276
+ const expanded = this.getAttribute("expanded");
1277
+ const tooltip = this.tooltip(label, iconOnly);
1278
+ return html`<button
1279
+ part="button"
1280
+ type="button"
1281
+ ?disabled=${present(this.disabled)}
1282
+ title=${tooltip || nothing}
1283
+ aria-label=${ifDefined(iconOnly && label ? label : void 0)}
1284
+ aria-keyshortcuts=${ifDefined(this.getAttribute("keyshortcuts") || void 0)}
1285
+ aria-pressed=${ifDefined(asAttribute(this.pressed))}
1286
+ aria-expanded=${ifDefined(expanded === null ? void 0 : String(expanded !== "false"))}
1287
+ @click=${this.onClick}
1288
+ @keydown=${this.onKeydown}
1289
+ >${glyph(this.iconName())}<span class="label" ?hidden=${iconOnly}
1290
+ >${label}${glyph("chevronDown", "caret", !this.showsCaret())}</span
1291
+ ><span class="badge" aria-hidden="true" ?hidden=${!badge}>${badge}</span></button
1292
+ >`;
1293
+ }
1294
+ };
1295
+ var defineButton = definer("office-ui-button", () => OfficeUiButton);
1296
+
1297
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-group.css?raw
1298
+ 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";
1299
+
1300
+ // src/ribbon/ribbon-group.ts
1301
+ function tokenPx(el, name, fallback) {
1302
+ const value = el.ownerDocument.defaultView?.getComputedStyle(el).getPropertyValue(name).trim();
1303
+ const px = value?.endsWith("px") ? parseFloat(value) : NaN;
1304
+ return Number.isFinite(px) ? px : fallback;
1305
+ }
1306
+ var OfficeUiRibbonGroup = class extends OfficeElement {
1307
+ static launcherEvent = "office-command";
1308
+ static collapseEvent = "office-ribbon-collapse-toggle";
1309
+ static styles = controlStyles(ribbon_group_default);
1310
+ /** The group is configured by attributes, as it always was (see `OfficeElement.watched`). */
1311
+ static watched = [
1312
+ "label",
1313
+ "launcher",
1314
+ "launcher-label",
1315
+ "launcher-disabled",
1316
+ "icon",
1317
+ "data-open"
1318
+ ];
1319
+ observer;
1320
+ /** The launcher's event detail; null emits nothing. */
1321
+ launcherDetail() {
1322
+ const command = this.getAttribute("launcher");
1323
+ return command ? { command } : null;
1324
+ }
1325
+ /** The registered icon on the collapsed face; a product subclass may map names. */
1326
+ iconName() {
1327
+ return this.getAttribute("icon") ?? "grid";
1328
+ }
1329
+ connectedCallback() {
1330
+ super.connectedCallback();
1331
+ this.observe();
1332
+ }
1333
+ disconnectedCallback() {
1334
+ super.disconnectedCallback();
1335
+ this.observer?.disconnect();
1336
+ }
1337
+ observe() {
1338
+ this.observer?.disconnect();
1339
+ if (!this.isConnected) return;
1340
+ const Observer = this.ownerDocument.defaultView?.ResizeObserver;
1341
+ if (Observer) this.observer ??= new Observer(() => this.measure());
1342
+ const slot = this.renderRoot.querySelector("slot");
1343
+ for (const el of slot?.assignedElements() ?? []) this.observer?.observe(el);
1344
+ this.measure();
1345
+ }
1346
+ measure() {
1347
+ const children = [...this.children];
1348
+ const stackable = children.filter((el) => {
1349
+ const gallery = el.matches('[mode="dropdown"]') ? el : el.querySelector('[mode="dropdown"]');
1350
+ return gallery !== null && !gallery.hasAttribute("data-command-large");
1351
+ });
1352
+ this.toggleAttribute(
1353
+ "data-stack",
1354
+ stackable.length > 1 && stackable.length === children.length
1355
+ );
1356
+ const tallest = Math.max(0, ...children.map((el) => el.getBoundingClientRect().height));
1357
+ if (tallest > 0)
1358
+ this.toggleAttribute(
1359
+ "data-compact-row",
1360
+ tallest <= tokenPx(this, "--office-ribbon-compact-row-max", 36)
1361
+ );
1362
+ }
1363
+ onLauncher() {
1364
+ if (this.hasAttribute("launcher-disabled")) return;
1365
+ const detail = this.launcherDetail();
1366
+ if (detail) this.fire(this.constructor.launcherEvent, detail);
1367
+ }
1368
+ /** The overflow controller listens for this and owns which popup is open. */
1369
+ onFace() {
1370
+ this.fire(this.constructor.collapseEvent, void 0);
1371
+ }
1372
+ willUpdate() {
1373
+ this.setAttribute("role", "group");
1374
+ this.setAttribute("aria-label", this.getAttribute("label") ?? "");
1375
+ }
1376
+ render() {
1377
+ const label = this.getAttribute("label") ?? "";
1378
+ const launcherLabel = this.getAttribute("launcher-label") ?? `${label} options`;
1379
+ return html`
1380
+ <div class="group">
1381
+ <button
1382
+ class="face"
1383
+ type="button"
1384
+ aria-haspopup="true"
1385
+ aria-label=${label}
1386
+ aria-expanded=${String(this.hasAttribute("data-open"))}
1387
+ title=${label}
1388
+ @click=${this.onFace}
1389
+ >${glyph(this.iconName(), "face-icon")}<span>${label}</span>${glyph("chevronDown", "chev")}</button
1390
+ >
1391
+ <div class="row">
1392
+ <slot @slotchange=${this.observe}></slot>
1393
+ </div>
1394
+ <div class="foot">
1395
+ <span class="caption">${label}</span>
1396
+ <button
1397
+ class="launcher"
1398
+ type="button"
1399
+ aria-label=${launcherLabel}
1400
+ title=${launcherLabel}
1401
+ ?hidden=${!this.hasAttribute("launcher")}
1402
+ ?disabled=${this.hasAttribute("launcher-disabled")}
1403
+ @click=${this.onLauncher}
1404
+ >${glyph("launcher", "launcher-icon")}</button
1405
+ >
1406
+ </div>
1407
+ </div>
1408
+ `;
1409
+ }
1410
+ };
1411
+ var defineRibbonGroup = definer("office-ui-ribbon-group", () => OfficeUiRibbonGroup);
1412
+
1113
1413
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-section.css?raw
1114
1414
  var ribbon_section_default = ":host {\n display: inline-flex;\n align-items: stretch;\n}\n::slotted(*) {\n flex: none;\n}\n";
1115
1415
 
@@ -1212,7 +1512,11 @@ var OfficeUiRibbonSection = class extends OfficeElement {
1212
1512
  }
1213
1513
  }
1214
1514
  };
1215
- var defineRibbonSection = definer("office-ui-ribbon-section", () => OfficeUiRibbonSection);
1515
+ var defineRibbonSection = definer(
1516
+ "office-ui-ribbon-section",
1517
+ () => OfficeUiRibbonSection,
1518
+ [defineRibbonGroup, defineButton]
1519
+ );
1216
1520
 
1217
1521
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/gallery.css?raw
1218
1522
  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";
@@ -1644,271 +1948,109 @@ var defaultStorage = () => {
1644
1948
  return void 0;
1645
1949
  }
1646
1950
  };
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
- });
1951
+ function readOfficeProfile(storage = defaultStorage(), key = OFFICE_PROFILE_STORAGE_KEY) {
1952
+ try {
1953
+ const raw = storage?.getItem(key);
1954
+ return raw ? sanitizeOfficeProfile(JSON.parse(raw)) : { ...DEFAULT_OFFICE_PROFILE };
1955
+ } catch {
1956
+ return { ...DEFAULT_OFFICE_PROFILE };
1858
1957
  }
1859
- connectedCallback() {
1860
- this.defaultChecked = present(this.checked);
1861
- super.connectedCallback();
1958
+ }
1959
+ function writeOfficeProfile(profile, storage = defaultStorage(), key = OFFICE_PROFILE_STORAGE_KEY) {
1960
+ try {
1961
+ storage?.setItem(key, JSON.stringify(sanitizeOfficeProfile(profile)));
1962
+ } catch {
1862
1963
  }
1863
- formResetCallback() {
1864
- this.checked = this.defaultChecked;
1964
+ }
1965
+ function clearOfficeProfile(storage = defaultStorage(), key = OFFICE_PROFILE_STORAGE_KEY) {
1966
+ try {
1967
+ storage?.removeItem(key);
1968
+ } catch {
1865
1969
  }
1866
- formDisabledCallback(disabled) {
1867
- this.disabled = disabled;
1970
+ }
1971
+
1972
+ // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/chrome/account.css?raw
1973
+ 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";
1974
+
1975
+ // src/chrome/account.ts
1976
+ var OfficeUiAccount = class extends OfficeElement {
1977
+ static styles = controlStyles(account_default);
1978
+ current = { ...DEFAULT_OFFICE_PROFILE };
1979
+ get profile() {
1980
+ return { ...this.current };
1868
1981
  }
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 }));
1982
+ set profile(value) {
1983
+ this.current = sanitizeOfficeProfile(value);
1984
+ this.requestUpdate("profile");
1873
1985
  }
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);
1986
+ change(next) {
1987
+ this.profile = next;
1988
+ this.fire("office-profile-change", { profile: this.profile });
1883
1989
  }
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>`;
1990
+ onSwatchKey(event) {
1991
+ const step = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[event.key];
1992
+ if (!step) return;
1993
+ event.preventDefault();
1994
+ const colors = OFFICE_AVATAR_SWATCHES;
1995
+ const at = Math.max(0, colors.indexOf(this.current.avatarColor));
1996
+ const color = colors[(at + step + colors.length) % colors.length];
1997
+ this.change({ ...this.current, avatarColor: color });
1998
+ this.renderRoot.querySelector(`[data-color="${color}"]`)?.focus();
1897
1999
  }
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
2000
  render() {
1908
- return this.renderCheck();
2001
+ const { displayName, avatarColor } = this.current;
2002
+ const known = OFFICE_AVATAR_SWATCHES.includes(avatarColor);
2003
+ return html`
2004
+ <h2>User Information</h2>
2005
+ <div class="who">
2006
+ <div class="avatar" aria-hidden="true" style="background:${avatarColor}"
2007
+ >${profileInitials(this.current)}</div
2008
+ >
2009
+ <div>
2010
+ <div class="name">${displayName || "No display name"}</div>
2011
+ <p class="note">Stored on this device only. Never sent anywhere.</p>
2012
+ </div>
2013
+ </div>
2014
+ <label>
2015
+ Display name
2016
+ <input
2017
+ type="text"
2018
+ maxlength="64"
2019
+ autocomplete="name"
2020
+ .value=${displayName}
2021
+ @change=${(event) => this.change({
2022
+ ...this.current,
2023
+ displayName: event.target.value.trim()
2024
+ })}
2025
+ />
2026
+ </label>
2027
+ <div id="color-label">Avatar colour</div>
2028
+ <div
2029
+ class="swatches"
2030
+ role="radiogroup"
2031
+ aria-labelledby="color-label"
2032
+ @keydown=${this.onSwatchKey}
2033
+ >
2034
+ ${OFFICE_AVATAR_SWATCHES.map((color, index) => {
2035
+ const checked = color === avatarColor;
2036
+ return html`<button
2037
+ type="button"
2038
+ role="radio"
2039
+ aria-label=${color}
2040
+ aria-checked=${String(checked)}
2041
+ tabindex=${checked || !known && index === 0 ? 0 : -1}
2042
+ data-color=${color}
2043
+ style="background:${color}"
2044
+ @click=${() => this.change({ ...this.current, avatarColor: color })}
2045
+ ></button>`;
2046
+ })}
2047
+ </div>
2048
+ <slot name="sign-in"></slot>
2049
+ <slot></slot>
2050
+ `;
1909
2051
  }
1910
2052
  };
1911
- var defineCheckbox = definer("office-ui-checkbox", () => OfficeUiCheckbox);
2053
+ var defineAccount = definer("office-ui-account", () => OfficeUiAccount);
1912
2054
 
1913
2055
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/switch.css?raw
1914
2056
  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";
@@ -1933,7 +2075,8 @@ var OfficeUiDialog = class extends OfficeElement {
1933
2075
  static properties = {
1934
2076
  open: flag,
1935
2077
  heading: { type: String },
1936
- dismissible: { type: String }
2078
+ dismissible: { type: String },
2079
+ closeLabel: { type: String, attribute: "close-label" }
1937
2080
  };
1938
2081
  returnFocus = null;
1939
2082
  constructor() {
@@ -1941,6 +2084,7 @@ var OfficeUiDialog = class extends OfficeElement {
1941
2084
  this.open = false;
1942
2085
  this.heading = "";
1943
2086
  this.dismissible = null;
2087
+ this.closeLabel = null;
1944
2088
  this.addEventListener("keydown", (event) => this.onKey(event));
1945
2089
  }
1946
2090
  show() {
@@ -2010,7 +2154,7 @@ var OfficeUiDialog = class extends OfficeElement {
2010
2154
  <button
2011
2155
  class="close"
2012
2156
  type="button"
2013
- aria-label="Close"
2157
+ aria-label=${this.closeLabel || "Close"}
2014
2158
  @click=${() => this.requestClose("close-button")}
2015
2159
  >${glyph("close", "icon")}</button
2016
2160
  >
@@ -3060,123 +3204,11 @@ var OfficeUiOptionsDialog = class extends OfficeElement {
3060
3204
  `;
3061
3205
  }
3062
3206
  };
3063
- var defineOptionsDialog = definer("office-ui-options-dialog", () => OfficeUiOptionsDialog);
3064
-
3065
- // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-group.css?raw
3066
- 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";
3067
-
3068
- // src/ribbon/ribbon-group.ts
3069
- function tokenPx(el, name, fallback) {
3070
- const value = el.ownerDocument.defaultView?.getComputedStyle(el).getPropertyValue(name).trim();
3071
- const px = value?.endsWith("px") ? parseFloat(value) : NaN;
3072
- return Number.isFinite(px) ? px : fallback;
3073
- }
3074
- var OfficeUiRibbonGroup = class extends OfficeElement {
3075
- static launcherEvent = "office-command";
3076
- static collapseEvent = "office-ribbon-collapse-toggle";
3077
- static styles = controlStyles(ribbon_group_default);
3078
- /** The group is configured by attributes, as it always was (see `OfficeElement.watched`). */
3079
- static watched = [
3080
- "label",
3081
- "launcher",
3082
- "launcher-label",
3083
- "launcher-disabled",
3084
- "icon",
3085
- "data-open"
3086
- ];
3087
- observer;
3088
- /** The launcher's event detail; null emits nothing. */
3089
- launcherDetail() {
3090
- const command = this.getAttribute("launcher");
3091
- return command ? { command } : null;
3092
- }
3093
- /** The registered icon on the collapsed face; a product subclass may map names. */
3094
- iconName() {
3095
- return this.getAttribute("icon") ?? "grid";
3096
- }
3097
- connectedCallback() {
3098
- super.connectedCallback();
3099
- this.observe();
3100
- }
3101
- disconnectedCallback() {
3102
- super.disconnectedCallback();
3103
- this.observer?.disconnect();
3104
- }
3105
- observe() {
3106
- this.observer?.disconnect();
3107
- if (!this.isConnected) return;
3108
- const Observer = this.ownerDocument.defaultView?.ResizeObserver;
3109
- if (Observer) this.observer ??= new Observer(() => this.measure());
3110
- const slot = this.renderRoot.querySelector("slot");
3111
- for (const el of slot?.assignedElements() ?? []) this.observer?.observe(el);
3112
- this.measure();
3113
- }
3114
- measure() {
3115
- const children = [...this.children];
3116
- const stackable = children.filter((el) => {
3117
- const gallery = el.matches('[mode="dropdown"]') ? el : el.querySelector('[mode="dropdown"]');
3118
- return gallery !== null && !gallery.hasAttribute("data-command-large");
3119
- });
3120
- this.toggleAttribute(
3121
- "data-stack",
3122
- stackable.length > 1 && stackable.length === children.length
3123
- );
3124
- const tallest = Math.max(0, ...children.map((el) => el.getBoundingClientRect().height));
3125
- if (tallest > 0)
3126
- this.toggleAttribute(
3127
- "data-compact-row",
3128
- tallest <= tokenPx(this, "--office-ribbon-compact-row-max", 36)
3129
- );
3130
- }
3131
- onLauncher() {
3132
- if (this.hasAttribute("launcher-disabled")) return;
3133
- const detail = this.launcherDetail();
3134
- if (detail) this.fire(this.constructor.launcherEvent, detail);
3135
- }
3136
- /** The overflow controller listens for this and owns which popup is open. */
3137
- onFace() {
3138
- this.fire(this.constructor.collapseEvent, void 0);
3139
- }
3140
- willUpdate() {
3141
- this.setAttribute("role", "group");
3142
- this.setAttribute("aria-label", this.getAttribute("label") ?? "");
3143
- }
3144
- render() {
3145
- const label = this.getAttribute("label") ?? "";
3146
- const launcherLabel = this.getAttribute("launcher-label") ?? `${label} options`;
3147
- return html`
3148
- <div class="group">
3149
- <button
3150
- class="face"
3151
- type="button"
3152
- aria-haspopup="true"
3153
- aria-label=${label}
3154
- aria-expanded=${String(this.hasAttribute("data-open"))}
3155
- title=${label}
3156
- @click=${this.onFace}
3157
- >${glyph(this.iconName(), "face-icon")}<span>${label}</span>${glyph("chevronDown", "chev")}</button
3158
- >
3159
- <div class="row">
3160
- <slot @slotchange=${this.observe}></slot>
3161
- </div>
3162
- <div class="foot">
3163
- <span class="caption">${label}</span>
3164
- <button
3165
- class="launcher"
3166
- type="button"
3167
- aria-label=${launcherLabel}
3168
- title=${launcherLabel}
3169
- ?hidden=${!this.hasAttribute("launcher")}
3170
- ?disabled=${this.hasAttribute("launcher-disabled")}
3171
- @click=${this.onLauncher}
3172
- >${glyph("launcher", "launcher-icon")}</button
3173
- >
3174
- </div>
3175
- </div>
3176
- `;
3177
- }
3178
- };
3179
- var defineRibbonGroup = definer("office-ui-ribbon-group", () => OfficeUiRibbonGroup);
3207
+ var defineOptionsDialog = definer(
3208
+ "office-ui-options-dialog",
3209
+ () => OfficeUiOptionsDialog,
3210
+ [defineDialog]
3211
+ );
3180
3212
 
3181
3213
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/ribbon/ribbon-stack.css?raw
3182
3214
  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";
@@ -3681,6 +3713,7 @@ var OfficeUiStatusBar = class extends OfficeElement {
3681
3713
  aria-label=${spec.label}
3682
3714
  aria-pressed=${ifDefined(spec.pressed === void 0 ? void 0 : String(spec.pressed))}
3683
3715
  ?hidden=${spec.hidden === true}
3716
+ ?disabled=${spec.disabled === true}
3684
3717
  @click=${() => this.activate(spec.id)}
3685
3718
  @keydown=${this.onKeydown}
3686
3719
  >${glyph(spec.icon, "icon")}${spec.text ? html`<span class="label">${spec.text}</span>` : ""}</button
@@ -4219,6 +4252,7 @@ var OfficeUiTitleBar = class extends OfficeElement {
4219
4252
  </span>
4220
4253
  ${this.searchBox()}
4221
4254
  <div class="end">
4255
+ <slot name="actions"></slot>
4222
4256
  <slot name="collaboration"></slot>
4223
4257
  <slot name="account"></slot>
4224
4258
  </div>
@@ -4226,7 +4260,10 @@ var OfficeUiTitleBar = class extends OfficeElement {
4226
4260
  `;
4227
4261
  }
4228
4262
  };
4229
- var defineTitleBar = definer("office-ui-title-bar", () => OfficeUiTitleBar);
4263
+ var defineTitleBar = definer("office-ui-title-bar", () => OfficeUiTitleBar, [
4264
+ defineSearchField,
4265
+ defineSwitch
4266
+ ]);
4230
4267
 
4231
4268
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/form/zoom-slider.css?raw
4232
4269
  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";
@@ -1,6 +1,6 @@
1
1
  import { registerIcon, getIcon } from './chunk-HZ422LTR.js';
2
- import { initialsOf } from './chunk-E47762NY.js';
3
- import { definer, OfficeElement, controlStyles } from './chunk-5A4REH6T.js';
2
+ import { initialsOf } from './chunk-XQWRKHC2.js';
3
+ import { definer, OfficeElement, controlStyles } from './chunk-PCZJV6HQ.js';
4
4
  import { html, nothing, unsafeCSS, svg } from 'lit';
5
5
  import { repeat } from 'lit/directives/repeat.js';
6
6
  import { live } from 'lit/directives/live.js';
@@ -1,4 +1,4 @@
1
- import { OfficeElement, controlStyles, definer } from './chunk-5A4REH6T.js';
1
+ import { OfficeElement, controlStyles, definer } from './chunk-PCZJV6HQ.js';
2
2
  import { html } from 'lit';
3
3
 
4
4
  // raw-css:/home/runner/work/ooxml/ooxml/packages/ui/src/presence/presence.css?raw
package/dist/controls.js CHANGED
@@ -1,5 +1,5 @@
1
- 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, nextEnabledIndex, parseSvgPreview, profileInitials, readOfficeProfile, registerControls, rulerDivisions, sanitizeOfficeProfile, typeAheadIndex, writeOfficeProfile } from './chunk-7ZE4WHUW.js';
1
+ 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, nextEnabledIndex, parseSvgPreview, profileInitials, readOfficeProfile, registerControls, rulerDivisions, sanitizeOfficeProfile, typeAheadIndex, writeOfficeProfile } from './chunk-SXCANAX7.js';
2
2
  import './chunk-HZ422LTR.js';
3
- import './chunk-E47762NY.js';
4
- import './chunk-5A4REH6T.js';
3
+ import './chunk-XQWRKHC2.js';
4
+ import './chunk-PCZJV6HQ.js';
5
5
  import './chunk-HSJH6SUW.js';
@@ -29,10 +29,16 @@ export declare class OfficeUiDialog extends OfficeElement {
29
29
  dismissible: {
30
30
  type: StringConstructor;
31
31
  };
32
+ closeLabel: {
33
+ type: StringConstructor;
34
+ attribute: string;
35
+ };
32
36
  };
33
37
  open: boolean;
34
38
  heading: string;
35
39
  dismissible: string | null;
40
+ /** Name of the close button (English by default), so a product can translate it. */
41
+ closeLabel: string | null;
36
42
  private returnFocus;
37
43
  constructor();
38
44
  show(): void;
package/dist/index.js CHANGED
@@ -1,15 +1,15 @@
1
- import { installOfficeUiTheme } from './chunk-2X6QYQ3Q.js';
2
- export { THEME_CSS, THEME_STYLE_ID, installOfficeUiTheme } from './chunk-2X6QYQ3Q.js';
3
- import { registerControls } from './chunk-7ZE4WHUW.js';
4
- 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 } from './chunk-7ZE4WHUW.js';
5
- import { defineSmartArt } from './chunk-3MDKXKKQ.js';
6
- export { OfficeUiSmartArt, defineSmartArt, renderDiagramDrawing } from './chunk-3MDKXKKQ.js';
7
- import { TEAMS_TAGS, registerTeams } from './chunk-HNGJI5EZ.js';
8
- export { EMOJI, OfficeUiAppRail, OfficeUiAvatar, OfficeUiCallControls, OfficeUiCallGrid, OfficeUiChannelList, OfficeUiChatComposer, OfficeUiChatList, OfficeUiPrejoin, QUICK_REACTIONS, TEAMS_TAGS, TeamsElement, colorFor, dayLabel, defineAppRail, defineAvatar, defineCallControls, defineCallGrid, defineChannelList, defineChatComposer, defineChatList, definePrejoin, formatElapsed, formatSize, formatTime, icon, registerTeams, withStyles } from './chunk-HNGJI5EZ.js';
1
+ import { installOfficeUiTheme } from './chunk-PAPKST6W.js';
2
+ export { THEME_CSS, THEME_STYLE_ID, installOfficeUiTheme, shadcnBridge, themeBridge } from './chunk-PAPKST6W.js';
3
+ import { registerControls } from './chunk-SXCANAX7.js';
4
+ 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 } from './chunk-SXCANAX7.js';
5
+ import { defineSmartArt } from './chunk-RVZAYXTI.js';
6
+ export { OfficeUiSmartArt, defineSmartArt, renderDiagramDrawing } from './chunk-RVZAYXTI.js';
7
+ import { TEAMS_TAGS, registerTeams } from './chunk-TCBUC6BI.js';
8
+ export { EMOJI, OfficeUiAppRail, OfficeUiAvatar, OfficeUiCallControls, OfficeUiCallGrid, OfficeUiChannelList, OfficeUiChatComposer, OfficeUiChatList, OfficeUiPrejoin, QUICK_REACTIONS, TEAMS_TAGS, TeamsElement, colorFor, dayLabel, defineAppRail, defineAvatar, defineCallControls, defineCallGrid, defineChannelList, defineChatComposer, defineChatList, definePrejoin, formatElapsed, formatSize, formatTime, icon, registerTeams, withStyles } from './chunk-TCBUC6BI.js';
9
9
  export { createIconSvg, getIcon, listIcons, paintIcon, registerIcon } from './chunk-HZ422LTR.js';
10
- import { definePresence } from './chunk-E47762NY.js';
11
- export { OfficeUiPresence, definePresence, initialsOf, participantsFromAwareness } from './chunk-E47762NY.js';
12
- export { CONTRACT_REVISION, OfficeElement, controlStyles, flag, readFlag, triState, valueOn, withTokens } from './chunk-5A4REH6T.js';
10
+ import { definePresence } from './chunk-XQWRKHC2.js';
11
+ export { OfficeUiPresence, definePresence, initialsOf, participantsFromAwareness } from './chunk-XQWRKHC2.js';
12
+ export { CONTRACT_REVISION, OfficeElement, controlStyles, flag, readFlag, triState, valueOn, withTokens } from './chunk-PCZJV6HQ.js';
13
13
  export { COMPACT, FOCUS_RING, OFFICE_TOKENS, TOUCH, tok } from './chunk-HSJH6SUW.js';
14
14
 
15
15
  // src/index.ts
package/dist/presence.js CHANGED
@@ -1,3 +1,3 @@
1
- export { OfficeUiPresence, definePresence, initialsOf, participantsFromAwareness } from './chunk-E47762NY.js';
2
- import './chunk-5A4REH6T.js';
1
+ export { OfficeUiPresence, definePresence, initialsOf, participantsFromAwareness } from './chunk-XQWRKHC2.js';
2
+ import './chunk-PCZJV6HQ.js';
3
3
  import './chunk-HSJH6SUW.js';
@@ -12,8 +12,12 @@ export type Definer = ((registry?: CustomElementRegistry) => void) & {
12
12
  };
13
13
  /** The registry of the current window, or undefined where there is no DOM (SSR). */
14
14
  export declare function browserRegistry(): CustomElementRegistry | undefined;
15
- /** Wrap a per-tag `make` factory into a public, SSR-safe, idempotent definer. */
16
- export declare function definer(tag: string, make: () => CustomElementConstructor): Definer;
15
+ /**
16
+ * Wrap a per-tag `make` factory into a public, SSR-safe, idempotent definer. `requires` are the
17
+ * elements this one renders inside its own template; defining the composite defines them first, so
18
+ * a product that registers only the element it uses never gets a silently inert inner control.
19
+ */
20
+ export declare function definer(tag: string, make: () => CustomElementConstructor, requires?: readonly Definer[]): Definer;
17
21
  /** Bubbling, composed event: crosses shadow boundaries like the native ones do. */
18
22
  export declare function emit<T>(host: HTMLElement, type: string, detail: T, cancelable?: boolean): boolean;
19
23
  /**
@@ -15,7 +15,9 @@ interface RibbonTab {
15
15
  * optionally `data-tab-keytip`; the element builds the tabs from them, keeps exactly one panel
16
16
  * visible and moves between tabs with the arrow keys, Home and End. Slots: `quick-access`,
17
17
  * `search`, `end`. Attributes: `selected`, `label` (tab list name), `file-label` (default "File"),
18
- * `no-file`, `file-expanded`, `file-keytip`. Events: `office-ribbon-select` `{ tab }`
18
+ * `no-file`, `file-expanded`, `file-keytip`. A panel with `data-tab-hidden` keeps its content but has
19
+ * no tab (a contextual tab, or one a customisation removed); if it was selected, the first
20
+ * remaining tab takes over. Events: `office-ribbon-select` `{ tab }`
19
21
  * (cancelable) and `office-ribbon-file` when File is activated.
20
22
  */
21
23
  export declare class OfficeUiRibbon extends OfficeElement {
@@ -68,12 +70,17 @@ export declare class OfficeUiRibbon extends OfficeElement {
68
70
  /** Focus the selected tab. */
69
71
  focusTab(): void;
70
72
  private panels;
73
+ /** The panels that have a tab: those not marked `data-tab-hidden`. */
74
+ private shown;
71
75
  /** Read the tabs off the panels (on connection and whenever the slotted panels change). */
72
76
  private build;
73
77
  private choose;
74
78
  private onTabClick;
75
79
  private onKey;
80
+ /** Rebuild the tab row when a panel's label or visibility flag changes. */
81
+ private watcher;
76
82
  connectedCallback(): void;
83
+ disconnectedCallback(): void;
77
84
  /** Exactly one panel shows. */
78
85
  protected updated(): void;
79
86
  protected render(): import("lit").TemplateResult<1>;
package/dist/smartart.js CHANGED
@@ -1,3 +1,3 @@
1
- export { OfficeUiSmartArt, defineSmartArt, renderDiagramDrawing } from './chunk-3MDKXKKQ.js';
2
- import './chunk-5A4REH6T.js';
1
+ export { OfficeUiSmartArt, defineSmartArt, renderDiagramDrawing } from './chunk-RVZAYXTI.js';
2
+ import './chunk-PCZJV6HQ.js';
3
3
  import './chunk-HSJH6SUW.js';
package/dist/teams.js CHANGED
@@ -1,5 +1,5 @@
1
- export { EMOJI, OfficeUiAppRail, OfficeUiAvatar, OfficeUiCallControls, OfficeUiCallGrid, OfficeUiChannelList, OfficeUiChatComposer, OfficeUiChatList, OfficeUiPrejoin, QUICK_REACTIONS, TEAMS_TAGS, TeamsElement, colorFor, dayLabel, defineAppRail, defineAvatar, defineCallControls, defineCallGrid, defineChannelList, defineChatComposer, defineChatList, definePrejoin, formatElapsed, formatSize, formatTime, icon, registerTeams, withStyles } from './chunk-HNGJI5EZ.js';
1
+ export { EMOJI, OfficeUiAppRail, OfficeUiAvatar, OfficeUiCallControls, OfficeUiCallGrid, OfficeUiChannelList, OfficeUiChatComposer, OfficeUiChatList, OfficeUiPrejoin, QUICK_REACTIONS, TEAMS_TAGS, TeamsElement, colorFor, dayLabel, defineAppRail, defineAvatar, defineCallControls, defineCallGrid, defineChannelList, defineChatComposer, defineChatList, definePrejoin, formatElapsed, formatSize, formatTime, icon, registerTeams, withStyles } from './chunk-TCBUC6BI.js';
2
2
  import './chunk-HZ422LTR.js';
3
- import './chunk-E47762NY.js';
4
- import './chunk-5A4REH6T.js';
3
+ import './chunk-XQWRKHC2.js';
4
+ import './chunk-PCZJV6HQ.js';
5
5
  import './chunk-HSJH6SUW.js';
@@ -0,0 +1,25 @@
1
+ import type { OfficeToken } from './tokens.js';
2
+ /**
3
+ * Feeds the `--office-*` tokens from a product's own theme, so the shared elements follow it.
4
+ * Products (docx, xlsx, pptx, visio) already name their theme after shadcn/ui
5
+ * (`--<prefix>foreground`, `--<prefix>muted-foreground`, ...); `shadcnBridge('--dve-')` is the
6
+ * whole bridge for such a product. `themeBridge` takes any mapping for the rest.
7
+ *
8
+ * The CSS is declared on the selector you give (usually `:host` inside a shadow root, or the
9
+ * product's root element), not on `:root`: a product's colours often resolve where they are read,
10
+ * so a nested theme, a dark scheme or a host override keeps working. Pure strings, no DOM.
11
+ */
12
+ export type ThemeBridgeMap = Partial<Record<OfficeToken, string>>;
13
+ /** `selector { --office-x: value; ... }` for a mapping; entries without a value are skipped. */
14
+ export declare function themeBridge(selector: string, map: ThemeBridgeMap): string;
15
+ export interface ShadcnBridgeOptions {
16
+ /** Extra or overriding `--office-*` values, for example `{ '--office-font': '...' }`. */
17
+ extra?: ThemeBridgeMap;
18
+ /** A literal each theme variable falls back to when the product does not define it. */
19
+ fallbacks?: Partial<Record<string, string>>;
20
+ }
21
+ /**
22
+ * The bridge for a product whose theme variables are `<prefix><shadcn-name>`
23
+ * (`prefix` includes the dashes, for example `'--dve-'` or `'--pptx-'`).
24
+ */
25
+ export declare function shadcnBridge(selector: string, prefix: string, options?: ShadcnBridgeOptions): string;
package/dist/theme.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { shadcnBridge, themeBridge, type ShadcnBridgeOptions, type ThemeBridgeMap, } from './theme-bridge.js';
1
2
  export { COMPACT, FOCUS_RING, OFFICE_TOKENS, tok, TOUCH, type OfficeToken } from './tokens.js';
2
3
  /**
3
4
  * Theme-token foundation. Custom properties cross shadow boundaries, so one `<style>` in the
package/dist/theme.js CHANGED
@@ -1,2 +1,2 @@
1
- export { THEME_CSS, THEME_STYLE_ID, installOfficeUiTheme } from './chunk-2X6QYQ3Q.js';
1
+ export { THEME_CSS, THEME_STYLE_ID, installOfficeUiTheme, shadcnBridge, themeBridge } from './chunk-PAPKST6W.js';
2
2
  export { COMPACT, FOCUS_RING, OFFICE_TOKENS, TOUCH, tok } from './chunk-HSJH6SUW.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ooxml-ui",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Shared, format-neutral web components and styles for the Office viewers (Word, PowerPoint, Excel)",
5
5
  "keywords": [
6
6
  "custom-elements",
@@ -82,7 +82,7 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "lit": "^3.3.3",
85
- "ooxml-core": "^0.15.0"
85
+ "ooxml-core": "^0.17.0"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@types/jsdom": "^30.0.0",