@qti-components/match-interaction 2.1.0-next.2 → 3.0.0-next.4

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.
@@ -68,6 +68,20 @@ export default [
68
68
  display: contents;
69
69
  }
70
70
 
71
+ /*
72
+ * qti-header-hidden (vocabulary §1.2.8.2): "Do not display the top row of the table where the
73
+ * column headers are displayed." The whole row goes, the top-left cell with it.
74
+ */
75
+ :host(.qti-match-tabular.qti-header-hidden) [part='grid'] {
76
+ grid-template-rows: repeat(var(--qti-match-rows, 1), auto);
77
+ }
78
+ :host(.qti-match-tabular.qti-header-hidden) :is([part='corner'], [part='cols-wrap']) {
79
+ display: none;
80
+ }
81
+ :host(.qti-match-tabular.qti-header-hidden) :is([part='rows-wrap'], [part='checkbox-grid']) {
82
+ grid-row: 1 / -1;
83
+ }
84
+
71
85
  /*
72
86
  * The radio/checkbox box centres its own mark. qti-simple-choice.styles.ts does the same for
73
87
  * the non-tabular control; here the cells are plain spans in this shadow root, so the rule
@@ -1 +1 @@
1
- {"version":3,"file":"qti-match-interaction-tabular.styles.js","sourceRoot":"","sources":["../src/qti-match-interaction-tabular.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD;;;;;GAKG;AACH,eAAe;IACb,SAAS;IACT,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EF;CACF,CAAC","sourcesContent":["import { css } from 'lit';\n\nimport { boxSizing } from '@qti-components/base';\n\n/**\n * Shadow-DOM styles for the tabular layout of qti-match-interaction\n * (class=\"qti-match-tabular\"). The two qti-simple-match-set children are\n * slotted into named slots and participate in a CSS grid as light-DOM\n * row/column headers via display: contents. Drag-drop mode is untouched.\n */\nexport default [\n boxSizing,\n css`\n :host(.qti-match-tabular) slot:not([hidden]) {\n display: contents;\n }\n /*\n * Each cell's radio or checkbox is a real input, visually hidden over the whole cell: it takes\n * Tab focus, the arrow keys move within a row's radio group and Space toggles a checkbox, all\n * natively. It was the hidden attribute, which took it out of the tab order, so the grid could not be\n * answered from a keyboard at all. The span beside it draws the control; the theme rings the\n * cell (::part(input-cell):focus-within).\n */\n :host(.qti-match-tabular) [part~='input-cell'] {\n position: relative;\n }\n :host(.qti-match-tabular) [part~='input-cell'] > input {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n }\n :host(.qti-match-tabular) [part='grid'] {\n display: grid;\n /* Clips the cell rules — which are the grid's own background showing through a 1px gap — to the\n theme's border-radius. */\n overflow: hidden;\n width: fit-content;\n max-width: 100%;\n grid-template-columns: minmax(min-content, max-content) repeat(var(--qti-match-cols, 1), auto);\n grid-template-rows: auto repeat(var(--qti-match-rows, 1), auto);\n }\n /* Subgrid wrappers carry the slotted choices and the input cells into the\n outer grid's tracks. This structure is shared with the editor's tabular\n template so both can inherit the same theme CSS. No per-choice inline\n style writes are needed — auto-flow + subgrid handle placement. */\n :host(.qti-match-tabular) [part='cols-wrap'] {\n grid-column: 2 / -1;\n grid-row: 1;\n display: grid;\n grid-template-columns: subgrid;\n }\n :host(.qti-match-tabular) [part='rows-wrap'] {\n grid-column: 1;\n grid-row: 2 / -1;\n display: grid;\n grid-template-rows: subgrid;\n }\n :host(.qti-match-tabular) [part='checkbox-grid'] {\n grid-column: 2 / -1;\n grid-row: 2 / -1;\n display: grid;\n grid-template-columns: subgrid;\n grid-template-rows: subgrid;\n }\n :host(.qti-match-tabular) ::slotted(qti-simple-match-set) {\n display: contents;\n }\n\n /*\n * The radio/checkbox box centres its own mark. qti-simple-choice.styles.ts does the same for\n * the non-tabular control; here the cells are plain spans in this shadow root, so the rule\n * belongs to this file.\n *\n * The theme's 'form' mixin used to carry 'display: grid; place-content: center', which centred\n * both — from a document stylesheet, overriding qti-simple-choice's own flex centring. The mixin\n * keeps the box's size and paint; the box itself is laid out here.\n */\n :host(.qti-match-tabular) [part~='control'] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n `\n];\n"]}
1
+ {"version":3,"file":"qti-match-interaction-tabular.styles.js","sourceRoot":"","sources":["../src/qti-match-interaction-tabular.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD;;;;;GAKG;AACH,eAAe;IACb,SAAS;IACT,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFF;CACF,CAAC","sourcesContent":["import { css } from 'lit';\n\nimport { boxSizing } from '@qti-components/base';\n\n/**\n * Shadow-DOM styles for the tabular layout of qti-match-interaction\n * (class=\"qti-match-tabular\"). The two qti-simple-match-set children are\n * slotted into named slots and participate in a CSS grid as light-DOM\n * row/column headers via display: contents. Drag-drop mode is untouched.\n */\nexport default [\n boxSizing,\n css`\n :host(.qti-match-tabular) slot:not([hidden]) {\n display: contents;\n }\n /*\n * Each cell's radio or checkbox is a real input, visually hidden over the whole cell: it takes\n * Tab focus, the arrow keys move within a row's radio group and Space toggles a checkbox, all\n * natively. It was the hidden attribute, which took it out of the tab order, so the grid could not be\n * answered from a keyboard at all. The span beside it draws the control; the theme rings the\n * cell (::part(input-cell):focus-within).\n */\n :host(.qti-match-tabular) [part~='input-cell'] {\n position: relative;\n }\n :host(.qti-match-tabular) [part~='input-cell'] > input {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n }\n :host(.qti-match-tabular) [part='grid'] {\n display: grid;\n /* Clips the cell rules — which are the grid's own background showing through a 1px gap — to the\n theme's border-radius. */\n overflow: hidden;\n width: fit-content;\n max-width: 100%;\n grid-template-columns: minmax(min-content, max-content) repeat(var(--qti-match-cols, 1), auto);\n grid-template-rows: auto repeat(var(--qti-match-rows, 1), auto);\n }\n /* Subgrid wrappers carry the slotted choices and the input cells into the\n outer grid's tracks. This structure is shared with the editor's tabular\n template so both can inherit the same theme CSS. No per-choice inline\n style writes are needed — auto-flow + subgrid handle placement. */\n :host(.qti-match-tabular) [part='cols-wrap'] {\n grid-column: 2 / -1;\n grid-row: 1;\n display: grid;\n grid-template-columns: subgrid;\n }\n :host(.qti-match-tabular) [part='rows-wrap'] {\n grid-column: 1;\n grid-row: 2 / -1;\n display: grid;\n grid-template-rows: subgrid;\n }\n :host(.qti-match-tabular) [part='checkbox-grid'] {\n grid-column: 2 / -1;\n grid-row: 2 / -1;\n display: grid;\n grid-template-columns: subgrid;\n grid-template-rows: subgrid;\n }\n :host(.qti-match-tabular) ::slotted(qti-simple-match-set) {\n display: contents;\n }\n\n /*\n * qti-header-hidden (vocabulary §1.2.8.2): \"Do not display the top row of the table where the\n * column headers are displayed.\" The whole row goes, the top-left cell with it.\n */\n :host(.qti-match-tabular.qti-header-hidden) [part='grid'] {\n grid-template-rows: repeat(var(--qti-match-rows, 1), auto);\n }\n :host(.qti-match-tabular.qti-header-hidden) :is([part='corner'], [part='cols-wrap']) {\n display: none;\n }\n :host(.qti-match-tabular.qti-header-hidden) :is([part='rows-wrap'], [part='checkbox-grid']) {\n grid-row: 1 / -1;\n }\n\n /*\n * The radio/checkbox box centres its own mark. qti-simple-choice.styles.ts does the same for\n * the non-tabular control; here the cells are plain spans in this shadow root, so the rule\n * belongs to this file.\n *\n * The theme's 'form' mixin used to carry 'display: grid; place-content: center', which centred\n * both — from a document stylesheet, overriding qti-simple-choice's own flex centring. The mixin\n * keeps the box's size and paint; the box itself is laid out here.\n */\n :host(.qti-match-tabular) [part~='control'] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n `\n];\n"]}
@@ -8,15 +8,17 @@ declare const QtiMatchInteraction_base: (abstract new (...args: any[]) => import
8
8
  * @customElement qti-match-interaction
9
9
  *
10
10
  * @attr {string} response-identifier - Required. Identifier of the bound response variable.
11
- * @attr {number} [max-associations=0] - Maximum pairings across the whole interaction; `0`
12
- * means unlimited. QTI's own default is `1`; this element defaults to unlimited because a
13
- * literal `1` breaks any multi-pair match item. See plans/interaction-attribute-spec-conformance.md.
14
- * @attr {number} [min-associations=1] - Minimum pairings for a valid response.
11
+ * @attr {number} [max-associations=1] - Maximum pairings across the whole interaction; `0`
12
+ * means unlimited. Without the attribute it is QTI's default of 1 (information model §5.89).
13
+ * @attr {number} [min-associations=0] - Minimum pairings for a valid response (QTI's default).
14
+ * @attr {string} data-first-column-header - QTI shared vocabulary, with `qti-match-tabular`: the
15
+ * text of the table's top-left cell.
15
16
  * @attr {boolean} [shuffle=false] - Requests shuffling of both match sets. Applied by the
16
17
  * transform pipeline (`qti-transformers`), not by this element.
17
- * @attr {'qti-match-tabular'|'qti-choices-top'|'qti-choices-bottom'|'qti-choices-left'|'qti-choices-right'} class -
18
+ * @attr {'qti-match-tabular'|'qti-header-hidden'|'qti-choices-top'|'qti-choices-bottom'|'qti-choices-left'|'qti-choices-right'} class -
18
19
  * QTI shared presentation vocabulary. `qti-match-tabular` renders a checkbox/radio grid
19
- * instead of drag-and-drop; the `qti-choices-*` values position the source set.
20
+ * instead of drag-and-drop, and with it `qti-header-hidden` leaves out the column-header row;
21
+ * the `qti-choices-*` values position the source set.
20
22
  * @attr {boolean} [auto-size-dropzones=false] - Extension, not QTI. Sizes every drop target to
21
23
  * the widest chip so placement does not reflow the layout. The mixin default is `true`; this
22
24
  * interaction is the one that overrides it to `false`, because a match target is a category card
@@ -30,7 +32,7 @@ declare const QtiMatchInteraction_base: (abstract new (...args: any[]) => import
30
32
  * @slot - Default slot for the two match sets (drag-and-drop mode).
31
33
  *
32
34
  * @csspart grid - The tabular grid container.
33
- * @csspart corner - The top-left corner cell of the grid.
35
+ * @csspart corner - The top-left corner cell of the grid; shows `data-first-column-header`.
34
36
  * @csspart cols-wrap - Wrapper row for the column headers.
35
37
  * @csspart c-header - The column header slot wrapper.
36
38
  * @csspart rows-wrap - Wrapper column for the row headers.
@@ -59,10 +61,14 @@ export declare class QtiMatchInteraction extends QtiMatchInteraction_base {
59
61
  * their drops are exactly the widest chip plus the inset.
60
62
  */
61
63
  autoSizeDropzones: boolean;
64
+ /** QTI's defaults (information model §5.89). */
65
+ maxAssociations: number;
66
+ minAssociations: number;
62
67
  protected sourceChoices: QtiSimpleAssociableChoice[];
63
68
  protected targetChoices: QtiSimpleAssociableChoice[];
64
69
  protected lastCheckedRadio: HTMLInputElement | null;
65
70
  class: string;
71
+ dataFirstColumnHeader: string;
66
72
  protected _response: string | string[];
67
73
  get response(): string[];
68
74
  set response(val: string[]);
@@ -17,15 +17,17 @@ import tabularStyles from './qti-match-interaction-tabular.styles';
17
17
  * @customElement qti-match-interaction
18
18
  *
19
19
  * @attr {string} response-identifier - Required. Identifier of the bound response variable.
20
- * @attr {number} [max-associations=0] - Maximum pairings across the whole interaction; `0`
21
- * means unlimited. QTI's own default is `1`; this element defaults to unlimited because a
22
- * literal `1` breaks any multi-pair match item. See plans/interaction-attribute-spec-conformance.md.
23
- * @attr {number} [min-associations=1] - Minimum pairings for a valid response.
20
+ * @attr {number} [max-associations=1] - Maximum pairings across the whole interaction; `0`
21
+ * means unlimited. Without the attribute it is QTI's default of 1 (information model §5.89).
22
+ * @attr {number} [min-associations=0] - Minimum pairings for a valid response (QTI's default).
23
+ * @attr {string} data-first-column-header - QTI shared vocabulary, with `qti-match-tabular`: the
24
+ * text of the table's top-left cell.
24
25
  * @attr {boolean} [shuffle=false] - Requests shuffling of both match sets. Applied by the
25
26
  * transform pipeline (`qti-transformers`), not by this element.
26
- * @attr {'qti-match-tabular'|'qti-choices-top'|'qti-choices-bottom'|'qti-choices-left'|'qti-choices-right'} class -
27
+ * @attr {'qti-match-tabular'|'qti-header-hidden'|'qti-choices-top'|'qti-choices-bottom'|'qti-choices-left'|'qti-choices-right'} class -
27
28
  * QTI shared presentation vocabulary. `qti-match-tabular` renders a checkbox/radio grid
28
- * instead of drag-and-drop; the `qti-choices-*` values position the source set.
29
+ * instead of drag-and-drop, and with it `qti-header-hidden` leaves out the column-header row;
30
+ * the `qti-choices-*` values position the source set.
29
31
  * @attr {boolean} [auto-size-dropzones=false] - Extension, not QTI. Sizes every drop target to
30
32
  * the widest chip so placement does not reflow the layout. The mixin default is `true`; this
31
33
  * interaction is the one that overrides it to `false`, because a match target is a category card
@@ -39,7 +41,7 @@ import tabularStyles from './qti-match-interaction-tabular.styles';
39
41
  * @slot - Default slot for the two match sets (drag-and-drop mode).
40
42
  *
41
43
  * @csspart grid - The tabular grid container.
42
- * @csspart corner - The top-left corner cell of the grid.
44
+ * @csspart corner - The top-left corner cell of the grid; shows `data-first-column-header`.
43
45
  * @csspart cols-wrap - Wrapper row for the column headers.
44
46
  * @csspart c-header - The column header slot wrapper.
45
47
  * @csspart rows-wrap - Wrapper column for the row headers.
@@ -69,6 +71,9 @@ export class QtiMatchInteraction extends DragDropSlottedSortableMixin(DragDropSl
69
71
  * their drops are exactly the widest chip plus the inset.
70
72
  */
71
73
  this.autoSizeDropzones = false;
74
+ /** QTI's defaults (information model §5.89). */
75
+ this.maxAssociations = 1;
76
+ this.minAssociations = 0;
72
77
  this.lastCheckedRadio = null;
73
78
  this.class = '';
74
79
  this._response = [];
@@ -230,7 +235,7 @@ export class QtiMatchInteraction extends DragDropSlottedSortableMixin(DragDropSl
230
235
  role="grid"
231
236
  style="--qti-match-rows: ${this.sourceChoices.length}; --qti-match-cols: ${this.targetChoices.length};"
232
237
  >
233
- <div part="corner"></div>
238
+ <div part="corner">${this.dataFirstColumnHeader ?? nothing}</div>
234
239
  <div part="cols-wrap" role="row"><slot name="match-cols" part="c-header"></slot></div>
235
240
  <div part="rows-wrap"><slot name="match-rows" part="r-header"></slot></div>
236
241
  <div part="checkbox-grid">
@@ -296,6 +301,9 @@ export class QtiMatchInteraction extends DragDropSlottedSortableMixin(DragDropSl
296
301
  __decorate([
297
302
  property({ type: String })
298
303
  ], QtiMatchInteraction.prototype, "class", void 0);
304
+ __decorate([
305
+ property({ type: String, attribute: 'data-first-column-header' })
306
+ ], QtiMatchInteraction.prototype, "dataFirstColumnHeader", void 0);
299
307
  __decorate([
300
308
  state()
301
309
  ], QtiMatchInteraction.prototype, "_response", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"qti-match-interaction.js","sourceRoot":"","sources":["../src/qti-match-interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { Interaction } from '@qti-components/base';\nimport {\n DragDropSlottedMixin,\n DragDropSlottedSortableMixin\n} from '@qti-components/interactions-core/mixins/drag-drop-observables';\n\n// import { DragDropInteractionMixin } from '@qti-components/interactions-core/mixins/drag-drop';\nimport styles from './qti-match-interaction.styles';\nimport tabularStyles from './qti-match-interaction-tabular.styles';\n\nimport type { ResponseVariable } from '@qti-components/base';\nimport type { CSSResultGroup, PropertyValues } from 'lit';\nimport type { ResponseInteraction } from '@qti-components/base';\nimport type { QtiSimpleAssociableChoice } from '@qti-components/interactions-core/elements/qti-simple-associable-choice';\n/**\n * Match interaction: candidates pair items from two sets, either as drag-and-drop or as a tabular grid.\n *\n * @customElement qti-match-interaction\n *\n * @attr {string} response-identifier - Required. Identifier of the bound response variable.\n * @attr {number} [max-associations=0] - Maximum pairings across the whole interaction; `0`\n * means unlimited. QTI's own default is `1`; this element defaults to unlimited because a\n * literal `1` breaks any multi-pair match item. See plans/interaction-attribute-spec-conformance.md.\n * @attr {number} [min-associations=1] - Minimum pairings for a valid response.\n * @attr {boolean} [shuffle=false] - Requests shuffling of both match sets. Applied by the\n * transform pipeline (`qti-transformers`), not by this element.\n * @attr {'qti-match-tabular'|'qti-choices-top'|'qti-choices-bottom'|'qti-choices-left'|'qti-choices-right'} class -\n * QTI shared presentation vocabulary. `qti-match-tabular` renders a checkbox/radio grid\n * instead of drag-and-drop; the `qti-choices-*` values position the source set.\n * @attr {boolean} [auto-size-dropzones=false] - Extension, not QTI. Sizes every drop target to\n * the widest chip so placement does not reflow the layout. The mixin default is `true`; this\n * interaction is the one that overrides it to `false`, because a match target is a category card\n * rather than a socket (see the field below and DROP-SIZING.md §1).\n * @attr {boolean} [disable-animations=false] - Extension, not QTI. Disables the FLIP move\n * animation.\n *\n * @slot prompt - The prompt shown above the interaction.\n * @slot match-cols - Column headers (tabular mode).\n * @slot match-rows - Row headers (tabular mode).\n * @slot - Default slot for the two match sets (drag-and-drop mode).\n *\n * @csspart grid - The tabular grid container.\n * @csspart corner - The top-left corner cell of the grid.\n * @csspart cols-wrap - Wrapper row for the column headers.\n * @csspart c-header - The column header slot wrapper.\n * @csspart rows-wrap - Wrapper column for the row headers.\n * @csspart r-header - The row header slot wrapper.\n * @csspart checkbox-grid - The interactive cell grid area.\n * @csspart input-cell - Each cell's label element.\n * @csspart control - The visible checkbox/radio box (tabular mode). Also carries the variant\n * tokens `radio` | `checkbox`, `checked`, and `correct` | `incorrect` — e.g.\n * `::part(control radio checked correct)`. These mirror the custom-state vocabulary; the cells\n * are plain spans in the shadow root, so they cannot carry real `:state()`.\n * @csspart control-mark - The inner mark element; carries the same variant tokens as `control`.\n * @csspart message - Live validation message region (role=\"alert\").\n */\nexport class QtiMatchInteraction extends DragDropSlottedSortableMixin(\n DragDropSlottedMixin(\n Interaction,\n 'qti-simple-match-set:first-of-type qti-simple-associable-choice, qti-simple-match-set:last-of-type > qti-simple-associable-choice > qti-simple-associable-choice',\n 'qti-simple-match-set:last-of-type > qti-simple-associable-choice',\n 'qti-simple-match-set:first-of-type'\n ),\n '[qti-draggable=\"true\"]'\n) {\n static override styles: CSSResultGroup = [styles, tabularStyles];\n\n /**\n * Match targets are categories, not slots. They should look able to hold several answers, so they\n * take a flat floor (the `--qti-dropzone-min-height` fallback) rather than the size of the widest chip.\n *\n * This is a property of the INTERACTION, not of an individual target. A `match-max=\"1\"` target is\n * still a category that happens to take one answer, and it sits in a grid beside targets that take\n * several — sizing each one to its own contents would make that grid ragged. So the whole\n * interaction opts out, and the per-target `match-max` decides nothing about size here.\n *\n * The interactions where a drop IS a slot — gap-match, order, associate — keep measurement on, and\n * their drops are exactly the widest chip plus the inset.\n */\n public override autoSizeDropzones = false;\n\n protected sourceChoices: QtiSimpleAssociableChoice[];\n protected targetChoices: QtiSimpleAssociableChoice[];\n protected lastCheckedRadio: HTMLInputElement | null = null;\n\n @property({ type: String }) class: string = '';\n\n @state() protected _response: string | string[] = [];\n // dragDropApi: TouchDragAndDrop;\n get response(): string[] {\n if (!this.classList.contains('qti-match-tabular')) return super.response as string[];\n else return this._response as string[];\n }\n set response(val: string[]) {\n if (!this.classList.contains('qti-match-tabular')) super.response = val;\n else this._response = val;\n }\n\n // Tabular mode is a pure radio/checkbox grid — drag-drop has no role there, so the choices\n // must not advertise themselves as draggable either. Suppresses `:state(drag)` on them.\n public override isDragDropEnabled(): boolean {\n return !this.classList.contains('qti-match-tabular');\n }\n\n // Tabular mode is a pure radio/checkbox grid — drag-drop has no role there.\n // Short-circuit drag initiation so no clone is created and no shadow/lightdom\n // state can diverge from this._response.\n public override initiateDrag(...args: any[]): void {\n if (this.classList.contains('qti-match-tabular')) return;\n (super.initiateDrag as (...a: any[]) => void)(...args);\n }\n\n // Skip the drag-drop auto-sizing pass in tabular mode — it would write\n // inline min-width/min-height onto each choice that forces the grid columns\n // to the draggable's natural width.\n public override afterCache(): void {\n if (this.classList.contains('qti-match-tabular')) return;\n super.afterCache();\n }\n\n @property({ type: String, attribute: 'response-identifier' }) override responseIdentifier: string = '';\n\n override async connectedCallback(): Promise<void> {\n super.connectedCallback();\n this.sourceChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:first-of-type qti-simple-associable-choice')\n );\n this.targetChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:last-of-type qti-simple-associable-choice')\n );\n\n this.syncTabularSlotting();\n }\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate?.(changed);\n if (changed.has('class')) this.syncTabularSlotting();\n }\n\n private syncTabularSlotting(): void {\n const isTabular = this.classList.contains('qti-match-tabular');\n const sets = this.querySelectorAll(':scope > qti-simple-match-set');\n const sourceSet = sets[0] as HTMLElement | undefined;\n const targetSet = sets[1] as HTMLElement | undefined;\n\n const stripSizing = (c: QtiSimpleAssociableChoice) => {\n c.style.removeProperty('min-width');\n c.style.removeProperty('min-height');\n c.style.removeProperty('--qti-dropzone-min-height');\n };\n if (isTabular) {\n sourceSet?.setAttribute('slot', 'match-rows');\n targetSet?.setAttribute('slot', 'match-cols');\n // Choices flow into the subgrid wrappers via DOM order — no per-choice\n // grid-row/grid-column writes required. --qti-match-rows/-cols are set\n // inline on [part='grid'] from render() so the editor (which can't write\n // to host style under ProseMirror) shares the same mechanism.\n // A header labels its row or column and does nothing when activated, so it leaves the tab\n // order: Tab goes from one answer cell to the next, not through every label first.\n this.sourceChoices?.forEach(c => {\n stripSizing(c);\n c.setAttribute('role', 'rowheader');\n c.tabIndex = -1;\n });\n this.targetChoices?.forEach(c => {\n stripSizing(c);\n c.setAttribute('role', 'columnheader');\n c.tabIndex = -1;\n });\n } else {\n sourceSet?.removeAttribute('slot');\n targetSet?.removeAttribute('slot');\n this.sourceChoices?.forEach(c => {\n c.removeAttribute('role');\n c.tabIndex = c.disabled ? -1 : 0;\n });\n this.targetChoices?.forEach(c => {\n c.removeAttribute('role');\n c.tabIndex = c.disabled ? -1 : 0;\n });\n }\n }\n\n protected handleRadioClick = (e: { target: HTMLInputElement }) => {\n const radio = e.target as HTMLInputElement;\n if (this.lastCheckedRadio === radio) {\n radio.checked = false;\n this.lastCheckedRadio = null;\n this.handleRadioChange(e);\n } else {\n this.lastCheckedRadio = radio;\n }\n };\n\n protected handleRadioChange = (e: { target: any }) => {\n const checkbox = e.target as HTMLInputElement;\n const value = checkbox.value;\n const name = checkbox.name;\n const type = checkbox.type;\n\n if (checkbox.checked) {\n if (!this.response) {\n this.response = [value];\n } else if (this.response.indexOf(value) === -1) {\n if (type === 'radio') {\n this.response = (this.response || []).filter(v => v.indexOf(name) === -1);\n }\n this.response = [...this.response, value];\n }\n this.lastCheckedRadio = checkbox;\n } else {\n this.response = (this.response || []).filter(v => v !== value);\n this.lastCheckedRadio = null;\n }\n\n this.requestUpdate();\n this.dispatchEvent(\n new CustomEvent<ResponseInteraction>('qti-interaction-response', {\n bubbles: true,\n composed: true,\n detail: {\n responseIdentifier: this.responseIdentifier,\n response: Array.isArray(this.response) ? [...this.response] : this.response\n }\n })\n );\n };\n\n validate(): boolean {\n if (this.class.split(' ').includes('qti-match-tabular')) {\n return this.response?.length === this.sourceChoices.length;\n } else {\n return super.validate();\n }\n }\n\n /** Extension hook for packages that add visual state tokens to tabular cells. */\n protected getCellStateVariant(_value: string, _checked: boolean): string {\n return '';\n }\n\n override render() {\n const isTabular = this.class.split(' ').includes('qti-match-tabular');\n return html`\n <slot name=\"prompt\"></slot>\n <slot ?hidden=${isTabular}></slot>\n\n ${isTabular\n ? html`\n <div\n part=\"grid\"\n role=\"grid\"\n style=\"--qti-match-rows: ${this.sourceChoices.length}; --qti-match-cols: ${this.targetChoices.length};\"\n >\n <div part=\"corner\"></div>\n <div part=\"cols-wrap\" role=\"row\"><slot name=\"match-cols\" part=\"c-header\"></slot></div>\n <div part=\"rows-wrap\"><slot name=\"match-rows\" part=\"r-header\"></slot></div>\n <div part=\"checkbox-grid\">\n ${this.sourceChoices.map(\n (row, r) => html`\n <div role=\"row\" style=\"display: contents;\">\n ${this.targetChoices.map((col, c) => {\n const rowId = row.getAttribute('identifier');\n const colId = col.getAttribute('identifier');\n const value = `${rowId} ${colId}`;\n const selectedInRowCount =\n (this.response || []).filter(v => v.split(' ')[0] === rowId).length || 0;\n const checked = this.response?.includes(value) || false;\n const type = row.matchMax === 1 ? 'radio' : 'checkbox';\n /*\n * The cells are plain spans in this interaction's shadow root, so they\n * have no ElementInternals and cannot carry `:state()` — and\n * `::part(x):state(y)` requires the state to live on the part's own\n * element. Multi-token parts are the correct mechanism here. The tokens\n * deliberately reuse the state vocabulary (`radio`, `checkbox`,\n * `checked`, `correct`, `incorrect`) so a themer reads the same words\n * everywhere.\n *\n */\n const stateVariant = this.getCellStateVariant(value, checked);\n const checkedMarker = checked ? 'checked' : '';\n const controlPart = `control ${type} ${checkedMarker} ${stateVariant}`.trim();\n const controlMarkPart = `control-mark ${type} ${checkedMarker} ${stateVariant}`.trim();\n const disable =\n row.matchMax === 1\n ? false\n : row.matchMax !== 0 && selectedInRowCount >= row.matchMax && !checked;\n return html`\n <label\n part=\"input-cell\"\n role=\"gridcell\"\n style=\"grid-row: ${r + 1}; grid-column: ${c +\n 1}; display: flex; align-items: center; justify-content: center; cursor: pointer;\"\n >\n <input\n type=${type}\n name=${rowId}\n value=${value}\n .checked=${checked}\n .disabled=${disable}\n @change=${(e: { target: any }) => this.handleRadioChange(e)}\n @click=${(e: { target: HTMLInputElement }) =>\n row.matchMax === 1 ? this.handleRadioClick(e) : null}\n />\n <span part=${controlPart} role=${type} aria-checked=${checked ? 'true' : 'false'}>\n <span part=${controlMarkPart}></span>\n </span>\n </label>\n `;\n })}\n </div>\n `\n )}\n </div>\n </div>\n `\n : nothing}\n\n <div role=\"alert\" part=\"message\" id=\"validation-message\"></div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-match-interaction': QtiMatchInteraction;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-match-interaction.js","sourceRoot":"","sources":["../src/qti-match-interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { Interaction } from '@qti-components/base';\nimport {\n DragDropSlottedMixin,\n DragDropSlottedSortableMixin\n} from '@qti-components/interactions-core/mixins/drag-drop-observables';\n\n// import { DragDropInteractionMixin } from '@qti-components/interactions-core/mixins/drag-drop';\nimport styles from './qti-match-interaction.styles';\nimport tabularStyles from './qti-match-interaction-tabular.styles';\n\nimport type { ResponseVariable } from '@qti-components/base';\nimport type { CSSResultGroup, PropertyValues } from 'lit';\nimport type { ResponseInteraction } from '@qti-components/base';\nimport type { QtiSimpleAssociableChoice } from '@qti-components/interactions-core/elements/qti-simple-associable-choice';\n/**\n * Match interaction: candidates pair items from two sets, either as drag-and-drop or as a tabular grid.\n *\n * @customElement qti-match-interaction\n *\n * @attr {string} response-identifier - Required. Identifier of the bound response variable.\n * @attr {number} [max-associations=1] - Maximum pairings across the whole interaction; `0`\n * means unlimited. Without the attribute it is QTI's default of 1 (information model §5.89).\n * @attr {number} [min-associations=0] - Minimum pairings for a valid response (QTI's default).\n * @attr {string} data-first-column-header - QTI shared vocabulary, with `qti-match-tabular`: the\n * text of the table's top-left cell.\n * @attr {boolean} [shuffle=false] - Requests shuffling of both match sets. Applied by the\n * transform pipeline (`qti-transformers`), not by this element.\n * @attr {'qti-match-tabular'|'qti-header-hidden'|'qti-choices-top'|'qti-choices-bottom'|'qti-choices-left'|'qti-choices-right'} class -\n * QTI shared presentation vocabulary. `qti-match-tabular` renders a checkbox/radio grid\n * instead of drag-and-drop, and with it `qti-header-hidden` leaves out the column-header row;\n * the `qti-choices-*` values position the source set.\n * @attr {boolean} [auto-size-dropzones=false] - Extension, not QTI. Sizes every drop target to\n * the widest chip so placement does not reflow the layout. The mixin default is `true`; this\n * interaction is the one that overrides it to `false`, because a match target is a category card\n * rather than a socket (see the field below and DROP-SIZING.md §1).\n * @attr {boolean} [disable-animations=false] - Extension, not QTI. Disables the FLIP move\n * animation.\n *\n * @slot prompt - The prompt shown above the interaction.\n * @slot match-cols - Column headers (tabular mode).\n * @slot match-rows - Row headers (tabular mode).\n * @slot - Default slot for the two match sets (drag-and-drop mode).\n *\n * @csspart grid - The tabular grid container.\n * @csspart corner - The top-left corner cell of the grid; shows `data-first-column-header`.\n * @csspart cols-wrap - Wrapper row for the column headers.\n * @csspart c-header - The column header slot wrapper.\n * @csspart rows-wrap - Wrapper column for the row headers.\n * @csspart r-header - The row header slot wrapper.\n * @csspart checkbox-grid - The interactive cell grid area.\n * @csspart input-cell - Each cell's label element.\n * @csspart control - The visible checkbox/radio box (tabular mode). Also carries the variant\n * tokens `radio` | `checkbox`, `checked`, and `correct` | `incorrect` — e.g.\n * `::part(control radio checked correct)`. These mirror the custom-state vocabulary; the cells\n * are plain spans in the shadow root, so they cannot carry real `:state()`.\n * @csspart control-mark - The inner mark element; carries the same variant tokens as `control`.\n * @csspart message - Live validation message region (role=\"alert\").\n */\nexport class QtiMatchInteraction extends DragDropSlottedSortableMixin(\n DragDropSlottedMixin(\n Interaction,\n 'qti-simple-match-set:first-of-type qti-simple-associable-choice, qti-simple-match-set:last-of-type > qti-simple-associable-choice > qti-simple-associable-choice',\n 'qti-simple-match-set:last-of-type > qti-simple-associable-choice',\n 'qti-simple-match-set:first-of-type'\n ),\n '[qti-draggable=\"true\"]'\n) {\n static override styles: CSSResultGroup = [styles, tabularStyles];\n\n /**\n * Match targets are categories, not slots. They should look able to hold several answers, so they\n * take a flat floor (the `--qti-dropzone-min-height` fallback) rather than the size of the widest chip.\n *\n * This is a property of the INTERACTION, not of an individual target. A `match-max=\"1\"` target is\n * still a category that happens to take one answer, and it sits in a grid beside targets that take\n * several — sizing each one to its own contents would make that grid ragged. So the whole\n * interaction opts out, and the per-target `match-max` decides nothing about size here.\n *\n * The interactions where a drop IS a slot — gap-match, order, associate — keep measurement on, and\n * their drops are exactly the widest chip plus the inset.\n */\n public override autoSizeDropzones = false;\n\n /** QTI's defaults (information model §5.89). */\n public override maxAssociations = 1;\n public override minAssociations = 0;\n\n protected sourceChoices: QtiSimpleAssociableChoice[];\n protected targetChoices: QtiSimpleAssociableChoice[];\n protected lastCheckedRadio: HTMLInputElement | null = null;\n\n @property({ type: String }) class: string = '';\n\n @property({ type: String, attribute: 'data-first-column-header' }) dataFirstColumnHeader: string;\n\n @state() protected _response: string | string[] = [];\n // dragDropApi: TouchDragAndDrop;\n get response(): string[] {\n if (!this.classList.contains('qti-match-tabular')) return super.response as string[];\n else return this._response as string[];\n }\n set response(val: string[]) {\n if (!this.classList.contains('qti-match-tabular')) super.response = val;\n else this._response = val;\n }\n\n // Tabular mode is a pure radio/checkbox grid — drag-drop has no role there, so the choices\n // must not advertise themselves as draggable either. Suppresses `:state(drag)` on them.\n public override isDragDropEnabled(): boolean {\n return !this.classList.contains('qti-match-tabular');\n }\n\n // Tabular mode is a pure radio/checkbox grid — drag-drop has no role there.\n // Short-circuit drag initiation so no clone is created and no shadow/lightdom\n // state can diverge from this._response.\n public override initiateDrag(...args: any[]): void {\n if (this.classList.contains('qti-match-tabular')) return;\n (super.initiateDrag as (...a: any[]) => void)(...args);\n }\n\n // Skip the drag-drop auto-sizing pass in tabular mode — it would write\n // inline min-width/min-height onto each choice that forces the grid columns\n // to the draggable's natural width.\n public override afterCache(): void {\n if (this.classList.contains('qti-match-tabular')) return;\n super.afterCache();\n }\n\n @property({ type: String, attribute: 'response-identifier' }) override responseIdentifier: string = '';\n\n override async connectedCallback(): Promise<void> {\n super.connectedCallback();\n this.sourceChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:first-of-type qti-simple-associable-choice')\n );\n this.targetChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:last-of-type qti-simple-associable-choice')\n );\n\n this.syncTabularSlotting();\n }\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate?.(changed);\n if (changed.has('class')) this.syncTabularSlotting();\n }\n\n private syncTabularSlotting(): void {\n const isTabular = this.classList.contains('qti-match-tabular');\n const sets = this.querySelectorAll(':scope > qti-simple-match-set');\n const sourceSet = sets[0] as HTMLElement | undefined;\n const targetSet = sets[1] as HTMLElement | undefined;\n\n const stripSizing = (c: QtiSimpleAssociableChoice) => {\n c.style.removeProperty('min-width');\n c.style.removeProperty('min-height');\n c.style.removeProperty('--qti-dropzone-min-height');\n };\n if (isTabular) {\n sourceSet?.setAttribute('slot', 'match-rows');\n targetSet?.setAttribute('slot', 'match-cols');\n // Choices flow into the subgrid wrappers via DOM order — no per-choice\n // grid-row/grid-column writes required. --qti-match-rows/-cols are set\n // inline on [part='grid'] from render() so the editor (which can't write\n // to host style under ProseMirror) shares the same mechanism.\n // A header labels its row or column and does nothing when activated, so it leaves the tab\n // order: Tab goes from one answer cell to the next, not through every label first.\n this.sourceChoices?.forEach(c => {\n stripSizing(c);\n c.setAttribute('role', 'rowheader');\n c.tabIndex = -1;\n });\n this.targetChoices?.forEach(c => {\n stripSizing(c);\n c.setAttribute('role', 'columnheader');\n c.tabIndex = -1;\n });\n } else {\n sourceSet?.removeAttribute('slot');\n targetSet?.removeAttribute('slot');\n this.sourceChoices?.forEach(c => {\n c.removeAttribute('role');\n c.tabIndex = c.disabled ? -1 : 0;\n });\n this.targetChoices?.forEach(c => {\n c.removeAttribute('role');\n c.tabIndex = c.disabled ? -1 : 0;\n });\n }\n }\n\n protected handleRadioClick = (e: { target: HTMLInputElement }) => {\n const radio = e.target as HTMLInputElement;\n if (this.lastCheckedRadio === radio) {\n radio.checked = false;\n this.lastCheckedRadio = null;\n this.handleRadioChange(e);\n } else {\n this.lastCheckedRadio = radio;\n }\n };\n\n protected handleRadioChange = (e: { target: any }) => {\n const checkbox = e.target as HTMLInputElement;\n const value = checkbox.value;\n const name = checkbox.name;\n const type = checkbox.type;\n\n if (checkbox.checked) {\n if (!this.response) {\n this.response = [value];\n } else if (this.response.indexOf(value) === -1) {\n if (type === 'radio') {\n this.response = (this.response || []).filter(v => v.indexOf(name) === -1);\n }\n this.response = [...this.response, value];\n }\n this.lastCheckedRadio = checkbox;\n } else {\n this.response = (this.response || []).filter(v => v !== value);\n this.lastCheckedRadio = null;\n }\n\n this.requestUpdate();\n this.dispatchEvent(\n new CustomEvent<ResponseInteraction>('qti-interaction-response', {\n bubbles: true,\n composed: true,\n detail: {\n responseIdentifier: this.responseIdentifier,\n response: Array.isArray(this.response) ? [...this.response] : this.response\n }\n })\n );\n };\n\n validate(): boolean {\n if (this.class.split(' ').includes('qti-match-tabular')) {\n return this.response?.length === this.sourceChoices.length;\n } else {\n return super.validate();\n }\n }\n\n /** Extension hook for packages that add visual state tokens to tabular cells. */\n protected getCellStateVariant(_value: string, _checked: boolean): string {\n return '';\n }\n\n override render() {\n const isTabular = this.class.split(' ').includes('qti-match-tabular');\n return html`\n <slot name=\"prompt\"></slot>\n <slot ?hidden=${isTabular}></slot>\n\n ${isTabular\n ? html`\n <div\n part=\"grid\"\n role=\"grid\"\n style=\"--qti-match-rows: ${this.sourceChoices.length}; --qti-match-cols: ${this.targetChoices.length};\"\n >\n <div part=\"corner\">${this.dataFirstColumnHeader ?? nothing}</div>\n <div part=\"cols-wrap\" role=\"row\"><slot name=\"match-cols\" part=\"c-header\"></slot></div>\n <div part=\"rows-wrap\"><slot name=\"match-rows\" part=\"r-header\"></slot></div>\n <div part=\"checkbox-grid\">\n ${this.sourceChoices.map(\n (row, r) => html`\n <div role=\"row\" style=\"display: contents;\">\n ${this.targetChoices.map((col, c) => {\n const rowId = row.getAttribute('identifier');\n const colId = col.getAttribute('identifier');\n const value = `${rowId} ${colId}`;\n const selectedInRowCount =\n (this.response || []).filter(v => v.split(' ')[0] === rowId).length || 0;\n const checked = this.response?.includes(value) || false;\n const type = row.matchMax === 1 ? 'radio' : 'checkbox';\n /*\n * The cells are plain spans in this interaction's shadow root, so they\n * have no ElementInternals and cannot carry `:state()` — and\n * `::part(x):state(y)` requires the state to live on the part's own\n * element. Multi-token parts are the correct mechanism here. The tokens\n * deliberately reuse the state vocabulary (`radio`, `checkbox`,\n * `checked`, `correct`, `incorrect`) so a themer reads the same words\n * everywhere.\n *\n */\n const stateVariant = this.getCellStateVariant(value, checked);\n const checkedMarker = checked ? 'checked' : '';\n const controlPart = `control ${type} ${checkedMarker} ${stateVariant}`.trim();\n const controlMarkPart = `control-mark ${type} ${checkedMarker} ${stateVariant}`.trim();\n const disable =\n row.matchMax === 1\n ? false\n : row.matchMax !== 0 && selectedInRowCount >= row.matchMax && !checked;\n return html`\n <label\n part=\"input-cell\"\n role=\"gridcell\"\n style=\"grid-row: ${r + 1}; grid-column: ${c +\n 1}; display: flex; align-items: center; justify-content: center; cursor: pointer;\"\n >\n <input\n type=${type}\n name=${rowId}\n value=${value}\n .checked=${checked}\n .disabled=${disable}\n @change=${(e: { target: any }) => this.handleRadioChange(e)}\n @click=${(e: { target: HTMLInputElement }) =>\n row.matchMax === 1 ? this.handleRadioClick(e) : null}\n />\n <span part=${controlPart} role=${type} aria-checked=${checked ? 'true' : 'false'}>\n <span part=${controlMarkPart}></span>\n </span>\n </label>\n `;\n })}\n </div>\n `\n )}\n </div>\n </div>\n `\n : nothing}\n\n <div role=\"alert\" part=\"message\" id=\"validation-message\"></div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-match-interaction': QtiMatchInteraction;\n }\n}\n"]}
@@ -1,4 +1,7 @@
1
1
  import { html } from 'lit';
2
+ import choiceB3Image from '../fixtures/72c4b1f4ea9f7ba5548683c37806580e.png';
3
+ import choiceB2Image from '../fixtures/80378beafd6b12ab7b97899ce39a2b78.jpg';
4
+ import choiceB1Image from '../fixtures/8c571f983f691e42e7b3a0dc2664e51d.png';
2
5
  /** Fixtures shared by the overview demos and the behavior stories. */
3
6
  export const testTemplate = html `<qti-prompt
4
7
  >Match the following characters to the Shakespeare play they appeared in:</qti-prompt
@@ -170,13 +173,13 @@ export const withImagesTemplate = html `
170
173
  </qti-simple-match-set>
171
174
  <qti-simple-match-set>
172
175
  <qti-simple-associable-choice identifier="B1" match-max="1">
173
- <img src="assets/qti-match-interaction/8c571f983f691e42e7b3a0dc2664e51d.png" alt="afbeelding" />
176
+ <img src=${choiceB1Image} alt="afbeelding" />
174
177
  </qti-simple-associable-choice>
175
178
  <qti-simple-associable-choice identifier="B2" match-max="1">
176
- <img src="assets/qti-match-interaction/80378beafd6b12ab7b97899ce39a2b78.jpg" alt="afbeelding" />
179
+ <img src=${choiceB2Image} alt="afbeelding" />
177
180
  </qti-simple-associable-choice>
178
181
  <qti-simple-associable-choice identifier="B3" match-max="1">
179
- <img src="assets/qti-match-interaction/72c4b1f4ea9f7ba5548683c37806580e.png" alt="afbeelding" />
182
+ <img src=${choiceB3Image} alt="afbeelding" />
180
183
  </qti-simple-associable-choice>
181
184
  </qti-simple-match-set>
182
185
  </qti-match-interaction>
@@ -1 +1 @@
1
- {"version":3,"file":"qti-match-interaction.templates.js","sourceRoot":"","sources":["../../src/stories/qti-match-interaction.templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAE3B,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;2BAgBL,CAAC;AAE5B,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAyCvB,CAAC;AAE1B,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BhD,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;CAgBvC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDtC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;CAoBrC,CAAC;AAEF,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAA;;;;;;;;;;;;CAYlD,CAAC;AAEF,MAAM,CAAC,MAAM,8CAA8C,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;CAgBjE,CAAC;AAEF,MAAM,CAAC,MAAM,yCAAyC,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;CAgB5D,CAAC","sourcesContent":["import { html } from 'lit';\n\n/** Fixtures shared by the overview demos and the behavior stories. */\nexport const testTemplate = html`<qti-prompt\n >Match the following characters to the Shakespeare play they appeared in:</qti-prompt\n >\n <qti-match-interaction data-testid=\"match-interaction\" response-identifier=\"RESPONSE\" max-associations=\"3\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"C\" match-max=\"1\">Capulet</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"D\" match-max=\"1\">Demetrius</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"L\" match-max=\"1\">Lysander</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"P\" match-max=\"1\">Prospero</qti-simple-associable-choice>\n </qti-simple-match-set>\n\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"M\" match-max=\"2\">A Midsummer-Nights</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"R\" match-max=\"2\">Romeo and Juliet</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T\" match-max=\"2\">The Tempest</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>`;\n\nexport const multipleAssociationsTemplate = html` <qti-match-interaction\n response-identifier=\"match_languages_countries\"\n shuffle=\"false\"\n max-associations=\"5\"\n min-associations=\"0\"\n>\n <qti-prompt>Can you match the cities below with the language most likely to be spoken there?</qti-prompt>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"french\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >French</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"hungarian\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Hungarian</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"polish\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Polish</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"dutch\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Dutch</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"lithuanian\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Lithuanian</qti-simple-associable-choice\n >\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"budapest\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Budapest</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"vilnius\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Vilnius</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"paris\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Paris</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"amsterdam\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Amsterdam</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"warsaw\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Warsaw</qti-simple-associable-choice\n >\n </qti-simple-match-set>\n</qti-match-interaction>`;\n\nexport const multipleAssociations2Template = html`\n <div>\n <p>Which properties do the following animal classes have?</p>\n\n <qti-match-interaction max-associations=\"3\" response-identifier=\"RESPONSE\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"r1\" match-max=\"0\">\n <p>Asexual</p>\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"r2\" match-max=\"0\">\n <p>Endothermic</p>\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"r3\" match-max=\"0\">\n <p>Possess Gills</p>\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"r4\" match-max=\"0\">\n <p>Bear Live Young</p>\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"h1\" match-max=\"2\">Birds</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"h2\" match-max=\"2\">Reptiles</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"h3\" match-max=\"2\">Mammals</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n </div>\n <p>Select all choices that apply.</p>\n`;\n\nexport const optionsRightTemplate = html`\n <qti-match-interaction response-identifier=\"RESPONSE\" shuffle=\"true\" max-associations=\"4\" class=\"qti-choices-right\">\n <qti-prompt>Combineer de teksten</qti-prompt>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A1\" match-max=\"1\">Berlijn</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A2\" match-max=\"1\">Amsterdam</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A3\" match-max=\"1\">Parijs</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A4\" match-max=\"1\">Brussel</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"B1\" match-max=\"1\">Duitsland</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B2\" match-max=\"1\">Nederland</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B3\" match-max=\"1\">Frankrijk</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B4\" match-max=\"1\">Belgie</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const manyOptionsTemplate = html`\n <div>\n <p>\n <strong>Organellen en hun functies.</strong><br /><br />Geef in het overzicht van de verschillende organellen in\n cellen hun functies.\n </p>\n </div>\n <qti-match-interaction response-identifier=\"RESPONSE\" shuffle=\"true\" max-associations=\"7\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A1\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n endoplasmatisch reticulum\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A2\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n ribosomen\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A3\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n mitochondriën\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A4\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n lysosomen\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A5\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n golgi-apparaat\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A6\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n kern\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A7\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n centrosomen\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"B1\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n isolatie en vervoer van eiwitten\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B2\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n aanmaak van eiwitten\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B3\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n energieproductie\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B4\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n afbreken en/of uitstoten van afval uit de cel\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B5\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n opslag van celproducten\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B6\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n besturing en voortplanting van de cel\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B7\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n vorming van draden bij de kerndeling\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const withImagesTemplate = html`\n <qti-match-interaction response-identifier=\"RESPONSE\" shuffle=\"true\" max-associations=\"3\" class=\"qti-choices-bottom\">\n <qti-prompt>Hoe heet iedereen?</qti-prompt>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A1\" match-max=\"1\">Ernie</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A2\" match-max=\"1\">Pino</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A3\" match-max=\"1\">Kermit</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"B1\" match-max=\"1\">\n <img src=\"assets/qti-match-interaction/8c571f983f691e42e7b3a0dc2664e51d.png\" alt=\"afbeelding\" />\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B2\" match-max=\"1\">\n <img src=\"assets/qti-match-interaction/80378beafd6b12ab7b97899ce39a2b78.jpg\" alt=\"afbeelding\" />\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B3\" match-max=\"1\">\n <img src=\"assets/qti-match-interaction/72c4b1f4ea9f7ba5548683c37806580e.png\" alt=\"afbeelding\" />\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const duplicateDropRegressionTemplate = html`\n <qti-match-interaction data-testid=\"match-interaction-duplicates\" response-identifier=\"RESPONSE\" max-associations=\"3\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A\" match-max=\"0\">Alpha</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B\" match-max=\"0\">Beta</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"T1\" match-max=\"1\">Target 1</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T2\" match-max=\"1\">Target 2</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T3\" match-max=\"1\">Target 3</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const sortableAppendWhenTargetAllowsMultipleTemplate = html`\n <qti-match-interaction\n data-testid=\"match-interaction-sortable-append\"\n response-identifier=\"RESPONSE\"\n max-associations=\"4\"\n >\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A\" match-max=\"0\">Alpha</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B\" match-max=\"0\">Beta</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"T1\" match-max=\"1\">Target 1</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T2\" match-max=\"1\">Target 2</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T3\" match-max=\"2\">Target 3</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const sortableAppendWhenTargetUnlimitedTemplate = html`\n <qti-match-interaction\n data-testid=\"match-interaction-sortable-unlimited-target\"\n response-identifier=\"RESPONSE\"\n max-associations=\"6\"\n >\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A\" match-max=\"0\">Alpha</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B\" match-max=\"0\">Beta</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"T1\" match-max=\"1\">Target 1</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T2\" match-max=\"1\">Target 2</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T3\" match-max=\"0\">Target 3</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n"]}
1
+ {"version":3,"file":"qti-match-interaction.templates.js","sourceRoot":"","sources":["../../src/stories/qti-match-interaction.templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAE3B,OAAO,aAAa,MAAM,kDAAkD,CAAC;AAC7E,OAAO,aAAa,MAAM,kDAAkD,CAAC;AAC7E,OAAO,aAAa,MAAM,kDAAkD,CAAC;AAE7E,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;2BAgBL,CAAC;AAE5B,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAyCvB,CAAC;AAE1B,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BhD,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;CAgBvC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDtC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAA;;;;;;;;;;mBAUnB,aAAa;;;mBAGb,aAAa;;;mBAGb,aAAa;;;;CAI/B,CAAC;AAEF,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAA;;;;;;;;;;;;CAYlD,CAAC;AAEF,MAAM,CAAC,MAAM,8CAA8C,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;CAgBjE,CAAC;AAEF,MAAM,CAAC,MAAM,yCAAyC,GAAG,IAAI,CAAA;;;;;;;;;;;;;;;;CAgB5D,CAAC","sourcesContent":["import { html } from 'lit';\n\nimport choiceB3Image from '../fixtures/72c4b1f4ea9f7ba5548683c37806580e.png';\nimport choiceB2Image from '../fixtures/80378beafd6b12ab7b97899ce39a2b78.jpg';\nimport choiceB1Image from '../fixtures/8c571f983f691e42e7b3a0dc2664e51d.png';\n\n/** Fixtures shared by the overview demos and the behavior stories. */\nexport const testTemplate = html`<qti-prompt\n >Match the following characters to the Shakespeare play they appeared in:</qti-prompt\n >\n <qti-match-interaction data-testid=\"match-interaction\" response-identifier=\"RESPONSE\" max-associations=\"3\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"C\" match-max=\"1\">Capulet</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"D\" match-max=\"1\">Demetrius</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"L\" match-max=\"1\">Lysander</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"P\" match-max=\"1\">Prospero</qti-simple-associable-choice>\n </qti-simple-match-set>\n\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"M\" match-max=\"2\">A Midsummer-Nights</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"R\" match-max=\"2\">Romeo and Juliet</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T\" match-max=\"2\">The Tempest</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>`;\n\nexport const multipleAssociationsTemplate = html` <qti-match-interaction\n response-identifier=\"match_languages_countries\"\n shuffle=\"false\"\n max-associations=\"5\"\n min-associations=\"0\"\n>\n <qti-prompt>Can you match the cities below with the language most likely to be spoken there?</qti-prompt>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"french\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >French</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"hungarian\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Hungarian</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"polish\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Polish</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"dutch\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Dutch</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"lithuanian\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Lithuanian</qti-simple-associable-choice\n >\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"budapest\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Budapest</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"vilnius\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Vilnius</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"paris\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Paris</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"amsterdam\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Amsterdam</qti-simple-associable-choice\n >\n <qti-simple-associable-choice identifier=\"warsaw\" fixed=\"false\" show-hide=\"show\" match-max=\"0\" match-min=\"0\"\n >Warsaw</qti-simple-associable-choice\n >\n </qti-simple-match-set>\n</qti-match-interaction>`;\n\nexport const multipleAssociations2Template = html`\n <div>\n <p>Which properties do the following animal classes have?</p>\n\n <qti-match-interaction max-associations=\"3\" response-identifier=\"RESPONSE\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"r1\" match-max=\"0\">\n <p>Asexual</p>\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"r2\" match-max=\"0\">\n <p>Endothermic</p>\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"r3\" match-max=\"0\">\n <p>Possess Gills</p>\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"r4\" match-max=\"0\">\n <p>Bear Live Young</p>\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"h1\" match-max=\"2\">Birds</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"h2\" match-max=\"2\">Reptiles</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"h3\" match-max=\"2\">Mammals</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n </div>\n <p>Select all choices that apply.</p>\n`;\n\nexport const optionsRightTemplate = html`\n <qti-match-interaction response-identifier=\"RESPONSE\" shuffle=\"true\" max-associations=\"4\" class=\"qti-choices-right\">\n <qti-prompt>Combineer de teksten</qti-prompt>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A1\" match-max=\"1\">Berlijn</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A2\" match-max=\"1\">Amsterdam</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A3\" match-max=\"1\">Parijs</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A4\" match-max=\"1\">Brussel</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"B1\" match-max=\"1\">Duitsland</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B2\" match-max=\"1\">Nederland</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B3\" match-max=\"1\">Frankrijk</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B4\" match-max=\"1\">Belgie</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const manyOptionsTemplate = html`\n <div>\n <p>\n <strong>Organellen en hun functies.</strong><br /><br />Geef in het overzicht van de verschillende organellen in\n cellen hun functies.\n </p>\n </div>\n <qti-match-interaction response-identifier=\"RESPONSE\" shuffle=\"true\" max-associations=\"7\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A1\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n endoplasmatisch reticulum\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A2\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n ribosomen\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A3\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n mitochondriën\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A4\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n lysosomen\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A5\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n golgi-apparaat\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A6\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n kern\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A7\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n centrosomen\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"B1\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n isolatie en vervoer van eiwitten\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B2\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n aanmaak van eiwitten\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B3\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n energieproductie\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B4\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n afbreken en/of uitstoten van afval uit de cel\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B5\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n opslag van celproducten\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B6\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n besturing en voortplanting van de cel\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B7\" fixed=\"false\" match-max=\"1\" match-min=\"0\">\n vorming van draden bij de kerndeling\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const withImagesTemplate = html`\n <qti-match-interaction response-identifier=\"RESPONSE\" shuffle=\"true\" max-associations=\"3\" class=\"qti-choices-bottom\">\n <qti-prompt>Hoe heet iedereen?</qti-prompt>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A1\" match-max=\"1\">Ernie</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A2\" match-max=\"1\">Pino</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"A3\" match-max=\"1\">Kermit</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"B1\" match-max=\"1\">\n <img src=${choiceB1Image} alt=\"afbeelding\" />\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B2\" match-max=\"1\">\n <img src=${choiceB2Image} alt=\"afbeelding\" />\n </qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B3\" match-max=\"1\">\n <img src=${choiceB3Image} alt=\"afbeelding\" />\n </qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const duplicateDropRegressionTemplate = html`\n <qti-match-interaction data-testid=\"match-interaction-duplicates\" response-identifier=\"RESPONSE\" max-associations=\"3\">\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A\" match-max=\"0\">Alpha</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B\" match-max=\"0\">Beta</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"T1\" match-max=\"1\">Target 1</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T2\" match-max=\"1\">Target 2</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T3\" match-max=\"1\">Target 3</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const sortableAppendWhenTargetAllowsMultipleTemplate = html`\n <qti-match-interaction\n data-testid=\"match-interaction-sortable-append\"\n response-identifier=\"RESPONSE\"\n max-associations=\"4\"\n >\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A\" match-max=\"0\">Alpha</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B\" match-max=\"0\">Beta</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"T1\" match-max=\"1\">Target 1</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T2\" match-max=\"1\">Target 2</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T3\" match-max=\"2\">Target 3</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n\nexport const sortableAppendWhenTargetUnlimitedTemplate = html`\n <qti-match-interaction\n data-testid=\"match-interaction-sortable-unlimited-target\"\n response-identifier=\"RESPONSE\"\n max-associations=\"6\"\n >\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"A\" match-max=\"0\">Alpha</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"B\" match-max=\"0\">Beta</qti-simple-associable-choice>\n </qti-simple-match-set>\n <qti-simple-match-set>\n <qti-simple-associable-choice identifier=\"T1\" match-max=\"1\">Target 1</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T2\" match-max=\"1\">Target 2</qti-simple-associable-choice>\n <qti-simple-associable-choice identifier=\"T3\" match-max=\"0\">Target 3</qti-simple-associable-choice>\n </qti-simple-match-set>\n </qti-match-interaction>\n`;\n"]}
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@qti-components/match-interaction",
3
3
  "description": "QTI match-interaction interaction",
4
- "version": "2.1.0-next.2",
4
+ "version": "3.0.0-next.4",
5
5
  "author": "",
6
6
  "dependencies": {
7
- "@qti-components/base": "^2.5.0-next.2",
8
- "@qti-components/interactions-core": "^2.2.0-next.3",
7
+ "@qti-components/base": "^3.0.0-next.5",
8
+ "@qti-components/interactions-core": "^3.0.0-next.6",
9
9
  "@qti-components/utilities": "^1.3.3"
10
10
  },
11
11
  "devDependencies": {