@pie-players/pie-tool-answer-eliminator 1.0.0-beta.0 → 1.0.0-beta.10
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/LICENSE +21 -0
- package/README.md +61 -11
- package/custom-elements.json +23 -4
- package/dist/THIRD-PARTY-LICENSES.txt +14 -0
- package/dist/element-props.d.ts +18 -0
- package/dist/index.d.ts +9 -7
- package/dist/tool-answer-eliminator.js +6 -2737
- package/package.json +11 -15
- package/dist/adapters/adapter-registry.d.ts +0 -25
- package/dist/adapters/adapter-registry.js +0 -50
- package/dist/adapters/choice-adapter.d.ts +0 -55
- package/dist/adapters/choice-adapter.js +0 -1
- package/dist/adapters/ebsr-adapter.d.ts +0 -21
- package/dist/adapters/ebsr-adapter.js +0 -69
- package/dist/adapters/inline-dropdown-adapter.d.ts +0 -17
- package/dist/adapters/inline-dropdown-adapter.js +0 -35
- package/dist/adapters/multiple-choice-adapter.d.ts +0 -31
- package/dist/adapters/multiple-choice-adapter.js +0 -130
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PIE Framework contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -82,15 +82,16 @@ For a custom layout inside the toolkit. The tool detects choices under the item
|
|
|
82
82
|
|
|
83
83
|
## Props/Attributes
|
|
84
84
|
|
|
85
|
-
`elementStateKeys`
|
|
85
|
+
`elementStateKeys` and `elementIds` are JS properties only; the others also take the attribute shown.
|
|
86
86
|
|
|
87
87
|
| Property | Attribute | Type | Default | Description |
|
|
88
88
|
|----------|-----------|------|---------|-------------|
|
|
89
89
|
| `visible` | `visible` | `boolean` | `false` | Shows the elimination buttons; the toolbar toggle sets it |
|
|
90
90
|
| `alwaysOn` | `always-on` | `boolean` | `false` | Shows the buttons regardless of `visible`, for a profile-based accommodation |
|
|
91
91
|
| `strategy` | `strategy` | `'strikethrough' \| 'mask'` | `'strikethrough'` | Elimination styling; any other value uses `strikethrough` |
|
|
92
|
-
| `buttonAlignment` | `button-alignment` | `'
|
|
92
|
+
| `buttonAlignment` | `button-alignment` | `'start' \| 'end' \| 'inline'` | `'inline'` | Toggle placement: `inline` in the flow after the choice, or `start`/`end` beside it, resolved against the choice's own direction so they mirror under right-to-left content. `left` and `right` are aliases of `start` and `end`; any other value is `inline` |
|
|
93
93
|
| `elementStateKeys` | | `Record<string, string>` | `{}` | Composite key `assessmentId:sectionId:attemptId: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 |
|
|
94
|
+
| `elementIds` | | `string[]` | unset | Model ids of the PIE elements whose choices take a toggle; unset, every element's do. A choice belongs to an element when that element or one of its ancestors up to the item has a listed id. The toolbar sets it from the host's element list |
|
|
94
95
|
|
|
95
96
|
## Global Element ID Format
|
|
96
97
|
|
|
@@ -182,15 +183,62 @@ const coordinator = new ToolkitCoordinator({
|
|
|
182
183
|
|
|
183
184
|
### 1. Choice Detection
|
|
184
185
|
|
|
185
|
-
|
|
186
|
+
Elements supply their own choices. An element takes the eliminator by declaring the AfA PNP 3.0 support term `answer-masking` on its class and answering `pieSupportTargets('answer-masking')` on its instances:
|
|
186
187
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
188
|
+
```typescript
|
|
189
|
+
class ChoiceGrid extends HTMLElement {
|
|
190
|
+
static pieSupports = ['answer-masking'];
|
|
191
|
+
|
|
192
|
+
pieSupportTargets(term: string) {
|
|
193
|
+
if (term !== 'answer-masking') return [];
|
|
194
|
+
return this.rows.map((row) => ({
|
|
195
|
+
id: row.value,
|
|
196
|
+
label: row.text,
|
|
197
|
+
container: row.element,
|
|
198
|
+
content: row.labelElement,
|
|
199
|
+
isSelected: () => this.isChosen(row.value),
|
|
200
|
+
isEliminable: () => this.mode === 'gather' && !this.isChosen(row.value),
|
|
201
|
+
setEliminated: (eliminated: boolean) => this.block(row.value, eliminated),
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Every field is required, and a choice missing one is skipped with a console warning:
|
|
208
|
+
|
|
209
|
+
| Field | Contract |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `id` | Unique within the element and stable across renders: eliminations persist by it. |
|
|
212
|
+
| `label` | The choice's accessible name; the toggle is announced as "Toggle elimination for `label`". |
|
|
213
|
+
| `container` | Receives the toggle and the eliminated state. The tool sets `position: relative` on it. An inline container, such as a token in running text, takes the toggle in the line. |
|
|
214
|
+
| `content` | The part that is struck through or masked. It must be inside `container`. |
|
|
215
|
+
| `isSelected()` | Whether the learner has chosen it. A selected choice hides its toggle and is never eliminated. |
|
|
216
|
+
| `isEliminable()` | False in evaluate and view modes, and for disabled or selected choices. |
|
|
217
|
+
| `setEliminated(eliminated)` | Blocks or restores the learner's selection of the choice. The element owns this: the tool never disables controls it did not render. A call with the value the choice already has changes nothing. |
|
|
218
|
+
|
|
219
|
+
The toolbar reads the same `static pieSupports` declaration to decide whether to offer the eliminator, so an element built outside this repository needs no change here. A declaration that omits `answer-masking`, including `[]`, means the element takes no eliminator.
|
|
220
|
+
|
|
221
|
+
An element may replace its choices' markup on any render, as select-text does. When a container leaves the document, the tool asks for the choices again and keeps their eliminations by id, moving focus to the replacement toggle. It does not call `setEliminated` again on the same element instance, so an element that re-renders from `setEliminated` settles; a new instance is asked to block its eliminated choices, and a choice no longer supplied is restored.
|
|
222
|
+
|
|
223
|
+
The outermost declaring element under the item owns its subtree: an element composed of declaring parts answers for them, prefixing ids with the part where they would collide, as EBSR does with `a-` and `b-`. Eliminations persist under the key of the nearest element listed in `elementStateKeys`, starting from the declaring element.
|
|
192
224
|
|
|
193
|
-
|
|
225
|
+
Elements that declare nothing fall back to the multiple-choice and EBSR markup this package reads (`.corespring-checkbox` and `.corespring-radio-button` choices, and the `ebsr-multiple-choice` parts with ids `a` and `b`). That fallback serves legacy pie-elements and pie-elements-ng releases from before elements declared their supports, and never runs inside a declaring element.
|
|
226
|
+
|
|
227
|
+
### Host element list
|
|
228
|
+
|
|
229
|
+
A host limits the eliminator to the elements whose support it has vetted, by package name, in the tool's provider entry:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
new ToolkitCoordinator({
|
|
233
|
+
tools: {
|
|
234
|
+
providers: {
|
|
235
|
+
answerEliminator: { elements: ['multiple-choice', 'ebsr'] },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
A name is the `<name>` of `@pie-element/<name>`, matched against the package the item's `elements` map binds a model's tag to, or, for a tag the map does not bind, against the tag with its version suffix and `pie-`/`pie-element-` prefix removed. A tag bound to a package outside `@pie-element` matches no name. A listed element must still support answer masking. The toolbar withdraws the eliminator from an item with no listed element, even under a PNP grant, and in a mixed item gives toggles only to the listed elements' choices. Omitting `elements` keeps every supporting element; an empty list allows none. A value that is not a list of names fails config validation and, under `toolConfigStrictness: "warn"` or `"off"`, allows none.
|
|
194
242
|
|
|
195
243
|
### 2. State Storage
|
|
196
244
|
|
|
@@ -226,11 +274,13 @@ Only MathJax containers are marked: natively rendered MathML keeps real text in
|
|
|
226
274
|
|
|
227
275
|
All three treatments — the text line-through, the diagonals over an image, the line over math — are drawn in one color, `--pie-answer-eliminator-strike-color` (a registered `component-public` token, defaulting to `--pie-incorrect`), so a choice mixing prose, pictures, and math reads as a single strike and can be restyled from one place.
|
|
228
276
|
|
|
229
|
-
For browsers without the Highlight API, each strategy falls back to a class on the choice container. Either way the eliminated choice also receives ARIA hooks (`data-pie-answer-eliminated`,
|
|
277
|
+
For browsers without the Highlight API, each strategy falls back to a class on the choice container. Either way the eliminated choice also receives ARIA hooks (`data-pie-answer-eliminated`, `aria-disabled`, and an offscreen "(eliminated)" announcement in the interface language) for assistive technology. The choice container is never `aria-hidden` — it hosts the strategy's toggle, which must stay reachable for screen-reader and keyboard users (WCAG 4.1.2).
|
|
230
278
|
|
|
231
279
|
### 4. Toggle Behavior
|
|
232
280
|
|
|
233
|
-
Each
|
|
281
|
+
Each choice gets its own elimination toggle button (class `pie-answer-eliminator-toggle`, rendered as `⊗`) appended to its `container`. Clicking the button toggles that choice through the active strategy and calls `setEliminated`. Eliminating is blocked while `isEliminable()` returns false. An active button is marked with `pie-answer-eliminator-toggle--active` and `aria-pressed="true"`; clicking again restores the choice.
|
|
282
|
+
|
|
283
|
+
The toggle's accessible name ("Toggle elimination for {choice}", with "Unlabeled choice" standing in for a choice without text) and the "(eliminated)" announcement come from the players' catalog under `tools.answerEliminator`, in the interface locale of the runtime context. Both carry that locale's `lang` and `dir` while the choice content keeps its own, and a locale change relabels them in place without dropping eliminations.
|
|
234
284
|
|
|
235
285
|
## Cleanup
|
|
236
286
|
|
package/custom-elements.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"declarations": [
|
|
8
8
|
{
|
|
9
9
|
"kind": "class",
|
|
10
|
-
"description": "Process-of-elimination tool for the item in its enclosing `<pie-item-scope>`: it adds a toggle beside each
|
|
10
|
+
"description": "Process-of-elimination tool for the item in its enclosing `<pie-item-scope>`: it adds a toggle beside each choice an element supplies for `answer-masking` that strikes the choice through or masks it, per `strategy`. It renders nothing of its own and is active while `visible` or `always-on` is set; `elementIds` limits the toggles to those elements' choices. Eliminations persist in the runtime context's element tool state store, under the key `elementStateKeys` gives each PIE element.",
|
|
11
11
|
"name": "ToolAnswerEliminator",
|
|
12
12
|
"tagName": "pie-tool-answer-eliminator",
|
|
13
13
|
"customElement": true,
|
|
@@ -43,9 +43,10 @@
|
|
|
43
43
|
"kind": "field",
|
|
44
44
|
"name": "buttonAlignment",
|
|
45
45
|
"type": {
|
|
46
|
-
"text": "'
|
|
46
|
+
"text": "'start' | 'end' | 'inline' | 'left' | 'right'"
|
|
47
47
|
},
|
|
48
|
-
"default": "'
|
|
48
|
+
"default": "'inline'",
|
|
49
|
+
"description": "Toggle placement: `inline` in the flow after the choice, or `start`/`end` beside it, mirrored under right-to-left content; `left` and `right` alias `start` and `end`.",
|
|
49
50
|
"attribute": "button-alignment"
|
|
50
51
|
},
|
|
51
52
|
{
|
|
@@ -57,6 +58,15 @@
|
|
|
57
58
|
"default": "{}",
|
|
58
59
|
"description": "Store key per PIE element, by model id (JS property only)",
|
|
59
60
|
"attribute": "elementstatekeys"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"kind": "field",
|
|
64
|
+
"name": "elementIds",
|
|
65
|
+
"type": {
|
|
66
|
+
"text": "readonly string[]"
|
|
67
|
+
},
|
|
68
|
+
"description": "Model ids of the PIE elements whose choices take a toggle. Unset, every element's choices do.",
|
|
69
|
+
"attribute": "elementids"
|
|
60
70
|
}
|
|
61
71
|
],
|
|
62
72
|
"attributes": [
|
|
@@ -89,7 +99,8 @@
|
|
|
89
99
|
"type": {
|
|
90
100
|
"text": "string"
|
|
91
101
|
},
|
|
92
|
-
"default": "'
|
|
102
|
+
"default": "'inline'",
|
|
103
|
+
"description": "Toggle placement: `inline` in the flow after the choice, or `start`/`end` beside it, mirrored under right-to-left content; `left` and `right` alias `start` and `end`.",
|
|
93
104
|
"fieldName": "buttonAlignment"
|
|
94
105
|
},
|
|
95
106
|
{
|
|
@@ -100,6 +111,14 @@
|
|
|
100
111
|
"default": "{}",
|
|
101
112
|
"description": "Store key per PIE element, by model id (JS property only)",
|
|
102
113
|
"fieldName": "elementStateKeys"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"name": "elementids",
|
|
117
|
+
"type": {
|
|
118
|
+
"text": "readonly string[]"
|
|
119
|
+
},
|
|
120
|
+
"description": "Model ids of the PIE elements whose choices take a toggle. Unset, every element's choices do.",
|
|
121
|
+
"fieldName": "elementIds"
|
|
103
122
|
}
|
|
104
123
|
],
|
|
105
124
|
"cssProperties": [
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Third-party software included in this package's bundles, with the licence each is used under.
|
|
2
|
+
|
|
3
|
+
========================================================================
|
|
4
|
+
|
|
5
|
+
svelte 5.57.3
|
|
6
|
+
License: MIT
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2016-2025 [Svelte Contributors](https://github.com/sveltejs/svelte/graphs/contributors)
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store key per PIE element, by the element's model id (which is also its DOM
|
|
3
|
+
* id). A choice persists under the key of the nearest ancestor listed here.
|
|
4
|
+
*/
|
|
5
|
+
export type ElementStateKeys = Readonly<Record<string, string>>;
|
|
6
|
+
/** Inputs shared by the component and its native element declaration. */
|
|
7
|
+
export interface AnswerEliminatorProps {
|
|
8
|
+
visible?: boolean;
|
|
9
|
+
strategy?: 'strikethrough' | 'mask';
|
|
10
|
+
alwaysOn?: boolean;
|
|
11
|
+
buttonAlignment?: 'start' | 'end' | 'inline' | 'left' | 'right';
|
|
12
|
+
elementStateKeys?: ElementStateKeys;
|
|
13
|
+
/**
|
|
14
|
+
* Model ids of the PIE elements whose choices take a toggle. Unset, every
|
|
15
|
+
* element's choices do.
|
|
16
|
+
*/
|
|
17
|
+
elementIds?: readonly string[];
|
|
18
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { AnswerEliminatorProps } from './element-props.js';
|
|
2
|
+
/** Native host interface for `<pie-tool-answer-eliminator>`. Properties may be unset until it mounts. */
|
|
3
|
+
export interface PieToolAnswerEliminatorElement extends HTMLElement, AnswerEliminatorProps {
|
|
4
|
+
}
|
|
5
|
+
declare global {
|
|
6
|
+
interface HTMLElementTagNameMap {
|
|
7
|
+
"pie-tool-answer-eliminator": PieToolAnswerEliminatorElement;
|
|
8
|
+
}
|
|
9
|
+
}
|