@pie-players/pie-tool-answer-eliminator 0.3.42 → 0.3.44

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.
Files changed (33) hide show
  1. package/dist/adapters/adapter-registry.d.ts +0 -1
  2. package/dist/adapters/choice-adapter.d.ts +0 -1
  3. package/dist/adapters/ebsr-adapter.d.ts +0 -1
  4. package/dist/adapters/inline-dropdown-adapter.d.ts +0 -1
  5. package/dist/adapters/multiple-choice-adapter.d.ts +0 -1
  6. package/dist/answer-eliminator-core.d.ts +0 -1
  7. package/dist/index.d.ts +0 -1
  8. package/dist/strategies/elimination-strategy.d.ts +0 -1
  9. package/dist/strategies/mask-strategy.d.ts +0 -1
  10. package/dist/strategies/strikethrough-strategy.d.ts +0 -1
  11. package/dist/vite.config.d.ts +0 -1
  12. package/package.json +6 -12
  13. package/adapters/adapter-registry.ts +0 -64
  14. package/adapters/choice-adapter.ts +0 -50
  15. package/adapters/ebsr-adapter.ts +0 -61
  16. package/adapters/inline-dropdown-adapter.ts +0 -46
  17. package/adapters/multiple-choice-adapter.ts +0 -144
  18. package/answer-eliminator-core.ts +0 -391
  19. package/dist/adapters/adapter-registry.d.ts.map +0 -1
  20. package/dist/adapters/choice-adapter.d.ts.map +0 -1
  21. package/dist/adapters/ebsr-adapter.d.ts.map +0 -1
  22. package/dist/adapters/inline-dropdown-adapter.d.ts.map +0 -1
  23. package/dist/adapters/multiple-choice-adapter.d.ts.map +0 -1
  24. package/dist/answer-eliminator-core.d.ts.map +0 -1
  25. package/dist/index.d.ts.map +0 -1
  26. package/dist/strategies/elimination-strategy.d.ts.map +0 -1
  27. package/dist/strategies/mask-strategy.d.ts.map +0 -1
  28. package/dist/strategies/strikethrough-strategy.d.ts.map +0 -1
  29. package/dist/vite.config.d.ts.map +0 -1
  30. package/strategies/elimination-strategy.ts +0 -47
  31. package/strategies/mask-strategy.ts +0 -171
  32. package/strategies/strikethrough-strategy.ts +0 -223
  33. package/tool-answer-eliminator.svelte +0 -223
@@ -1,391 +0,0 @@
1
- import { AdapterRegistry } from "./adapters/adapter-registry.js";
2
- import type { ChoiceAdapter } from "./adapters/choice-adapter.js";
3
- import type { EliminationStrategy } from "./strategies/elimination-strategy.js";
4
- import { MaskStrategy } from "./strategies/mask-strategy.js";
5
- import { StrikethroughStrategy } from "./strategies/strikethrough-strategy.js";
6
-
7
- /**
8
- * Core engine for answer eliminator tool
9
- * Coordinates adapters, strategies, and state management
10
- */
11
- export class AnswerEliminatorCore {
12
- private static readonly TOGGLE_CLASS = "pie-answer-eliminator-toggle";
13
- private static readonly TOGGLE_ACTIVE_CLASS =
14
- "pie-answer-eliminator-toggle--active";
15
- private registry: AdapterRegistry;
16
- private strategy: EliminationStrategy;
17
- private eliminatedChoices = new Set<string>(); // Set<choiceId> for current element
18
- private choiceElements = new Map<string, HTMLElement>(); // choiceId -> element
19
- private choiceButtons = new Map<string, HTMLButtonElement>(); // choiceId -> button
20
- private choiceAdapters = new Map<string, ChoiceAdapter>(); // choiceId -> adapter
21
- private buttonAlignment: "left" | "right" | "inline" = "right";
22
- private shouldRestoreState: boolean = true; // Whether to restore eliminations from state storage
23
-
24
- // Store integration (replaces session/localStorage)
25
- private storeIntegration: {
26
- store: any; // ElementToolStateStore
27
- globalElementId: string; // Composite key: "assessmentId:sectionId:itemId:elementId"
28
- } | null = null;
29
-
30
- constructor(
31
- strategyType: "strikethrough" | "mask" | "gray" = "strikethrough",
32
- buttonAlignment: "left" | "right" | "inline" = "right",
33
- ) {
34
- this.registry = new AdapterRegistry();
35
- this.strategy = this.createStrategy(strategyType);
36
- this.strategy.initialize();
37
- this.buttonAlignment = buttonAlignment;
38
- }
39
-
40
- private createStrategy(type: string): EliminationStrategy {
41
- switch (type) {
42
- case "mask":
43
- return new MaskStrategy();
44
- case "strikethrough":
45
- default:
46
- return new StrikethroughStrategy();
47
- }
48
- }
49
-
50
- /**
51
- * Initialize eliminator for a question
52
- */
53
- initializeForQuestion(questionRoot: HTMLElement): void {
54
- // Clean up previous question
55
- this.cleanupButtons();
56
-
57
- // Find all choices with their adapters
58
- const choicesWithAdapters =
59
- this.registry.findAllChoicesWithAdapters(questionRoot);
60
-
61
- // Attach elimination functionality to each choice
62
- for (const { choice, adapter } of choicesWithAdapters) {
63
- this.initializeChoice(choice, adapter);
64
- }
65
-
66
- // Restore eliminated state from store (only if enabled)
67
- if (this.shouldRestoreState) {
68
- this.restoreState();
69
- }
70
- }
71
-
72
- /**
73
- * Initialize a single choice
74
- */
75
- private initializeChoice(choice: HTMLElement, adapter: ChoiceAdapter): void {
76
- const choiceId = adapter.getChoiceId(choice);
77
-
78
- // Track element
79
- this.choiceElements.set(choiceId, choice);
80
- this.choiceAdapters.set(choiceId, adapter);
81
-
82
- // Create elimination toggle button
83
- const button = this.createToggleButton(choice, adapter);
84
- if (!button) return;
85
-
86
- this.choiceButtons.set(choiceId, button);
87
-
88
- // Attach button to choice
89
- const container = adapter.getButtonContainer(choice);
90
- if (container) {
91
- // Position button within container
92
- container.style.position = "relative";
93
- container.appendChild(button);
94
- }
95
- }
96
-
97
- /**
98
- * Create elimination toggle button
99
- */
100
- private createToggleButton(
101
- choice: HTMLElement,
102
- adapter: ChoiceAdapter,
103
- ): HTMLButtonElement | null {
104
- const choiceId = adapter.getChoiceId(choice);
105
- const choiceLabel = adapter.getChoiceLabel(choice);
106
-
107
- const button = document.createElement("button");
108
- button.type = "button";
109
- button.className = AnswerEliminatorCore.TOGGLE_CLASS;
110
- button.setAttribute("aria-label", `Toggle elimination for ${choiceLabel}`);
111
- button.setAttribute("data-choice-id", choiceId);
112
- button.textContent = "⊗"; // Cross mark (use textContent instead of innerHTML for better security)
113
-
114
- // Apply positioning based on alignment configuration
115
- this.applyButtonAlignment(button);
116
-
117
- button.addEventListener("click", (e) => {
118
- e.preventDefault();
119
- e.stopPropagation();
120
- this.toggleElimination(choice, adapter);
121
- });
122
-
123
- return button;
124
- }
125
-
126
- /**
127
- * Toggle elimination for a choice
128
- */
129
- toggleElimination(choice: HTMLElement, adapter: ChoiceAdapter): void {
130
- const choiceId = adapter.getChoiceId(choice);
131
-
132
- // Check if already eliminated
133
- const isEliminated = this.strategy.isEliminated(choiceId);
134
-
135
- if (isEliminated) {
136
- // Restore
137
- this.restoreChoice(choiceId);
138
- } else {
139
- // Eliminate
140
- if (!adapter.canEliminate(choice)) {
141
- console.warn(
142
- "Cannot eliminate this choice (already selected or in evaluate mode)",
143
- );
144
- return;
145
- }
146
-
147
- this.eliminateChoice(choice, adapter);
148
- }
149
-
150
- // Save state
151
- this.saveState();
152
- }
153
-
154
- /**
155
- * Eliminate a choice
156
- */
157
- private eliminateChoice(choice: HTMLElement, adapter: ChoiceAdapter): void {
158
- const choiceId = adapter.getChoiceId(choice);
159
-
160
- // Create range for CSS Highlight API
161
- const range = adapter.createChoiceRange(choice);
162
- if (!range) {
163
- console.error("Failed to create range for choice");
164
- return;
165
- }
166
-
167
- // Apply strategy
168
- this.strategy.apply(choiceId, range);
169
-
170
- // Track in state
171
- this.eliminatedChoices.add(choiceId);
172
-
173
- // Update button appearance to show eliminated state
174
- const button = this.choiceButtons.get(choiceId);
175
- if (button) {
176
- button.classList.add(AnswerEliminatorCore.TOGGLE_ACTIVE_CLASS);
177
- button.setAttribute("aria-pressed", "true");
178
- }
179
-
180
- // Save to store
181
- this.saveState();
182
- }
183
-
184
- /**
185
- * Restore a choice
186
- */
187
- private restoreChoice(choiceId: string): void {
188
- // Remove from strategy
189
- this.strategy.remove(choiceId);
190
-
191
- // Remove from state
192
- this.eliminatedChoices.delete(choiceId);
193
-
194
- // Reset button appearance to default state
195
- const button = this.choiceButtons.get(choiceId);
196
- if (button) {
197
- button.classList.remove(AnswerEliminatorCore.TOGGLE_ACTIVE_CLASS);
198
- button.setAttribute("aria-pressed", "false");
199
- }
200
-
201
- // Save to store
202
- this.saveState();
203
- }
204
-
205
- /**
206
- * Reset all eliminations for current element
207
- */
208
- resetAll(): void {
209
- if (this.eliminatedChoices.size === 0) return;
210
-
211
- // Restore all choices
212
- for (const choiceId of Array.from(this.eliminatedChoices)) {
213
- this.restoreChoice(choiceId);
214
- }
215
-
216
- // Clear state
217
- this.eliminatedChoices.clear();
218
- this.saveState();
219
- }
220
-
221
- /**
222
- * Get count of eliminated choices for current element
223
- */
224
- getEliminatedCount(): number {
225
- return this.eliminatedChoices.size;
226
- }
227
-
228
- /**
229
- * Set store integration for element-level state
230
- * @param store ElementToolStateStore instance
231
- * @param globalElementId Composite key: "assessmentId:sectionId:itemId:elementId"
232
- */
233
- setStoreIntegration(store: any, globalElementId: string): void {
234
- this.storeIntegration = { store, globalElementId };
235
- }
236
-
237
- /**
238
- * Save state to ElementToolStateStore
239
- */
240
- private saveState(): void {
241
- if (!this.storeIntegration) return;
242
-
243
- const state = {
244
- eliminatedChoices: Array.from(this.eliminatedChoices),
245
- };
246
-
247
- this.storeIntegration.store.setState(
248
- this.storeIntegration.globalElementId,
249
- "answerEliminator",
250
- state,
251
- );
252
- }
253
-
254
- /**
255
- * Restore state from ElementToolStateStore
256
- */
257
- private restoreState(): void {
258
- if (!this.storeIntegration) return;
259
-
260
- const state = this.storeIntegration.store.getState(
261
- this.storeIntegration.globalElementId,
262
- "answerEliminator",
263
- );
264
-
265
- if (!state || !state.eliminatedChoices) return;
266
-
267
- try {
268
- const eliminated = state.eliminatedChoices;
269
-
270
- if (!eliminated || eliminated.length === 0) return;
271
-
272
- // Restore eliminated choices for current element
273
- for (const choiceId of eliminated) {
274
- const choice = this.choiceElements.get(choiceId);
275
- if (!choice) continue;
276
-
277
- // Use the adapter captured during initialization
278
- const adapter = this.choiceAdapters.get(choiceId);
279
- if (!adapter) continue;
280
-
281
- // Re-eliminate without saving (already in state)
282
- const range = adapter.createChoiceRange(choice);
283
- if (range) {
284
- this.strategy.apply(choiceId, range);
285
-
286
- // Track in memory
287
- this.eliminatedChoices.add(choiceId);
288
-
289
- // Update button appearance to show eliminated state
290
- const button = this.choiceButtons.get(choiceId);
291
- if (button) {
292
- button.classList.add(AnswerEliminatorCore.TOGGLE_ACTIVE_CLASS);
293
- button.setAttribute("aria-pressed", "true");
294
- }
295
- }
296
- }
297
- } catch (error) {
298
- console.error("Failed to restore eliminator state:", error);
299
- }
300
- }
301
-
302
- /**
303
- * Cleanup buttons from previous element
304
- */
305
- private cleanupButtons(): void {
306
- for (const button of this.choiceButtons.values()) {
307
- button.remove();
308
- }
309
-
310
- this.choiceButtons.clear();
311
- this.choiceElements.clear();
312
- this.choiceAdapters.clear();
313
- }
314
-
315
- /**
316
- * Apply button positioning based on alignment configuration
317
- */
318
- private applyButtonAlignment(button: HTMLButtonElement): void {
319
- switch (this.buttonAlignment) {
320
- case "right":
321
- // Right-aligned (industry standard) - after choice text
322
- Object.assign(button.style, {
323
- position: "absolute",
324
- right: "8px",
325
- top: "50%",
326
- transform: "translateY(-50%)",
327
- });
328
- break;
329
-
330
- case "left":
331
- // Left-aligned - before choice text
332
- Object.assign(button.style, {
333
- position: "absolute",
334
- left: "8px",
335
- top: "50%",
336
- transform: "translateY(-50%)",
337
- });
338
- break;
339
-
340
- case "inline":
341
- // Inline with checkbox - no absolute positioning
342
- Object.assign(button.style, {
343
- position: "relative",
344
- marginLeft: "8px",
345
- marginRight: "8px",
346
- display: "inline-flex",
347
- verticalAlign: "middle",
348
- });
349
- break;
350
- }
351
- }
352
-
353
- /**
354
- * Enable state restoration from localStorage
355
- */
356
- enableStateRestoration(): void {
357
- this.shouldRestoreState = true;
358
- }
359
-
360
- /**
361
- * Disable state restoration from localStorage
362
- */
363
- disableStateRestoration(): void {
364
- this.shouldRestoreState = false;
365
- }
366
-
367
- /**
368
- * Cleanup when tool is turned off (but don't destroy strategy)
369
- * Hides elimination buttons AND clears all visual eliminations
370
- * Note: State is preserved in localStorage for when tool is turned back on
371
- */
372
- cleanup(): void {
373
- // Disable state restoration to prevent restoreState() from re-applying eliminations
374
- this.disableStateRestoration();
375
-
376
- // Remove all buttons
377
- this.cleanupButtons();
378
-
379
- // Clear all visual eliminations (strikethroughs)
380
- // This removes the CSS highlights but keeps localStorage state
381
- this.strategy.clearAll();
382
- }
383
-
384
- /**
385
- * Destroy and cleanup
386
- */
387
- destroy(): void {
388
- this.cleanupButtons();
389
- this.strategy.destroy();
390
- }
391
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter-registry.d.ts","sourceRoot":"","sources":["../../adapters/adapter-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;CACvB;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAkB;;IAWlC;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,GAAG,IAAI;IAIvD;;OAEG;IACH,0BAA0B,CAAC,IAAI,EAAE,WAAW,GAAG,iBAAiB,EAAE;IAoBlE;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;CAI7C"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"choice-adapter.d.ts","sourceRoot":"","sources":["../../adapters/choice-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC;IAEzC;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IAE9C;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI,CAAC;IAErD;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC;IAEzC;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC;IAE5C;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC;IAE3C;;OAEG;IACH,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC;CAC5D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ebsr-adapter.d.ts","sourceRoot":"","sources":["../../adapters/ebsr-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,aAAa;IAChD,QAAQ,CAAC,WAAW,UAAU;IAC9B,QAAQ,CAAC,QAAQ,MAAM;IAEvB,OAAO,CAAC,SAAS,CAA+B;IAEhD,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO;IAOxC,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,EAAE;IAgB7C,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI;IAIpD,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAOxC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAI3C,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO;IAI1C,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI;CAG3D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"inline-dropdown-adapter.d.ts","sourceRoot":"","sources":["../../adapters/inline-dropdown-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IAC1D,QAAQ,CAAC,WAAW,qBAAqB;IACzC,QAAQ,CAAC,QAAQ,MAAM;IAEvB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO;IAOxC,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,EAAE;IAK7C,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI;IAMpD,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAIxC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAI3C,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO;IAK1C,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI;CAG3D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"multiple-choice-adapter.d.ts","sourceRoot":"","sources":["../../adapters/multiple-choice-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IAC1D,QAAQ,CAAC,WAAW,qBAAqB;IACzC,QAAQ,CAAC,QAAQ,OAAO;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CACH;IACrC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAsC;IAC7E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAqC;IAC3E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CACE;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAC+D;IAEtG,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO;IAOxC,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,EAAE;IAY7C,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI;IAcpD,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAYxC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAK3C,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO;IA2B1C,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI;IAK3D,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,qBAAqB;CAU7B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"answer-eliminator-core.d.ts","sourceRoot":"","sources":["../answer-eliminator-core.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAKlE;;;GAGG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAkC;IACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CACH;IACxC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,eAAe,CAAwC;IAC/D,OAAO,CAAC,kBAAkB,CAAiB;IAG3C,OAAO,CAAC,gBAAgB,CAGR;gBAGf,YAAY,GAAE,eAAe,GAAG,MAAM,GAAG,MAAwB,EACjE,eAAe,GAAE,MAAM,GAAG,OAAO,GAAG,QAAkB;IAQvD,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,qBAAqB,CAAC,YAAY,EAAE,WAAW,GAAG,IAAI;IAmBtD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA0B1B;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAyBpE;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,QAAQ,IAAI,IAAI;IAahB;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAI5B;;;;OAIG;IACH,mBAAmB,CAAC,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI;IAI9D;;OAEG;IACH,OAAO,CAAC,SAAS;IAcjB;;OAEG;IACH,OAAO,CAAC,YAAY;IA6CpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAmC5B;;OAEG;IACH,sBAAsB,IAAI,IAAI;IAI9B;;OAEG;IACH,uBAAuB,IAAI,IAAI;IAI/B;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAYf;;OAEG;IACH,OAAO,IAAI,IAAI;CAIf"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"elimination-strategy.d.ts","sourceRoot":"","sources":["../../strategies/elimination-strategy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IACnC,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAE5C;;;OAGG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAExC;;OAEG;IACH,QAAQ,IAAI,IAAI,CAAC;IAEjB;;OAEG;IACH,gBAAgB,IAAI,MAAM,EAAE,CAAC;IAE7B;;OAEG;IACH,UAAU,IAAI,IAAI,CAAC;IAEnB;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mask-strategy.d.ts","sourceRoot":"","sources":["../../strategies/mask-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;;GAGG;AACH,qBAAa,YAAa,YAAW,mBAAmB;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CACL;IACzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAwB;IACrE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAgC;IACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CACH;IACrC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAgC;IACvE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAmC;IAE7E,QAAQ,CAAC,IAAI,UAAU;IAEvB,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,UAAU,IAAI,IAAI;IAIlB,OAAO,IAAI,IAAI;IAIf,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAqB3C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAqB9B,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIvC,QAAQ,IAAI,IAAI;IAOhB,gBAAgB,IAAI,MAAM,EAAE;IAI5B,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,cAAc;CAatB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"strikethrough-strategy.d.ts","sourceRoot":"","sources":["../../strategies/strikethrough-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;;;;;GAMG;AACH,qBAAa,qBAAsB,YAAW,mBAAmB;IAChE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CACV;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAA4B;IACzE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CACO;IAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAA2C;IAC3E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CACH;IACrC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAsC;IAC7E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAgC;IACvE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAmC;IAE7E,QAAQ,CAAC,IAAI,mBAAmB;IAEhC,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,UAAU,IAAI,IAAI;IAOlB,OAAO,IAAI,IAAI;IAIf,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IA0B3C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAyB9B,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIvC,QAAQ,IAAI,IAAI;IAQhB,gBAAgB,IAAI,MAAM,EAAE;IAI5B,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,oBAAoB;IAc5B,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,cAAc;IAetB,OAAO,CAAC,mBAAmB;CAO3B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"vite.config.d.ts","sourceRoot":"","sources":["../vite.config.ts"],"names":[],"mappings":";AAKA,wBAkCG"}
@@ -1,47 +0,0 @@
1
- /**
2
- * Strategy interface for visual elimination styles
3
- * Uses CSS Custom Highlight API for zero DOM mutation
4
- */
5
- export interface EliminationStrategy {
6
- /** Strategy name */
7
- readonly name: string;
8
-
9
- /**
10
- * Apply elimination visual to a choice
11
- * @param choiceId - Unique identifier for this choice
12
- * @param range - DOM Range covering the choice content
13
- */
14
- apply(choiceId: string, range: Range): void;
15
-
16
- /**
17
- * Remove elimination visual from a choice
18
- * @param choiceId - Unique identifier for this choice
19
- */
20
- remove(choiceId: string): void;
21
-
22
- /**
23
- * Check if a choice is currently eliminated
24
- * @param choiceId - Unique identifier for this choice
25
- */
26
- isEliminated(choiceId: string): boolean;
27
-
28
- /**
29
- * Clear all eliminations
30
- */
31
- clearAll(): void;
32
-
33
- /**
34
- * Get all eliminated choice IDs
35
- */
36
- getEliminatedIds(): string[];
37
-
38
- /**
39
- * Initialize strategy (inject CSS, etc.)
40
- */
41
- initialize(): void;
42
-
43
- /**
44
- * Cleanup strategy (remove CSS, etc.)
45
- */
46
- destroy(): void;
47
- }
@@ -1,171 +0,0 @@
1
- import type { EliminationStrategy } from "./elimination-strategy.js";
2
-
3
- /**
4
- * Mask strategy using CSS Custom Highlight API
5
- * Partially hides/grays eliminated choices
6
- */
7
- export class MaskStrategy implements EliminationStrategy {
8
- private static readonly HIGHLIGHT_STYLE_PREFIX =
9
- "pie-answer-eliminator-mask-highlight-";
10
- private static readonly HIGHLIGHT_NAME_PREFIX = "pie-answer-masked-";
11
- private static readonly FALLBACK_CLASS = "pie-answer-masked-fallback";
12
- private static readonly CHOICE_HOOK_ATTR =
13
- "data-pie-answer-eliminator-choice";
14
- private static readonly ELIMINATED_ATTR = "data-pie-answer-eliminated";
15
- private static readonly ELIMINATED_ID_ATTR = "data-pie-answer-eliminated-id";
16
-
17
- readonly name = "mask";
18
-
19
- private highlights = new Map<string, Highlight>();
20
- private ranges = new Map<string, Range>();
21
- private fallbackContainers = new Map<string, HTMLElement>();
22
-
23
- initialize(): void {
24
- // No-op: shared fallback styles are owned by @pie-players/pie-theme/components.css.
25
- }
26
-
27
- destroy(): void {
28
- this.clearAll();
29
- }
30
-
31
- apply(choiceId: string, range: Range): void {
32
- if (!this.isSupported()) {
33
- this.applyFallback(choiceId, range);
34
- return;
35
- }
36
-
37
- // Inject CSS for this specific highlight
38
- this.injectHighlightCSS(choiceId);
39
-
40
- const highlight = new Highlight(range);
41
- CSS.highlights.set(
42
- `${MaskStrategy.HIGHLIGHT_NAME_PREFIX}${choiceId}`,
43
- highlight,
44
- );
45
-
46
- this.highlights.set(choiceId, highlight);
47
- this.ranges.set(choiceId, range);
48
-
49
- this.addAriaAttributes(range);
50
- }
51
-
52
- remove(choiceId: string): void {
53
- if (!this.isSupported()) {
54
- this.removeFallback(choiceId);
55
- return;
56
- }
57
-
58
- CSS.highlights.delete(`${MaskStrategy.HIGHLIGHT_NAME_PREFIX}${choiceId}`);
59
-
60
- // Remove CSS for this specific highlight
61
- this.removeHighlightCSS(choiceId);
62
-
63
- const range = this.ranges.get(choiceId);
64
- if (range) {
65
- this.removeAriaAttributes(range);
66
- }
67
-
68
- this.highlights.delete(choiceId);
69
- this.ranges.delete(choiceId);
70
- this.fallbackContainers.delete(choiceId);
71
- }
72
-
73
- isEliminated(choiceId: string): boolean {
74
- return this.highlights.has(choiceId);
75
- }
76
-
77
- clearAll(): void {
78
- for (const choiceId of this.highlights.keys()) {
79
- this.remove(choiceId);
80
- }
81
- this.fallbackContainers.clear();
82
- }
83
-
84
- getEliminatedIds(): string[] {
85
- return Array.from(this.highlights.keys());
86
- }
87
-
88
- private isSupported(): boolean {
89
- return typeof CSS !== "undefined" && "highlights" in CSS;
90
- }
91
-
92
- private injectHighlightCSS(choiceId: string): void {
93
- const styleId = `${MaskStrategy.HIGHLIGHT_STYLE_PREFIX}${choiceId}`;
94
- if (document.getElementById(styleId)) return;
95
-
96
- const style = document.createElement("style");
97
- style.id = styleId;
98
- style.textContent = `
99
- ::highlight(pie-answer-masked-${choiceId}) {
100
- opacity: 0.2;
101
- filter: blur(2px);
102
- }
103
- `;
104
- document.head.appendChild(style);
105
- }
106
-
107
- private removeHighlightCSS(choiceId: string): void {
108
- document
109
- .getElementById(`${MaskStrategy.HIGHLIGHT_STYLE_PREFIX}${choiceId}`)
110
- ?.remove();
111
- }
112
-
113
- private addAriaAttributes(range: Range): void {
114
- const container = this.findChoiceContainer(range);
115
- if (!container) return;
116
-
117
- container.setAttribute(MaskStrategy.ELIMINATED_ATTR, "true");
118
- container.setAttribute("aria-hidden", "true");
119
- }
120
-
121
- private removeAriaAttributes(range: Range): void {
122
- const container = this.findChoiceContainer(range);
123
- if (!container) return;
124
-
125
- container.removeAttribute(MaskStrategy.ELIMINATED_ATTR);
126
- container.removeAttribute("aria-hidden");
127
- }
128
-
129
- private findChoiceContainer(range: Range): HTMLElement | null {
130
- let element: HTMLElement | null = range.startContainer as HTMLElement;
131
-
132
- // If startContainer is a text node, get its parent
133
- if (element.nodeType === Node.TEXT_NODE) {
134
- element = element.parentElement;
135
- }
136
-
137
- while (element && element !== document.body) {
138
- if (element.getAttribute(MaskStrategy.CHOICE_HOOK_ATTR) === "true") {
139
- return element;
140
- }
141
- element = element.parentElement;
142
- }
143
-
144
- return null;
145
- }
146
-
147
- private applyFallback(choiceId: string, range: Range): void {
148
- const container = this.findChoiceContainer(range);
149
- if (!container) return;
150
-
151
- container.classList.add(MaskStrategy.FALLBACK_CLASS);
152
- container.setAttribute(MaskStrategy.ELIMINATED_ATTR, "true");
153
- container.setAttribute(MaskStrategy.ELIMINATED_ID_ATTR, choiceId);
154
- this.fallbackContainers.set(choiceId, container);
155
- this.addAriaAttributes(range);
156
- }
157
-
158
- private removeFallback(choiceId: string): void {
159
- const container = this.fallbackContainers.get(choiceId);
160
- if (!container) return;
161
-
162
- container.classList.remove(MaskStrategy.FALLBACK_CLASS);
163
- container.removeAttribute(MaskStrategy.ELIMINATED_ATTR);
164
- container.removeAttribute(MaskStrategy.ELIMINATED_ID_ATTR);
165
-
166
- const range = document.createRange();
167
- range.selectNodeContents(container);
168
- this.removeAriaAttributes(range);
169
- this.fallbackContainers.delete(choiceId);
170
- }
171
- }