ooxml-ui 0.29.3 → 0.30.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/dist/edit-worker.js +1 -0
- package/dist/parse-worker.js +1 -0
- package/dist/suite/index.d.ts +1 -0
- package/dist/suite/tab-state.d.ts +47 -0
- package/dist/suite.js +90 -0
- package/dist/xlsx.js +1 -1
- package/package.json +8 -3
package/dist/edit-worker.js
CHANGED
|
@@ -4,6 +4,7 @@ import { editVsdx, parseVsdx } from 'ooxml-core/visio';
|
|
|
4
4
|
// src/visio/edit-worker.ts
|
|
5
5
|
var worker = self;
|
|
6
6
|
worker.onmessage = async (event) => {
|
|
7
|
+
if (event.origin && event.origin !== self.location.origin) return;
|
|
7
8
|
try {
|
|
8
9
|
const result = await editVsdx(event.data.bytes, event.data.edits);
|
|
9
10
|
const document = await parseVsdx(result.bytes, { metafileConverter: convertMetafileToSvgTree });
|
package/dist/parse-worker.js
CHANGED
|
@@ -4,6 +4,7 @@ import { loadVisio } from 'ooxml-core/visio';
|
|
|
4
4
|
// src/visio/parse-worker.ts
|
|
5
5
|
var worker = self;
|
|
6
6
|
worker.onmessage = async (event) => {
|
|
7
|
+
if (event.origin && event.origin !== self.location.origin) return;
|
|
7
8
|
try {
|
|
8
9
|
const document = await loadVisio(event.data, { metafileConverter: convertMetafileToSvgTree });
|
|
9
10
|
worker.postMessage({ ok: true, document });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tab-state.js';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The suite's tab model: a pure list of tabs and which one is active, with no DOM and no host
|
|
3
|
+
* assumptions, so the web shell and the desktop shell drive the same code. Every tab is one app
|
|
4
|
+
* (and the framework demo or editor it runs on); the same app may be open in several tabs. Home is
|
|
5
|
+
* not a tab: it is the state where nothing is active.
|
|
6
|
+
*/
|
|
7
|
+
export interface SuiteTab {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly app: string;
|
|
10
|
+
readonly framework: string;
|
|
11
|
+
/** Pinned tabs sit first, show only their icon and cannot be closed by accident. */
|
|
12
|
+
readonly pinned: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface SuiteTabState {
|
|
15
|
+
readonly tabs: readonly SuiteTab[];
|
|
16
|
+
/** The active tab's id, or null for Home. */
|
|
17
|
+
readonly active: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare const EMPTY_SUITE_STATE: SuiteTabState;
|
|
20
|
+
/** Read a persisted state, dropping anything malformed, duplicated or pointing at a missing tab. */
|
|
21
|
+
export declare function parseSuiteState(value: unknown): SuiteTabState;
|
|
22
|
+
export declare function openSuiteTab(state: SuiteTabState, app: string, framework: string, id: string): SuiteTabState;
|
|
23
|
+
/** Activate a tab; an unknown id or null means Home. */
|
|
24
|
+
export declare function activateSuiteTab(state: SuiteTabState, id: string | null): SuiteTabState;
|
|
25
|
+
/**
|
|
26
|
+
* Close a tab. Closing the active one activates its right neighbour, else the left one, else
|
|
27
|
+
* Home, as browser tab strips do.
|
|
28
|
+
*/
|
|
29
|
+
export declare function closeSuiteTab(state: SuiteTabState, id: string): SuiteTabState;
|
|
30
|
+
export declare function setSuiteTabFramework(state: SuiteTabState, id: string, framework: string): SuiteTabState;
|
|
31
|
+
/** Titles for the strip: the app name, numbered from the second tab of the same app on. */
|
|
32
|
+
export declare function suiteTabTitles(tabs: readonly SuiteTab[], nameOf: (app: string) => string): Map<string, string>;
|
|
33
|
+
/** An id no live tab uses. */
|
|
34
|
+
export declare function newSuiteTabId(tabs: readonly SuiteTab[]): string;
|
|
35
|
+
/** Stable partition: pinned tabs first, every group keeping its order. */
|
|
36
|
+
export declare function pinnedFirst(tabs: readonly SuiteTab[]): SuiteTab[];
|
|
37
|
+
/** Pin a tab (it joins the end of the pinned group) or unpin it (it leads the unpinned ones). */
|
|
38
|
+
export declare function pinSuiteTab(state: SuiteTabState, id: string, pinned: boolean): SuiteTabState;
|
|
39
|
+
/**
|
|
40
|
+
* Move a tab to just before `beforeId` (the end when null). A tab never leaves its group: a
|
|
41
|
+
* pinned tab stays among the pinned ones and an unpinned tab among the others.
|
|
42
|
+
*/
|
|
43
|
+
export declare function moveSuiteTab(state: SuiteTabState, id: string, beforeId: string | null): SuiteTabState;
|
|
44
|
+
/** Close every tab but this one; pinned tabs stay. */
|
|
45
|
+
export declare function closeOtherSuiteTabs(state: SuiteTabState, id: string): SuiteTabState;
|
|
46
|
+
/** Close the tabs after this one; pinned tabs stay. */
|
|
47
|
+
export declare function closeSuiteTabsToRight(state: SuiteTabState, id: string): SuiteTabState;
|
package/dist/suite.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/suite/tab-state.ts
|
|
2
|
+
var EMPTY_SUITE_STATE = { tabs: [], active: null };
|
|
3
|
+
function parseSuiteState(value) {
|
|
4
|
+
if (!value || typeof value !== "object") return EMPTY_SUITE_STATE;
|
|
5
|
+
const raw = value;
|
|
6
|
+
if (!Array.isArray(raw.tabs)) return EMPTY_SUITE_STATE;
|
|
7
|
+
const tabs = [];
|
|
8
|
+
for (const item of raw.tabs) {
|
|
9
|
+
if (!item || typeof item !== "object") continue;
|
|
10
|
+
const { id, app, framework, pinned } = item;
|
|
11
|
+
if (typeof id !== "string" || typeof app !== "string" || typeof framework !== "string")
|
|
12
|
+
continue;
|
|
13
|
+
if (tabs.some((tab) => tab.id === id)) continue;
|
|
14
|
+
tabs.push({ id, app, framework, pinned: pinned === true });
|
|
15
|
+
}
|
|
16
|
+
const active = tabs.some((tab) => tab.id === raw.active) ? raw.active : null;
|
|
17
|
+
return { tabs: pinnedFirst(tabs), active };
|
|
18
|
+
}
|
|
19
|
+
function openSuiteTab(state, app, framework, id) {
|
|
20
|
+
return { tabs: [...state.tabs, { id, app, framework, pinned: false }], active: id };
|
|
21
|
+
}
|
|
22
|
+
function activateSuiteTab(state, id) {
|
|
23
|
+
return { tabs: state.tabs, active: id && state.tabs.some((t) => t.id === id) ? id : null };
|
|
24
|
+
}
|
|
25
|
+
function closeSuiteTab(state, id) {
|
|
26
|
+
const index = state.tabs.findIndex((tab) => tab.id === id);
|
|
27
|
+
if (index < 0) return state;
|
|
28
|
+
const tabs = state.tabs.filter((tab) => tab.id !== id);
|
|
29
|
+
if (state.active !== id) return { tabs, active: state.active };
|
|
30
|
+
const next = tabs[index] ?? tabs[index - 1];
|
|
31
|
+
return { tabs, active: next ? next.id : null };
|
|
32
|
+
}
|
|
33
|
+
function setSuiteTabFramework(state, id, framework) {
|
|
34
|
+
return {
|
|
35
|
+
tabs: state.tabs.map((tab) => tab.id === id ? { ...tab, framework } : tab),
|
|
36
|
+
active: state.active
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function suiteTabTitles(tabs, nameOf) {
|
|
40
|
+
const seen = /* @__PURE__ */ new Map();
|
|
41
|
+
const titles = /* @__PURE__ */ new Map();
|
|
42
|
+
for (const tab of tabs) {
|
|
43
|
+
const count = (seen.get(tab.app) ?? 0) + 1;
|
|
44
|
+
seen.set(tab.app, count);
|
|
45
|
+
titles.set(tab.id, count === 1 ? nameOf(tab.app) : `${nameOf(tab.app)} ${count}`);
|
|
46
|
+
}
|
|
47
|
+
return titles;
|
|
48
|
+
}
|
|
49
|
+
function newSuiteTabId(tabs) {
|
|
50
|
+
let n = tabs.length + 1;
|
|
51
|
+
while (tabs.some((tab) => tab.id === `t${n}`)) n++;
|
|
52
|
+
return `t${n}`;
|
|
53
|
+
}
|
|
54
|
+
function pinnedFirst(tabs) {
|
|
55
|
+
return [...tabs.filter((t) => t.pinned), ...tabs.filter((t) => !t.pinned)];
|
|
56
|
+
}
|
|
57
|
+
function pinSuiteTab(state, id, pinned) {
|
|
58
|
+
const tab = state.tabs.find((t) => t.id === id);
|
|
59
|
+
if (!tab || tab.pinned === pinned) return state;
|
|
60
|
+
const rest = state.tabs.filter((t) => t.id !== id);
|
|
61
|
+
const moved = { ...tab, pinned };
|
|
62
|
+
const pinnedCount = rest.filter((t) => t.pinned).length;
|
|
63
|
+
rest.splice(pinned ? pinnedCount : pinnedCount, 0, moved);
|
|
64
|
+
return { tabs: rest, active: state.active };
|
|
65
|
+
}
|
|
66
|
+
function moveSuiteTab(state, id, beforeId) {
|
|
67
|
+
const tab = state.tabs.find((t) => t.id === id);
|
|
68
|
+
if (!tab || beforeId === id) return state;
|
|
69
|
+
const rest = state.tabs.filter((t) => t.id !== id);
|
|
70
|
+
const pinnedCount = rest.filter((t) => t.pinned).length;
|
|
71
|
+
const wanted = beforeId === null ? rest.length : rest.findIndex((t) => t.id === beforeId);
|
|
72
|
+
if (wanted < 0) return state;
|
|
73
|
+
const index = tab.pinned ? Math.min(wanted, pinnedCount) : Math.max(wanted, pinnedCount);
|
|
74
|
+
rest.splice(index, 0, tab);
|
|
75
|
+
return { tabs: rest, active: state.active };
|
|
76
|
+
}
|
|
77
|
+
function without(state, keep, anchor) {
|
|
78
|
+
const tabs = state.tabs.filter(keep);
|
|
79
|
+
return { tabs, active: tabs.some((t) => t.id === state.active) ? state.active : anchor };
|
|
80
|
+
}
|
|
81
|
+
function closeOtherSuiteTabs(state, id) {
|
|
82
|
+
return without(state, (t) => t.id === id || t.pinned, id);
|
|
83
|
+
}
|
|
84
|
+
function closeSuiteTabsToRight(state, id) {
|
|
85
|
+
const at = state.tabs.findIndex((t) => t.id === id);
|
|
86
|
+
if (at < 0) return state;
|
|
87
|
+
return without(state, (t, i) => i <= at || t.pinned, id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { EMPTY_SUITE_STATE, activateSuiteTab, closeOtherSuiteTabs, closeSuiteTab, closeSuiteTabsToRight, moveSuiteTab, newSuiteTabId, openSuiteTab, parseSuiteState, pinSuiteTab, pinnedFirst, setSuiteTabFramework, suiteTabTitles };
|
package/dist/xlsx.js
CHANGED
|
@@ -3711,7 +3711,7 @@ function safeSvg(doc, markup) {
|
|
|
3711
3711
|
}
|
|
3712
3712
|
for (const attribute of [...node.attributes]) {
|
|
3713
3713
|
const value = attribute.value.trim().toLowerCase();
|
|
3714
|
-
if (/^on/i.test(attribute.name) || value.
|
|
3714
|
+
if (/^on/i.test(attribute.name) || /^(?:javascript|vbscript|data):/i.test(value.replace(/[\p{Cc}\p{Z}]/gu, "")) || /href$/i.test(attribute.name) && !value.startsWith("#"))
|
|
3715
3715
|
node.removeAttribute(attribute.name);
|
|
3716
3716
|
}
|
|
3717
3717
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ooxml-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.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",
|
|
@@ -69,6 +69,11 @@
|
|
|
69
69
|
"import": "./dist/teams.js",
|
|
70
70
|
"default": "./dist/teams.js"
|
|
71
71
|
},
|
|
72
|
+
"./suite": {
|
|
73
|
+
"types": "./dist/suite/index.d.ts",
|
|
74
|
+
"import": "./dist/suite.js",
|
|
75
|
+
"default": "./dist/suite.js"
|
|
76
|
+
},
|
|
72
77
|
"./docx": {
|
|
73
78
|
"types": "./dist/docx/index.d.ts",
|
|
74
79
|
"import": "./dist/docx.js",
|
|
@@ -99,9 +104,9 @@
|
|
|
99
104
|
"gen:manifest": "node scripts/gen-manifest.mjs"
|
|
100
105
|
},
|
|
101
106
|
"dependencies": {
|
|
102
|
-
"emf-converter": "4.8.
|
|
107
|
+
"emf-converter": "4.8.21",
|
|
103
108
|
"lit": "^3.3.3",
|
|
104
|
-
"ooxml-core": "^0.
|
|
109
|
+
"ooxml-core": "^0.22.0",
|
|
105
110
|
"prosemirror-collab": "^1.3.1",
|
|
106
111
|
"prosemirror-commands": "^1.7.2",
|
|
107
112
|
"prosemirror-history": "^1.5.1",
|