@pie-players/pie-tool-answer-eliminator 0.3.73 → 0.3.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,63 +28,74 @@ The answer eliminator is automatically integrated when using the PIE Section Pla
28
28
  <script type="module">
29
29
  import '@pie-players/pie-section-player';
30
30
  import { ToolkitCoordinator } from '@pie-players/pie-assessment-toolkit';
31
+ import {
32
+ createPackagedToolRegistry,
33
+ DEFAULT_TOOL_MODULE_LOADERS,
34
+ } from '@pie-players/pie-default-tool-loaders';
31
35
 
36
+ const toolRegistry = createPackagedToolRegistry({
37
+ toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
38
+ });
32
39
  const coordinator = new ToolkitCoordinator({
33
40
  assessmentId: 'my-assessment',
41
+ toolRegistry,
34
42
  tools: {
35
- answerEliminator: { enabled: true }
43
+ placement: { item: ['answerEliminator'] }
36
44
  }
37
45
  });
38
46
 
39
47
  const player = document.getElementById('player');
40
- player.toolkitCoordinator = coordinator;
48
+ player.runtime = { ...(player.runtime ?? {}), coordinator };
41
49
  player.section = mySection;
42
50
  </script>
43
51
 
44
- <pie-section-player id="player"></pie-section-player>
52
+ <pie-section-player-splitpane id="player"></pie-section-player-splitpane>
45
53
  ```
46
54
 
47
55
  The section player automatically:
48
56
  - Renders answer eliminator buttons in question toolbars
49
57
  - Generates global element IDs
50
- - Passes ElementToolStateStore to the tool
51
58
  - Manages state lifecycle
52
59
 
60
+ The tool reads its coordinator and the `ElementToolStateStore` from the toolkit
61
+ runtime context, so it has to sit inside `<pie-assessment-toolkit>`.
62
+
53
63
  ### Manual Integration (Advanced)
54
64
 
55
- For custom implementations outside the section player:
65
+ For a custom layout inside the toolkit:
56
66
 
57
67
  ```html
58
68
  <script type="module">
59
69
  import '@pie-players/pie-tool-answer-eliminator';
60
- import { ElementToolStateStore } from '@pie-players/pie-assessment-toolkit';
61
-
62
- const store = new ElementToolStateStore();
63
- const globalElementId = store.getGlobalElementId(
64
- 'my-assessment',
65
- 'section-1',
66
- 'question-1',
67
- 'mc1'
68
- );
69
70
 
70
71
  const tool = document.querySelector('pie-tool-answer-eliminator');
71
- tool.globalElementId = globalElementId;
72
- tool.elementToolStateStore = store;
72
+ // Store key per PIE element in the question, by model id
73
+ tool.elementStateKeys = {
74
+ mc1: 'my-assessment:section-1:question-1:mc1',
75
+ mc2: 'my-assessment:section-1:question-1:mc2',
76
+ };
73
77
  tool.scopeElement = document.querySelector('.question-content');
78
+ tool.visible = true; // or alwaysOn = true; the tool injects no buttons while neither is set
74
79
  </script>
75
80
 
76
- <pie-tool-answer-eliminator></pie-tool-answer-eliminator>
81
+ <pie-assessment-toolkit>
82
+ <pie-tool-answer-eliminator></pie-tool-answer-eliminator>
83
+ </pie-assessment-toolkit>
77
84
  ```
78
85
 
79
86
  ## Props/Attributes
80
87
 
81
- The web component accepts the following properties (set via JavaScript, not HTML attributes):
88
+ `elementStateKeys` and `scopeElement` are JS properties only; the others also take the attribute shown.
82
89
 
83
- | Property | Type | Required | Description |
84
- |----------|------|----------|-------------|
85
- | `globalElementId` | `string` | Yes | Composite key: `assessmentId:sectionId:itemId:elementId` |
86
- | `elementToolStateStore` | `IElementToolStateStore` | Yes | Store for element-level tool state |
87
- | `scopeElement` | `HTMLElement` | No | DOM element to scope choice detection (defaults to document) |
90
+ | Property | Attribute | Type | Default | Description |
91
+ |----------|-----------|------|---------|-------------|
92
+ | `visible` | `visible` | `boolean` | `false` | Shows the elimination buttons; the toolbar toggle sets it |
93
+ | `alwaysOn` | `always-on` | `boolean` | `false` | Shows the buttons regardless of `visible`, for a profile-based accommodation |
94
+ | `strategy` | `strategy` | `'strikethrough' \| 'mask'` | `'strikethrough'` | Elimination styling; any other value uses `strikethrough` |
95
+ | `buttonAlignment` | `button-alignment` | `'left' \| 'right' \| 'inline'` | `'right'` | Button placement relative to the choice |
96
+ | `toolId` | `tool-id` | `string` | `'answerEliminator'` | Id the tool registers with the coordinator under |
97
+ | `elementStateKeys` | | `Record<string, string>` | `{}` | Composite key `assessmentId:sectionId:itemId:elementId` per PIE element, by model id, into the runtime context's element tool state store. A choice is tracked under its nearest ancestor with a listed id and persists under that key; choices outside a listed element are kept in memory only |
98
+ | `scopeElement` | | `HTMLElement` | | Root to detect choices in. Without it the tool uses the root its enclosing `pie-item-scope` provides, and with neither it warns and injects nothing |
88
99
 
89
100
  ## Global Element ID Format
90
101
 
@@ -97,7 +108,7 @@ ${assessmentId}:${sectionId}:${itemId}:${elementId}
97
108
  **Example:**
98
109
  ```typescript
99
110
  "demo-assessment:section-1:question-1:mc1"
100
- "biology-exam:section-2:genetics-q1:ebsr-part1"
111
+ "biology-exam:section-2:genetics-q1:ebsr1"
101
112
  ```
102
113
 
103
114
  ### Benefits of Composite Keys
@@ -109,16 +120,16 @@ ${assessmentId}:${sectionId}:${itemId}:${elementId}
109
120
 
110
121
  ### Why Element-Level?
111
122
 
112
- Items can contain **multiple interactive elements** (e.g., EBSR with two parts). Each element needs independent state:
123
+ Items can contain **multiple interactive elements** whose choice ids repeat (two multiple-choice elements both have a choice `a`). Each element needs independent state:
113
124
 
114
125
  ```typescript
115
126
  // ✅ Correct: Element-level state
116
127
  {
117
- "demo:section-1:question-1:ebsr-part1": {
118
- "answerEliminator": { "eliminatedChoices": ["choice-a", "choice-c"] }
128
+ "demo:section-1:question-1:mc1": {
129
+ "answerEliminator": { "eliminatedChoices": ["a", "c"] }
119
130
  },
120
- "demo:section-1:question-1:ebsr-part2": {
121
- "answerEliminator": { "eliminatedChoices": ["choice-b"] }
131
+ "demo:section-1:question-1:mc2": {
132
+ "answerEliminator": { "eliminatedChoices": ["a"] }
122
133
  }
123
134
  }
124
135
  ```
@@ -157,9 +168,18 @@ The answer eliminator stores state in **ElementToolStateStore** (ephemeral, clie
157
168
  To persist tool state across page refreshes:
158
169
 
159
170
  ```typescript
171
+ import {
172
+ createPackagedToolRegistry,
173
+ DEFAULT_TOOL_MODULE_LOADERS,
174
+ } from '@pie-players/pie-default-tool-loaders';
175
+
176
+ const toolRegistry = createPackagedToolRegistry({
177
+ toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
178
+ });
160
179
  const coordinator = new ToolkitCoordinator({
161
180
  assessmentId: 'my-assessment',
162
- tools: { answerEliminator: { enabled: true } }
181
+ toolRegistry,
182
+ tools: { placement: { item: ['answerEliminator'] } }
163
183
  });
164
184
 
165
185
  // Save to localStorage on change
@@ -187,14 +207,7 @@ The tool detects choices through an **adapter registry** (`AdapterRegistry`), no
187
207
  | `EBSRAdapter` | `ebsr` | delegates to the multiple-choice adapter for each `ebsr-multiple-choice` part (Part A/B), prefixing choice IDs with the part |
188
208
  | `InlineDropdownAdapter` | `inline-dropdown` | dropdown items with `role="option"` |
189
209
 
190
- Support more element types at runtime by registering your own adapter (any object implementing the `ChoiceAdapter` interface: `canHandle`, `findChoices`, `getChoiceId`, `getChoiceLabel`, `canEliminate`, `createChoiceRange`, `getButtonContainer`):
191
-
192
- ```typescript
193
- import { AdapterRegistry } from '@pie-players/pie-tool-answer-eliminator/adapters/adapter-registry';
194
-
195
- const registry = new AdapterRegistry();
196
- registry.registerAdapter(myCustomChoiceAdapter);
197
- ```
210
+ The tool builds its registry internally with these three adapters and has no injection point. The `./adapters/adapter-registry` export provides `AdapterRegistry` and `registerAdapter(...)`, and a registry a host constructs is not the one the tool reads, so supporting another element type is a change to this package. A `ChoiceAdapter` declares `elementType`, `priority`, `canHandle`, `findChoices`, `getChoiceId`, `getChoiceLabel`, `canEliminate`, `createChoiceRange` and `getButtonContainer`.
198
211
 
199
212
  ### 2. State Storage
200
213
 
@@ -259,7 +272,7 @@ store.clearAll();
259
272
  Full TypeScript definitions included:
260
273
 
261
274
  ```typescript
262
- import type { IElementToolStateStore } from '@pie-players/pie-assessment-toolkit';
275
+ import type { ElementToolStateStoreApi } from '@pie-players/pie-assessment-toolkit';
263
276
 
264
277
  interface AnswerEliminatorState {
265
278
  eliminatedChoices: string[];
@@ -1,14 +1,9 @@
1
+ import { parseVersionedTagName } from "@pie-players/pie-players-shared/pie/tag-names";
1
2
  import { MultipleChoiceAdapter } from "./multiple-choice-adapter.js";
2
3
  const EBSR_MULTIPLE_CHOICE_TAG = "ebsr-multiple-choice";
3
- const EBSR_MULTIPLE_CHOICE_VERSIONED_PREFIX = `${EBSR_MULTIPLE_CHOICE_TAG}--version-`;
4
- const isEbsrMultipleChoiceElement = (element) => {
5
- if (!(element instanceof HTMLElement)) {
6
- return false;
7
- }
8
- const tagName = element.tagName.toLowerCase();
9
- return (tagName === EBSR_MULTIPLE_CHOICE_TAG ||
10
- tagName.startsWith(EBSR_MULTIPLE_CHOICE_VERSIONED_PREFIX));
11
- };
4
+ const isEbsrMultipleChoiceElement = (element) => element instanceof HTMLElement &&
5
+ parseVersionedTagName(element.tagName.toLowerCase()).baseName ===
6
+ EBSR_MULTIPLE_CHOICE_TAG;
12
7
  const findEbsrMultipleChoicePart = (root, id) => Array.from(root.querySelectorAll(`[id="${id}"]`)).find(isEbsrMultipleChoiceElement) ?? null;
13
8
  const findClosestEbsrMultipleChoicePart = (choice) => {
14
9
  let element = choice.parentElement;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- export {}
2
- import PieToolAnswerEliminator from './tool-answer-eliminator.svelte.js'
3
- export default PieToolAnswerEliminator
4
- export {}
1
+ /**
2
+ * pie-tool-answer-eliminator - PIE Assessment Tool
3
+ *
4
+ * Importing this package registers `<pie-tool-answer-eliminator>`. The build
5
+ * entry is the component, so this file is the type entry alone.
6
+ */
7
+ export type {};