@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
@@ -23,4 +23,3 @@ export declare class AdapterRegistry {
23
23
  */
24
24
  registerAdapter(adapter: ChoiceAdapter): void;
25
25
  }
26
- //# sourceMappingURL=adapter-registry.d.ts.map
@@ -40,4 +40,3 @@ export interface ChoiceAdapter {
40
40
  */
41
41
  getButtonContainer(choice: HTMLElement): HTMLElement | null;
42
42
  }
43
- //# sourceMappingURL=choice-adapter.d.ts.map
@@ -17,4 +17,3 @@ export declare class EBSRAdapter implements ChoiceAdapter {
17
17
  canEliminate(choice: HTMLElement): boolean;
18
18
  getButtonContainer(choice: HTMLElement): HTMLElement | null;
19
19
  }
20
- //# sourceMappingURL=ebsr-adapter.d.ts.map
@@ -15,4 +15,3 @@ export declare class InlineDropdownAdapter implements ChoiceAdapter {
15
15
  canEliminate(choice: HTMLElement): boolean;
16
16
  getButtonContainer(choice: HTMLElement): HTMLElement | null;
17
17
  }
18
- //# sourceMappingURL=inline-dropdown-adapter.d.ts.map
@@ -25,4 +25,3 @@ export declare class MultipleChoiceAdapter implements ChoiceAdapter {
25
25
  private resolveLabelElement;
26
26
  private annotateFeedbackTicks;
27
27
  }
28
- //# sourceMappingURL=multiple-choice-adapter.d.ts.map
@@ -90,4 +90,3 @@ export declare class AnswerEliminatorCore {
90
90
  */
91
91
  destroy(): void;
92
92
  }
93
- //# sourceMappingURL=answer-eliminator-core.d.ts.map
package/dist/index.d.ts CHANGED
@@ -5,4 +5,3 @@
5
5
  * Import the built version for CDN usage, or the .svelte source for Svelte projects.
6
6
  */
7
7
  export { AdapterRegistry } from './adapters/adapter-registry.js';
8
- //# sourceMappingURL=index.d.ts.map
@@ -38,4 +38,3 @@ export interface EliminationStrategy {
38
38
  */
39
39
  destroy(): void;
40
40
  }
41
- //# sourceMappingURL=elimination-strategy.d.ts.map
@@ -30,4 +30,3 @@ export declare class MaskStrategy implements EliminationStrategy {
30
30
  private applyFallback;
31
31
  private removeFallback;
32
32
  }
33
- //# sourceMappingURL=mask-strategy.d.ts.map
@@ -36,4 +36,3 @@ export declare class StrikethroughStrategy implements EliminationStrategy {
36
36
  private removeFallback;
37
37
  private resolveLabelElement;
38
38
  }
39
- //# sourceMappingURL=strikethrough-strategy.d.ts.map
@@ -1,3 +1,2 @@
1
1
  declare const _default: import('vite').UserConfig;
2
2
  export default _default;
3
- //# sourceMappingURL=vite.config.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/pie-tool-answer-eliminator",
3
- "version": "0.3.42",
3
+ "version": "0.3.44",
4
4
  "type": "module",
5
5
  "description": "Answer eliminator tool for PIE assessment player - supports process-of-elimination test-taking strategy",
6
6
  "repository": {
@@ -20,13 +20,11 @@
20
20
  "wcag",
21
21
  "accessibility"
22
22
  ],
23
- "svelte": "./tool-answer-eliminator.svelte",
24
23
  "main": "./dist/tool-answer-eliminator.js",
25
24
  "exports": {
26
25
  ".": {
27
26
  "types": "./dist/index.d.ts",
28
- "import": "./dist/tool-answer-eliminator.js",
29
- "svelte": "./tool-answer-eliminator.svelte"
27
+ "import": "./dist/tool-answer-eliminator.js"
30
28
  },
31
29
  "./adapters/adapter-registry": {
32
30
  "types": "./dist/adapters/adapter-registry.d.ts",
@@ -35,19 +33,15 @@
35
33
  },
36
34
  "files": [
37
35
  "dist",
38
- "tool-answer-eliminator.svelte",
39
- "package.json",
40
- "answer-eliminator-core.ts",
41
- "adapters/",
42
- "strategies/"
36
+ "package.json"
43
37
  ],
44
38
  "license": "MIT",
45
39
  "unpkg": "./dist/tool-answer-eliminator.js",
46
40
  "jsdelivr": "./dist/tool-answer-eliminator.js",
47
41
  "dependencies": {
48
- "@pie-players/pie-assessment-toolkit": "0.3.42",
49
- "@pie-players/pie-context": "0.3.42",
50
- "@pie-players/pie-players-shared": "0.3.42"
42
+ "@pie-players/pie-assessment-toolkit": "0.3.44",
43
+ "@pie-players/pie-context": "0.3.44",
44
+ "@pie-players/pie-players-shared": "0.3.44"
51
45
  },
52
46
  "types": "./dist/index.d.ts",
53
47
  "scripts": {
@@ -1,64 +0,0 @@
1
- import type { ChoiceAdapter } from "./choice-adapter.js";
2
- import { EBSRAdapter } from "./ebsr-adapter.js";
3
- import { InlineDropdownAdapter } from "./inline-dropdown-adapter.js";
4
- import { MultipleChoiceAdapter } from "./multiple-choice-adapter.js";
5
-
6
- export interface ChoiceWithAdapter {
7
- choice: HTMLElement;
8
- adapter: ChoiceAdapter;
9
- }
10
-
11
- /**
12
- * Registry for choice adapters
13
- * Automatically detects which adapter to use for different PIE elements
14
- */
15
- export class AdapterRegistry {
16
- private adapters: ChoiceAdapter[];
17
-
18
- constructor() {
19
- // Register adapters in priority order (higher priority first)
20
- this.adapters = [
21
- new MultipleChoiceAdapter(),
22
- new EBSRAdapter(),
23
- new InlineDropdownAdapter(),
24
- ].sort((a, b) => b.priority - a.priority);
25
- }
26
-
27
- /**
28
- * Find the appropriate adapter for an element
29
- */
30
- findAdapter(element: HTMLElement): ChoiceAdapter | null {
31
- return this.adapters.find((adapter) => adapter.canHandle(element)) || null;
32
- }
33
-
34
- /**
35
- * Find all choices with their adapters within a root element
36
- */
37
- findAllChoicesWithAdapters(root: HTMLElement): ChoiceWithAdapter[] {
38
- const results: ChoiceWithAdapter[] = [];
39
- const processedChoices = new Set<HTMLElement>();
40
-
41
- // Try each adapter
42
- for (const adapter of this.adapters) {
43
- const choices = adapter.findChoices(root);
44
-
45
- for (const choice of choices) {
46
- // Avoid duplicates (in case adapters overlap)
47
- if (processedChoices.has(choice)) continue;
48
-
49
- processedChoices.add(choice);
50
- results.push({ choice, adapter });
51
- }
52
- }
53
-
54
- return results;
55
- }
56
-
57
- /**
58
- * Register a custom adapter
59
- */
60
- registerAdapter(adapter: ChoiceAdapter): void {
61
- this.adapters.push(adapter);
62
- this.adapters.sort((a, b) => b.priority - a.priority);
63
- }
64
- }
@@ -1,50 +0,0 @@
1
- /**
2
- * Adapter interface for detecting and working with choice elements
3
- * from different PIE element types.
4
- *
5
- * Adapters enable the answer eliminator to work generically with
6
- * multiple-choice, EBSR, inline-dropdown, and future choice elements.
7
- */
8
- export interface ChoiceAdapter {
9
- /** Element type this adapter handles */
10
- readonly elementType: string;
11
-
12
- /** Priority (higher = checked first) */
13
- readonly priority: number;
14
-
15
- /**
16
- * Check if this adapter can handle the given element
17
- */
18
- canHandle(element: HTMLElement): boolean;
19
-
20
- /**
21
- * Find all choice elements within the root
22
- */
23
- findChoices(root: HTMLElement): HTMLElement[];
24
-
25
- /**
26
- * Create a Range covering the choice content (for CSS Highlight API)
27
- */
28
- createChoiceRange(choice: HTMLElement): Range | null;
29
-
30
- /**
31
- * Get unique identifier for this choice (for persistence)
32
- */
33
- getChoiceId(choice: HTMLElement): string;
34
-
35
- /**
36
- * Get human-readable label (for screen readers)
37
- */
38
- getChoiceLabel(choice: HTMLElement): string;
39
-
40
- /**
41
- * Check if choice can be eliminated
42
- * (not already selected, not in evaluate mode, etc.)
43
- */
44
- canEliminate(choice: HTMLElement): boolean;
45
-
46
- /**
47
- * Get the container element to attach elimination button
48
- */
49
- getButtonContainer(choice: HTMLElement): HTMLElement | null;
50
- }
@@ -1,61 +0,0 @@
1
- import type { ChoiceAdapter } from "./choice-adapter.js";
2
- import { MultipleChoiceAdapter } from "./multiple-choice-adapter.js";
3
-
4
- /**
5
- * Adapter for EBSR (Evidence-Based Selected Response) elements
6
- *
7
- * EBSR contains two multiple-choice questions (Part A and Part B),
8
- * so we delegate to MultipleChoiceAdapter and prefix IDs with part identifier
9
- */
10
- export class EBSRAdapter implements ChoiceAdapter {
11
- readonly elementType = "ebsr";
12
- readonly priority = 95;
13
-
14
- private mcAdapter = new MultipleChoiceAdapter();
15
-
16
- canHandle(element: HTMLElement): boolean {
17
- return (
18
- element.tagName.toLowerCase() === "ebsr" ||
19
- element.querySelector("ebsr-multiple-choice") !== null
20
- );
21
- }
22
-
23
- findChoices(root: HTMLElement): HTMLElement[] {
24
- // EBSR contains ebsr-multiple-choice elements
25
- // Find choices in both Part A and Part B
26
- const partA = root.querySelector('ebsr-multiple-choice[id="a"]');
27
- const partB = root.querySelector('ebsr-multiple-choice[id="b"]');
28
-
29
- const choices: HTMLElement[] = [];
30
- if (partA)
31
- choices.push(...this.mcAdapter.findChoices(partA as HTMLElement));
32
- if (partB)
33
- choices.push(...this.mcAdapter.findChoices(partB as HTMLElement));
34
-
35
- return choices;
36
- }
37
-
38
- // Delegate all other methods to MultipleChoiceAdapter
39
- createChoiceRange(choice: HTMLElement): Range | null {
40
- return this.mcAdapter.createChoiceRange(choice);
41
- }
42
-
43
- getChoiceId(choice: HTMLElement): string {
44
- // Prefix with part ID (a or b)
45
- const part = choice.closest("ebsr-multiple-choice")?.id || "unknown";
46
- const choiceId = this.mcAdapter.getChoiceId(choice);
47
- return `${part}-${choiceId}`;
48
- }
49
-
50
- getChoiceLabel(choice: HTMLElement): string {
51
- return this.mcAdapter.getChoiceLabel(choice);
52
- }
53
-
54
- canEliminate(choice: HTMLElement): boolean {
55
- return this.mcAdapter.canEliminate(choice);
56
- }
57
-
58
- getButtonContainer(choice: HTMLElement): HTMLElement | null {
59
- return this.mcAdapter.getButtonContainer(choice);
60
- }
61
- }
@@ -1,46 +0,0 @@
1
- import type { ChoiceAdapter } from "./choice-adapter.js";
2
-
3
- /**
4
- * Adapter for PIE inline-dropdown elements
5
- *
6
- * Works with dropdown menu items (role="option")
7
- */
8
- export class InlineDropdownAdapter implements ChoiceAdapter {
9
- readonly elementType = "inline-dropdown";
10
- readonly priority = 90;
11
-
12
- canHandle(element: HTMLElement): boolean {
13
- return (
14
- element.tagName.toLowerCase() === "inline-dropdown" ||
15
- element.querySelector('[role="combobox"]') !== null
16
- );
17
- }
18
-
19
- findChoices(root: HTMLElement): HTMLElement[] {
20
- // Find dropdown menu items
21
- return Array.from(root.querySelectorAll<HTMLElement>('[role="option"]'));
22
- }
23
-
24
- createChoiceRange(choice: HTMLElement): Range | null {
25
- const range = document.createRange();
26
- range.selectNodeContents(choice);
27
- return range;
28
- }
29
-
30
- getChoiceId(choice: HTMLElement): string {
31
- return choice.id || choice.getAttribute("data-value") || "";
32
- }
33
-
34
- getChoiceLabel(choice: HTMLElement): string {
35
- return choice.textContent?.trim() || "Unlabeled option";
36
- }
37
-
38
- canEliminate(choice: HTMLElement): boolean {
39
- // Can't eliminate if already selected
40
- return choice.getAttribute("aria-selected") !== "true";
41
- }
42
-
43
- getButtonContainer(choice: HTMLElement): HTMLElement | null {
44
- return choice;
45
- }
46
- }
@@ -1,144 +0,0 @@
1
- import type { ChoiceAdapter } from "./choice-adapter.js";
2
-
3
- /**
4
- * Adapter for PIE multiple-choice elements
5
- *
6
- * Works with both single-select (radio) and multiple-select (checkbox) modes
7
- * Detects PIE's corespring-checkbox and corespring-radio-button classes
8
- */
9
- export class MultipleChoiceAdapter implements ChoiceAdapter {
10
- readonly elementType = "multiple-choice";
11
- readonly priority = 100;
12
- private static readonly CHOICE_HOOK_ATTR =
13
- "data-pie-answer-eliminator-choice";
14
- private static readonly LABEL_HOOK_ATTR = "data-pie-answer-eliminator-label";
15
- private static readonly ROOT_HOOK_ATTR = "data-pie-answer-eliminator-root";
16
- private static readonly FEEDBACK_HOOK_ATTR =
17
- "data-pie-answer-eliminator-feedback-tick";
18
- private static readonly CHOICE_SELECTOR =
19
- `[${MultipleChoiceAdapter.CHOICE_HOOK_ATTR}="true"], .corespring-checkbox, .corespring-radio-button`;
20
-
21
- canHandle(element: HTMLElement): boolean {
22
- return (
23
- element.tagName.toLowerCase() === "multiple-choice" ||
24
- element.classList.contains("multiple-choice")
25
- );
26
- }
27
-
28
- findChoices(root: HTMLElement): HTMLElement[] {
29
- root.setAttribute(MultipleChoiceAdapter.ROOT_HOOK_ATTR, "true");
30
- const choices = Array.from(
31
- root.querySelectorAll<HTMLElement>(MultipleChoiceAdapter.CHOICE_SELECTOR),
32
- );
33
- for (const choice of choices) {
34
- this.annotateChoice(choice);
35
- }
36
- this.annotateFeedbackTicks(root);
37
- return choices;
38
- }
39
-
40
- createChoiceRange(choice: HTMLElement): Range | null {
41
- // Create range covering the label content
42
- // Try multiple possible selectors for the label
43
- const labelElement = this.resolveLabelElement(choice);
44
-
45
- if (!labelElement) {
46
- return null;
47
- }
48
-
49
- const range = document.createRange();
50
- range.selectNodeContents(labelElement);
51
- return range;
52
- }
53
-
54
- getChoiceId(choice: HTMLElement): string {
55
- // Get value from input element
56
- const input = choice.querySelector(
57
- 'input[type="checkbox"], input[type="radio"]',
58
- );
59
- return (
60
- input?.getAttribute("value") ||
61
- input?.id ||
62
- this.generateFallbackId(choice)
63
- );
64
- }
65
-
66
- getChoiceLabel(choice: HTMLElement): string {
67
- const label = this.resolveLabelElement(choice);
68
- return label?.textContent?.trim() || "Unlabeled choice";
69
- }
70
-
71
- canEliminate(choice: HTMLElement): boolean {
72
- const input = choice.querySelector(
73
- 'input[type="checkbox"], input[type="radio"]',
74
- );
75
- if (!input) return false;
76
-
77
- // Can't eliminate if:
78
- // 1. Already selected (checked)
79
- if (input.getAttribute("aria-checked") === "true") return false;
80
-
81
- // 2. Disabled
82
- if ((input as HTMLInputElement).disabled) return false;
83
-
84
- // 3. In evaluate/view mode (has feedback tick)
85
- const root =
86
- choice.closest(`[${MultipleChoiceAdapter.ROOT_HOOK_ATTR}="true"]`) ||
87
- choice.closest("multiple-choice");
88
- if (
89
- root?.querySelector(
90
- `[${MultipleChoiceAdapter.FEEDBACK_HOOK_ATTR}="true"]`,
91
- )
92
- )
93
- return false;
94
-
95
- return true;
96
- }
97
-
98
- getButtonContainer(choice: HTMLElement): HTMLElement | null {
99
- // Return the choice-input container
100
- return choice;
101
- }
102
-
103
- private generateFallbackId(choice: HTMLElement): string {
104
- // Generate stable ID based on choice position
105
- const parent =
106
- choice.closest(`[${MultipleChoiceAdapter.ROOT_HOOK_ATTR}="true"]`) ||
107
- choice.closest("multiple-choice");
108
- const choices =
109
- parent?.querySelectorAll(
110
- `[${MultipleChoiceAdapter.CHOICE_HOOK_ATTR}="true"]`,
111
- ) || [];
112
- const index = Array.from(choices).indexOf(choice);
113
- return `choice-${index}`;
114
- }
115
-
116
- private annotateChoice(choice: HTMLElement): void {
117
- choice.setAttribute(MultipleChoiceAdapter.CHOICE_HOOK_ATTR, "true");
118
- const label = this.resolveLabelElement(choice);
119
- if (label) {
120
- label.setAttribute(MultipleChoiceAdapter.LABEL_HOOK_ATTR, "true");
121
- }
122
- }
123
-
124
- private resolveLabelElement(choice: HTMLElement): HTMLElement | null {
125
- return (
126
- choice.querySelector<HTMLElement>(
127
- `[${MultipleChoiceAdapter.LABEL_HOOK_ATTR}="true"]`,
128
- ) ||
129
- choice.querySelector<HTMLElement>("label") ||
130
- choice.querySelector<HTMLElement>("span")
131
- );
132
- }
133
-
134
- private annotateFeedbackTicks(root: HTMLElement): void {
135
- for (const feedbackTick of root.querySelectorAll<HTMLElement>(
136
- ".feedback-tick",
137
- )) {
138
- feedbackTick.setAttribute(
139
- MultipleChoiceAdapter.FEEDBACK_HOOK_ATTR,
140
- "true",
141
- );
142
- }
143
- }
144
- }