@pie-element/placement-ordering 14.1.2-next.2 → 14.1.2-next.20

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.
Files changed (50) hide show
  1. package/author.js +2 -0
  2. package/dist/author/choice-editor.js +2 -2
  3. package/dist/author/choice-tile.js +3 -2
  4. package/dist/author/design.js +1 -1
  5. package/dist/author/index.d.ts +1 -0
  6. package/dist/author/index.js +5 -2
  7. package/dist/browser/assets/Symbola-4c507403.woff2 +0 -0
  8. package/dist/browser/author/index.js +14940 -19583
  9. package/dist/browser/author/index.js.map +1 -1
  10. package/dist/browser/{browser-Dbw2uHQ7.js → browser-DMpDEszG.js} +7 -3
  11. package/dist/browser/browser-DMpDEszG.js.map +1 -0
  12. package/dist/browser/controller/index.js +140 -55
  13. package/dist/browser/controller/index.js.map +1 -1
  14. package/dist/browser/delivery/index.js +474 -325
  15. package/dist/browser/delivery/index.js.map +1 -1
  16. package/dist/browser/{dist-D9ARZhQk.js → dist-DCmVXYeO.js} +109 -7
  17. package/dist/browser/dist-DCmVXYeO.js.map +1 -0
  18. package/dist/browser/{drag-provider-C4BiCI76.js → drag-provider-LgVkQ5Yi.js} +2219 -2266
  19. package/dist/browser/drag-provider-LgVkQ5Yi.js.map +1 -0
  20. package/dist/browser/editor-runtime/assets/Symbola-4c507403.woff2 +0 -0
  21. package/dist/browser/editor-runtime/author/index.js +19668 -0
  22. package/dist/browser/editor-runtime/author/index.js.map +1 -0
  23. package/dist/browser/editor-runtime/browser-0nthm_zo.js +376 -0
  24. package/dist/browser/editor-runtime/browser-0nthm_zo.js.map +1 -0
  25. package/dist/browser/editor-runtime/controller/index.js +327 -0
  26. package/dist/browser/editor-runtime/controller/index.js.map +1 -0
  27. package/dist/browser/editor-runtime/delivery/index.js +1184 -0
  28. package/dist/browser/editor-runtime/delivery/index.js.map +1 -0
  29. package/dist/browser/editor-runtime/dist-DCmVXYeO.js +1528 -0
  30. package/dist/browser/editor-runtime/dist-DCmVXYeO.js.map +1 -0
  31. package/dist/browser/editor-runtime/drag-provider-Dpx1BjBS.js +10508 -0
  32. package/dist/browser/editor-runtime/drag-provider-Dpx1BjBS.js.map +1 -0
  33. package/dist/controller/scoring.js +3 -3
  34. package/dist/delivery/index.js +2 -2
  35. package/dist/delivery/keyboard-coordinates.d.ts +47 -0
  36. package/dist/delivery/keyboard-coordinates.js +51 -0
  37. package/dist/delivery/main.js +1 -1
  38. package/dist/delivery/ordering.js +14 -19
  39. package/dist/delivery/placement-ordering.d.ts +13 -0
  40. package/dist/delivery/placement-ordering.js +168 -83
  41. package/dist/delivery/tile.d.ts +3 -0
  42. package/dist/delivery/tile.js +75 -61
  43. package/dist/delivery/tiler.d.ts +6 -0
  44. package/dist/delivery/tiler.js +6 -3
  45. package/dist/index.iife.js +35 -35
  46. package/package.json +36 -25
  47. package/dist/browser/browser-Dbw2uHQ7.js.map +0 -1
  48. package/dist/browser/dist-D9ARZhQk.js.map +0 -1
  49. package/dist/browser/drag-provider-C4BiCI76.js.map +0 -1
  50. package/dist/browser/placement-ordering.css +0 -2
@@ -1,6 +1,6 @@
1
1
  import { differenceWith as e, isEqual as t, uniq as n } from "@pie-element/shared-lodash";
2
2
  import r from "debug";
3
- import { combination as i } from "js-combinatorics";
3
+ import { Combination as i } from "js-combinatorics";
4
4
  //#region src/controller/scoring.ts
5
5
  var a = r("pie-elements:placement-ordering:controller"), o = (e) => /* @__PURE__ */ Error(`Cant score answer: ${e} it has duplicates and allowDuplicates is false`), s = (r, s, c) => {
6
6
  if (c = {
@@ -12,7 +12,7 @@ var a = r("pie-elements:placement-ordering:controller"), o = (e) => /* @__PURE__
12
12
  if (s = c.allowDuplicates === !1 ? n(s) : s, a("answer:", s), !Array.isArray(r) || r.length === 0) throw Error("correct must be non empty an array");
13
13
  if (r.length === 1) return +!!t(r, s);
14
14
  if (!Array.isArray(s) || s.length < 2 || s.length > r.length || r.length !== s.length && c.orderMustBeComplete) return 0;
15
- let l = i(r, 2).toArray(), u = i(s, 2).toArray(), d = e(u, l, t), f = l.length - u.length, p = l.length, m = ((p - d.length - f) / p).toFixed(2);
15
+ let l = new i(r, 2).toArray(), u = new i(s, 2).toArray(), d = e(u, l, t), f = l.length - u.length, p = l.length, m = ((p - d.length - f) / p).toFixed(2);
16
16
  return parseFloat(m);
17
17
  }, c = (e) => e.correctResponse ? e.correctResponse.map((e) => e && e.id ? e.id : e) : [], l = (e) => {
18
18
  let t = (e.alternateResponses || []).map((e) => Array.isArray(e) ? e : e.response).filter((e) => e !== void 0);
@@ -27,4 +27,4 @@ var a = r("pie-elements:placement-ordering:controller"), o = (e) => /* @__PURE__
27
27
  }), a;
28
28
  };
29
29
  //#endregion
30
- export { c as flattenCorrect, l as getAllCorrectResponses, u as score };
30
+ export { c as flattenCorrect, l as getAllCorrectResponses, o as illegalArgumentError, s as pairwiseCombinationScore, u as score };
@@ -60,10 +60,10 @@ var s = i("pie-elements:placement-ordering"), c = ({ model: e, session: t }) =>
60
60
  }
61
61
  }
62
62
  connectedCallback() {
63
- this._initMathObserver();
63
+ this._initMathObserver(), this.render();
64
64
  }
65
65
  disconnectedCallback() {
66
- this._disconnectMathObserver(), this._root && this._root.unmount();
66
+ this._disconnectMathObserver(), this._root &&= (this._root.unmount(), null);
67
67
  }
68
68
  };
69
69
  //#endregion
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @synced-from pie-elements/packages/placement-ordering/src/keyboard-coordinates.js
3
+ * @auto-generated
4
+ *
5
+ * This file is automatically synced from pie-elements and converted to TypeScript.
6
+ * Manual edits will be overwritten on next sync.
7
+ * To make changes, edit the upstream JavaScript file and run sync again.
8
+ */
9
+ /**
10
+ * Custom keyboard coordinate getter for placement-ordering's Tab-based placement mode
11
+ * (enabled only when the item is configured with `placementArea` / `includeTargets`).
12
+ *
13
+ * Tab/Shift+Tab cycle the dragged choice through the target column's response areas
14
+ * in on-screen order (first target, then the rest in sequence), followed by exactly
15
+ * one source-column stop as a fixed last step before the cycle wraps back to the first
16
+ * target — so picking up a choice always starts the cycle at the first target,
17
+ * regardless of where that choice's own source-column slot happens to sit on screen.
18
+ *
19
+ * Arrow keys are delegated to dnd-kit's own `defaultKeyboardCoordinateGetter` so the
20
+ * pre-existing free-form arrow-key dragging behavior is left completely unchanged.
21
+ *
22
+ * Whichever slot a drag started from — a target the choice was already placed in, or
23
+ * (see below) the choice's own pool slot — stays reachable by Tab as a no-op "drop it
24
+ * back where it came from": tile.tsx disables a droppable for the whole duration of
25
+ * its own drag (a self-collision guard for pointer drags), which would otherwise make
26
+ * that exact slot unreachable; that guard is bypassed here specifically for the one
27
+ * droppable matching the active drag's own id. A target slot picked up this way stays
28
+ * in its normal position in the target sequence (it's just another target); nothing
29
+ * special is needed there beyond the bypass itself.
30
+ *
31
+ * The choices column isn't a place a student can choose to relocate an answer within —
32
+ * there's exactly one meaningful "source" stop for whichever choice is currently being
33
+ * dragged: its own fixed slot, whether that's the vacated gap left behind (if the
34
+ * choice is already placed in a target — landing back on it returns the choice to the
35
+ * pool, undoing the placement) or the very tile being dragged (if the choice is still
36
+ * sitting there, unplaced — landing back on it is a no-op that effectively cancels the
37
+ * pick-up; the same disabled-droppable bypass applies here too). Every *other*
38
+ * choice-column tile — filled or an empty gap — is excluded from the cycle:
39
+ * `ordering.ts`'s `updateResponse` has no `choice -> choice` case, so they'd be no-op
40
+ * drop targets anyway. Matching is by id (the vacated gap carries its original choice's
41
+ * id specifically so this comparison works), not by type alone.
42
+ */
43
+ export declare const closestDroppableKeyboardCoordinates: (event: any, { active, context, currentCoordinates }: {
44
+ active: any;
45
+ context: any;
46
+ currentCoordinates: any;
47
+ }) => any;
@@ -0,0 +1,51 @@
1
+ import { KeyboardCode as e, defaultKeyboardCoordinateGetter as t } from "@dnd-kit/core";
2
+ //#region src/delivery/keyboard-coordinates.ts
3
+ var n = (n, { active: i, context: a, currentCoordinates: o }) => {
4
+ let { code: s } = n, c = s === "Tab", l = s === e.Down || s === e.Up || s === e.Left || s === e.Right;
5
+ if (!c && !l) return;
6
+ if (l) return t(n, {
7
+ context: a,
8
+ currentCoordinates: o
9
+ });
10
+ n.preventDefault();
11
+ let { droppableRects: u, droppableContainers: d, collisionRect: f, draggableNodes: p, active: m } = a;
12
+ if (!u || u.size === 0) return o;
13
+ let h = {
14
+ x: (f?.width || 0) / 2,
15
+ y: (f?.height || 0) / 2
16
+ }, g = {
17
+ x: o.x + h.x,
18
+ y: o.y + h.y
19
+ }, _ = p?.get(i)?.data?.current?.id, v = m?.rect?.current?.initial, y = typeof i == "string" ? i.replace(/^tile-/, "drop-") : void 0, b = [], x;
20
+ for (let [e, t] of d) {
21
+ let n = t?.data?.current, r = e === y;
22
+ if (t?.disabled && !r) continue;
23
+ let i = n?.type === "choice" && n?.id === _;
24
+ if (n?.type === "choice" && !i) continue;
25
+ let a = (r ? v : null) || u.get(e);
26
+ if (!a) continue;
27
+ let o = {
28
+ x: a.left + a.width / 2,
29
+ y: a.top + a.height / 2
30
+ }, s = {
31
+ id: e,
32
+ dropPosition: {
33
+ x: a.left,
34
+ y: a.top + a.height / 2 - h.y
35
+ },
36
+ center: o
37
+ };
38
+ i ? x = s : b.push(s);
39
+ }
40
+ if (b.length === 0 && !x) return o;
41
+ let S = n.shiftKey;
42
+ b.sort((e, t) => Math.abs(e.center.y - t.center.y) > 10 ? e.center.y - t.center.y : e.center.x - t.center.x), x && b.push(x);
43
+ let C = 0, w = Infinity;
44
+ for (let e = 0; e < b.length; e++) {
45
+ let t = r(g, b[e].center);
46
+ t < w && (w = t, C = e);
47
+ }
48
+ return b[S ? (C - 1 + b.length) % b.length : (C + 1) % b.length].dropPosition;
49
+ }, r = (e, t) => Math.sqrt((e.x - t.x) ** 2 + (e.y - t.y) ** 2);
50
+ //#endregion
51
+ export { n as closestDroppableKeyboardCoordinates };
@@ -1,4 +1,4 @@
1
- import e from "./placement-ordering.js";
1
+ import { PlacementOrdering as e } from "./placement-ordering.js";
2
2
  import t from "react";
3
3
  import n from "prop-types";
4
4
  import { jsx as r } from "react/jsx-runtime";
@@ -8,26 +8,20 @@ var a = i("pie-elements:placement-ordering:ordering"), o = (e, t, n) => {
8
8
  r,
9
9
  ...i.slice(n)
10
10
  ];
11
- }, s = (e, n, r) => {
12
- if (a("[swap]", e, n, r), !e || e.length <= 1 || n === void 0 || r === void 0) throw Error(`swap requires a non-empty array, fromIndex, toIndex: ${e}, ${n} ${r}`);
13
- let i = t(e), o = e[r];
14
- return i[r] = i[n], i[n] = o, i;
15
11
  };
16
- function c(e, n) {
12
+ function s(e, n) {
17
13
  let r = t(e.response);
18
14
  if (r[n.index] === n.id) r[n.index] = void 0;
19
15
  else throw Error(`Tried to remove from index: ${n.index}, but the id doesn't match: array: ${r}, target: ${n.id}`);
20
16
  return r;
21
17
  }
22
- function l(e, n, r) {
23
- let { response: i, opts: c } = e, l = t(i);
24
- if (c.includeTargets) return n.type === "choice" && r.type === "target" ? (l[r.index] = n.id, l) : n.type === "target" && r.type === "choice" ? (l[n.index] = void 0, l) : n.type === "target" && r.type === "target" ? s(i, n.index, r.index) : (a("do nothing to the response"), i);
25
- {
26
- let t = e.response.findIndex((e) => e !== void 0 && e === n.id), i = e.response.findIndex((e) => e !== void 0 && e === r.id);
27
- return a("fromIndex: ", t, "toIndex:", i), o(e.response, t, i);
28
- }
18
+ function c(e, n, r) {
19
+ let { response: i, opts: s } = e, c = t(i);
20
+ if (s.includeTargets) return n.type === "choice" && r.type === "target" ? (c[r.index] = n.id, c) : n.type === "target" && r.type === "choice" ? (c[n.index] = void 0, c) : n.type === "target" && r.type === "target" ? (c[r.index] = n.id, c[n.index] = void 0, c) : (a("do nothing to the response"), i);
21
+ let l = e.response.findIndex((e) => e !== void 0 && e === n.id), u = e.response.findIndex((e) => e !== void 0 && e === r.id);
22
+ return a("fromIndex: ", l, "toIndex:", u), o(e.response, l, u);
29
23
  }
30
- function u(e, t, n, r) {
24
+ function l(e, t, n, r) {
31
25
  if (r.includeTargets) {
32
26
  let i = [];
33
27
  for (let r = 0; r < t.length; r++) {
@@ -40,6 +34,7 @@ function u(e, t, n, r) {
40
34
  }
41
35
  return e.map((e) => t.indexOf(e.id) !== -1 && !r.allowSameChoiceInTargets ? {
42
36
  type: "choice",
37
+ id: e.id,
43
38
  empty: !0,
44
39
  droppable: !0,
45
40
  draggable: !1
@@ -61,26 +56,26 @@ function u(e, t, n, r) {
61
56
  return i === 0 && t.push(a), t.push(o, a), t;
62
57
  }, []);
63
58
  }
64
- function d(t, i, a, o) {
59
+ function u(t, i, a, o) {
65
60
  return o = e({ includeTargets: !0 }, o), a ||= [], i = !i || n(i) ? o.includeTargets ? Array(t.length) : r(t, (e) => e.id) : i, {
66
61
  choices: t,
67
62
  response: i,
68
63
  opts: o,
69
64
  outcomes: a,
70
- tiles: u(t, i, a, o)
65
+ tiles: l(t, i, a, o)
71
66
  };
72
67
  }
73
- function f(e, t) {
68
+ function d(e, t) {
74
69
  switch (e.type) {
75
70
  case "move": {
76
- let { from: n, to: r } = e, i = l(t, n, r), a = u(t.choices, i, t.outcomes, t.opts);
71
+ let { from: n, to: r } = e, i = c(t, n, r), a = l(t.choices, i, t.outcomes, t.opts);
77
72
  return Object.assign({}, t, {
78
73
  response: i,
79
74
  tiles: a
80
75
  });
81
76
  }
82
77
  case "remove": {
83
- let { target: n } = e, r = c(t, n), i = u(t.choices, r, t.outcomes, t.opts);
78
+ let { target: n } = e, r = s(t, n), i = l(t.choices, r, t.outcomes, t.opts);
84
79
  return Object.assign({}, t, {
85
80
  response: r,
86
81
  tiles: i
@@ -90,4 +85,4 @@ function f(e, t) {
90
85
  return t;
91
86
  }
92
87
  //#endregion
93
- export { d as buildState, f as reducer };
88
+ export { u as buildState, o as push, d as reducer };
@@ -23,6 +23,19 @@ export declare class PlacementOrdering extends React.Component {
23
23
  onRemoveChoice: any;
24
24
  createOrdering: any;
25
25
  onDragEnd: any;
26
+ onDragStart: any;
27
+ onDragCancel: any;
28
+ isSameChoice: (a: any, b: any) => boolean;
29
+ selectChoice: any;
30
+ toggleChoiceSelection: any;
31
+ cancelSelection: any;
32
+ endAnyLiveKeyboardDrag: any;
33
+ placeSelectedChoice: any;
34
+ getDestinationSlotId: any;
35
+ focusTile: any;
36
+ isClickSoonAfterDragEnd: () => boolean;
37
+ onChoiceClick: any;
38
+ onPlacementClick: any;
26
39
  render(): React.JSX.Element;
27
40
  }
28
41
  export default PlacementOrdering;
@@ -1,62 +1,88 @@
1
1
  import { HorizontalTiler as e, VerticalTiler as t } from "./tiler.js";
2
2
  import { buildState as n, reducer as r } from "./ordering.js";
3
3
  import { haveSameValuesButDifferentOrder as i } from "./utils.js";
4
- import a from "react";
5
- import { difference as o, isEqual as s, uniqueId as c } from "@pie-element/shared-lodash";
6
- import l from "debug";
7
- import { renderMath as u } from "@pie-element/shared-math-rendering-mathjax";
8
- import d from "prop-types";
9
- import f from "react-dom";
10
- import { styled as p } from "@mui/material/styles";
11
- import { closestCenter as m } from "@dnd-kit/core";
12
- import * as h from "@pie-lib/render-ui";
13
- import { Collapsible as g, Feedback as _, PreviewPrompt as v, UiLayout as y, color as b, hasMedia as x, hasText as S } from "@pie-lib/render-ui";
14
- import { jsx as C, jsxs as w } from "react/jsx-runtime";
15
- import T from "@pie-lib/translator";
16
- import E from "@pie-lib/correct-answer-toggle";
17
- import { DragProvider as D } from "@pie-lib/drag";
4
+ import { closestDroppableKeyboardCoordinates as a } from "./keyboard-coordinates.js";
5
+ import o from "react";
6
+ import { difference as s, isEqual as c, uniqueId as l } from "@pie-element/shared-lodash";
7
+ import u from "debug";
8
+ import { renderMath as d } from "@pie-element/shared-math-rendering-mathjax";
9
+ import f from "prop-types";
10
+ import p from "react-dom";
11
+ import { styled as m } from "@mui/material/styles";
12
+ import { rectIntersection as h } from "@dnd-kit/core";
13
+ import { restrictToParentElement as g } from "@dnd-kit/modifiers";
14
+ import * as _ from "@pie-lib/render-ui";
15
+ import { Collapsible as v, Feedback as y, PreviewPrompt as b, UiLayout as x, color as S, hasMedia as C, hasText as w } from "@pie-lib/render-ui";
16
+ import { jsx as T, jsxs as E } from "react/jsx-runtime";
17
+ import D from "@pie-lib/translator";
18
+ import O from "@pie-lib/correct-answer-toggle";
19
+ import { DragProvider as k } from "@pie-lib/drag";
18
20
  //#region src/delivery/placement-ordering.tsx
19
- function O(e) {
21
+ function A(e) {
20
22
  return typeof e == "function" || typeof e == "object" && !!e && typeof e.$$typeof == "symbol";
21
23
  }
22
- function k(e, t) {
23
- return !e || O(e) ? e : O(e.default) ? e.default : t && O(e[t]) ? e[t] : t && O(e[t]?.default) ? e[t].default : e;
24
+ function j(e, t) {
25
+ return !e || A(e) ? e : A(e.default) ? e.default : t && A(e[t]) ? e[t] : t && A(e[t]?.default) ? e[t].default : e;
24
26
  }
25
- var A = k(y, "UiLayout") || k(I.UiLayout, "UiLayout"), j = k(v, "PreviewPrompt") || k(I.PreviewPrompt, "PreviewPrompt"), M = k(_, "Feedback") || k(I.Feedback, "Feedback"), N = k(g, "Collapsible") || k(I.Collapsible, "Collapsible"), P = h, F = P.default, I = F && typeof F == "object" ? F : P, { translator: L } = T, R = l("pie-elements:placement-ordering"), z = p("div")({
26
- color: b.text(),
27
- backgroundColor: b.background(),
27
+ var M = j(x, "UiLayout") || j(R.UiLayout, "UiLayout"), N = j(b, "PreviewPrompt") || j(R.PreviewPrompt, "PreviewPrompt"), P = j(y, "Feedback") || j(R.Feedback, "Feedback"), F = j(v, "Collapsible") || j(R.Collapsible, "Collapsible"), I = _, L = I.default, R = L && typeof L == "object" ? L : I, z = 250, B = (e, t) => {
28
+ if (!e || !t) return !1;
29
+ let n = t.left + t.width / 2, r = t.top + t.height / 2;
30
+ return n >= e.left && n <= e.right && r >= e.top && r <= e.bottom;
31
+ }, V = (e) => e ? {
32
+ keyboardCoordinateGetter: a,
33
+ keyboardCodes: {
34
+ start: ["Space", "Enter"],
35
+ cancel: ["Escape"],
36
+ end: ["Space", "Enter"]
37
+ },
38
+ accessibility: { screenReaderInstructions: { draggable: "Press Space or Enter to pick up this answer choice. Once picked up, use Tab or Shift+Tab to cycle through response areas, or use arrow keys to move it freely. Press Space or Enter to drop, or Escape to cancel." } }
39
+ } : {}, { translator: H } = D, U = u("pie-elements:placement-ordering"), W = m("div")({
40
+ color: S.text(),
41
+ backgroundColor: S.background(),
28
42
  display: "flex",
29
43
  flexDirection: "column",
30
44
  alignItems: "center",
31
45
  boxSizing: "border-box"
32
- }), B = p("div")(({ theme: e }) => ({ paddingBottom: e.spacing(1) })), V = p("div")(({ theme: e }) => ({ paddingBottom: e.spacing(2) })), H = p(E)(({ theme: e }) => ({ paddingBottom: e.spacing(1) })), U = p(N)(({ theme: e }) => ({
46
+ }), G = m("div")({
47
+ alignSelf: "stretch",
48
+ maxWidth: "100%",
49
+ overflowX: "auto",
50
+ overflowY: "hidden"
51
+ }), K = m("div")({
52
+ display: "flex",
53
+ justifyContent: "center",
54
+ minWidth: "min-content"
55
+ }), q = m("div")(({ theme: e }) => ({ paddingBottom: e.spacing(1) })), J = m("div")(({ theme: e }) => ({ paddingBottom: e.spacing(2) })), Y = m(O)(({ theme: e }) => ({ paddingBottom: e.spacing(1) })), X = m(F)(({ theme: e }) => ({
33
56
  marginBottom: e.spacing(2),
34
57
  alignSelf: "flex-start"
35
- })), W = (e) => {
58
+ })), Z = (e) => {
36
59
  let { tiler: t, ordering: n, onDropChoice: r, onRemoveChoice: i, ...a } = e;
37
- return /* @__PURE__ */ C(t, {
60
+ return /* @__PURE__ */ T(t, {
38
61
  ...a,
39
62
  tiles: n.tiles,
40
63
  onDropChoice: (e, t) => r(e, t, n),
41
64
  onRemoveChoice: (e) => i(e, n)
42
65
  });
43
66
  };
44
- W.propTypes = {
45
- tiler: d.func,
46
- ordering: d.any,
47
- onDropChoice: d.func,
48
- onRemoveChoice: d.func
67
+ Z.propTypes = {
68
+ tiler: f.func,
69
+ ordering: f.any,
70
+ onDropChoice: f.func,
71
+ onRemoveChoice: f.func
49
72
  };
50
- var G = class extends a.Component {
73
+ var Q = class extends o.Component {
51
74
  static propTypes = {
52
- onSessionChange: d.func.isRequired,
53
- model: d.object.isRequired,
54
- session: d.oneOfType([d.array.isRequired, d.object.isRequired])
75
+ onSessionChange: f.func.isRequired,
76
+ model: f.object.isRequired,
77
+ session: f.oneOfType([f.array.isRequired, f.object.isRequired])
55
78
  };
56
79
  constructor(e) {
57
- super(e), this.instanceId = c();
80
+ super(e), this.instanceId = l();
58
81
  let { value: t, needsReset: n } = this.validateSession(e);
59
- this.state = { showingCorrect: !1 };
82
+ this.state = {
83
+ showingCorrect: !1,
84
+ selectedChoice: null
85
+ }, this.lastDragEndAt = 0;
60
86
  let { model: r } = e || {}, { env: i } = r || {}, { mode: a } = i || {};
61
87
  n && a === "gather" && this.props.onSessionChange({
62
88
  ...e.session,
@@ -65,18 +91,18 @@ var G = class extends a.Component {
65
91
  }
66
92
  toggleCorrect = (e) => this.setState({ showingCorrect: e });
67
93
  componentDidUpdate() {
68
- u(f.findDOMNode(this));
94
+ d(p.findDOMNode(this));
69
95
  }
70
96
  validateSession = ({ model: e, session: t }, n = !1) => {
71
- let { config: r, choices: i } = e || {}, { includeTargets: a } = r || {}, c = i.map((e) => e.id), { value: l } = t || {}, u;
72
- if (a) l && l.length !== c.length && (u = !0, l = l.length < c.length ? l.concat(Array(c.length - l.length)) : l.filter((e) => c.includes(e)));
97
+ let { config: r, choices: i } = e || {}, { includeTargets: a } = r || {}, o = i.map((e) => e.id), { value: l } = t || {}, u;
98
+ if (a) l && l.length !== o.length && (u = !0, l = l.length < o.length ? l.concat(Array(o.length - l.length)) : l.filter((e) => o.includes(e)));
73
99
  else {
74
- if (!l || !l.length || n) l = c;
100
+ if (!l || !l.length || n) l = o;
75
101
  else {
76
- let e = o(c, l), t = o(l, c);
77
- (e.length || t.length) && (l = c);
102
+ let e = s(o, l), t = s(l, o);
103
+ (e.length || t.length) && (l = o);
78
104
  }
79
- u = !s(t.value, l);
105
+ u = !c(t.value, l);
80
106
  }
81
107
  return u && console.warn("This session is not valid anymore. It will be reset."), {
82
108
  value: l,
@@ -84,8 +110,8 @@ var G = class extends a.Component {
84
110
  };
85
111
  };
86
112
  UNSAFE_componentWillReceiveProps(e) {
87
- let { model: t = {} } = e || {}, { model: n = {} } = this.props || {}, { correctResponse: r, config: a, choices: o = [], env: s } = t, { includeTargets: c } = a || {}, l = {}, u = n.language && n.language !== t.language, d = n.note && n.note === L.t("common:commonCorrectAnswerWithAlternates", { lng: n.language });
88
- u && d && (n.note = L.t("common:commonCorrectAnswerWithAlternates", { lng: t.language })), r || (l.showingCorrect = !1);
113
+ let { model: t = {} } = e || {}, { model: n = {} } = this.props || {}, { correctResponse: r, config: a, choices: o = [], env: s } = t, { includeTargets: c } = a || {}, l = {}, u = n.language && n.language !== t.language, d = n.note && n.note === H.t("common:commonCorrectAnswerWithAlternates", { lng: n.language });
114
+ u && d && (n.note = H.t("common:commonCorrectAnswerWithAlternates", { lng: t.language })), r || (l.showingCorrect = !1);
89
115
  let f = i(o, n.choices), { value: p, needsReset: m } = this.validateSession(e, f), h = {
90
116
  ...e.session,
91
117
  value: p
@@ -97,7 +123,7 @@ var G = class extends a.Component {
97
123
  }
98
124
  onDropChoice = (e, t, n) => {
99
125
  let { onSessionChange: i, session: a } = this.props, o = n.tiles.find((e) => e.id === t.id && e.type === t.type && e.index === t.index), s = e;
100
- R("[onDropChoice] ", o, s);
126
+ U("[onDropChoice] ", o, s);
101
127
  let c = r({
102
128
  type: "move",
103
129
  from: o,
@@ -107,7 +133,7 @@ var G = class extends a.Component {
107
133
  };
108
134
  onRemoveChoice = (e, t) => {
109
135
  let { onSessionChange: n, session: i } = this.props;
110
- R("[onRemoveChoice]", e);
136
+ U("[onRemoveChoice]", e);
111
137
  let a = r({
112
138
  type: "remove",
113
139
  target: e
@@ -132,75 +158,134 @@ var G = class extends a.Component {
132
158
  };
133
159
  onDragEnd = (e) => {
134
160
  let { over: t, active: n } = e, r = this.createOrdering();
135
- if (t && n) {
161
+ if (this.cancelSelection(), this.lastDragEndAt = Date.now(), t && n) {
136
162
  let e = n.data.current, i = t.data.current;
137
163
  if (e && i && i.type === "target") {
138
- this.onDropChoice(i, e, r);
164
+ this.onDropChoice(i, e, r), this.focusTile(this.getDestinationSlotId(i));
139
165
  return;
140
166
  }
141
- e && i && this.onDropChoice(i, e, r);
167
+ e && i && (this.onDropChoice(i, e, r), this.focusTile(this.getDestinationSlotId(i)));
142
168
  } else if (!t && n) {
143
- let e = n.data.current;
144
- e && e.type === "target" && this.onRemoveChoice(e, r);
169
+ let e = n.data.current, t = B(n.rect?.current?.initial, n.rect?.current?.translated);
170
+ e && e.type === "target" && !t && (this.onRemoveChoice(e, r), this.focusTile(this.getDestinationSlotId({
171
+ type: "choice",
172
+ id: e.id
173
+ })));
145
174
  }
146
175
  };
176
+ onDragStart = (e) => {
177
+ let { active: t } = e;
178
+ t?.data?.current && this.selectChoice(t.data.current);
179
+ };
180
+ onDragCancel = () => {
181
+ this.cancelSelection(), this.lastDragEndAt = Date.now();
182
+ };
183
+ isSameChoice = (e, t) => !!e && !!t && e.type === t.type && e.id === t.id && e.index === t.index;
184
+ selectChoice = (e) => {
185
+ this.setState({ selectedChoice: e });
186
+ };
187
+ toggleChoiceSelection = (e) => {
188
+ this.setState((t) => ({ selectedChoice: this.isSameChoice(t.selectedChoice, e) ? null : e }));
189
+ };
190
+ cancelSelection = () => {
191
+ this.setState({ selectedChoice: null });
192
+ };
193
+ endAnyLiveKeyboardDrag = () => {
194
+ document.dispatchEvent(new KeyboardEvent("keydown", {
195
+ code: "Escape",
196
+ bubbles: !0,
197
+ cancelable: !0
198
+ }));
199
+ };
200
+ placeSelectedChoice = (e) => {
201
+ let { selectedChoice: t } = this.state;
202
+ if (!t) return;
203
+ let n = this.createOrdering(), r = this.getDestinationSlotId(e);
204
+ this.onDropChoice(e, t, n), this.cancelSelection(), this.endAnyLiveKeyboardDrag(), this.lastDragEndAt = Date.now(), this.focusTile(r);
205
+ };
206
+ getDestinationSlotId = (e) => e ? e.type === "target" ? `target-${e.index}` : e.type === "choice" ? `choice-${e.id}` : null : null;
207
+ focusTile = (e) => {
208
+ if (!e) return;
209
+ let t = `[data-tile-id="${this.instanceId}:${e}"]`;
210
+ requestAnimationFrame(() => {
211
+ requestAnimationFrame(() => {
212
+ document.querySelector(t)?.focus();
213
+ });
214
+ });
215
+ };
216
+ isClickSoonAfterDragEnd = () => Date.now() - this.lastDragEndAt < z;
217
+ onChoiceClick = (e) => {
218
+ this.isClickSoonAfterDragEnd() || this.toggleChoiceSelection(e);
219
+ };
220
+ onPlacementClick = (e) => {
221
+ this.isClickSoonAfterDragEnd() || this.placeSelectedChoice(e);
222
+ };
147
223
  render() {
148
- let { model: n } = this.props, { correctResponse: r, correctness: i, extraCSSRules: a, prompt: o, rationale: s, feedback: c, config: l, note: u, showNote: d, env: f, disabled: p, teacherInstructions: h, language: g } = n, _ = r && r.length > 0, { showingCorrect: v } = this.state, y = l || {
224
+ let { model: n } = this.props, { correctResponse: r, correctness: i, extraCSSRules: a, prompt: o, rationale: s, feedback: c, config: l, note: u, showNote: d, env: f, disabled: p, teacherInstructions: m, language: _ } = n, v = r && r.length > 0, { showingCorrect: y } = this.state, b = l || {
149
225
  orientation: "vertical",
150
226
  includeTargets: !0
151
- }, { orientation: T, includeTargets: E } = y, O = T === "vertical", k = this.createOrdering(), { mode: N, role: P } = f || {}, F = O ? t : e, I = (v || N === "view" && P === "instructor") && d && u, L = s && (S(s) || x(s)), R = h && (S(h) || x(h)), G = {
152
- color: b.text(),
153
- backgroundColor: b.background(),
227
+ }, { orientation: x, includeTargets: D } = b, O = x === "vertical", A = this.createOrdering(), { mode: j, role: F } = f || {}, I = O ? t : e, L = (y || j === "view" && F === "instructor") && d && u, R = s && (w(s) || C(s)), z = m && (w(m) || C(m)), B = {
228
+ color: S.text(),
229
+ backgroundColor: S.background(),
154
230
  display: "flex",
155
231
  flexDirection: "column",
156
232
  alignItems: "center",
157
- boxSizing: "border-box"
158
- };
159
- return /* @__PURE__ */ C(D, {
160
- onDragStart: () => {},
233
+ boxSizing: "border-box",
234
+ width: "100%"
235
+ }, H = D ? {
236
+ selectedChoice: this.state.selectedChoice,
237
+ onChoiceClick: this.onChoiceClick,
238
+ onPlacementClick: this.onPlacementClick
239
+ } : {};
240
+ return /* @__PURE__ */ T(k, {
241
+ onDragStart: this.onDragStart,
161
242
  onDragEnd: this.onDragEnd,
162
- collisionDetection: m,
163
- children: /* @__PURE__ */ C(z, { children: /* @__PURE__ */ w(A, {
243
+ onDragCancel: this.onDragCancel,
244
+ collisionDetection: h,
245
+ modifiers: [g],
246
+ ...V(D),
247
+ children: /* @__PURE__ */ T(W, { children: /* @__PURE__ */ E(M, {
164
248
  extraCSSRules: a,
165
- style: G,
249
+ style: B,
166
250
  children: [
167
- R && /* @__PURE__ */ C(U, {
251
+ z && /* @__PURE__ */ T(X, {
168
252
  labels: {
169
253
  hidden: "Show Teacher Instructions",
170
254
  visible: "Hide Teacher Instructions"
171
255
  },
172
256
  className: "collapsible",
173
- children: /* @__PURE__ */ C(j, { prompt: h })
257
+ children: /* @__PURE__ */ T(N, { prompt: m })
174
258
  }),
175
- /* @__PURE__ */ C(B, { children: /* @__PURE__ */ C(j, { prompt: o }) }),
176
- /* @__PURE__ */ C(H, {
177
- show: _,
178
- toggled: v,
259
+ /* @__PURE__ */ T(q, { children: /* @__PURE__ */ T(N, { prompt: o }) }),
260
+ /* @__PURE__ */ T(Y, {
261
+ show: v,
262
+ toggled: y,
179
263
  onToggle: this.toggleCorrect,
180
- language: g
264
+ language: _
181
265
  }),
182
- /* @__PURE__ */ C(W, {
266
+ /* @__PURE__ */ T(G, { children: /* @__PURE__ */ T(K, { children: /* @__PURE__ */ T(Z, {
183
267
  instanceId: this.instanceId,
184
- choiceLabel: y.choiceLabel,
185
- targetLabel: y.targetLabel,
186
- ordering: k,
187
- tiler: F,
268
+ choiceLabel: b.choiceLabel,
269
+ targetLabel: b.targetLabel,
270
+ ordering: A,
271
+ tiler: I,
188
272
  disabled: p,
189
- addGuide: y.showOrdering,
190
- tileSize: y.tileSize,
191
- includeTargets: E,
192
- choiceLabelEnabled: n.config && n.config.choiceLabelEnabled
193
- }),
194
- I && /* @__PURE__ */ C(V, { dangerouslySetInnerHTML: { __html: u } }),
195
- L && /* @__PURE__ */ C(U, {
273
+ addGuide: b.showOrdering,
274
+ tileSize: b.tileSize,
275
+ includeTargets: D,
276
+ choiceLabelEnabled: n.config && n.config.choiceLabelEnabled,
277
+ ...H
278
+ }) }) }),
279
+ L && /* @__PURE__ */ T(J, { dangerouslySetInnerHTML: { __html: u } }),
280
+ R && /* @__PURE__ */ T(X, {
196
281
  labels: {
197
282
  hidden: "Show Rationale",
198
283
  visible: "Hide Rationale"
199
284
  },
200
285
  className: "collapsible",
201
- children: /* @__PURE__ */ C(j, { prompt: s })
286
+ children: /* @__PURE__ */ T(N, { prompt: s })
202
287
  }),
203
- !v && /* @__PURE__ */ C(M, {
288
+ !y && /* @__PURE__ */ T(P, {
204
289
  correctness: i,
205
290
  feedback: c
206
291
  })
@@ -210,4 +295,4 @@ var G = class extends a.Component {
210
295
  }
211
296
  };
212
297
  //#endregion
213
- export { G as default };
298
+ export { Q as PlacementOrdering, Q as default };
@@ -23,6 +23,9 @@ export declare const Tile: {
23
23
  instanceId: PropTypes.Requireable<any>;
24
24
  draggable: PropTypes.Requireable<boolean>;
25
25
  tileIndex: PropTypes.Requireable<number>;
26
+ selectedChoice: PropTypes.Requireable<object>;
27
+ onChoiceClick: PropTypes.Requireable<(...args: any[]) => any>;
28
+ onPlacementClick: PropTypes.Requireable<(...args: any[]) => any>;
26
29
  };
27
30
  };
28
31
  export default Tile;