@embedpdf/plugin-interaction-manager 1.4.1 → 2.0.0-next.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/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +456 -185
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +61 -36
- package/dist/lib/interaction-manager-plugin.d.ts +19 -14
- package/dist/lib/reducer.d.ts +2 -1
- package/dist/lib/types.d.ts +58 -50
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +61 -40
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +61 -40
- package/dist/react/index.js.map +1 -1
- package/dist/shared/components/global-pointer-provider.d.ts +2 -1
- package/dist/shared/components/page-pointer-provider.d.ts +4 -5
- package/dist/shared/hooks/use-interaction-manager.d.ts +9 -7
- package/dist/shared-preact/components/global-pointer-provider.d.ts +2 -1
- package/dist/shared-preact/components/page-pointer-provider.d.ts +4 -5
- package/dist/shared-preact/hooks/use-interaction-manager.d.ts +9 -7
- package/dist/shared-react/components/global-pointer-provider.d.ts +2 -1
- package/dist/shared-react/components/page-pointer-provider.d.ts +4 -5
- package/dist/shared-react/hooks/use-interaction-manager.d.ts +9 -7
- package/dist/svelte/components/GlobalPointerProvider.svelte.d.ts +1 -0
- package/dist/svelte/components/PagePointerProvider.svelte.d.ts +4 -5
- package/dist/svelte/hooks/use-interaction-manager.svelte.d.ts +10 -8
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +128 -56
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/components/global-pointer-provider.vue.d.ts +6 -2
- package/dist/vue/components/page-pointer-provider.vue.d.ts +7 -7
- package/dist/vue/hooks/use-interaction-manager.d.ts +19 -26
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +131 -63
- package/dist/vue/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,52 +1,53 @@
|
|
|
1
1
|
import { BasePlugin, createEmitter, createBehaviorEmitter } from "@embedpdf/core";
|
|
2
|
+
const INIT_INTERACTION_STATE = "INTERACTION/INIT_STATE";
|
|
3
|
+
const CLEANUP_INTERACTION_STATE = "INTERACTION/CLEANUP_STATE";
|
|
2
4
|
const ACTIVATE_MODE = "INTERACTION/ACTIVATE_MODE";
|
|
3
5
|
const PAUSE_INTERACTION = "INTERACTION/PAUSE";
|
|
4
6
|
const RESUME_INTERACTION = "INTERACTION/RESUME";
|
|
5
7
|
const SET_CURSOR = "INTERACTION/SET_CURSOR";
|
|
8
|
+
const SET_ACTIVE_DOCUMENT = "INTERACTION/SET_ACTIVE_DOCUMENT";
|
|
6
9
|
const SET_DEFAULT_MODE = "INTERACTION/SET_DEFAULT_MODE";
|
|
7
10
|
const SET_EXCLUSION_RULES = "INTERACTION/SET_EXCLUSION_RULES";
|
|
8
11
|
const ADD_EXCLUSION_CLASS = "INTERACTION/ADD_EXCLUSION_CLASS";
|
|
9
12
|
const REMOVE_EXCLUSION_CLASS = "INTERACTION/REMOVE_EXCLUSION_CLASS";
|
|
10
13
|
const ADD_EXCLUSION_ATTRIBUTE = "INTERACTION/ADD_EXCLUSION_ATTRIBUTE";
|
|
11
14
|
const REMOVE_EXCLUSION_ATTRIBUTE = "INTERACTION/REMOVE_EXCLUSION_ATTRIBUTE";
|
|
12
|
-
|
|
13
|
-
type:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
payload:
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
payload: {
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
type:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
type: RESUME_INTERACTION
|
|
49
|
-
});
|
|
15
|
+
function initInteractionState(documentId, state) {
|
|
16
|
+
return { type: INIT_INTERACTION_STATE, payload: { documentId, state } };
|
|
17
|
+
}
|
|
18
|
+
function cleanupInteractionState(documentId) {
|
|
19
|
+
return { type: CLEANUP_INTERACTION_STATE, payload: documentId };
|
|
20
|
+
}
|
|
21
|
+
function activateMode(documentId, mode) {
|
|
22
|
+
return { type: ACTIVATE_MODE, payload: { documentId, mode } };
|
|
23
|
+
}
|
|
24
|
+
function pauseInteraction(documentId) {
|
|
25
|
+
return { type: PAUSE_INTERACTION, payload: documentId };
|
|
26
|
+
}
|
|
27
|
+
function resumeInteraction(documentId) {
|
|
28
|
+
return { type: RESUME_INTERACTION, payload: documentId };
|
|
29
|
+
}
|
|
30
|
+
function setCursor(documentId, cursor) {
|
|
31
|
+
return { type: SET_CURSOR, payload: { documentId, cursor } };
|
|
32
|
+
}
|
|
33
|
+
function setDefaultMode(mode) {
|
|
34
|
+
return { type: SET_DEFAULT_MODE, payload: { mode } };
|
|
35
|
+
}
|
|
36
|
+
function setExclusionRules(rules) {
|
|
37
|
+
return { type: SET_EXCLUSION_RULES, payload: { rules } };
|
|
38
|
+
}
|
|
39
|
+
function addExclusionClass(className) {
|
|
40
|
+
return { type: ADD_EXCLUSION_CLASS, payload: { className } };
|
|
41
|
+
}
|
|
42
|
+
function removeExclusionClass(className) {
|
|
43
|
+
return { type: REMOVE_EXCLUSION_CLASS, payload: { className } };
|
|
44
|
+
}
|
|
45
|
+
function addExclusionAttribute(attribute) {
|
|
46
|
+
return { type: ADD_EXCLUSION_ATTRIBUTE, payload: { attribute } };
|
|
47
|
+
}
|
|
48
|
+
function removeExclusionAttribute(attribute) {
|
|
49
|
+
return { type: REMOVE_EXCLUSION_ATTRIBUTE, payload: { attribute } };
|
|
50
|
+
}
|
|
50
51
|
function mergeHandlers(list) {
|
|
51
52
|
const keys = [
|
|
52
53
|
"onPointerDown",
|
|
@@ -73,55 +74,92 @@ function mergeHandlers(list) {
|
|
|
73
74
|
}
|
|
74
75
|
return out;
|
|
75
76
|
}
|
|
76
|
-
const INITIAL_MODE = "pointerMode";
|
|
77
|
+
const INITIAL_MODE$1 = "pointerMode";
|
|
77
78
|
const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePlugin {
|
|
78
79
|
constructor(id, registry, config) {
|
|
79
80
|
super(id, registry);
|
|
80
81
|
this.modes = /* @__PURE__ */ new Map();
|
|
81
82
|
this.cursorClaims = /* @__PURE__ */ new Map();
|
|
82
83
|
this.buckets = /* @__PURE__ */ new Map();
|
|
83
|
-
this.alwaysGlobal = /* @__PURE__ */ new
|
|
84
|
+
this.alwaysGlobal = /* @__PURE__ */ new Map();
|
|
84
85
|
this.alwaysPage = /* @__PURE__ */ new Map();
|
|
85
86
|
this.onModeChange$ = createEmitter();
|
|
86
87
|
this.onHandlerChange$ = createEmitter();
|
|
87
88
|
this.onCursorChange$ = createEmitter();
|
|
88
89
|
this.onStateChange$ = createBehaviorEmitter();
|
|
89
90
|
this.registerMode({
|
|
90
|
-
id: INITIAL_MODE,
|
|
91
|
+
id: INITIAL_MODE$1,
|
|
91
92
|
scope: "page",
|
|
92
93
|
exclusive: false,
|
|
93
94
|
cursor: "auto"
|
|
94
95
|
});
|
|
95
|
-
this.setDefaultMode(INITIAL_MODE);
|
|
96
|
-
this.activate(INITIAL_MODE);
|
|
96
|
+
this.dispatch(setDefaultMode(INITIAL_MODE$1));
|
|
97
97
|
if (config.exclusionRules) {
|
|
98
98
|
this.dispatch(setExclusionRules(config.exclusionRules));
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
|
|
101
|
+
// ─────────────────────────────────────────────────────────
|
|
102
|
+
// Document Lifecycle Hooks (from BasePlugin)
|
|
103
|
+
// ─────────────────────────────────────────────────────────
|
|
104
|
+
onDocumentLoadingStarted(documentId) {
|
|
105
|
+
const docState = {
|
|
106
|
+
activeMode: this.state.defaultMode,
|
|
107
|
+
cursor: "auto",
|
|
108
|
+
paused: false
|
|
109
|
+
};
|
|
110
|
+
this.dispatch(initInteractionState(documentId, docState));
|
|
111
|
+
this.cursorClaims.set(documentId, /* @__PURE__ */ new Map());
|
|
112
|
+
this.buckets.set(documentId, /* @__PURE__ */ new Map());
|
|
113
|
+
this.alwaysGlobal.set(documentId, /* @__PURE__ */ new Set());
|
|
114
|
+
this.alwaysPage.set(documentId, /* @__PURE__ */ new Map());
|
|
115
|
+
const docBuckets = this.buckets.get(documentId);
|
|
116
|
+
for (const modeId of this.modes.keys()) {
|
|
117
|
+
docBuckets.set(modeId, { global: /* @__PURE__ */ new Set(), page: /* @__PURE__ */ new Map() });
|
|
118
|
+
}
|
|
119
|
+
this.logger.debug(
|
|
120
|
+
"InteractionManagerPlugin",
|
|
121
|
+
"DocumentOpened",
|
|
122
|
+
`Initialized interaction state for document: ${documentId}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
onDocumentClosed(documentId) {
|
|
126
|
+
this.cursorClaims.delete(documentId);
|
|
127
|
+
this.buckets.delete(documentId);
|
|
128
|
+
this.alwaysGlobal.delete(documentId);
|
|
129
|
+
this.alwaysPage.delete(documentId);
|
|
130
|
+
this.dispatch(cleanupInteractionState(documentId));
|
|
131
|
+
this.logger.debug(
|
|
132
|
+
"InteractionManagerPlugin",
|
|
133
|
+
"DocumentClosed",
|
|
134
|
+
`Cleaned up interaction state for document: ${documentId}`
|
|
135
|
+
);
|
|
102
136
|
}
|
|
137
|
+
// ─────────────────────────────────────────────────────────
|
|
138
|
+
// Capability
|
|
139
|
+
// ─────────────────────────────────────────────────────────
|
|
103
140
|
buildCapability() {
|
|
104
141
|
return {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
onCursorChange: this.onCursorChange$.on,
|
|
108
|
-
onHandlerChange: this.onHandlerChange$.on,
|
|
109
|
-
onStateChange: this.onStateChange$.on,
|
|
110
|
-
getActiveMode: () => this.state.activeMode,
|
|
142
|
+
// Active document operations
|
|
143
|
+
getActiveMode: () => this.getActiveMode(),
|
|
111
144
|
getActiveInteractionMode: () => this.getActiveInteractionMode(),
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
setCursor: (token, cursor, priority = 0) => this.setCursor(token, cursor, priority),
|
|
145
|
+
activate: (modeId) => this.activate(modeId),
|
|
146
|
+
activateDefaultMode: () => this.activateDefaultMode(),
|
|
147
|
+
setCursor: (token, cursor, priority) => this.setCursor(token, cursor, priority),
|
|
148
|
+
getCurrentCursor: () => this.getCurrentCursor(),
|
|
117
149
|
removeCursor: (token) => this.removeCursor(token),
|
|
118
|
-
getCurrentCursor: () => this.state.cursor,
|
|
119
150
|
getHandlersForScope: (scope) => this.getHandlersForScope(scope),
|
|
120
151
|
activeModeIsExclusive: () => this.activeModeIsExclusive(),
|
|
121
|
-
pause: () => this.
|
|
122
|
-
resume: () => this.
|
|
152
|
+
pause: () => this.pause(),
|
|
153
|
+
resume: () => this.resume(),
|
|
123
154
|
// Treat a destroyed registry as "paused" so late DOM events are ignored during teardown.
|
|
124
|
-
isPaused: () => this.registry.isDestroyed() || this.
|
|
155
|
+
isPaused: () => this.registry.isDestroyed() || this.isPaused(),
|
|
156
|
+
getState: () => this.getDocumentStateOrThrow(),
|
|
157
|
+
// Document-scoped operations
|
|
158
|
+
forDocument: (documentId) => this.createInteractionScope(documentId),
|
|
159
|
+
// Global management
|
|
160
|
+
registerMode: (mode) => this.registerMode(mode),
|
|
161
|
+
registerHandlers: (options) => this.registerHandlers(options),
|
|
162
|
+
registerAlways: (options) => this.registerAlways(options),
|
|
125
163
|
setDefaultMode: (id) => this.setDefaultMode(id),
|
|
126
164
|
getDefaultMode: () => this.state.defaultMode,
|
|
127
165
|
getExclusionRules: () => this.state.exclusionRules,
|
|
@@ -129,21 +167,83 @@ const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePl
|
|
|
129
167
|
addExclusionClass: (className) => this.dispatch(addExclusionClass(className)),
|
|
130
168
|
removeExclusionClass: (className) => this.dispatch(removeExclusionClass(className)),
|
|
131
169
|
addExclusionAttribute: (attribute) => this.dispatch(addExclusionAttribute(attribute)),
|
|
132
|
-
removeExclusionAttribute: (attribute) => this.dispatch(removeExclusionAttribute(attribute))
|
|
170
|
+
removeExclusionAttribute: (attribute) => this.dispatch(removeExclusionAttribute(attribute)),
|
|
171
|
+
// Events
|
|
172
|
+
onModeChange: this.onModeChange$.on,
|
|
173
|
+
onCursorChange: this.onCursorChange$.on,
|
|
174
|
+
onHandlerChange: this.onHandlerChange$.on,
|
|
175
|
+
onStateChange: this.onStateChange$.on
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// ─────────────────────────────────────────────────────────
|
|
179
|
+
// Document Scoping
|
|
180
|
+
// ─────────────────────────────────────────────────────────
|
|
181
|
+
createInteractionScope(documentId) {
|
|
182
|
+
return {
|
|
183
|
+
getActiveMode: () => this.getActiveMode(documentId),
|
|
184
|
+
getActiveInteractionMode: () => this.getActiveInteractionMode(documentId),
|
|
185
|
+
activate: (modeId) => this.activate(modeId, documentId),
|
|
186
|
+
activateDefaultMode: () => this.activateDefaultMode(documentId),
|
|
187
|
+
setCursor: (token, cursor, priority) => this.setCursor(token, cursor, priority, documentId),
|
|
188
|
+
getCurrentCursor: () => this.getCurrentCursor(documentId),
|
|
189
|
+
removeCursor: (token) => this.removeCursor(token, documentId),
|
|
190
|
+
getHandlersForScope: (scope) => this.getHandlersForScope(scope),
|
|
191
|
+
activeModeIsExclusive: () => this.activeModeIsExclusive(documentId),
|
|
192
|
+
pause: () => this.pause(documentId),
|
|
193
|
+
resume: () => this.resume(documentId),
|
|
194
|
+
isPaused: () => this.isPaused(documentId),
|
|
195
|
+
getState: () => this.getDocumentStateOrThrow(documentId),
|
|
196
|
+
onModeChange: (listener) => this.onModeChange$.on((event) => {
|
|
197
|
+
if (event.documentId === documentId) listener(event.activeMode);
|
|
198
|
+
}),
|
|
199
|
+
onCursorChange: (listener) => this.onCursorChange$.on((event) => {
|
|
200
|
+
if (event.documentId === documentId) listener(event.cursor);
|
|
201
|
+
}),
|
|
202
|
+
onStateChange: (listener) => this.onStateChange$.on((event) => {
|
|
203
|
+
if (event.documentId === documentId) listener(event.state);
|
|
204
|
+
})
|
|
133
205
|
};
|
|
134
206
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
207
|
+
// ─────────────────────────────────────────────────────────
|
|
208
|
+
// State Helpers
|
|
209
|
+
// ─────────────────────────────────────────────────────────
|
|
210
|
+
getDocumentState(documentId) {
|
|
211
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
212
|
+
return this.state.documents[id] ?? null;
|
|
213
|
+
}
|
|
214
|
+
getDocumentStateOrThrow(documentId) {
|
|
215
|
+
const state = this.getDocumentState(documentId);
|
|
216
|
+
if (!state) {
|
|
217
|
+
throw new Error(`Interaction state not found for document: ${documentId ?? "active"}`);
|
|
138
218
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
219
|
+
return state;
|
|
220
|
+
}
|
|
221
|
+
// ─────────────────────────────────────────────────────────
|
|
222
|
+
// Core Operations
|
|
223
|
+
// ─────────────────────────────────────────────────────────
|
|
224
|
+
activate(modeId, documentId) {
|
|
225
|
+
var _a;
|
|
226
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
227
|
+
const docState = this.getDocumentStateOrThrow(id);
|
|
228
|
+
if (!this.modes.has(modeId)) {
|
|
229
|
+
throw new Error(`[interaction] unknown mode '${modeId}'`);
|
|
230
|
+
}
|
|
231
|
+
if (modeId === docState.activeMode) return;
|
|
232
|
+
const previousMode = docState.activeMode;
|
|
233
|
+
(_a = this.cursorClaims.get(id)) == null ? void 0 : _a.clear();
|
|
234
|
+
this.notifyHandlersInactive(id, previousMode);
|
|
235
|
+
this.dispatch(activateMode(id, modeId));
|
|
236
|
+
this.emitCursor(id);
|
|
237
|
+
this.notifyHandlersActive(id, modeId);
|
|
238
|
+
this.onModeChange$.emit({
|
|
239
|
+
documentId: id,
|
|
240
|
+
activeMode: modeId,
|
|
241
|
+
previousMode
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
activateDefaultMode(documentId) {
|
|
245
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
246
|
+
this.activate(this.state.defaultMode, id);
|
|
147
247
|
}
|
|
148
248
|
setDefaultMode(modeId) {
|
|
149
249
|
if (!this.modes.has(modeId)) {
|
|
@@ -151,78 +251,58 @@ const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePl
|
|
|
151
251
|
}
|
|
152
252
|
this.dispatch(setDefaultMode(modeId));
|
|
153
253
|
}
|
|
154
|
-
|
|
155
|
-
this.
|
|
156
|
-
var _a;
|
|
157
|
-
(_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
|
|
158
|
-
});
|
|
159
|
-
this.alwaysPage.forEach((handlerSet) => {
|
|
160
|
-
handlerSet.forEach((handler) => {
|
|
161
|
-
var _a;
|
|
162
|
-
(_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
const mode = this.modes.get(modeId);
|
|
166
|
-
if (!mode) return;
|
|
167
|
-
const bucket = this.buckets.get(modeId);
|
|
168
|
-
if (!bucket) return;
|
|
169
|
-
if (mode.scope === "global") {
|
|
170
|
-
bucket.global.forEach((handler) => {
|
|
171
|
-
var _a;
|
|
172
|
-
(_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
if (mode.scope === "page") {
|
|
176
|
-
bucket.page.forEach((handlerSet, pageIndex) => {
|
|
177
|
-
handlerSet.forEach((handler) => {
|
|
178
|
-
var _a;
|
|
179
|
-
(_a = handler.onHandlerActiveStart) == null ? void 0 : _a.call(handler, modeId);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
}
|
|
254
|
+
getActiveMode(documentId) {
|
|
255
|
+
return this.getDocumentStateOrThrow(documentId).activeMode;
|
|
183
256
|
}
|
|
184
|
-
|
|
185
|
-
this.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
bucket.global.forEach((handler) => {
|
|
201
|
-
var _a;
|
|
202
|
-
(_a = handler.onHandlerActiveEnd) == null ? void 0 : _a.call(handler, modeId);
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
if (mode.scope === "page") {
|
|
206
|
-
bucket.page.forEach((handlerSet, pageIndex) => {
|
|
207
|
-
handlerSet.forEach((handler) => {
|
|
208
|
-
var _a;
|
|
209
|
-
(_a = handler.onHandlerActiveEnd) == null ? void 0 : _a.call(handler, modeId);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
}
|
|
257
|
+
getActiveInteractionMode(documentId) {
|
|
258
|
+
const docState = this.getDocumentState(documentId);
|
|
259
|
+
if (!docState) return null;
|
|
260
|
+
return this.modes.get(docState.activeMode) ?? null;
|
|
261
|
+
}
|
|
262
|
+
activeModeIsExclusive(documentId) {
|
|
263
|
+
const mode = this.getActiveInteractionMode(documentId);
|
|
264
|
+
return !!(mode == null ? void 0 : mode.exclusive);
|
|
265
|
+
}
|
|
266
|
+
pause(documentId) {
|
|
267
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
268
|
+
this.dispatch(pauseInteraction(id));
|
|
269
|
+
}
|
|
270
|
+
resume(documentId) {
|
|
271
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
272
|
+
this.dispatch(resumeInteraction(id));
|
|
213
273
|
}
|
|
274
|
+
isPaused(documentId) {
|
|
275
|
+
return this.getDocumentStateOrThrow(documentId).paused;
|
|
276
|
+
}
|
|
277
|
+
// ─────────────────────────────────────────────────────────
|
|
278
|
+
// Mode Management
|
|
279
|
+
// ─────────────────────────────────────────────────────────
|
|
214
280
|
registerMode(mode) {
|
|
215
281
|
this.modes.set(mode.id, mode);
|
|
216
|
-
|
|
217
|
-
this.buckets.
|
|
282
|
+
for (const documentId of this.buckets.keys()) {
|
|
283
|
+
const docBuckets = this.buckets.get(documentId);
|
|
284
|
+
if (!docBuckets.has(mode.id)) {
|
|
285
|
+
docBuckets.set(mode.id, { global: /* @__PURE__ */ new Set(), page: /* @__PURE__ */ new Map() });
|
|
286
|
+
}
|
|
218
287
|
}
|
|
219
288
|
}
|
|
220
|
-
|
|
221
|
-
|
|
289
|
+
// ─────────────────────────────────────────────────────────
|
|
290
|
+
// Handler Management
|
|
291
|
+
// ─────────────────────────────────────────────────────────
|
|
292
|
+
registerHandlers({
|
|
293
|
+
documentId,
|
|
294
|
+
modeId,
|
|
295
|
+
handlers,
|
|
296
|
+
pageIndex
|
|
297
|
+
}) {
|
|
222
298
|
const modeIds = Array.isArray(modeId) ? modeId : [modeId];
|
|
223
299
|
const cleanupFunctions = [];
|
|
300
|
+
const docBuckets = this.buckets.get(documentId);
|
|
301
|
+
if (!docBuckets) {
|
|
302
|
+
throw new Error(`No buckets found for document: ${documentId}`);
|
|
303
|
+
}
|
|
224
304
|
for (const id of modeIds) {
|
|
225
|
-
const bucket =
|
|
305
|
+
const bucket = docBuckets.get(id);
|
|
226
306
|
if (!bucket) throw new Error(`unknown mode '${id}'`);
|
|
227
307
|
if (pageIndex == null) {
|
|
228
308
|
bucket.global.add(handlers);
|
|
@@ -253,72 +333,182 @@ const _InteractionManagerPlugin = class _InteractionManagerPlugin extends BasePl
|
|
|
253
333
|
}
|
|
254
334
|
registerAlways({ scope, handlers }) {
|
|
255
335
|
if (scope.type === "global") {
|
|
256
|
-
this.alwaysGlobal.
|
|
336
|
+
const set2 = this.alwaysGlobal.get(scope.documentId) ?? /* @__PURE__ */ new Set();
|
|
337
|
+
set2.add(handlers);
|
|
338
|
+
this.alwaysGlobal.set(scope.documentId, set2);
|
|
257
339
|
this.onHandlerChange$.emit({ ...this.state });
|
|
258
|
-
return () =>
|
|
340
|
+
return () => {
|
|
341
|
+
set2.delete(handlers);
|
|
342
|
+
this.onHandlerChange$.emit({ ...this.state });
|
|
343
|
+
};
|
|
259
344
|
}
|
|
260
|
-
const
|
|
345
|
+
const docPageMap = this.alwaysPage.get(scope.documentId) ?? /* @__PURE__ */ new Map();
|
|
346
|
+
const set = docPageMap.get(scope.pageIndex) ?? /* @__PURE__ */ new Set();
|
|
261
347
|
set.add(handlers);
|
|
262
|
-
|
|
348
|
+
docPageMap.set(scope.pageIndex, set);
|
|
349
|
+
this.alwaysPage.set(scope.documentId, docPageMap);
|
|
263
350
|
this.onHandlerChange$.emit({ ...this.state });
|
|
264
351
|
return () => {
|
|
265
352
|
set.delete(handlers);
|
|
266
353
|
this.onHandlerChange$.emit({ ...this.state });
|
|
267
354
|
};
|
|
268
355
|
}
|
|
269
|
-
/** Returns the *merged* handler set that should be active for the given
|
|
270
|
-
* provider (`global` wrapper or a single page wrapper).
|
|
271
|
-
* – `alwaysGlobal` / `alwaysPage` are **always** active.
|
|
272
|
-
* – Handlers that belong to the current mode are added on top **iff**
|
|
273
|
-
* the mode’s own scope matches the provider’s scope. */
|
|
274
356
|
getHandlersForScope(scope) {
|
|
275
|
-
|
|
276
|
-
const
|
|
357
|
+
var _a;
|
|
358
|
+
const docState = this.getDocumentState(scope.documentId);
|
|
359
|
+
if (!docState) return null;
|
|
360
|
+
const mode = this.modes.get(docState.activeMode);
|
|
277
361
|
if (!mode) return null;
|
|
278
|
-
const
|
|
362
|
+
const docBuckets = this.buckets.get(scope.documentId);
|
|
363
|
+
if (!docBuckets) return null;
|
|
364
|
+
const bucket = docBuckets.get(mode.id);
|
|
279
365
|
if (!bucket) return null;
|
|
280
366
|
const mergeSets = (a, b) => a.size || b.size ? mergeHandlers([...a, ...b]) : null;
|
|
281
367
|
if (scope.type === "global") {
|
|
368
|
+
const alwaysSet = this.alwaysGlobal.get(scope.documentId) ?? /* @__PURE__ */ new Set();
|
|
282
369
|
const modeSpecific = mode.scope === "global" ? bucket.global : /* @__PURE__ */ new Set();
|
|
283
|
-
return mergeSets(
|
|
370
|
+
return mergeSets(alwaysSet, modeSpecific);
|
|
284
371
|
}
|
|
285
|
-
const alwaysPageSet = this.alwaysPage.get(scope.pageIndex) ?? /* @__PURE__ */ new Set();
|
|
372
|
+
const alwaysPageSet = ((_a = this.alwaysPage.get(scope.documentId)) == null ? void 0 : _a.get(scope.pageIndex)) ?? /* @__PURE__ */ new Set();
|
|
286
373
|
const modePageSet = mode.scope === "page" ? bucket.page.get(scope.pageIndex) ?? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set();
|
|
287
374
|
return mergeSets(alwaysPageSet, modePageSet);
|
|
288
375
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
376
|
+
// ─────────────────────────────────────────────────────────
|
|
377
|
+
// Cursor Management
|
|
378
|
+
// ─────────────────────────────────────────────────────────
|
|
379
|
+
setCursor(token, cursor, priority = 0, documentId) {
|
|
380
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
381
|
+
const claims = this.cursorClaims.get(id);
|
|
382
|
+
if (!claims) return;
|
|
383
|
+
claims.set(token, { cursor, priority });
|
|
384
|
+
this.emitCursor(id);
|
|
293
385
|
}
|
|
294
|
-
removeCursor(token) {
|
|
295
|
-
this.
|
|
296
|
-
this.
|
|
386
|
+
removeCursor(token, documentId) {
|
|
387
|
+
const id = documentId ?? this.getActiveDocumentId();
|
|
388
|
+
const claims = this.cursorClaims.get(id);
|
|
389
|
+
if (!claims) return;
|
|
390
|
+
claims.delete(token);
|
|
391
|
+
this.emitCursor(id);
|
|
297
392
|
}
|
|
298
|
-
|
|
393
|
+
getCurrentCursor(documentId) {
|
|
394
|
+
return this.getDocumentStateOrThrow(documentId).cursor;
|
|
395
|
+
}
|
|
396
|
+
emitCursor(documentId) {
|
|
299
397
|
var _a;
|
|
300
|
-
const
|
|
301
|
-
|
|
398
|
+
const claims = this.cursorClaims.get(documentId);
|
|
399
|
+
if (!claims) return;
|
|
400
|
+
const docState = this.getDocumentState(documentId);
|
|
401
|
+
if (!docState) return;
|
|
402
|
+
const top = [...claims.values()].sort((a, b) => b.priority - a.priority)[0] ?? {
|
|
403
|
+
cursor: ((_a = this.modes.get(docState.activeMode)) == null ? void 0 : _a.cursor) ?? "auto"
|
|
302
404
|
};
|
|
303
|
-
if (top.cursor !==
|
|
304
|
-
this.dispatch(setCursor(top.cursor));
|
|
305
|
-
this.onCursorChange$.emit(
|
|
405
|
+
if (top.cursor !== docState.cursor) {
|
|
406
|
+
this.dispatch(setCursor(documentId, top.cursor));
|
|
407
|
+
this.onCursorChange$.emit({
|
|
408
|
+
documentId,
|
|
409
|
+
cursor: top.cursor
|
|
410
|
+
});
|
|
306
411
|
}
|
|
307
412
|
}
|
|
308
|
-
|
|
309
|
-
|
|
413
|
+
// ─────────────────────────────────────────────────────────
|
|
414
|
+
// Handler Lifecycle Notifications
|
|
415
|
+
// ─────────────────────────────────────────────────────────
|
|
416
|
+
notifyHandlersActive(documentId, modeId) {
|
|
417
|
+
var _a, _b;
|
|
418
|
+
(_a = this.alwaysGlobal.get(documentId)) == null ? void 0 : _a.forEach((handler) => {
|
|
419
|
+
var _a2;
|
|
420
|
+
(_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
|
|
421
|
+
});
|
|
422
|
+
(_b = this.alwaysPage.get(documentId)) == null ? void 0 : _b.forEach((handlerSet) => {
|
|
423
|
+
handlerSet.forEach((handler) => {
|
|
424
|
+
var _a2;
|
|
425
|
+
(_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
const mode = this.modes.get(modeId);
|
|
429
|
+
if (!mode) return;
|
|
430
|
+
const docBuckets = this.buckets.get(documentId);
|
|
431
|
+
if (!docBuckets) return;
|
|
432
|
+
const bucket = docBuckets.get(modeId);
|
|
433
|
+
if (!bucket) return;
|
|
434
|
+
if (mode.scope === "global") {
|
|
435
|
+
bucket.global.forEach((handler) => {
|
|
436
|
+
var _a2;
|
|
437
|
+
(_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
if (mode.scope === "page") {
|
|
441
|
+
bucket.page.forEach((handlerSet) => {
|
|
442
|
+
handlerSet.forEach((handler) => {
|
|
443
|
+
var _a2;
|
|
444
|
+
(_a2 = handler.onHandlerActiveStart) == null ? void 0 : _a2.call(handler, modeId);
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
310
448
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
449
|
+
notifyHandlersInactive(documentId, modeId) {
|
|
450
|
+
var _a, _b;
|
|
451
|
+
(_a = this.alwaysGlobal.get(documentId)) == null ? void 0 : _a.forEach((handler) => {
|
|
452
|
+
var _a2;
|
|
453
|
+
(_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
|
|
454
|
+
});
|
|
455
|
+
(_b = this.alwaysPage.get(documentId)) == null ? void 0 : _b.forEach((handlerSet) => {
|
|
456
|
+
handlerSet.forEach((handler) => {
|
|
457
|
+
var _a2;
|
|
458
|
+
(_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
const mode = this.modes.get(modeId);
|
|
462
|
+
if (!mode) return;
|
|
463
|
+
const docBuckets = this.buckets.get(documentId);
|
|
464
|
+
if (!docBuckets) return;
|
|
465
|
+
const bucket = docBuckets.get(modeId);
|
|
466
|
+
if (!bucket) return;
|
|
467
|
+
if (mode.scope === "global") {
|
|
468
|
+
bucket.global.forEach((handler) => {
|
|
469
|
+
var _a2;
|
|
470
|
+
(_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
if (mode.scope === "page") {
|
|
474
|
+
bucket.page.forEach((handlerSet) => {
|
|
475
|
+
handlerSet.forEach((handler) => {
|
|
476
|
+
var _a2;
|
|
477
|
+
(_a2 = handler.onHandlerActiveEnd) == null ? void 0 : _a2.call(handler, modeId);
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// ─────────────────────────────────────────────────────────
|
|
483
|
+
// Store Update Handlers
|
|
484
|
+
// ─────────────────────────────────────────────────────────
|
|
485
|
+
onStoreUpdated(prevState, newState) {
|
|
486
|
+
for (const documentId in newState.documents) {
|
|
487
|
+
const prevDoc = prevState.documents[documentId];
|
|
488
|
+
const newDoc = newState.documents[documentId];
|
|
489
|
+
if (prevDoc !== newDoc) {
|
|
490
|
+
this.onStateChange$.emit({
|
|
491
|
+
documentId,
|
|
492
|
+
state: newDoc
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
314
496
|
}
|
|
315
|
-
|
|
316
|
-
|
|
497
|
+
// ─────────────────────────────────────────────────────────
|
|
498
|
+
// Lifecycle
|
|
499
|
+
// ─────────────────────────────────────────────────────────
|
|
500
|
+
async initialize(_) {
|
|
501
|
+
this.logger.info(
|
|
502
|
+
"InteractionManagerPlugin",
|
|
503
|
+
"Initialize",
|
|
504
|
+
"Interaction Manager Plugin initialized"
|
|
505
|
+
);
|
|
317
506
|
}
|
|
318
|
-
// keep emitter clean
|
|
319
507
|
async destroy() {
|
|
320
508
|
this.onModeChange$.clear();
|
|
321
509
|
this.onCursorChange$.clear();
|
|
510
|
+
this.onHandlerChange$.clear();
|
|
511
|
+
this.onStateChange$.clear();
|
|
322
512
|
await super.destroy();
|
|
323
513
|
}
|
|
324
514
|
};
|
|
@@ -340,38 +530,119 @@ const manifest = {
|
|
|
340
530
|
}
|
|
341
531
|
}
|
|
342
532
|
};
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
533
|
+
const INITIAL_MODE = "pointerMode";
|
|
534
|
+
const initialDocumentState = {
|
|
535
|
+
activeMode: INITIAL_MODE,
|
|
346
536
|
cursor: "auto",
|
|
347
|
-
paused: false
|
|
537
|
+
paused: false
|
|
538
|
+
};
|
|
539
|
+
const initialState = {
|
|
540
|
+
defaultMode: INITIAL_MODE,
|
|
348
541
|
exclusionRules: {
|
|
349
542
|
classes: [],
|
|
350
|
-
dataAttributes: []
|
|
351
|
-
}
|
|
543
|
+
dataAttributes: ["data-no-interaction"]
|
|
544
|
+
},
|
|
545
|
+
documents: {},
|
|
546
|
+
activeDocumentId: null
|
|
352
547
|
};
|
|
353
|
-
const reducer = (state, action) => {
|
|
548
|
+
const reducer = (state = initialState, action) => {
|
|
354
549
|
switch (action.type) {
|
|
355
|
-
|
|
550
|
+
// ─────────────────────────────────────────────────────────
|
|
551
|
+
// Document Lifecycle
|
|
552
|
+
// ─────────────────────────────────────────────────────────
|
|
553
|
+
case INIT_INTERACTION_STATE: {
|
|
554
|
+
const { documentId, state: docState } = action.payload;
|
|
555
|
+
return {
|
|
556
|
+
...state,
|
|
557
|
+
documents: {
|
|
558
|
+
...state.documents,
|
|
559
|
+
[documentId]: docState
|
|
560
|
+
},
|
|
561
|
+
// Set as active if no active document
|
|
562
|
+
activeDocumentId: state.activeDocumentId ?? documentId
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
case CLEANUP_INTERACTION_STATE: {
|
|
566
|
+
const documentId = action.payload;
|
|
567
|
+
const { [documentId]: removed, ...remainingDocs } = state.documents;
|
|
568
|
+
return {
|
|
569
|
+
...state,
|
|
570
|
+
documents: remainingDocs,
|
|
571
|
+
activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
case SET_ACTIVE_DOCUMENT: {
|
|
356
575
|
return {
|
|
357
576
|
...state,
|
|
358
|
-
|
|
577
|
+
activeDocumentId: action.payload
|
|
359
578
|
};
|
|
360
|
-
|
|
579
|
+
}
|
|
580
|
+
// ─────────────────────────────────────────────────────────
|
|
581
|
+
// Per-Document Actions
|
|
582
|
+
// ─────────────────────────────────────────────────────────
|
|
583
|
+
case ACTIVATE_MODE: {
|
|
584
|
+
const { documentId, mode } = action.payload;
|
|
585
|
+
const docState = state.documents[documentId];
|
|
586
|
+
if (!docState) return state;
|
|
361
587
|
return {
|
|
362
588
|
...state,
|
|
363
|
-
|
|
589
|
+
documents: {
|
|
590
|
+
...state.documents,
|
|
591
|
+
[documentId]: {
|
|
592
|
+
...docState,
|
|
593
|
+
activeMode: mode
|
|
594
|
+
}
|
|
595
|
+
}
|
|
364
596
|
};
|
|
365
|
-
|
|
597
|
+
}
|
|
598
|
+
case SET_CURSOR: {
|
|
599
|
+
const { documentId, cursor } = action.payload;
|
|
600
|
+
const docState = state.documents[documentId];
|
|
601
|
+
if (!docState) return state;
|
|
366
602
|
return {
|
|
367
603
|
...state,
|
|
368
|
-
|
|
604
|
+
documents: {
|
|
605
|
+
...state.documents,
|
|
606
|
+
[documentId]: {
|
|
607
|
+
...docState,
|
|
608
|
+
cursor
|
|
609
|
+
}
|
|
610
|
+
}
|
|
369
611
|
};
|
|
370
|
-
|
|
612
|
+
}
|
|
613
|
+
case PAUSE_INTERACTION: {
|
|
614
|
+
const documentId = action.payload;
|
|
615
|
+
const docState = state.documents[documentId];
|
|
616
|
+
if (!docState) return state;
|
|
371
617
|
return {
|
|
372
618
|
...state,
|
|
373
|
-
|
|
619
|
+
documents: {
|
|
620
|
+
...state.documents,
|
|
621
|
+
[documentId]: {
|
|
622
|
+
...docState,
|
|
623
|
+
paused: true
|
|
624
|
+
}
|
|
625
|
+
}
|
|
374
626
|
};
|
|
627
|
+
}
|
|
628
|
+
case RESUME_INTERACTION: {
|
|
629
|
+
const documentId = action.payload;
|
|
630
|
+
const docState = state.documents[documentId];
|
|
631
|
+
if (!docState) return state;
|
|
632
|
+
return {
|
|
633
|
+
...state,
|
|
634
|
+
documents: {
|
|
635
|
+
...state.documents,
|
|
636
|
+
[documentId]: {
|
|
637
|
+
...docState,
|
|
638
|
+
paused: false
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
// ─────────────────────────────────────────────────────────
|
|
644
|
+
// Global Actions
|
|
645
|
+
// ─────────────────────────────────────────────────────────
|
|
375
646
|
case SET_DEFAULT_MODE:
|
|
376
647
|
return {
|
|
377
648
|
...state,
|
|
@@ -390,7 +661,6 @@ const reducer = (state, action) => {
|
|
|
390
661
|
classes: [...state.exclusionRules.classes || [], action.payload.className].filter(
|
|
391
662
|
(v, i, a) => a.indexOf(v) === i
|
|
392
663
|
)
|
|
393
|
-
// Remove duplicates
|
|
394
664
|
}
|
|
395
665
|
};
|
|
396
666
|
case REMOVE_EXCLUSION_CLASS:
|
|
@@ -438,6 +708,7 @@ export {
|
|
|
438
708
|
INTERACTION_MANAGER_PLUGIN_ID,
|
|
439
709
|
InteractionManagerPlugin,
|
|
440
710
|
InteractionManagerPluginPackage,
|
|
711
|
+
initialDocumentState,
|
|
441
712
|
initialState,
|
|
442
713
|
manifest,
|
|
443
714
|
reducer
|