@pie-players/pie-section-player 0.3.63 → 0.3.64
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.
- package/README.md +29 -0
- package/dist/components/shared/section-item-media.d.ts +88 -0
- package/dist/components/shared/shell-registration.d.ts +55 -0
- package/dist/pie-section-player.js +14095 -12636
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -232,6 +232,35 @@ When both max-width attributes are set, the with-passage cap resolves to the gre
|
|
|
232
232
|
of the two configured values (after clamp), so with-passage mode never ends up narrower
|
|
233
233
|
than no-passage mode.
|
|
234
234
|
|
|
235
|
+
### Item card media region
|
|
236
|
+
|
|
237
|
+
An item card can carry a `data-region="media"` region beside its content region,
|
|
238
|
+
holding a resolved accessibility-catalog media card — today a sign-language
|
|
239
|
+
(ASL) video, gated on the `signLanguage` PNP support. The region only appears
|
|
240
|
+
when the item carries a matching catalog card *and* policy grants the feature, so
|
|
241
|
+
there is no dead affordance on items without signing.
|
|
242
|
+
|
|
243
|
+
It sits to the right of the question content and is resizable via a
|
|
244
|
+
keyboard-accessible divider (`role="separator"`; arrow keys, `Home`/`End`,
|
|
245
|
+
`Escape` to cancel a drag). Below a card width of 560px the region stacks under
|
|
246
|
+
the content and the divider is withdrawn. Placement is fixed in this iteration:
|
|
247
|
+
there is no orientation toggle and no free repositioning.
|
|
248
|
+
|
|
249
|
+
Signing legibility needs height for hands and face, so the video is sized by an
|
|
250
|
+
aspect-ratio target with a height floor rather than by width alone. Hosts can
|
|
251
|
+
retune it:
|
|
252
|
+
|
|
253
|
+
- `--pie-section-player-item-media-aspect-ratio` (default `3 / 4`)
|
|
254
|
+
- `--pie-section-player-item-media-min-height` (default `220px`)
|
|
255
|
+
- `--pie-section-player-item-media-max-height` (default `60vh`)
|
|
256
|
+
|
|
257
|
+
```css
|
|
258
|
+
pie-section-player-item-card {
|
|
259
|
+
--pie-section-player-item-media-aspect-ratio: 4 / 5;
|
|
260
|
+
--pie-section-player-item-media-min-height: 260px;
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
235
264
|
### API direction: CE defaults first, JS customization for advanced cases
|
|
236
265
|
|
|
237
266
|
The intended usage model is:
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { AccessibilityCatalogResolverApi, CatalogOwnerContext, SignLanguageMedia } from '@pie-players/pie-assessment-toolkit';
|
|
2
|
+
import { ItemEntity } from '@pie-players/pie-players-shared/types';
|
|
3
|
+
/**
|
|
4
|
+
* QTI 3.0 / AfA support id gating signed alternates. Excluded from the computed
|
|
5
|
+
* default PNP profile by `ACCOMMODATION_ONLY_SUPPORT_IDS`, so it is only ever
|
|
6
|
+
* granted deliberately.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SIGN_LANGUAGE_FEATURE_ID = "signLanguage";
|
|
9
|
+
/** A catalog on this item that carries at least one sign-language card. */
|
|
10
|
+
export interface SignLanguageCatalogRef {
|
|
11
|
+
catalogId: string;
|
|
12
|
+
/** Set when the catalog is owned by one model rather than the item root. */
|
|
13
|
+
modelId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedSignLanguageAlternate extends SignLanguageMedia {
|
|
16
|
+
catalogId: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Collect the catalog identifiers on an item that carry signing cards.
|
|
20
|
+
*
|
|
21
|
+
* The region resolves through the catalog resolver, but the resolver is keyed by
|
|
22
|
+
* identifier within an owner scope — something has to say *which* identifiers
|
|
23
|
+
* this item put in play, and whether each belongs to the item or to one model.
|
|
24
|
+
* That is the same walk the runtime does when it registers an item's catalogs,
|
|
25
|
+
* so it is borrowed rather than repeated: a ref can only name a scope
|
|
26
|
+
* registration actually files under. Only the "does this catalog carry signing"
|
|
27
|
+
* filter is ours.
|
|
28
|
+
*
|
|
29
|
+
* The owner identity here is a placeholder: the assessment and section ids are
|
|
30
|
+
* not known at extraction time and are supplied by the caller at lookup time
|
|
31
|
+
* (see `resolveSignLanguageAlternate`). Nothing but `modelId` is read from the
|
|
32
|
+
* walk's contexts.
|
|
33
|
+
*/
|
|
34
|
+
export declare function collectSignLanguageCatalogRefs(item: ItemEntity | null | undefined): SignLanguageCatalogRef[];
|
|
35
|
+
export interface SignLanguageLookupArgs {
|
|
36
|
+
resolver: AccessibilityCatalogResolverApi;
|
|
37
|
+
refs: SignLanguageCatalogRef[];
|
|
38
|
+
/** Owner context for the item, without `modelId` — added per ref. */
|
|
39
|
+
ownerContext: CatalogOwnerContext;
|
|
40
|
+
/** Sign language the learner is entitled to, ISO 639-3. */
|
|
41
|
+
requestedSignLang?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the signed alternate to show for an item, or `null`.
|
|
45
|
+
*
|
|
46
|
+
* Priority, language fallback and owner scoping are the resolver's job and are
|
|
47
|
+
* not re-implemented here. What *is* enforced here is the one deliberate
|
|
48
|
+
* departure from the resolver's default behaviour: **no cross-sign-language
|
|
49
|
+
* substitution.** The resolver's last fallback rung matches any card of the
|
|
50
|
+
* requested type regardless of language, which for spoken content is helpful
|
|
51
|
+
* and for signing is not — handing an ASL learner a BSL recording is worse than
|
|
52
|
+
* handing them nothing. So a card the resolver returns by that rung is accepted
|
|
53
|
+
* only if its language actually matches, or if the card asserts no language at
|
|
54
|
+
* all.
|
|
55
|
+
*
|
|
56
|
+
* MVP takes the first resolvable card: sample content is one recording per item.
|
|
57
|
+
* The refs stay per-node capable for when choice-level docking lands.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveSignLanguageAlternate(args: SignLanguageLookupArgs): ResolvedSignLanguageAlternate | null;
|
|
60
|
+
/**
|
|
61
|
+
* Default and bounds for the media region's share of the card width.
|
|
62
|
+
*
|
|
63
|
+
* Signing needs height for hands and face, so the region is sized by an
|
|
64
|
+
* aspect-ratio target with a minimum height rather than by width alone — a flat
|
|
65
|
+
* viewport percentage either wastes space on a short clip or crushes it on a
|
|
66
|
+
* narrow device. The percentage below only decides how the card's width is
|
|
67
|
+
* split; legibility is defended by the region's CSS.
|
|
68
|
+
*/
|
|
69
|
+
export declare const MEDIA_REGION_DEFAULT_PERCENT = 34;
|
|
70
|
+
export declare const MEDIA_REGION_MIN_PERCENT = 20;
|
|
71
|
+
export declare const MEDIA_REGION_MAX_PERCENT = 55;
|
|
72
|
+
/** Below this card width the split is dropped and the region stacks. */
|
|
73
|
+
export declare const MEDIA_REGION_STACK_BREAKPOINT_PX = 560;
|
|
74
|
+
export declare function clampMediaRegionPercent(value: number, min?: number, max?: number): number;
|
|
75
|
+
/**
|
|
76
|
+
* Convert a pointer drag into a media-region width percentage.
|
|
77
|
+
*
|
|
78
|
+
* Container-relative, unlike the passage/items divider's fixed 0.1%-per-pixel
|
|
79
|
+
* factor: the same drag has to mean the same thing in a wide card and a narrow
|
|
80
|
+
* one. The region is on the right, so dragging left grows it.
|
|
81
|
+
*/
|
|
82
|
+
export declare function mediaRegionPercentFromDrag(args: {
|
|
83
|
+
startPercent: number;
|
|
84
|
+
deltaX: number;
|
|
85
|
+
containerWidthPx: number;
|
|
86
|
+
min?: number;
|
|
87
|
+
max?: number;
|
|
88
|
+
}): number;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registration dispatch for the item and passage shells.
|
|
3
|
+
*
|
|
4
|
+
* Registration is a statement of fact to the runtime, and the runtime takes it
|
|
5
|
+
* literally: a `pie-register` makes the toolkit unregister and re-register the
|
|
6
|
+
* content's accessibility catalogs, re-run `sectionEngine.register`, and
|
|
7
|
+
* re-notify the section controller. Both shells used to dispatch it from the
|
|
8
|
+
* same effect that attached their listeners, with `pie-unregister` in that
|
|
9
|
+
* effect's cleanup — so every re-run announced a teardown and a rebuild of state
|
|
10
|
+
* that had not moved, and between the two the content had no catalogs at all.
|
|
11
|
+
*
|
|
12
|
+
* Those effects re-run on prop changes, and a shell's props are re-applied
|
|
13
|
+
* whenever its parent card's template updates. That made the dispatch the far
|
|
14
|
+
* half of a cycle: a reader that re-renders in response to a catalog change
|
|
15
|
+
* re-applies a shell's props, which re-registers, which changes catalogs again.
|
|
16
|
+
* It shipped once at roughly a thousand rounds per item, ending in Svelte
|
|
17
|
+
* abandoning the update at its depth limit with the DOM half-applied — and every
|
|
18
|
+
* assertion about the rendered output passed while it was happening, because the
|
|
19
|
+
* elements were all present.
|
|
20
|
+
*
|
|
21
|
+
* So the dispatcher keeps the last identity it announced and says nothing when
|
|
22
|
+
* the next one matches.
|
|
23
|
+
*/
|
|
24
|
+
export type ShellRegistrationIdentity = {
|
|
25
|
+
kind: "item" | "passage";
|
|
26
|
+
host: HTMLElement;
|
|
27
|
+
itemId: string;
|
|
28
|
+
canonicalItemId: string;
|
|
29
|
+
contentKind: string;
|
|
30
|
+
item: unknown;
|
|
31
|
+
};
|
|
32
|
+
export type ShellRegistrationDispatcher = {
|
|
33
|
+
/**
|
|
34
|
+
* Announce `identity` if it says anything new, and retire the live
|
|
35
|
+
* registration when there is nothing left to describe — a shell without a host
|
|
36
|
+
* or an id no longer stands for content, and leaving its registration behind
|
|
37
|
+
* would strand it in the runtime.
|
|
38
|
+
*
|
|
39
|
+
* Never dispatches `pie-unregister` before a re-register: both registration
|
|
40
|
+
* paths in the toolkit are keyed by element and replace what is there, so the
|
|
41
|
+
* unregister only ever created the gap.
|
|
42
|
+
*/
|
|
43
|
+
sync: (identity: ShellRegistrationIdentity | null) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Retire the live registration, replaying the identity it was made under
|
|
46
|
+
* rather than whatever the props say now — by the time a registration is
|
|
47
|
+
* retired the props may already describe its replacement, and unregistering
|
|
48
|
+
* under the new identity would leave the old one live.
|
|
49
|
+
*
|
|
50
|
+
* Belongs in a teardown that runs on teardown only. Attaching it to an effect
|
|
51
|
+
* that re-runs on prop changes is what made the churn.
|
|
52
|
+
*/
|
|
53
|
+
retire: () => void;
|
|
54
|
+
};
|
|
55
|
+
export declare function createShellRegistrationDispatcher(): ShellRegistrationDispatcher;
|