ooxml-ui 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/{chunk-LCHYMVBZ.js → chunk-L5FYB6M7.js} +135 -1
- package/dist/controls.d.ts +1 -0
- package/dist/controls.js +1 -1
- package/dist/index.js +2 -2
- package/dist/keytips.d.ts +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,11 @@ with an incompatible build of this package.
|
|
|
77
77
|
| `office-ui-smartart` | `drawing` (core `DiagramDrawing`), `schemeColors`, `label` | `office-smartart-render` (render report) |
|
|
78
78
|
| `office-ui-icon` | `name`, `label` | none |
|
|
79
79
|
|
|
80
|
+
`attachKeyTips(scope)` (from `ooxml-ui/controls`) adds Office KeyTips to any element or shadow
|
|
81
|
+
root: press and release Alt to show badges on controls marked `data-keytip="H"`, type a badge to
|
|
82
|
+
activate it, and use `data-keytip-panel` / `data-keytip-level` for a tab's second level. Escape
|
|
83
|
+
steps back; disabled controls are dimmed and never run.
|
|
84
|
+
|
|
80
85
|
Setting a property never emits an event; only user activation does. Controls honour
|
|
81
86
|
`prefers-color-scheme`, `forced-colors: active` (system colours and a visible focus ring) and
|
|
82
87
|
coarse pointers (44px targets through `--office-target-size`). Tokens are the `--office-*`
|
|
@@ -1732,6 +1732,140 @@ var defineZoomSlider = definer("office-ui-zoom-slider", () => {
|
|
|
1732
1732
|
return OfficeUiZoomSlider;
|
|
1733
1733
|
});
|
|
1734
1734
|
|
|
1735
|
+
// src/keytips.ts
|
|
1736
|
+
var BADGE_CSS = "position:fixed;inset:auto;margin:0;padding:0;border:0;background:transparent;overflow:visible;pointer-events:none";
|
|
1737
|
+
var TIP_CSS = 'position:fixed;transform:translate(-50%,-30%);min-width:14px;padding:1px 4px;border:1px solid #8a8886;border-radius:2px;background:#fff;color:#1f1f1f;font:600 11px/14px "Segoe UI",system-ui,sans-serif;text-align:center;box-shadow:0 1px 3px rgb(0 0 0 / 25%)';
|
|
1738
|
+
var isDisabled = (el) => el.hasAttribute("disabled") || el.getAttribute("aria-disabled") === "true";
|
|
1739
|
+
var visible = (el) => {
|
|
1740
|
+
const box = el.getBoundingClientRect();
|
|
1741
|
+
return box.width > 0 && box.height > 0;
|
|
1742
|
+
};
|
|
1743
|
+
function activate(el) {
|
|
1744
|
+
const inner = el.shadowRoot?.querySelector(".main, button");
|
|
1745
|
+
(inner ?? el).click();
|
|
1746
|
+
}
|
|
1747
|
+
function attachKeyTips(scope) {
|
|
1748
|
+
const doc = (scope instanceof ShadowRoot ? scope.host : scope).ownerDocument;
|
|
1749
|
+
const view = doc.defaultView;
|
|
1750
|
+
const layer = doc.createElement("div");
|
|
1751
|
+
layer.setAttribute("aria-hidden", "true");
|
|
1752
|
+
layer.style.cssText = BADGE_CSS;
|
|
1753
|
+
const topLayer = typeof layer.showPopover === "function";
|
|
1754
|
+
if (topLayer) layer.setAttribute("popover", "manual");
|
|
1755
|
+
else layer.hidden = true;
|
|
1756
|
+
scope.append(layer);
|
|
1757
|
+
let levels = [];
|
|
1758
|
+
let typed = "";
|
|
1759
|
+
let altAlone = false;
|
|
1760
|
+
const tipsAt = (container) => [...(container ?? scope).querySelectorAll("[data-keytip]")].filter((el) => {
|
|
1761
|
+
const owner = el.parentElement?.closest("[data-keytip-level]") ?? null;
|
|
1762
|
+
return owner === container && visible(el);
|
|
1763
|
+
});
|
|
1764
|
+
const current = () => tipsAt(levels.at(-1) ?? null);
|
|
1765
|
+
const render = () => {
|
|
1766
|
+
if (!handle.active) return;
|
|
1767
|
+
layer.replaceChildren(
|
|
1768
|
+
...current().map((el) => {
|
|
1769
|
+
const box = el.getBoundingClientRect();
|
|
1770
|
+
const tip = doc.createElement("span");
|
|
1771
|
+
tip.textContent = el.dataset.keytip;
|
|
1772
|
+
tip.style.cssText = `${TIP_CSS};left:${Math.round(box.left + box.width / 2)}px;top:${Math.round(box.bottom - 4)}px;opacity:${isDisabled(el) || typed && !el.dataset.keytip.startsWith(typed) ? "0.45" : "1"}`;
|
|
1773
|
+
return tip;
|
|
1774
|
+
})
|
|
1775
|
+
);
|
|
1776
|
+
};
|
|
1777
|
+
const show = () => {
|
|
1778
|
+
if (topLayer && !layer.matches(":popover-open")) layer.showPopover?.();
|
|
1779
|
+
else layer.hidden = false;
|
|
1780
|
+
render();
|
|
1781
|
+
};
|
|
1782
|
+
const stop = () => {
|
|
1783
|
+
levels = [];
|
|
1784
|
+
typed = "";
|
|
1785
|
+
if (topLayer) {
|
|
1786
|
+
try {
|
|
1787
|
+
layer.hidePopover?.();
|
|
1788
|
+
} catch {
|
|
1789
|
+
}
|
|
1790
|
+
} else layer.hidden = true;
|
|
1791
|
+
layer.replaceChildren();
|
|
1792
|
+
handle.active = false;
|
|
1793
|
+
};
|
|
1794
|
+
const start = () => {
|
|
1795
|
+
levels = [];
|
|
1796
|
+
typed = "";
|
|
1797
|
+
handle.active = true;
|
|
1798
|
+
show();
|
|
1799
|
+
};
|
|
1800
|
+
const choose = (el) => {
|
|
1801
|
+
typed = "";
|
|
1802
|
+
if (isDisabled(el)) return render();
|
|
1803
|
+
const panel = el.dataset.keytipPanel;
|
|
1804
|
+
activate(el);
|
|
1805
|
+
const next = panel ? [...scope.querySelectorAll("[data-keytip-level]")].find((el2) => el2.id === panel) ?? null : null;
|
|
1806
|
+
if (next) {
|
|
1807
|
+
levels.push(next);
|
|
1808
|
+
view?.requestAnimationFrame ? view.requestAnimationFrame(render) : render();
|
|
1809
|
+
} else stop();
|
|
1810
|
+
};
|
|
1811
|
+
const onKeyDown = (event) => {
|
|
1812
|
+
const key = event;
|
|
1813
|
+
if (key.key === "Alt") {
|
|
1814
|
+
altAlone = !key.ctrlKey && !key.metaKey && !key.shiftKey;
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
altAlone = false;
|
|
1818
|
+
if (!handle.active) return;
|
|
1819
|
+
if (key.key === "Escape") {
|
|
1820
|
+
key.preventDefault();
|
|
1821
|
+
key.stopPropagation();
|
|
1822
|
+
typed = "";
|
|
1823
|
+
if (levels.length) {
|
|
1824
|
+
levels.pop();
|
|
1825
|
+
render();
|
|
1826
|
+
} else stop();
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
if (key.key.length !== 1 || key.ctrlKey || key.metaKey) return stop();
|
|
1830
|
+
key.preventDefault();
|
|
1831
|
+
key.stopPropagation();
|
|
1832
|
+
typed += key.key.toUpperCase();
|
|
1833
|
+
const candidates = current().filter((el) => el.dataset.keytip.toUpperCase().startsWith(typed));
|
|
1834
|
+
const exact = candidates.find((el) => el.dataset.keytip.toUpperCase() === typed);
|
|
1835
|
+
if (exact) choose(exact);
|
|
1836
|
+
else if (!candidates.length) typed = "";
|
|
1837
|
+
render();
|
|
1838
|
+
};
|
|
1839
|
+
const onKeyUp = (event) => {
|
|
1840
|
+
const key = event;
|
|
1841
|
+
if (key.key !== "Alt" || !altAlone) return;
|
|
1842
|
+
altAlone = false;
|
|
1843
|
+
key.preventDefault();
|
|
1844
|
+
if (handle.active) stop();
|
|
1845
|
+
else start();
|
|
1846
|
+
};
|
|
1847
|
+
const onPointer = () => {
|
|
1848
|
+
if (handle.active) stop();
|
|
1849
|
+
};
|
|
1850
|
+
const target = scope;
|
|
1851
|
+
target.addEventListener("keydown", onKeyDown, true);
|
|
1852
|
+
target.addEventListener("keyup", onKeyUp, true);
|
|
1853
|
+
target.addEventListener("pointerdown", onPointer, true);
|
|
1854
|
+
view?.addEventListener("blur", onPointer);
|
|
1855
|
+
const handle = {
|
|
1856
|
+
active: false,
|
|
1857
|
+
dispose() {
|
|
1858
|
+
stop();
|
|
1859
|
+
target.removeEventListener("keydown", onKeyDown, true);
|
|
1860
|
+
target.removeEventListener("keyup", onKeyUp, true);
|
|
1861
|
+
target.removeEventListener("pointerdown", onPointer, true);
|
|
1862
|
+
view?.removeEventListener("blur", onPointer);
|
|
1863
|
+
layer.remove();
|
|
1864
|
+
}
|
|
1865
|
+
};
|
|
1866
|
+
return handle;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1735
1869
|
// src/controls.ts
|
|
1736
1870
|
var CONTROL_DEFINERS = [
|
|
1737
1871
|
defineIcon,
|
|
@@ -1757,4 +1891,4 @@ function registerControls(registry) {
|
|
|
1757
1891
|
for (const define of CONTROL_DEFINERS) define(registry);
|
|
1758
1892
|
}
|
|
1759
1893
|
|
|
1760
|
-
export { CONTROL_DEFINERS, defineButton, defineCheckbox, defineCommandSearch, defineContextMenu, defineDialog, defineMenuButton, defineMenuItem, defineMenuSeparator, defineRibbonGroup, defineRibbonStack, defineSelect, defineStatusBar, defineStatusItem, defineSwitch, defineTabStrip, defineToolbar, defineZoomSlider, registerControls };
|
|
1894
|
+
export { CONTROL_DEFINERS, attachKeyTips, defineButton, defineCheckbox, defineCommandSearch, defineContextMenu, defineDialog, defineMenuButton, defineMenuItem, defineMenuSeparator, defineRibbonGroup, defineRibbonStack, defineSelect, defineStatusBar, defineStatusItem, defineSwitch, defineTabStrip, defineToolbar, defineZoomSlider, registerControls };
|
package/dist/controls.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type { OfficeSelectOption } from './select.js';
|
|
|
17
17
|
export type { OfficeStatusActivateEvent } from './status-bar.js';
|
|
18
18
|
export type { OfficeTab, OfficeTabSelectEvent } from './tab-strip.js';
|
|
19
19
|
export type { OfficeSearchCommand } from './command-search.js';
|
|
20
|
+
export { attachKeyTips, type KeyTipsHandle } from './keytips.js';
|
|
20
21
|
/** Every control of this entry. */
|
|
21
22
|
export declare const CONTROL_DEFINERS: readonly [import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer, import("./registry.js").Definer];
|
|
22
23
|
/** Idempotent, SSR-safe (no-op without a DOM). */
|
package/dist/controls.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { CONTROL_DEFINERS, defineButton, defineCheckbox, defineCommandSearch, defineContextMenu, defineDialog, defineMenuButton, defineMenuItem, defineMenuSeparator, defineRibbonGroup, defineRibbonStack, defineSelect, defineStatusBar, defineStatusItem, defineSwitch, defineTabStrip, defineToolbar, defineZoomSlider, registerControls } from './chunk-
|
|
1
|
+
export { CONTROL_DEFINERS, attachKeyTips, defineButton, defineCheckbox, defineCommandSearch, defineContextMenu, defineDialog, defineMenuButton, defineMenuItem, defineMenuSeparator, defineRibbonGroup, defineRibbonStack, defineSelect, defineStatusBar, defineStatusItem, defineSwitch, defineTabStrip, defineToolbar, defineZoomSlider, registerControls } from './chunk-L5FYB6M7.js';
|
|
2
2
|
export { defineIcon } from './chunk-PCSUQPMC.js';
|
|
3
3
|
import './chunk-EY266IK3.js';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { installOfficeUiTheme } from './chunk-H4KFVIGT.js';
|
|
2
2
|
export { THEME_CSS, THEME_STYLE_ID, installOfficeUiTheme } from './chunk-H4KFVIGT.js';
|
|
3
|
-
import { registerControls } from './chunk-
|
|
4
|
-
export { CONTROL_DEFINERS, defineButton, defineCheckbox, defineCommandSearch, defineContextMenu, defineDialog, defineMenuButton, defineMenuItem, defineMenuSeparator, defineRibbonGroup, defineRibbonStack, defineSelect, defineStatusBar, defineStatusItem, defineSwitch, defineTabStrip, defineToolbar, defineZoomSlider, registerControls } from './chunk-
|
|
3
|
+
import { registerControls } from './chunk-L5FYB6M7.js';
|
|
4
|
+
export { CONTROL_DEFINERS, attachKeyTips, defineButton, defineCheckbox, defineCommandSearch, defineContextMenu, defineDialog, defineMenuButton, defineMenuItem, defineMenuSeparator, defineRibbonGroup, defineRibbonStack, defineSelect, defineStatusBar, defineStatusItem, defineSwitch, defineTabStrip, defineToolbar, defineZoomSlider, registerControls } from './chunk-L5FYB6M7.js';
|
|
5
5
|
export { ICON_CSS, createIconSvg, defineIcon, getIcon, listIcons, paintIcon, registerIcon } from './chunk-PCSUQPMC.js';
|
|
6
6
|
import { definePresence } from './chunk-RLPL7Q6J.js';
|
|
7
7
|
export { definePresence, initialsOf, participantsFromAwareness } from './chunk-RLPL7Q6J.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Office KeyTips: press and release Alt to show letter badges on ribbon controls, then type a
|
|
3
|
+
* badge to activate it. Products annotate controls only:
|
|
4
|
+
* - `data-keytip="H"` marks a control (one or two characters);
|
|
5
|
+
* - `data-keytip-panel="home-panel"` on a control (a ribbon tab) makes the controls inside the
|
|
6
|
+
* element with that id the next level after it is activated.
|
|
7
|
+
* First-level tips are those not inside any `[data-keytip-level]` container; a panel element
|
|
8
|
+
* carries `data-keytip-level` so its tips only appear after its tab. Escape steps back a level;
|
|
9
|
+
* Alt, a click or focus leaving the scope exits. Disabled controls show dimmed and never run.
|
|
10
|
+
* Badges render in a `popover` top layer, so containment and overflow never misplace them.
|
|
11
|
+
*/
|
|
12
|
+
export interface KeyTipsHandle {
|
|
13
|
+
/** Whether badges are currently shown. */
|
|
14
|
+
readonly active: boolean;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
}
|
|
17
|
+
type Root = ShadowRoot | HTMLElement;
|
|
18
|
+
export declare function attachKeyTips(scope: Root): KeyTipsHandle;
|
|
19
|
+
export {};
|