@citolab/qti-components 7.5.2 → 7.8.1

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.
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  QtiFeedback,
3
3
  convertNumberToUniversalFormat,
4
+ liveQuery,
4
5
  removeDoubleSlashes
5
- } from "./chunk-K47QFOQX.js";
6
+ } from "./chunk-YD7FVKDP.js";
6
7
  import {
7
8
  watch
8
9
  } from "./chunk-ELDMXTUQ.js";
@@ -38,41 +39,6 @@ import {
38
39
  import { provide } from "@lit/context";
39
40
  import { LitElement, html } from "lit";
40
41
  import { customElement, property } from "lit/decorators.js";
41
-
42
- // src/lib/decorators/live-query.ts
43
- function liveQuery(querySelector, _options) {
44
- let observer;
45
- return (proto, decoratedFnName) => {
46
- const { connectedCallback, disconnectedCallback } = proto;
47
- proto.connectedCallback = function() {
48
- connectedCallback.call(this);
49
- const callback = (mutationList) => {
50
- const elementsToWatch = Array.from(this.querySelectorAll(querySelector)).concat(
51
- Array.from(this.shadowRoot?.querySelectorAll(querySelector) || [])
52
- );
53
- for (const mutation of mutationList) {
54
- const addedNodes = Array.from(mutation.addedNodes).map((e) => e);
55
- const removedNodes = Array.from(mutation.addedNodes).map((e) => e);
56
- if (mutation.type === "childList" && addedNodes.find((n) => elementsToWatch.includes(n))) {
57
- this[decoratedFnName](addedNodes, removedNodes);
58
- }
59
- }
60
- };
61
- observer = new MutationObserver(callback);
62
- observer.observe(this, { childList: true, subtree: true });
63
- const elementsAdded = Array.from(this.querySelectorAll(querySelector)).concat(
64
- Array.from(this.shadowRoot?.querySelectorAll(querySelector) || [])
65
- );
66
- this[decoratedFnName](Array.from(elementsAdded), []);
67
- };
68
- proto.disconnectedCallback = function() {
69
- disconnectedCallback.call(this);
70
- observer.disconnect();
71
- };
72
- };
73
- }
74
-
75
- // src/lib/qti-components/qti-assessment-item/qti-assessment-item.ts
76
42
  var QtiAssessmentItem = class extends LitElement {
77
43
  constructor() {
78
44
  super(...arguments);
@@ -778,6 +744,9 @@ var DragDropInteractionMixin = (superClass, draggablesSelector, droppablesSelect
778
744
  // Last touch target
779
745
  this.currentDropTarget = null;
780
746
  // Current droppable element
747
+ this.onMove = this.handleTouchMove.bind(this);
748
+ this.onEnd = this.handleTouchEnd.bind(this);
749
+ this.onCancel = this.handleTouchCancel.bind(this);
781
750
  this.MIN_DRAG_DISTANCE = 5;
782
751
  // Minimum pixel movement to start dragging
783
752
  this.DRAG_CLONE_OPACITY = 1;
@@ -886,11 +855,11 @@ var DragDropInteractionMixin = (superClass, draggablesSelector, droppablesSelect
886
855
  if (this.isMatchTabular()) return;
887
856
  const disabled = this.hasAttribute("disabled");
888
857
  if (!disabled) {
889
- document.addEventListener("touchmove", this.handleTouchMove.bind(this), { passive: false });
890
- document.addEventListener("mousemove", this.handleTouchMove.bind(this), { passive: false });
891
- document.addEventListener("touchend", this.handleTouchEnd.bind(this), { passive: false });
892
- document.addEventListener("mouseup", this.handleTouchEnd.bind(this), { passive: false });
893
- document.addEventListener("touchcancel", this.handleTouchCancel.bind(this), { passive: false });
858
+ document.addEventListener("mousemove", this.onMove, { passive: false });
859
+ document.addEventListener("mouseup", this.onEnd, { passive: false });
860
+ document.addEventListener("touchmove", this.onMove, { passive: false });
861
+ document.addEventListener("touchend", this.onEnd, { passive: false });
862
+ document.addEventListener("touchcancel", this.onCancel, { passive: false });
894
863
  }
895
864
  const draggables = Array.from(this.querySelectorAll(draggablesSelector) || []).concat(
896
865
  Array.from(this.shadowRoot?.querySelectorAll(draggablesSelector) || [])
@@ -1029,11 +998,11 @@ var DragDropInteractionMixin = (superClass, draggablesSelector, droppablesSelect
1029
998
  this.resizeObserver.disconnect();
1030
999
  this.resizeObserver = null;
1031
1000
  }
1032
- document.removeEventListener("touchmove", this.handleTouchMove);
1033
- document.removeEventListener("mousemove", this.handleTouchMove);
1034
- document.removeEventListener("touchend", this.handleTouchEnd);
1035
- document.removeEventListener("mouseup", this.handleTouchEnd);
1036
- document.removeEventListener("touchcancel", this.handleTouchCancel);
1001
+ document.removeEventListener("mousemove", this.onMove);
1002
+ document.removeEventListener("mouseup", this.onEnd);
1003
+ document.removeEventListener("touchmove", this.onMove);
1004
+ document.removeEventListener("touchend", this.onEnd);
1005
+ document.removeEventListener("touchcancel", this.onCancel);
1037
1006
  }
1038
1007
  handleTouchMove(e) {
1039
1008
  if (this.isMatchTabular()) return;
@@ -1065,7 +1034,7 @@ var DragDropInteractionMixin = (superClass, draggablesSelector, droppablesSelect
1065
1034
  }
1066
1035
  handleTouchEnd(e) {
1067
1036
  if (this.isMatchTabular()) return;
1068
- if (this.isDragging) {
1037
+ if (this.isDragging || this.isDraggable || this.dragClone) {
1069
1038
  this.resetDragState();
1070
1039
  }
1071
1040
  this._internals.states.delete("--dragzone-active");
@@ -1319,30 +1288,38 @@ var DragDropInteractionMixin = (superClass, draggablesSelector, droppablesSelect
1319
1288
  const yDist = Math.abs(touch.clientY - this.touchStartPoint.y);
1320
1289
  return xDist + yDist;
1321
1290
  }
1291
+ getDropzoneRect(el) {
1292
+ const slot = el.shadowRoot?.querySelector('slot[part="dropslot"]');
1293
+ return (slot ?? el).getBoundingClientRect();
1294
+ }
1322
1295
  findClosestDropzone() {
1323
- const allActiveDropzones = this.allDropzones.filter((d) => !d.hasAttribute("disabled"));
1324
- if (!this.dragClone || allActiveDropzones.length === 0) return null;
1296
+ const activeDrops = this.allDropzones.filter((d) => !d.hasAttribute("disabled"));
1297
+ if (!this.dragClone || activeDrops.length === 0) return null;
1325
1298
  const dragRect = this.dragClone.getBoundingClientRect();
1326
1299
  let closestDropzone = null;
1327
- let maxOverlapArea = 0;
1328
- for (const dropzone of allActiveDropzones) {
1329
- const dropRect = dropzone.getBoundingClientRect();
1330
- const overlapArea = this.calculateOverlapArea(dragRect, dropRect);
1331
- if (overlapArea > maxOverlapArea) {
1332
- maxOverlapArea = overlapArea;
1333
- closestDropzone = dropzone;
1300
+ let maxArea = 0;
1301
+ const prefer = (elements) => {
1302
+ for (const dz of elements) {
1303
+ const dzRect = this.getDropzoneRect(dz);
1304
+ const area = this.calculateOverlapArea(dragRect, dzRect);
1305
+ if (area > maxArea) {
1306
+ maxArea = area;
1307
+ closestDropzone = dz;
1308
+ }
1334
1309
  }
1310
+ };
1311
+ prefer(this.droppables.filter((droppable) => !droppable.hasAttribute("disabled")));
1312
+ if (!closestDropzone) {
1313
+ prefer(this.dragContainers.filter((drags) => !drags.hasAttribute("disabled")));
1335
1314
  }
1336
1315
  if (!closestDropzone) {
1337
- let minDistance = 200;
1338
- for (const dropzone of allActiveDropzones) {
1339
- const dropRect = dropzone.getBoundingClientRect();
1340
- const distance = Math.sqrt(
1341
- Math.pow(dragRect.left - dropRect.left, 2) + Math.pow(dragRect.top - dropRect.top, 2)
1342
- );
1343
- if (distance < minDistance) {
1344
- minDistance = distance;
1345
- closestDropzone = dropzone;
1316
+ let minDist = Number.POSITIVE_INFINITY;
1317
+ for (const dz of activeDrops) {
1318
+ const dzRect = this.getDropzoneRect(dz);
1319
+ const dist = Math.hypot(dragRect.left - dzRect.left, dragRect.top - dzRect.top);
1320
+ if (dist < minDist) {
1321
+ minDist = dist;
1322
+ closestDropzone = dz;
1346
1323
  }
1347
1324
  }
1348
1325
  }
@@ -1487,6 +1464,15 @@ var qti_associate_interaction_styles_default = css4`
1487
1464
  gap: 0.5rem;
1488
1465
  }
1489
1466
 
1467
+ [part='drop-list'][active] {
1468
+ border-color: var(--qti-border-active) !important;
1469
+ background-color: var(--qti-bg-active) !important;
1470
+ }
1471
+
1472
+ [part='drop-list'][enabled] {
1473
+ background-color: var(--qti-bg-active) !important;
1474
+ }
1475
+
1490
1476
  :host::part(associables-container) {
1491
1477
  display: flex;
1492
1478
  padding: 0.5rem;
@@ -3176,11 +3162,13 @@ import { css as css15 } from "lit";
3176
3162
  var qti_hotspot_interaction_styles_default = css15`
3177
3163
  slot:not([name='prompt']) {
3178
3164
  position: relative; /* qti-hotspot-choice relative to the slot */
3179
- display: block;
3165
+ display: inline-block;
3180
3166
  width: fit-content; /* hotspots not stretching further if image is at max size */
3167
+ line-height: 0; /* remove gaps below image */
3181
3168
  }
3182
3169
  ::slotted(img) {
3183
3170
  /* image not selectable anymore */
3171
+ display: block;
3184
3172
  pointer-events: none;
3185
3173
  user-select: none;
3186
3174
  /* width:100%; */
@@ -3189,19 +3177,40 @@ var qti_hotspot_interaction_styles_default = css15`
3189
3177
 
3190
3178
  // src/lib/qti-components/qti-interaction/qti-hotspot-interaction/qti-hotspot-interaction.ts
3191
3179
  var QtiHotspotInteraction = class extends ChoicesMixin(Interaction, "qti-hotspot-choice") {
3180
+ constructor() {
3181
+ super(...arguments);
3182
+ this.imageLoadPromise = null;
3183
+ }
3192
3184
  render() {
3193
3185
  return html18`
3194
3186
  <slot name="prompt"></slot>
3195
3187
  <slot></slot>
3196
3188
  `;
3197
3189
  }
3198
- positionHotspotOnRegister(e) {
3190
+ getImageLoadPromise(img) {
3191
+ if (!this.imageLoadPromise) {
3192
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
3193
+ this.imageLoadPromise = Promise.resolve(img);
3194
+ } else {
3195
+ this.imageLoadPromise = new Promise((resolve) => {
3196
+ const handler = () => {
3197
+ img.removeEventListener("load", handler);
3198
+ resolve(img);
3199
+ };
3200
+ img.addEventListener("load", handler);
3201
+ });
3202
+ }
3203
+ }
3204
+ return this.imageLoadPromise;
3205
+ }
3206
+ async positionHotspotOnRegister(e) {
3199
3207
  const img = this.querySelector("img");
3200
3208
  const hotspot = e.target;
3201
3209
  const coords = hotspot.getAttribute("coords");
3202
3210
  const shape = hotspot.getAttribute("shape");
3203
3211
  const coordsNumber = coords.split(",").map((s) => parseInt(s));
3204
- positionShapes(shape, coordsNumber, img, hotspot);
3212
+ const loadedImg = await this.getImageLoadPromise(img);
3213
+ positionShapes(shape, coordsNumber, loadedImg, hotspot);
3205
3214
  }
3206
3215
  connectedCallback() {
3207
3216
  super.connectedCallback();
@@ -3916,6 +3925,15 @@ var qti_order_interaction_styles_default = css22`
3916
3925
  flex: 1;
3917
3926
  }
3918
3927
 
3928
+ [part='drop-list'][active] {
3929
+ border-color: var(--qti-border-active) !important;
3930
+ background-color: var(--qti-bg-active) !important;
3931
+ }
3932
+
3933
+ [part='drop-list'][enabled] {
3934
+ background-color: var(--qti-bg-active) !important;
3935
+ }
3936
+
3919
3937
  [part='container'] {
3920
3938
  display: flex;
3921
3939
  gap: 0.5rem;
@@ -8104,6 +8122,9 @@ function qtiSubtractMixin(Base) {
8104
8122
  };
8105
8123
  }
8106
8124
 
8125
+ // src/lib/qti-components/qti-response-processing/qti-expression/qti-subtract/index.ts
8126
+ customElements.define("qti-subtract", QtiSubtract);
8127
+
8107
8128
  // src/lib/qti-components/qti-response-processing/qti-expression/qti-string-match/qti-string-match.ts
8108
8129
  import { property as property36 } from "lit/decorators.js";
8109
8130
  var QtiStringMatch = class extends QtiExpression {
@@ -8792,4 +8813,4 @@ export {
8792
8813
  QtiOutcomeDeclaration,
8793
8814
  QtiResponseDeclaration
8794
8815
  };
8795
- //# sourceMappingURL=chunk-7RW7LCEJ.js.map
8816
+ //# sourceMappingURL=chunk-7YO5JFT3.js.map