import * as lit from 'lit'; import { LitElement, CSSResultGroup, PropertyValues } from 'lit'; import { I as Interaction, b as RecordValue } from './record.d-wB9uYs-j.js'; import { V as ValidatableInteraction } from './interaction.interface.d-CvjHM2bi.js'; import { C as ConfigContext } from './config.context.d-BvU9r-Mg.js'; import { B as BaseType, a as Cardinality, b as ItemContext, Q as QtiAreaMapEntry } from './item.types.d-DuVR0ZVK.js'; import { Q as QtiContext } from './qti.context.d-CejJMlfZ.js'; import * as lit_directives_ref_js from 'lit/directives/ref.js'; /** * A drop target positioned over an image. Like `qti-gap`, it renders the chips it holds from * `dragDropContext` rather than having them appended into it. * * @csspart drop - The region a chip lands in. */ declare class QtiAssociableHotspot extends LitElement { static styles: CSSResultGroup; /** Carries `:state(filled)` while the hotspot holds a chip. */ internals: ElementInternals; /** Opt-in marker read by `DragDropSlottedMixin`; see qti-gap. */ readonly acceptsDeclarativeDrops = true; identifier: string; private _dragDrop?; /** The chips this hotspot holds. Stable node references, cloned once at drop time. */ get drags(): readonly HTMLElement[]; connectedCallback(): void; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-associable-hotspot': QtiAssociableHotspot; } } /** * A gap in the prose, and the first drop target to render its own chips. * * The interaction owns placement and publishes it on `dragDropContext`; this element subscribes and * renders what it holds. Nothing appends into it. That inverts the old flow, where `handleDrop` did * `cloneNode` + `appendChild` and the response was recovered afterwards by reading the DOM back * out — which is why placement and the response could ever disagree at all. * * The chips land in this shadow root rather than in the light DOM, so a theme reaches them as * `::part(drag)` — a bare `::part()` matches in any shadow root — instead of by tag. The gap's own * `:state(filled)` says whether it holds anything. * * @customElement qti-gap * * @attr {string} identifier - Required. Value recorded as the gap side of the response pair. * @attr {number} [match-max=1] - How many tokens this gap can hold; `0` means unlimited. Read * from the DOM by the drag-drop mixin. * @attr {number} [match-min=0] - Minimum tokens this gap must hold. * @attr {boolean} [required=false] - Not implemented. Whether this gap must be filled for the * response to be valid. * @attr {string} template-identifier - Not implemented. Identifier of a template variable * controlling this gap's visibility. * @attr {'show'|'hide'} [show-hide=show] - Not implemented. How `template-identifier` controls * visibility. * * @csspart drop - The region a chip lands in. Reserves the widest chip's box; see qti-gap.styles. */ declare class QtiGap extends LitElement { static styles: CSSResultGroup; /** Carries `:state(filled)` while the gap holds a chip. */ internals: ElementInternals; /** * Opt-in marker, read by `DragDropSlottedMixin`. While this is true the interaction keeps this * target's chips in its placement map and never touches this element's children. */ readonly acceptsDeclarativeDrops = true; tabindex: number | undefined; identifier: string; private _dragDrop?; /** The chips this gap holds. Stable node references, cloned once at drop time by the interaction. */ get drags(): readonly HTMLElement[]; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-gap': QtiGap; } } declare class QtiGapImg extends LitElement { static styles: CSSResultGroup; tabindex: number | undefined; /** * `qti-gap-img` is a draggable chip, so it needs ElementInternals to carry the drag states * (`dragging`, `placeholder`). */ internals: ElementInternals; connectedCallback(): void; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-gap-img': QtiGapImg; } } declare const QtiHotspotChoice_base: (abstract new (...args: any[]) => ActiveElementMixinInterface) & typeof LitElement; declare class QtiHotspotChoice extends QtiHotspotChoice_base { static styles: CSSResultGroup; order: number; } declare global { interface HTMLElementTagNameMap { 'qti-hotspot-choice': QtiHotspotChoice; } } declare const QtiHottext_base: (abstract new (...args: any[]) => ActiveElementMixinInterface) & typeof LitElement; /** * A selectable span of text inside `qti-hottext-interaction`. * * @customElement qti-hottext * * @attr {string} identifier - Required. Value recorded in the response when this span is * selected. * @attr {string} template-identifier - Not implemented. Identifier of a template variable * controlling this span's visibility. * @attr {'show'|'hide'} [show-hide=show] - Not implemented. How `template-identifier` controls * visibility. */ declare class QtiHottext extends QtiHottext_base { static styles: CSSResultGroup; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-hottext': QtiHottext; } } declare class QtiPrompt extends LitElement { static styles: CSSResultGroup; render(): lit.TemplateResult<1>; connectedCallback(): void; } declare global { interface HTMLElementTagNameMap { 'qti-prompt': QtiPrompt; } } declare const QtiSimpleAssociableChoice_base: (abstract new (...args: any[]) => ActiveElementMixinInterface) & typeof LitElement; /** * A choice in one of the two match sets of `qti-match-interaction`, or in * `qti-associate-interaction`. * * @customElement qti-simple-associable-choice * * @attr {string} identifier - Required. Value recorded in the response pair when this choice * participates in an association. * @attr {number} [match-max=1] - How many associations this choice may take part in; `0` means * unlimited. On a target this is its capacity — the bucket size in a sorting item. * @attr {number} [match-min=0] - Minimum associations this choice must take part in. * @attr {boolean} [fixed=false] - Pins this choice in place when the enclosing interaction is * shuffled. * @attr {string} template-identifier - Not implemented. Identifier of a template variable * controlling this choice's visibility. * @attr {'show'|'hide'} [show-hide=show] - Not implemented. How `template-identifier` controls * visibility. */ declare class QtiSimpleAssociableChoice extends QtiSimpleAssociableChoice_base { static styles: CSSResultGroup; /** the minimal number of selections a candidate must make */ matchMin: number; /** the maximum number of selections a candidate must make, the other options will be disabled when max options is checked */ matchMax: number; fixed: boolean; connectedCallback(): void; /** Opt-in marker read by `DragDropSlottedMixin`; see qti-gap. */ readonly acceptsDeclarativeDrops = true; private _dragDrop?; /** The chips this choice holds when it acts as a drop target (match-interaction's second set). */ get drags(): readonly HTMLElement[]; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-simple-associable-choice': QtiSimpleAssociableChoice; } } declare const QtiSimpleChoice_base: (abstract new (...args: any[]) => ActiveElementMixinInterface) & typeof LitElement; /** * A choice, used by `qti-choice-interaction` and `qti-order-interaction`. * * The `control` part is one box whose *role* is carried by state, not by its name: a radio * circle under `:state(radio)`, a checkbox square under `:state(checkbox)`, and a drag grip * when the choice is draggable. Style it as `qti-simple-choice::part(control)`. * * @customElement qti-simple-choice * * @attr {string} identifier - Required. Value recorded in the response when this choice is * selected, or its position when used for ordering. * @attr {boolean} [fixed=false] - Pins this choice in place when the enclosing interaction is * shuffled. * @attr {string} template-identifier - Identifier of a template variable controlling this * choice's visibility. * @attr {'show'|'hide'} [show-hide=show] - How `template-identifier` controls visibility. * * @csspart control - The box before the label (radio / checkbox / drag grip). * @csspart control-mark - The mark inside the box (inner dot / checkmark). * @csspart label - The default slot holding the choice content. * @csspart marker - The order-number badge, when `marker` is set. */ declare class QtiSimpleChoice extends QtiSimpleChoice_base { static styles: CSSResultGroup; static shadowRootOptions: ShadowRootInit; templateIdentifier: string | null; showHide: string | null; fixed: boolean; marker: string; get checked(): boolean; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-simple-choice': QtiSimpleChoice; } } /** * What each drop target currently holds, published by the drag-drop interaction. * * Today this is *derived* from the DOM in one place (`DragDropSlottedMixin`), replacing nine * separate `querySelectorAll` readbacks scattered across capacity checks, association counts and * response serialisation. It is the seam for the next step: once drop targets render their own * chips from this context, the map becomes authoritative and the DOM becomes derived from it. * * Provided by the `qti-*-interaction`, never by `qti-assessment-item` - interactions must work * standalone. Consumers read `this.ctx?.dragsByTarget?.[id] ?? []`. * * Reassign, never mutate: an in-place change does not notify consumers. */ interface DragDropState { /** * Target identifier -> the drag identifiers it holds, in DOM order. * Matched with the interaction's `draggablesSelector`, which is what the response is built from. */ readonly dragsByTarget: Readonly>; /** * Target identifier -> how many chips it holds for capacity purposes. * * Deliberately *not* `dragsByTarget[id].length`. Capacity counts * `max(draggablesSelector matches, [qti-draggable] matches)`, because a dropped clone may not * match the interaction's structural selector (match-interaction's nested choices do; a bare * clone elsewhere may only carry the attribute). Response serialisation uses the selector * only. Collapsing the two would silently change `matchMax` behaviour. */ readonly countByTarget: Readonly>; /** * Target identifier -> the chip nodes it holds, for targets that render their own chips. * * This is the authoritative half. A target that opts in (`acceptsDeclarativeDrops`) renders * `repeat(nodesByTarget[id])` into its own shadow root, and the interaction never appends into * it. The nodes are cloned **once, when the drop happens**, and the same node reference is * passed on every render -- Lit renders a `Node` by reference, so a stable node is moved, never * recreated. Cloning inside `render()` would mint a new element per update: focus loss, * restarted transitions, a half-built shadow root. * * Today it is still *derived* from the DOM alongside `dragsByTarget`. It becomes authoritative * when the first target opts in to rendering its own chips. */ readonly nodesByTarget: Readonly>; } interface ChoiceInterface { identifier: string; disabled: boolean; readonly: boolean; } /** * A mixin that adds choice functionality to a LitElement-based class. * It dispatches events with a custom `type` and handles selection logic. * * @param Base - The base class to extend. * @param type - The type of the choice, used in event names. * @returns A new class extending the base class with choice functionality. */ interface ActiveElementMixinInterface { identifier: string; tabIndex: number; disabled: boolean; readonly: boolean; internals: ElementInternals; } type Choice = HTMLElement & ChoiceInterface & { internals: ElementInternals; }; interface ChoicesInterface extends ValidatableInteraction { minChoices: number; maxChoices: number; value: string | null; response: string | string[] | null; } /** The elements a measurement pass looks at. */ interface DropzoneAutoSizeTargets { /** Measured. The largest of these decides the reservation. */ draggables: HTMLElement[]; /** Not measured — only their presence gates the pass (nothing to reserve for, nothing to do). */ droppables: HTMLElement[]; /** The chip banks, which get a matching `min-height` so an emptied bank does not collapse. */ dragContainers: HTMLElement[]; } type DropzoneAutoSize = LitElement & { autoSizeDropzones: boolean; /** * False when something else owns the width axis — see the class below. * * Declared as an accessor, not a `readonly` property. The class implements it as a getter and * `DragDropSlottedMixin` overrides it with another getter; a property here makes that override a * TS2611 ("defined as a property in the base, overridden as an accessor"). */ get autoSizeDropzoneWidth(): boolean; updateMinDimensionsForDropZones(): void; /** Overridable hooks — see the class below. Public because a subclass has to be able to say * `override`, which the house style already does for `afterCache` and `isDragDropEnabled`. */ collectAutoSizeTargets(): DropzoneAutoSizeTargets; dropzonePropertyTarget(): HTMLElement; }; type MenuAutoSize = LitElement & { updateMenuWidth(): void; shouldAutoSizeMenu(): boolean; menuAutoSizeExtraWidth(): number; menuAutoSizePropertyTarget(): HTMLElement | null; menuAutoSizeRows(): HTMLElement[]; withMenuMeasurable(measure: () => T): T; }; declare const QtiInlineChoice_base: (abstract new (...args: any[]) => ActiveElementMixinInterface) & typeof LitElement; /** * An option in the dropdown of `qti-inline-choice-interaction`. * * @customElement qti-inline-choice * * @attr {string} identifier - Required. Value recorded in the response when this option is * selected. * @attr {boolean} [fixed=false] - Pins this option in place when the interaction is shuffled. * @attr {string} template-identifier - Not implemented. Identifier of a template variable * controlling this option's visibility. * @attr {'show'|'hide'} [show-hide=show] - Not implemented. How `template-identifier` controls * visibility. */ declare class QtiInlineChoice extends QtiInlineChoice_base { #private; static styles: CSSResultGroup; identifier: string; connectedCallback(): void; disconnectedCallback(): void; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-inline-choice': QtiInlineChoice; } } declare const QtiGapText_base: (abstract new (...args: any[]) => ActiveElementMixinInterface) & typeof LitElement; /** * A draggable text token that fills a `qti-gap` inside `qti-gap-match-interaction`. * * @customElement qti-gap-text * * @attr {string} identifier - Required. Value recorded in the response pair when this token is * placed into a gap. * @attr {number} [match-max=1] - How many gaps this token may fill; `0` means unlimited. * Read from the DOM by the drag-drop mixin. * @attr {number} [match-min=0] - Minimum gaps this token must fill. * @attr {boolean} [fixed=false] - Pins this token in place when the interaction is shuffled. * @attr {string} template-identifier - Not implemented. Identifier of a template variable * controlling this token's visibility. * @attr {'show'|'hide'} [show-hide=show] - Not implemented. How `template-identifier` controls * visibility. */ declare class QtiGapText extends QtiGapText_base { static styles: CSSResultGroup; tabindex: number | undefined; connectedCallback(): void; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-gap-text': QtiGapText; } } /** * Collision detection algorithms for drag and drop interactions * Based on dnd-kit collision detection strategies */ type CollisionDetectionAlgorithm = 'pointerWithin' | 'rectangleIntersection' | 'closestCenter' | 'closestCorners' | 'closestCornersWithInventoryPriority'; type DragDropCore = Interaction & { isDragDropEnabled(): boolean; trackedDraggables: HTMLElement[]; trackedDroppables: HTMLElement[]; trackedDragContainers: HTMLElement[]; allDropzones: HTMLElement[]; dragState: DragState; collisionDetectionAlgorithm: CollisionDetectionAlgorithm; get response(): string | string[] | null; set response(value: string | string[] | null); saveResponse(value?: string | string[]): void; validate(): boolean; reportValidity(): boolean; cacheInteractiveElements(): void; resetDragState(): void; afterCache(): void; removeChipFromDroppable(droppable: HTMLElement | null, chip: HTMLElement): void; allowDrop(draggable: HTMLElement, droppable: HTMLElement): boolean; handleDrop(draggable: HTMLElement, droppable: HTMLElement): void; handleInvalidDrop(dragSource: HTMLElement | null): void; initiateDrag(dragElement: HTMLElement, startX: number, startY: number, inputType: 'mouse' | 'touch', eventSource?: 'pointer' | 'mouse' | 'touch', pointerId?: number, startedFromTrustedEvent?: boolean): void; createDragClone(element: HTMLElement, rect: DOMRect): HTMLElement; updateClonePosition(clientX: number, clientY: number): void; activateAllDroppables(): void; setupMoveObservables(inputType: 'mouse' | 'touch', eventSource?: 'pointer' | 'mouse' | 'touch'): void; }; interface DragState { dragging: boolean; dragSource: HTMLElement | null; dragClone: HTMLElement | null; startOffset: { x: number; y: number; }; currentTarget: HTMLElement | null; sourceDroppable: HTMLElement | null; inputType: 'mouse' | 'touch' | null; pointerId?: number; startedFromTrustedEvent?: boolean; initialCoordinates?: { x: number; y: number; }; activationTimeout?: number; touchCleanup?: () => void; lastTargetChangeTime?: number; returnAnchorItems?: HTMLElement[]; /** Last pointer position, so the clone can be repositioned after it is re-hosted mid-drag. */ lastCoordinates?: { x: number; y: number; }; } type DragDropSlotted = DragDropCore & DropzoneAutoSize & { minAssociations: number; maxAssociations: number; disableAnimations: boolean; enableFlipAnimations: boolean; validate(): boolean; reportValidity(): boolean; reset(): void; saveResponse(value?: string | string[]): void; shouldTreatBlockedMaxAsInvalid(): boolean; shouldReturnToInventoryOnInventoryDrop(): boolean; /** Placement, published on `dragDropContext`. An interaction that renders its own drop targets * reads `nodesByTarget` straight out of it. */ _dragDrop: Readonly; /** The chips a target holds — from the placement map, or the DOM, depending on the target. */ chipsIn(droppable: HTMLElement): HTMLElement[]; isDeclarativeTarget(el: HTMLElement | null | undefined): boolean; /** Republish placement on `dragDropContext` and re-render the targets that read it. */ syncDragDropState(): void; /** * How many associations exist, for `min-associations` / `max-associations`. * * The default counts placed chips, which is one-to-one with associations only when a chip *is* * an association — a gap holding one chip, a match target holding one chip. Associate pairs two * chips per association and overrides this. Declared here so it can be. */ totalAssociationsFromState(): number; }; /** * Shared utilities for sortable drag-and-drop behavior. * Used by both DragDropSortableMixin (standalone) and SlotSortableEnhancerMixin (composition). */ /** * Configuration for drop placeholder styling */ interface PlaceholderConfig { borderStyle?: string; borderColor?: string; background?: string; transitionDuration?: number; } type DragDropSlottedSortable = DragDropSlotted & { allowReorder: boolean; readonly isDraggingFromSlot: boolean; sortablePlaceholderConfig: PlaceholderConfig; }; declare const QtiAssociateInteraction_base: (abstract new (...args: any[]) => DragDropSlottedSortable) & (abstract new (...args: any[]) => DragDropSlotted) & typeof Interaction; /** * Drag-and-drop associate interaction: candidates pair associable choices. * * @slot prompt - The prompt shown above the choices. * @slot qti-simple-associable-choice - The associable choice sources. * * @csspart drags - Wrapper around the associable choice slot. * @csspart drops - The container holding all drop targets. * @csspart drop-row - Container for one left/right drop pair. * @csspart drop - Each individual drop target. * @csspart message - Live validation message region (role="alert"). */ declare class QtiAssociateInteraction extends QtiAssociateInteraction_base { #private; static styles: CSSResultGroup; protected _childrenMap: Element[]; protected _registerChoiceHandler: (event: CustomEvent) => void; constructor(); protected _registerChoice(event: CustomEvent): void; protected getResponse(): string[]; /** * How many associations the candidate has made. * * The mixin counts placed chips, which is right for gap-match — one chip in one gap is one * association — and wrong here. An association is a *pair*, so three associations are six chips, * and `max-associations="3"` compared 6 against 3 and declared the item invalid the moment it was * correctly filled in. * * A half-filled row is not an association yet, and `getResponse()` already knows that: it emits a * pair only when both sides hold a chip. Counting its entries counts associations by definition, * and keeps the number validation sees equal to the number the response reports. */ totalAssociationsFromState(): number; getValue(val: string[]): string[]; render(): lit.TemplateResult<1>; protected updated(changedProperties: Map): void; disconnectedCallback(): void; } declare global { interface HTMLElementTagNameMap { 'qti-associate-interaction': QtiAssociateInteraction; } } type Orientation = 'horizontal' | 'vertical' | undefined; declare const QtiChoiceInteraction_base: (abstract new (...args: any[]) => {}) & (abstract new (...args: any[]) => ChoicesInterface) & typeof Interaction; /** * A single-response or multiple-response choice interaction. * * @customElement qti-choice-interaction * * @attr {string} response-identifier - Required. Identifier of the bound response variable. * @attr {number} [max-choices=1] - Maximum selectable choices. `1` is a single-response * item; `0` means unlimited and makes it a multiple-response item. * @attr {number} [min-choices=0] - Minimum selectable choices for a valid response. * @attr {'horizontal'|'vertical'} [orientation=vertical] - Deprecated by QTI in favour of * the `qti-orientation-*` class vocabulary. * @attr {boolean} [shuffle=false] - Requests choice shuffling. Applied by the transform * pipeline (`qti-transformers`), not by this element. * @attr {'qti-orientation-horizontal'|'qti-orientation-vertical'|'qti-choices-stacking-1'|'qti-choices-stacking-2'|'qti-choices-stacking-3'|'qti-choices-stacking-4'|'qti-choices-stacking-5'|'qti-labels-none'|'qti-labels-decimal'|'qti-labels-lower-alpha'|'qti-labels-upper-alpha'|'qti-labels-suffix-none'|'qti-labels-suffix-period'|'qti-labels-suffix-parenthesis'|'qti-input-control-hidden'} class - QTI shared presentation vocabulary. Space-separated; takes precedence over `orientation`. * * @slot prompt - The prompt shown above the choices. * @slot - Default slot for `qti-simple-choice` elements. * * @csspart prompt - Wrapper around the prompt slot. * @csspart slot - Wrapper around the default slot containing the choices. * @csspart message - Live validation message region (role="alert"). */ declare class QtiChoiceInteraction extends QtiChoiceInteraction_base implements ChoicesInterface { #private; static styles: CSSResultGroup; /** @deprecated, use 'qti-orientation-horizontal' or 'qti-orientation-vertical' instead */ orientation: Orientation; render(): lit.TemplateResult<1>; } declare global { interface HTMLElementTagNameMap { 'qti-choice-interaction': QtiChoiceInteraction; } } /** * A multi-line text-entry interaction. * * @customElement qti-extended-text-interaction * * @attr {string} response-identifier - Required. Identifier of the bound response variable. * @attr {number} expected-length - Hint at the expected answer length in characters; sets the * textarea's `maxlength`. Also the last-resort row count, used only when neither a * `qti-height-lines-*` class nor `expected-lines` is present. * @attr {string} pattern-mask - Regular expression the value must match to be valid. * @attr {string} placeholder-text - Placeholder shown while the textarea is empty. * @attr {string} data-patternmask-message - Custom validation message shown when * `pattern-mask` fails. Part of the QTI shared interaction vocabulary. * @attr {'qti-height-lines-3'|'qti-height-lines-6'|'qti-height-lines-15'} class - QTI shared * presentation vocabulary controlling the rendered height. Outranks both `expected-lines` * and `expected-length`. * @attr {number} expected-lines - Expected number of lines of response, and the rendered row * count unless a `qti-height-lines-*` class is present. * @attr {number} [base=10] - Not implemented. Numeric base used when recording the value. * @attr {string} string-identifier - Not implemented. Identifier of a second, string-typed * response variable that also receives the raw entry. * @attr {number} max-strings - Not implemented. Maximum number of separate string responses. QTI * defines these for a multi-string response — several bound variables, one field each. This * element renders a single textarea bound to one variable, so there is nothing for the bounds * to count: reading them as a word or paragraph limit would invent a rule QTI does not state. * Implement alongside multi-string rendering, not before it. * @attr {number} min-strings - Not implemented. See `max-strings`. * @attr {'plain'|'preformatted'|'xhtml'} [format=plain] - How the response is presented. * `preformatted` renders the field monospaced; `xhtml` is accepted but downgraded to plain, * because the field is a textarea with no rich-text editor behind it. * * @slot prompt - The prompt shown above the textarea. * * @csspart textarea - The textarea input element. */ declare class QtiExtendedTextInteraction extends Interaction { #private; static styles: CSSResultGroup; protected _rows: number; /** expected length is mapped to the property maxlength on the textarea */ expectedLength: number; /** QTI's own hint at the response height. Outranked by `qti-height-lines-*`; see `#resolveRows`. */ expectedLines: number; /** * How the response text is presented. Reflected so themes can key off * `:host([format='preformatted'])`. * * `xhtml` is accepted and recorded but renders as `plain`: the field is a `