@qti-components/associate-interaction 1.1.5 → 1.1.6

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.
@@ -0,0 +1,6 @@
1
+ import { QtiAssociateInteraction } from './qti-associate-interaction';
2
+ export declare const elements: readonly [{
3
+ readonly tag: "qti-associate-interaction";
4
+ readonly ctor: typeof QtiAssociateInteraction;
5
+ }];
6
+ export { QtiAssociateInteraction };
@@ -0,0 +1,4 @@
1
+ import { QtiAssociateInteraction } from './qti-associate-interaction';
2
+ export const elements = [{ tag: 'qti-associate-interaction', ctor: QtiAssociateInteraction }];
3
+ export { QtiAssociateInteraction };
4
+ //# sourceMappingURL=elements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elements.js","sourceRoot":"","sources":["../src/elements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EAAE,GAAG,EAAE,2BAA2B,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAU,CAAC;AAEvG,OAAO,EAAE,uBAAuB,EAAE,CAAC","sourcesContent":["import { QtiAssociateInteraction } from './qti-associate-interaction';\n\nexport const elements = [{ tag: 'qti-associate-interaction', ctor: QtiAssociateInteraction }] as const;\n\nexport { QtiAssociateInteraction };\n"]}
@@ -1,13 +1,39 @@
1
1
  import { Interaction } from '@qti-components/base';
2
2
  import type { CSSResultGroup } from 'lit';
3
3
  declare const QtiAssociateInteraction_base: (abstract new (...args: any[]) => import("@qti-components/interactions-core/mixins/drag-drop-observables").DragDropSlottedSortable) & (abstract new (...args: any[]) => import("@qti-components/interactions-core/mixins/drag-drop-observables").DragDropSlotted) & typeof Interaction;
4
+ /**
5
+ * Drag-and-drop associate interaction: candidates pair associable choices.
6
+ *
7
+ * @slot prompt - The prompt shown above the choices.
8
+ * @slot qti-simple-associable-choice - The associable choice sources.
9
+ *
10
+ * @csspart drags - Wrapper around the associable choice slot.
11
+ * @csspart drops - The container holding all drop targets.
12
+ * @csspart drop-row - Container for one left/right drop pair.
13
+ * @csspart drop - Each individual drop target.
14
+ * @csspart message - Live validation message region (role="alert").
15
+ */
4
16
  export declare class QtiAssociateInteraction extends QtiAssociateInteraction_base {
17
+ #private;
5
18
  static styles: CSSResultGroup;
6
19
  protected _childrenMap: Element[];
7
20
  protected _registerChoiceHandler: (event: CustomEvent) => void;
8
21
  constructor();
9
22
  protected _registerChoice(event: CustomEvent): void;
10
23
  protected getResponse(): string[];
24
+ /**
25
+ * How many associations the candidate has made.
26
+ *
27
+ * The mixin counts placed chips, which is right for gap-match — one chip in one gap is one
28
+ * association — and wrong here. An association is a *pair*, so three associations are six chips,
29
+ * and `max-associations="3"` compared 6 against 3 and declared the item invalid the moment it was
30
+ * correctly filled in.
31
+ *
32
+ * A half-filled row is not an association yet, and `getResponse()` already knows that: it emits a
33
+ * pair only when both sides hold a chip. Counting its entries counts associations by definition,
34
+ * and keeps the number validation sees equal to the number the response reports.
35
+ */
36
+ totalAssociationsFromState(): number;
11
37
  getValue(val: string[]): string[];
12
38
  render(): import("lit").TemplateResult<1>;
13
39
  protected updated(changedProperties: Map<string, unknown>): void;
@@ -5,12 +5,25 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { html } from 'lit';
8
+ import { repeat } from 'lit/directives/repeat.js';
8
9
  import { state } from 'lit/decorators.js';
9
10
  import { Interaction } from '@qti-components/base';
10
11
  import { DragDropSlottedMixin, DragDropSlottedSortableMixin } from '@qti-components/interactions-core/mixins/drag-drop-observables';
11
12
  // import { DragDropInteractionMixin } from '@qti-components/interactions-core/mixins/drag-drop';
12
13
  import styles from './qti-associate-interaction.styles';
13
- const SlottedBase = DragDropSlottedMixin(Interaction, 'qti-simple-associable-choice', '.dl', `slot[name='qti-simple-associable-choice']`, 'pointerWithin');
14
+ const SlottedBase = DragDropSlottedMixin(Interaction, 'qti-simple-associable-choice', `[part~='drop']`, `slot[name='qti-simple-associable-choice']`, 'pointerWithin');
15
+ /**
16
+ * Drag-and-drop associate interaction: candidates pair associable choices.
17
+ *
18
+ * @slot prompt - The prompt shown above the choices.
19
+ * @slot qti-simple-associable-choice - The associable choice sources.
20
+ *
21
+ * @csspart drags - Wrapper around the associable choice slot.
22
+ * @csspart drops - The container holding all drop targets.
23
+ * @csspart drop-row - Container for one left/right drop pair.
24
+ * @csspart drop - Each individual drop target.
25
+ * @csspart message - Live validation message region (role="alert").
26
+ */
14
27
  export class QtiAssociateInteraction extends DragDropSlottedSortableMixin(SlottedBase, '[qti-draggable="true"]') {
15
28
  static { this.styles = styles; }
16
29
  constructor() {
@@ -31,20 +44,66 @@ export class QtiAssociateInteraction extends DragDropSlottedSortableMixin(Slotte
31
44
  this._childrenMap = [...this._childrenMap, choice];
32
45
  }
33
46
  }
47
+ /**
48
+ * A drop target, holding whatever the interaction's placement map says it holds.
49
+ * `data-declarative-drops` tells the mixin to keep the chips in that map rather than appending
50
+ * them into this div.
51
+ */
52
+ #renderDrop(identifier, name) {
53
+ const drags = this._dragDrop?.nodesByTarget?.[identifier] ?? [];
54
+ return html `<div name=${name} part="drop" data-declarative-drops identifier=${identifier}>
55
+ ${repeat(drags, node => node.getAttribute('identifier'), node => node)}
56
+ </div>`;
57
+ }
58
+ /**
59
+ * How many association rows to render.
60
+ *
61
+ * `max-associations` is how many associations the candidate may *make*, so it is exactly the
62
+ * number of rows. This used to be `ceil(choices / 2)` — the number of pairs the choices could
63
+ * possibly form — which made the limit unenforceable: with six choices the interaction always
64
+ * drew three rows, so `max-associations="3"` could never be exceeded and its validation always
65
+ * passed. Worse, the number read as a cap on *drags placed* rather than on associations formed.
66
+ *
67
+ * `0` is QTI's "no limit". With no limit there is still a natural ceiling: every choice paired
68
+ * with one other.
69
+ */
70
+ get #rowCount() {
71
+ return this.maxAssociations > 0 ? this.maxAssociations : Math.ceil(this._childrenMap.length / 2);
72
+ }
34
73
  getResponse() {
35
- const pairCount = Math.ceil(this._childrenMap.length / 2);
36
74
  const response = [];
37
- for (let i = 0; i < pairCount; i++) {
38
- const leftDrop = this.shadowRoot?.querySelector(`.dl[identifier="droplist${i}_left"]`);
39
- const rightDrop = this.shadowRoot?.querySelector(`.dl[identifier="droplist${i}_right"]`);
40
- const leftId = leftDrop?.querySelector('[qti-draggable="true"]')?.getAttribute('identifier');
41
- const rightId = rightDrop?.querySelector('[qti-draggable="true"]')?.getAttribute('identifier');
75
+ // Same count as `render()`. Read fewer rows than were drawn and the pairs in the last rows
76
+ // vanish from the response without a trace.
77
+ for (let i = 0; i < this.#rowCount; i++) {
78
+ const leftDrop = this.shadowRoot?.querySelector(`[part~='drop'][identifier="droplist${i}_left"]`);
79
+ const rightDrop = this.shadowRoot?.querySelector(`[part~='drop'][identifier="droplist${i}_right"]`);
80
+ // Read placement, not the DOM: the DOM is rendered from placement now.
81
+ const leftId = leftDrop && this.chipsIn(leftDrop)[0]?.getAttribute('identifier');
82
+ const rightId = rightDrop && this.chipsIn(rightDrop)[0]?.getAttribute('identifier');
42
83
  if (leftId && rightId) {
43
84
  response.push(`${leftId} ${rightId}`);
44
85
  }
45
86
  }
46
87
  return response;
47
88
  }
89
+ /**
90
+ * How many associations the candidate has made.
91
+ *
92
+ * The mixin counts placed chips, which is right for gap-match — one chip in one gap is one
93
+ * association — and wrong here. An association is a *pair*, so three associations are six chips,
94
+ * and `max-associations="3"` compared 6 against 3 and declared the item invalid the moment it was
95
+ * correctly filled in.
96
+ *
97
+ * A half-filled row is not an association yet, and `getResponse()` already knows that: it emits a
98
+ * pair only when both sides hold a chip. Counting its entries counts associations by definition,
99
+ * and keeps the number validation sees equal to the number the response reports.
100
+ */
101
+ totalAssociationsFromState() {
102
+ // The mixin's version syncs first, and callers depend on that: `getResponse` reads the
103
+ // placement map, which is stale until the sync runs.
104
+ this.syncDragDropState();
105
+ return this.getResponse().length;
106
+ }
48
107
  getValue(val) {
49
108
  return (val?.flatMap((pair, i) => {
50
109
  const parts = pair.split(' ');
@@ -55,12 +114,12 @@ export class QtiAssociateInteraction extends DragDropSlottedSortableMixin(Slotte
55
114
  }
56
115
  render() {
57
116
  return html ` <slot name="prompt"></slot>
58
- <slot part="associable-choices" name="qti-simple-associable-choice"></slot>
59
- <div part="drop-container">
117
+ <slot part="drags" name="qti-simple-associable-choice"></slot>
118
+ <div part="drops">
60
119
  ${this._childrenMap.length > 0 &&
61
- Array.from(Array(Math.ceil(this._childrenMap.length / 2)).keys()).map((_, index) => html `<div part="associables-container">
62
- <div name="left${index}" part="drop-list" class="dl" identifier="droplist${index}_left"></div>
63
- <div name="right${index}" part="drop-list" class="dl" identifier="droplist${index}_right"></div>
120
+ Array.from({ length: this.#rowCount }, (_, index) => html `<div part="drop-row">
121
+ ${this.#renderDrop(`droplist${index}_left`, `left${index}`)}
122
+ ${this.#renderDrop(`droplist${index}_right`, `right${index}`)}
64
123
  </div>`)}
65
124
 
66
125
  <div role="alert" part="message" id="validation-message"></div>
@@ -1 +1 @@
1
- {"version":3,"file":"qti-associate-interaction.js","sourceRoot":"","sources":["../src/qti-associate-interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit';\nimport { 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-associate-interaction.styles';\n\nimport type { QtiSimpleAssociableChoice } from '@qti-components/interactions-core/elements/qti-simple-associable-choice';\nimport type { CSSResultGroup } from 'lit';\nconst SlottedBase = DragDropSlottedMixin(\n Interaction,\n 'qti-simple-associable-choice',\n '.dl',\n `slot[name='qti-simple-associable-choice']`,\n 'pointerWithin'\n);\n\nexport class QtiAssociateInteraction extends DragDropSlottedSortableMixin(SlottedBase, '[qti-draggable=\"true\"]') {\n static override styles: CSSResultGroup = styles;\n @state() protected _childrenMap: Element[] = [];\n\n protected _registerChoiceHandler: (event: CustomEvent) => void;\n\n constructor() {\n super();\n this._registerChoiceHandler = this._registerChoice.bind(this);\n this.addEventListener('register-qti-simple-associable-choice', this._registerChoiceHandler);\n }\n\n protected _registerChoice(event: CustomEvent) {\n // Use composedPath()[0] to get the actual origin — event.target is retargeted to `this`\n // when a composed event crosses the shadow DOM boundary, making it useless for filtering.\n const origin = event.composedPath()[0] as HTMLElement;\n // Ignore clones placed in shadow DOM drop zones — only register light DOM choices\n if (this.shadowRoot?.contains(origin)) return;\n const choice = origin as QtiSimpleAssociableChoice;\n if (!this._childrenMap.includes(choice)) {\n this._childrenMap = [...this._childrenMap, choice];\n }\n }\n\n protected getResponse(): string[] {\n const pairCount = Math.ceil(this._childrenMap.length / 2);\n const response: string[] = [];\n for (let i = 0; i < pairCount; i++) {\n const leftDrop = this.shadowRoot?.querySelector(`.dl[identifier=\"droplist${i}_left\"]`);\n const rightDrop = this.shadowRoot?.querySelector(`.dl[identifier=\"droplist${i}_right\"]`);\n const leftId = leftDrop?.querySelector('[qti-draggable=\"true\"]')?.getAttribute('identifier');\n const rightId = rightDrop?.querySelector('[qti-draggable=\"true\"]')?.getAttribute('identifier');\n if (leftId && rightId) {\n response.push(`${leftId} ${rightId}`);\n }\n }\n return response;\n }\n\n getValue(val: string[]) {\n return (\n val?.flatMap((pair, i) => {\n const parts = pair.split(' ');\n if (parts.length !== 2) return [];\n return [`${parts[0]} droplist${i}_left`, `${parts[1]} droplist${i}_right`];\n }) ?? []\n );\n }\n\n override render() {\n return html` <slot name=\"prompt\"></slot>\n <slot part=\"associable-choices\" name=\"qti-simple-associable-choice\"></slot>\n <div part=\"drop-container\">\n ${this._childrenMap.length > 0 &&\n Array.from(Array(Math.ceil(this._childrenMap.length / 2)).keys()).map(\n (_, index) =>\n html`<div part=\"associables-container\">\n <div name=\"left${index}\" part=\"drop-list\" class=\"dl\" identifier=\"droplist${index}_left\"></div>\n <div name=\"right${index}\" part=\"drop-list\" class=\"dl\" identifier=\"droplist${index}_right\"></div>\n </div>`\n )}\n\n <div role=\"alert\" part=\"message\" id=\"validation-message\"></div>\n </div>`;\n }\n\n protected override updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n if (changedProperties.has('_childrenMap')) {\n this.cacheInteractiveElements();\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('register-qti-simple-associable-choice', this._registerChoiceHandler);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-associate-interaction': QtiAssociateInteraction;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-associate-interaction.js","sourceRoot":"","sources":["../src/qti-associate-interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { 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-associate-interaction.styles';\n\nimport type { QtiSimpleAssociableChoice } from '@qti-components/interactions-core/elements/qti-simple-associable-choice';\nimport type { CSSResultGroup } from 'lit';\nconst SlottedBase = DragDropSlottedMixin(\n Interaction,\n 'qti-simple-associable-choice',\n `[part~='drop']`,\n `slot[name='qti-simple-associable-choice']`,\n 'pointerWithin'\n);\n\n/**\n * Drag-and-drop associate interaction: candidates pair associable choices.\n *\n * @slot prompt - The prompt shown above the choices.\n * @slot qti-simple-associable-choice - The associable choice sources.\n *\n * @csspart drags - Wrapper around the associable choice slot.\n * @csspart drops - The container holding all drop targets.\n * @csspart drop-row - Container for one left/right drop pair.\n * @csspart drop - Each individual drop target.\n * @csspart message - Live validation message region (role=\"alert\").\n */\nexport class QtiAssociateInteraction extends DragDropSlottedSortableMixin(SlottedBase, '[qti-draggable=\"true\"]') {\n static override styles: CSSResultGroup = styles;\n @state() protected _childrenMap: Element[] = [];\n\n protected _registerChoiceHandler: (event: CustomEvent) => void;\n\n constructor() {\n super();\n this._registerChoiceHandler = this._registerChoice.bind(this);\n this.addEventListener('register-qti-simple-associable-choice', this._registerChoiceHandler);\n }\n\n protected _registerChoice(event: CustomEvent) {\n // Use composedPath()[0] to get the actual origin — event.target is retargeted to `this`\n // when a composed event crosses the shadow DOM boundary, making it useless for filtering.\n const origin = event.composedPath()[0] as HTMLElement;\n // Ignore clones placed in shadow DOM drop zones — only register light DOM choices\n if (this.shadowRoot?.contains(origin)) return;\n const choice = origin as QtiSimpleAssociableChoice;\n if (!this._childrenMap.includes(choice)) {\n this._childrenMap = [...this._childrenMap, choice];\n }\n }\n\n /**\n * A drop target, holding whatever the interaction's placement map says it holds.\n * `data-declarative-drops` tells the mixin to keep the chips in that map rather than appending\n * them into this div.\n */\n #renderDrop(identifier: string, name: string) {\n const drags = this._dragDrop?.nodesByTarget?.[identifier] ?? [];\n return html`<div name=${name} part=\"drop\" data-declarative-drops identifier=${identifier}>\n ${repeat(\n drags,\n node => node.getAttribute('identifier'),\n node => node\n )}\n </div>`;\n }\n\n /**\n * How many association rows to render.\n *\n * `max-associations` is how many associations the candidate may *make*, so it is exactly the\n * number of rows. This used to be `ceil(choices / 2)` — the number of pairs the choices could\n * possibly form — which made the limit unenforceable: with six choices the interaction always\n * drew three rows, so `max-associations=\"3\"` could never be exceeded and its validation always\n * passed. Worse, the number read as a cap on *drags placed* rather than on associations formed.\n *\n * `0` is QTI's \"no limit\". With no limit there is still a natural ceiling: every choice paired\n * with one other.\n */\n get #rowCount(): number {\n return this.maxAssociations > 0 ? this.maxAssociations : Math.ceil(this._childrenMap.length / 2);\n }\n\n protected getResponse(): string[] {\n const response: string[] = [];\n // Same count as `render()`. Read fewer rows than were drawn and the pairs in the last rows\n // vanish from the response without a trace.\n for (let i = 0; i < this.#rowCount; i++) {\n const leftDrop = this.shadowRoot?.querySelector(`[part~='drop'][identifier=\"droplist${i}_left\"]`);\n const rightDrop = this.shadowRoot?.querySelector(`[part~='drop'][identifier=\"droplist${i}_right\"]`);\n // Read placement, not the DOM: the DOM is rendered from placement now.\n const leftId = leftDrop && this.chipsIn(leftDrop as HTMLElement)[0]?.getAttribute('identifier');\n const rightId = rightDrop && this.chipsIn(rightDrop as HTMLElement)[0]?.getAttribute('identifier');\n if (leftId && rightId) {\n response.push(`${leftId} ${rightId}`);\n }\n }\n return response;\n }\n\n /**\n * How many associations the candidate has made.\n *\n * The mixin counts placed chips, which is right for gap-match — one chip in one gap is one\n * association — and wrong here. An association is a *pair*, so three associations are six chips,\n * and `max-associations=\"3\"` compared 6 against 3 and declared the item invalid the moment it was\n * correctly filled in.\n *\n * A half-filled row is not an association yet, and `getResponse()` already knows that: it emits a\n * pair only when both sides hold a chip. Counting its entries counts associations by definition,\n * and keeps the number validation sees equal to the number the response reports.\n */\n public override totalAssociationsFromState(): number {\n // The mixin's version syncs first, and callers depend on that: `getResponse` reads the\n // placement map, which is stale until the sync runs.\n this.syncDragDropState();\n return this.getResponse().length;\n }\n\n getValue(val: string[]) {\n return (\n val?.flatMap((pair, i) => {\n const parts = pair.split(' ');\n if (parts.length !== 2) return [];\n return [`${parts[0]} droplist${i}_left`, `${parts[1]} droplist${i}_right`];\n }) ?? []\n );\n }\n\n override render() {\n return html` <slot name=\"prompt\"></slot>\n <slot part=\"drags\" name=\"qti-simple-associable-choice\"></slot>\n <div part=\"drops\">\n ${this._childrenMap.length > 0 &&\n Array.from(\n { length: this.#rowCount },\n (_, index) =>\n html`<div part=\"drop-row\">\n ${this.#renderDrop(`droplist${index}_left`, `left${index}`)}\n ${this.#renderDrop(`droplist${index}_right`, `right${index}`)}\n </div>`\n )}\n\n <div role=\"alert\" part=\"message\" id=\"validation-message\"></div>\n </div>`;\n }\n\n protected override updated(changedProperties: Map<string, unknown>): void {\n super.updated(changedProperties);\n if (changedProperties.has('_childrenMap')) {\n this.cacheInteractiveElements();\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('register-qti-simple-associable-choice', this._registerChoiceHandler);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-associate-interaction': QtiAssociateInteraction;\n }\n}\n"]}
@@ -1,2 +1,11 @@
1
- declare const _default: import("lit").CSSResult;
1
+ /**
2
+ * Layout and sizing only. Paint and state live in the theme.
3
+ *
4
+ * `[part~='drop'][active]` and `[part~='drop'][enabled]` moved to
5
+ * `styles/qti-theme/interactions/qti-associate-interaction.css` as `::part(active)` /
6
+ * `::part(enabled)`, since an attribute selector cannot follow `::part()` and a <div> cannot carry
7
+ * a custom state. Both carried `!important`, which only existed to beat the shadow's own rules from
8
+ * the shadow. Neither needs it from the theme.
9
+ */
10
+ declare const _default: import("lit").CSSResult[];
2
11
  export default _default;
@@ -1,51 +1,71 @@
1
1
  import { css } from 'lit';
2
- export default css `
3
- :host {
4
- display: block; /* necessary to calculate scaling position */
5
- }
6
- slot[part='associable-choices'] {
7
- display: flex;
8
- align-items: flex-start;
9
- flex: 1;
10
- border: 2px solid transparent;
11
- margin: 1rem 0;
12
- border-radius: 0.3rem;
13
- gap: 0.5rem;
14
- }
2
+ import { boxSizing, dropRegion, validationMessage } from '@qti-components/base';
3
+ /**
4
+ * Layout and sizing only. Paint and state live in the theme.
5
+ *
6
+ * `[part~='drop'][active]` and `[part~='drop'][enabled]` moved to
7
+ * `styles/qti-theme/interactions/qti-associate-interaction.css` as `::part(active)` /
8
+ * `::part(enabled)`, since an attribute selector cannot follow `::part()` and a <div> cannot carry
9
+ * a custom state. Both carried `!important`, which only existed to beat the shadow's own rules from
10
+ * the shadow. Neither needs it from the theme.
11
+ */
12
+ export default [
13
+ boxSizing,
14
+ validationMessage,
15
+ dropRegion,
16
+ css `
17
+ :host {
18
+ display: block; /* necessary to calculate scaling position */
19
+ }
20
+ /* The bank WRAPS. Without flex-wrap the row is nowrap, and a flex item's default flex-shrink: 1
21
+ then compresses the chips instead of moving them down: a bank of six squeezed the last chip
22
+ until only its grip was left, at no width the author could see coming. A chip is a fixed
23
+ thing, so the only place the overflow can go is the next line. */
24
+ slot[part~='drags'] {
25
+ display: flex;
26
+ flex-wrap: wrap;
27
+ align-items: flex-start;
28
+ flex: 1;
29
+ border: 2px solid transparent;
30
+ margin: 1rem 0;
31
+ border-radius: 0.3rem;
32
+ gap: 0.5rem;
33
+ }
15
34
 
16
- [part='drop-list'][active] {
17
- border-color: var(--qti-border-active) !important;
18
- background-color: var(--qti-bg-active) !important;
19
- }
35
+ /* Drop target. --qti-dropzone-min-height is published by the interaction from the measured
36
+ chip height; 3rem is the fallback when there is nothing to measure. Was an inline style. */
37
+ [part~='drop'] {
38
+ display: grid;
39
+ min-height: var(--qti-dropzone-min-height, 3rem);
40
+ min-width: var(--qti-dropzone-min-width, 3rem);
41
+ border: 1px solid var(--qti-border-color, #d1d5db);
42
+ border-radius: 0.25rem;
43
+ overflow: hidden;
44
+ }
20
45
 
21
- [part='drop-list'] {
22
- display: grid;
23
- min-height: 3rem;
24
- min-width: 10rem;
25
- border: 1px solid var(--qti-border-color, #d1d5db);
26
- border-radius: 0.25rem;
27
- box-sizing: border-box;
28
- }
46
+ [part='drops'] {
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: 0.5rem;
50
+ }
29
51
 
30
- [part='drop-container'] {
31
- display: flex;
32
- flex-direction: column;
33
- gap: 0.5rem;
34
- }
52
+ :host::part(drop-row) {
53
+ display: flex;
54
+ justify-content: space-between;
55
+ background: linear-gradient(
56
+ 180deg,
57
+ rgb(0 0 0 / 0%) calc(50% - 1px),
58
+ var(--qti-border-color) calc(50%),
59
+ rgb(0 0 0 / 0%) calc(50% + 1px)
60
+ );
61
+ }
35
62
 
36
- [part='drop-list'][enabled] {
37
- background-color: var(--qti-bg-active) !important;
38
- }
39
-
40
- :host::part(associables-container) {
41
- display: flex;
42
- justify-content: space-between;
43
- background: linear-gradient(
44
- 180deg,
45
- rgb(0 0 0 / 0%) calc(50% - 1px),
46
- var(--qti-border-color) calc(50%),
47
- rgb(0 0 0 / 0%) calc(50% + 1px)
48
- );
49
- }
50
- `;
63
+ /* An answer key shows the answer, not the bank you would have dragged it from. The attribute is
64
+ set on the clone by the correct-response mixin; the theme used to do this with a
65
+ '.full-correct-response' descendant selector, from the document. */
66
+ :host([answer-key]) ::slotted(qti-simple-associable-choice) {
67
+ display: none;
68
+ }
69
+ `
70
+ ];
51
71
  //# sourceMappingURL=qti-associate-interaction.styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"qti-associate-interaction.styles.js","sourceRoot":"","sources":["../src/qti-associate-interaction.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDjB,CAAC","sourcesContent":["import { css } from 'lit';\n\nexport default css`\n :host {\n display: block; /* necessary to calculate scaling position */\n }\n slot[part='associable-choices'] {\n display: flex;\n align-items: flex-start;\n flex: 1;\n border: 2px solid transparent;\n margin: 1rem 0;\n border-radius: 0.3rem;\n gap: 0.5rem;\n }\n\n [part='drop-list'][active] {\n border-color: var(--qti-border-active) !important;\n background-color: var(--qti-bg-active) !important;\n }\n\n [part='drop-list'] {\n display: grid;\n min-height: 3rem;\n min-width: 10rem;\n border: 1px solid var(--qti-border-color, #d1d5db);\n border-radius: 0.25rem;\n box-sizing: border-box;\n }\n\n [part='drop-container'] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n }\n\n [part='drop-list'][enabled] {\n background-color: var(--qti-bg-active) !important;\n }\n\n :host::part(associables-container) {\n display: flex;\n justify-content: space-between;\n background: linear-gradient(\n 180deg,\n rgb(0 0 0 / 0%) calc(50% - 1px),\n var(--qti-border-color) calc(50%),\n rgb(0 0 0 / 0%) calc(50% + 1px)\n );\n }\n`;\n"]}
1
+ {"version":3,"file":"qti-associate-interaction.styles.js","sourceRoot":"","sources":["../src/qti-associate-interaction.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEhF;;;;;;;;GAQG;AAEH,eAAe;IACb,SAAS;IACT,iBAAiB;IACjB,UAAU;IACV,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDF;CACF,CAAC","sourcesContent":["import { css } from 'lit';\n\nimport { boxSizing, dropRegion, validationMessage } from '@qti-components/base';\n\n/**\n * Layout and sizing only. Paint and state live in the theme.\n *\n * `[part~='drop'][active]` and `[part~='drop'][enabled]` moved to\n * `styles/qti-theme/interactions/qti-associate-interaction.css` as `::part(active)` /\n * `::part(enabled)`, since an attribute selector cannot follow `::part()` and a <div> cannot carry\n * a custom state. Both carried `!important`, which only existed to beat the shadow's own rules from\n * the shadow. Neither needs it from the theme.\n */\n\nexport default [\n boxSizing,\n validationMessage,\n dropRegion,\n css`\n :host {\n display: block; /* necessary to calculate scaling position */\n }\n /* The bank WRAPS. Without flex-wrap the row is nowrap, and a flex item's default flex-shrink: 1\n then compresses the chips instead of moving them down: a bank of six squeezed the last chip\n until only its grip was left, at no width the author could see coming. A chip is a fixed\n thing, so the only place the overflow can go is the next line. */\n slot[part~='drags'] {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n flex: 1;\n border: 2px solid transparent;\n margin: 1rem 0;\n border-radius: 0.3rem;\n gap: 0.5rem;\n }\n\n /* Drop target. --qti-dropzone-min-height is published by the interaction from the measured\n chip height; 3rem is the fallback when there is nothing to measure. Was an inline style. */\n [part~='drop'] {\n display: grid;\n min-height: var(--qti-dropzone-min-height, 3rem);\n min-width: var(--qti-dropzone-min-width, 3rem);\n border: 1px solid var(--qti-border-color, #d1d5db);\n border-radius: 0.25rem;\n overflow: hidden;\n }\n\n [part='drops'] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n }\n\n :host::part(drop-row) {\n display: flex;\n justify-content: space-between;\n background: linear-gradient(\n 180deg,\n rgb(0 0 0 / 0%) calc(50% - 1px),\n var(--qti-border-color) calc(50%),\n rgb(0 0 0 / 0%) calc(50% + 1px)\n );\n }\n\n /* An answer key shows the answer, not the bank you would have dragged it from. The attribute is\n set on the clone by the correct-response mixin; the theme used to do this with a\n '.full-correct-response' descendant selector, from the document. */\n :host([answer-key]) ::slotted(qti-simple-associable-choice) {\n display: none;\n }\n `\n];\n"]}
package/dist/register.js CHANGED
@@ -1,3 +1,7 @@
1
- import { QtiAssociateInteraction } from './qti-associate-interaction';
2
- customElements.define('qti-associate-interaction', QtiAssociateInteraction);
1
+ import { elements } from './elements';
2
+ for (const { tag, ctor } of elements) {
3
+ if (!customElements.get(tag)) {
4
+ customElements.define(tag, ctor);
5
+ }
6
+ }
3
7
  //# sourceMappingURL=register.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,cAAc,CAAC,MAAM,CAAC,2BAA2B,EAAE,uBAAuB,CAAC,CAAC","sourcesContent":["import { QtiAssociateInteraction } from './qti-associate-interaction';\ncustomElements.define('qti-associate-interaction', QtiAssociateInteraction);\n"]}
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,KAAK,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;IACrC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;AACH,CAAC","sourcesContent":["import { elements } from './elements';\n\nfor (const { tag, ctor } of elements) {\n if (!customElements.get(tag)) {\n customElements.define(tag, ctor);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@qti-components/associate-interaction",
3
3
  "description": "QTI associate-interaction interaction",
4
- "version": "1.1.5",
4
+ "version": "1.1.6",
5
5
  "author": "",
6
6
  "dependencies": {
7
+ "@qti-components/base": "^2.0.0",
7
8
  "@qti-components/utilities": "^1.3.3",
8
- "@qti-components/base": "^1.5.1",
9
- "@qti-components/interactions-core": "^1.1.4"
9
+ "@qti-components/interactions-core": "^2.0.0"
10
10
  },
11
11
  "devDependencies": {
12
12
  "@lit/context": "^1.1.6",
@@ -45,6 +45,10 @@
45
45
  "./styles": {
46
46
  "types": "./dist/qti-associate-interaction.styles.d.ts",
47
47
  "import": "./dist/qti-associate-interaction.styles.js"
48
+ },
49
+ "./elements": {
50
+ "types": "./dist/elements.d.ts",
51
+ "import": "./dist/elements.js"
48
52
  }
49
53
  },
50
54
  "repository": {
@@ -55,6 +59,8 @@
55
59
  "scripts": {
56
60
  "attw": "attw --profile esm-only --pack",
57
61
  "build": "tsc --paths null",
58
- "clean": "rm -rf dist"
62
+ "clean": "rm -rf dist",
63
+ "yalc": "yalc publish",
64
+ "yalc:push": "yalc push"
59
65
  }
60
66
  }